Hibernate is deleting nested relationships when editing parent - java

i have this four classes:
#Entity
#Table(name = "products")
public class Product extends RegionDiscriminator {
#Column(nullable = false, name = "product_id")
private Long productId;
#Column(nullable = false, name = "name")
private String name;
#Column(nullable = false, name = "category_id")
private Long categoryId;
#Column(nullable = false, name = "unit")
private String unit;
#OneToMany(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true)
private List<SkuGroup> skuGroups = new ArrayList<>();
#OneToMany(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Tax> taxes = new ArrayList<>();
#Column(nullable = false, name = "active")
private boolean active;
}
#Entity
#Table(name = "sku_groups")
public class SkuGroup extends RegionDiscriminator {
#Column(nullable = false, name = "name")
private String name;
#ManyToOne
#JoinColumn(name = "product_id")
private Product product;
#OneToMany(mappedBy = "skuGroup", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Sku> skus = new ArrayList<>();
#OneToMany(mappedBy = "skuGroup", cascade = CascadeType.ALL, orphanRemoval = true)
#OrderBy("period DESC")
private List<Cluster> clusters = new ArrayList<>();
}
#Entity
#Table(name = "skus")
public class Sku extends GrowthEntity {
#Column(nullable = false, name = "sku_id")
private Long skuId;
#Column(nullable = false, name = "name")
private String name;
#Column(nullable = false, name = "min_weight_unit")
private BigDecimal minWeightUnit;
#Column(name = "upc")
private String upc;
#OneToMany(mappedBy = "sku", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Tier> tiers = new ArrayList<>();
#OneToMany(mappedBy = "sku", cascade = CascadeType.ALL, orphanRemoval = true)
#Size(max = 10)
#OrderBy("creationTimestamp DESC")
private List<Price> prices = new ArrayList<>();
#ManyToOne
#JoinColumn(name = "sku_group_id")
private SkuGroup skuGroup;
#OneToMany(mappedBy = "sku", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Discount> discounts = new ArrayList<>();
#Column(nullable = false, name = "active")
private boolean active;
#OneToOne(mappedBy = "sku")
private SkuCost cost;
#OneToOne(mappedBy = "sku")
private SkuBenchmark benchmark;
#OneToMany(mappedBy = "sku", cascade = CascadeType.REMOVE, orphanRemoval = true)
private List<PricingProcessDetail> pricingProcessDetails;
}
#Entity
#Table(name = "prices")
public class Price extends GrowthEntity {
#Column(nullable = false, name = "retail_price")
private BigDecimal retailPrice;
#Column(nullable = false, name = "tax_price")
private BigDecimal taxPrice;
#Column(nullable = false, name = "pricing_price")
private BigDecimal pricingPrice;
#Column(nullable = false, name = "sale_price")
private BigDecimal salePrice;
#ManyToOne
#JoinColumn(name = "sku_id")
private Sku sku;
#ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "price_discount",
joinColumns = #JoinColumn(name = "price_id"),
inverseJoinColumns = #JoinColumn(name = "discount_id")
)
private List<Discount> discounts = new ArrayList<>();
#ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "price_tax",
joinColumns = #JoinColumn(name = "price_id"),
inverseJoinColumns = #JoinColumn(name = "tax_id")
)
private List<Tax> taxes = new ArrayList<>();
#OneToMany(mappedBy = "price", cascade = CascadeType.ALL, orphanRemoval = true)
private List<PriceTier> tiers = new ArrayList<>();
}
This are the methods executed by my service:
#Transactional
public Product updateProduct(final ProductDTO productDTO) {
final Product product = productDao.findByProductId(productDTO.getProductId())
.orElseThrow(() -> new GrowthNotFoundException(format("Product with id %s not found",
productDTO.getProductId())));
product.update(productDTO);
return save(product);
}
public Product update(final ProductDTO productDTO) {
categoryId = productDTO.getCategory() != null ? productDTO.getCategory().getId() : null;
name = productDTO.getName();
unit = productDTO.getUnit();
active = productDTO.isActive();
this.updateTaxes(productDTO);
this.updateSkuGroups(productDTO);
return this;
}
private void updateTaxes(final ProductDTO productDTO) {
List<Tax> newTaxes = buildTaxes(productDTO);
List<Tax> currentTaxes = new ArrayList<>(taxes);
if (!currentTaxes.equals(newTaxes)) {
taxes.clear();
taxes.addAll(newTaxes);
}
}
private void updateSkuGroups(final ProductDTO productDTO) {
Map<Long, Sku> skusMap = allSkus().stream()
.collect(Collectors.toMap(Sku::getSkuId, Function.identity()));
Map<String, List<SkuDTO>> newSkuGroups = ProductDTO.groupSkus(productDTO.getSkus());
Map<String, SkuGroup> currentSkuGroups = skuGroups.stream().collect(
Collectors.toMap(SkuGroup::getName, skuGroup -> skuGroup));
newSkuGroups.forEach((group, skus) -> {
if (currentSkuGroups.containsKey(group)) {
SkuGroup skuGroup = currentSkuGroups.get(group);
skuGroup.update(
skus.stream().map(skuDTO -> createOrUpdateSku(skusMap, skuDTO, skuGroup))
.collect(Collectors.toList()));
} else {
SkuGroup skuGroup = SkuGroup.create(group, this);
skuGroup.update(
skus.stream().map(skuDTO -> createOrUpdateSku(skusMap, skuDTO, skuGroup))
.collect(Collectors.toList()));
skuGroups.add(skuGroup);
}
});
currentSkuGroups.forEach((group, skuGroup) -> {
if (!newSkuGroups.containsKey(group)) {
skuGroups.remove(skuGroup);
}
});
}
private Sku createOrUpdateSku(Map<Long, Sku> skusMap, SkuDTO skuDTO, SkuGroup skuGroup) {
if (skusMap.containsKey(skuDTO.getId())) {
return skusMap.get(skuDTO.getId()).update(skuDTO, skuGroup);
} else {
return Sku.create(skuDTO, skuGroup);
}
}
public static Sku create(final SkuDTO skuDTO, final SkuGroup skuGroup) {
Sku sku = Sku.builder()
.withSkuId(skuDTO.getId())
.withMinWeightUnit(skuDTO.getMinWeightUnit())
.withUpc(skuDTO.getUpc())
.withName(skuDTO.getName())
.withSkuGroup(skuGroup)
.isActive(skuDTO.isActive())
.build();
sku.createTiers(skuDTO.getTiers(), skuDTO.getPrice());
sku.createPrice(skuDTO);
return sku;
}
public Sku update(final SkuDTO skuDTO, final SkuGroup theSkuGroup) {
minWeightUnit = skuDTO.getMinWeightUnit();
upc = skuDTO.getUpc();
name = skuDTO.getName();
skuGroup = theSkuGroup;
active = skuDTO.isActive();
this.updateTiers(skuDTO.getTiers(), skuDTO.getPrice());
this.updatePriceFromRetailPrice(skuDTO.getPrice());
return this;
}
This is an example of ProductDTO:
{
"productId": 16825,
"name": "Producto prueba pricing 30",
"category": {
"id": 100515,
"name": "Categoria prueba 13"
},
"iva": null,
"unit": "UNID",
"activeStartDate": "2022-11-22 12:18:08",
"activeEndDate": "2022-11-22 12:18:08",
"skus": [
{
"id": 22937,
"upc": "BOG-FRU1-CAT4252-16825:17883:17882:22937",
"name": "Producto prueba pricing 30 (Caja x 12) - 30 unidades 160g",
"owner": {
"id": 1,
"name": "Frubana"
},
"price": 999,
"stepUnit": 1,
"minWeightUnit": 1,
"conversion": {
"conversionTypeX": "UNID",
"conversionValueX": null,
"conversionTypeY": null,
"conversionValueY": null
},
"slot": false,
"cooled": false,
"activeStartDate": "2022-10-19 12:17:04",
"activeEndDate": "2022-10-19 12:17:04",
"tiers": null,
"group": "(Caja x 24)",
"active": true
},
{
"id": 22938,
"upc": "BOG-FRU1-CAT4252-16825:17885:17884:22938",
"name": "Producto prueba pricing 30 (Caja x 24) - 30 unidades de 150g",
"owner": {
"id": 1,
"name": "Frubana"
},
"price": 888,
"stepUnit": 1,
"minWeightUnit": 1,
"conversion": {
"conversionTypeX": "UNID",
"conversionValueX": null,
"conversionTypeY": null,
"conversionValueY": null
},
"slot": false,
"cooled": false,
"activeStartDate": "2022-10-19 12:17:04",
"activeEndDate": "2022-10-19 12:17:04",
"tiers": null,
"group": "(Caja x 12)",
"active": true
},
{
"id": 22939,
"upc": "BOG-FRU1-CAT4252-16825:17887:17886:22939",
"name": "Producto prueba pricing 30 (Caja x 24) - 30 unidades de 160g",
"owner": {
"id": 1,
"name": "Frubana"
},
"price": 777,
"stepUnit": 1,
"minWeightUnit": 2,
"conversion": {
"conversionTypeX": "UNID",
"conversionValueX": null,
"conversionTypeY": null,
"conversionValueY": null
},
"slot": false,
"cooled": false,
"activeStartDate": "2022-10-19 12:17:04",
"activeEndDate": "2022-10-19 12:17:04",
"tiers": null,
"group": "(Caja x 24)",
"active": true
}
],
"image": "/cmsstatic/products/sku_sin_imagen.png",
"active": true
}
My goal is to assign the SKUs to their respective SKU Groups based on the name, eliminating the groups that are left empty and creating the new ones that are necessary.
The code works for the first part, but fails when the reassignment needs the creation of a new group.
ERROR: null value in column "sku_id" violates not-null constraint

Related

How to control fetched sub entities spring boot

I have following relationship in my spring boot:
public class Clazz {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
#Column(name = "id")
private Integer id;
#Column(name = "lesson_id")
#NotNull(message = "{lesson.is_required}")
private Integer lessonId;
#Column(name = "level_id")
#NotNull(message = "{level.is_required}")
private Integer levelId;
#Column(name = "name")
#NotEmpty(message = "{name.is_required}")
private String name;
#OneToOne
#JoinColumn(name = "lesson_id", referencedColumnName = "id", insertable = false, updatable = false)
private Lesson lesson;
#OneToOne
#JoinColumn(name = "level_id", referencedColumnName = "id", insertable = false, updatable = false)
private Level level;
}
Now my Lesson entity has Level entity:
public class Lesson {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "name")
#NotEmpty(message = "{name.is_required}")
private String name;
#Column(name = "description")
private String description = "";
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "lesson_id")
Collection<Level> levels = new ArrayList<>();
}
And finally my Level class:
public class Level {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
#Column(name = "id", insertable = false, updatable = false)
private Integer id;
#Column(name = "lesson_id")
private Integer lessonId;
#Column(name = "name")
private String name;
#Column(name = "step")
private Integer step;
#Column(name = "description")
private String description="";
}
With this relationship defined, when I fetch data, getting some extra info in my results:
[
{
"id": 2,
"lessonId": 1,
"levelId": 1,
"name": "English Class March",
"branch": {
"id": 1,
"name": "3 mikr branch",
"email": "",
"phone": "",
"address": "3 mikr, foo, bar"
},
"lesson": {
"id": 1,
"name": "math",
"description": "math lesson",
"levels": [
{
"id": 1,
"lessonId": 1,
"name": "First level",
"step": 1,
"description": "1st level descr"
}
]
},
"level": {
"id": 1,
"lessonId": 1,
"name": "First level",
"step": 1,
"description": "1st level descr"
}
}
]
How can I tell JPA not to fetch sub-entities, ideally I do not need levels in lesson field of result.
Use #JsonIgnore. It can be used at setter,getter or field.
Please refer the code below.
public class Lesson {
#Id #Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "name")
#NotEmpty(message = "{name.is_required}")
private String name;
#Column(name = "description")
private String description = "";
#JsonIgnore // Add this to ignore the property in json output
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "lesson_id")
Collection<Level> levels = new ArrayList<>();
}

Violation of PRIMARY KEY With User Registration in Authorization Server

A simple application is created by using oauth2 password flow. The Authorization Server is working as the identity provider and when a new user is registered then the jwt token should be received as a response.
Relationships between entities
User (M) ------------------ Role (M)
Role (M) ------------------ Permission (M)
when I signup with a new user the following error has thrown
Caused by: java.sql.SQLSyntaxErrorException: ORA-02275: such a referential constraint already exists in the table
How can I resolve the primary key violation with JPA? If I use Native Hibernate API then we can use session.merge() but in JPA there is no option like that.
Request
{
"userName": "Nafaz Benzema",
"password": "stackoverflow",
"email": "benz#gmail.com",
"active": "y",
"accNonExpired": "y",
"credentialNonExpired": "y",
"accNonLocked": "y",
"roles" : [
{
"id": 101,
"name": "ROLE_USER",
"permissions": [
{
"id": 10,
"name": "CAN_CREATE"
},
{
"id": 40,
"name": "CAN_READ"
}
]
},
{
"id": 102,
"name": "ROLE_ADMIN",
"permissions": [
{
"id": 10,
"name": "CAN_CREATE"
},
{
"id": 40,
"name": "CAN_READ"
},
{
"id": 20,
"name": "CAN_UPDATE"
},
{
"id": 40,
"name": "CAN_DELETE"
}
]
}
]
}
Entity classes
User class
#Entity
#Table(name = "USER90",schema = Schema.TESTDB,uniqueConstraints = {
#UniqueConstraint(name = "userName",columnNames = "USER_NAME"),
#UniqueConstraint(name = "email",columnNames = "EMAIL")
})
#Getter
#Setter
public class User {
#Id
#SequenceGenerator(name = "USER_ID_GEN",sequenceName = Schema.TESTDB+".USER_ID_SEQ",initialValue = 1003,allocationSize = 1)
#GeneratedValue(generator = "USER_ID_GEN",strategy = GenerationType.SEQUENCE)
#Column(name = "USER_ID")
private int userId;
#Column(name = "USER_NAME",nullable = false)
private String userName;
#Column(name = "PASSWORD",nullable = false)
private String password;
#Column(name = "EMAIL",nullable = false)
private String email;
#Column(name = "ACTIVE",nullable = false)
private String active;
#Column(name = "ACC_NON_EXPIRED")
private String accNonExpired;
#Column(name = "CREDENTIAL_NON_EXPIRED")
private String credentialNonExpired;
#Column(name = "ACC_NON_LOCKED")
private String accNonLocked;
#ManyToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinTable(name = "USER_ROLE",joinColumns = {#JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID")},
inverseJoinColumns = {#JoinColumn(name = "ROLE_ID",referencedColumnName = "ID")})
private Set<Role> roles;
}
Role class
#Entity
#Table(name = "ROLE",schema = Schema.TESTDB,uniqueConstraints = {
#UniqueConstraint(name = "name",columnNames = "NAME")
})
#Getter
#Setter
public class Role {
#Id
#Column(name = "ID")
private long id;
#Enumerated(EnumType.STRING)
#Column(name = "NAME")
private ERole name;
// bi-directional
/* #ManyToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private Set<User> users;*/
// uni-directional
#ManyToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinTable(name = "PERMISSION_ROLE",joinColumns = {#JoinColumn(name = "ROLE_ID",referencedColumnName = "ID")},
inverseJoinColumns = {#JoinColumn(name = "PERMISSION_ID",referencedColumnName = "ID")})
private Set<Permission> permissions;
}
Permission class
#Entity
#Table(name = "PERMISSION",schema = Schema.TESTDB,uniqueConstraints = {
#UniqueConstraint(name = "name",columnNames = "NAME")
})
#Getter
#Setter
public class Permission {
#Id
#Column(name = "ID")
private long id;
#Enumerated(EnumType.STRING)
#Column(name = "NAME")
private EPermission name;
}
Service class
public Response userRegistration(SignupRequest signup) {
if(!Objects.isNull(userDAO.findUserByEmail(signup.getEmail()).orElse(null)))
throw new UserIsExistedException(String.format("User is existed with %s",signup.getEmail()));
try {
User user=new User();
String password="{bcrypt}";
password = password.concat(BCrypt.hashpw(signup.getPassword(),BCrypt.gensalt(12)));
user.setUserName(signup.getUserName());
user.setEmail(signup.getEmail());
user.setPassword(password);
user.setActive("y");
user.setAccNonExpired("y");
user.setCredentialNonExpired("y");
user.setAccNonLocked("y");
user.setRoles(signup.getRoles());
Set<Role> roles = new HashSet<>();
user.getRoles().forEach(role->{
roles.add(role);
Set<Permission> permissions=new HashSet<>();
role.getPermissions().forEach(perm->{
permissions.add(perm);
});
role.setPermissions(permissions);
});
user.setRoles(roles);
userDAO.save(user);
LOGGER.info("user is saved and response is returned successfully");
return new Response(user.getEmail(), authenticationProvider.obtainToken(user.getEmail(), user.getPassword()).toString());
}catch (NumberFormatException ex){
LOGGER.error("NumberFormat Exception");
throw new NumberFormatException("NumberFormat Exception");
}
catch (Exception ex){
LOGGER.error("invalid username or password");
throw new BadCredentialsException("invalid username or password",ex);
}
}
Note - If you need more resource here GitHub link
github_link
config file
spring:
datasource:
url: jdbc:oracle:thin:#localhost:1521:orcl
username: TESTDB
password: 14292
driver-class-name: oracle.jdbc.OracleDriver
jpa:
database-platform: org.hibernate.dialect.Oracle10gDialect
hibernate:
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
ddl-auto: update
I have solved it by changing the CascadeType.
#ManyToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinTable(name = "USER_ROLE",joinColumns = {#JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID")},
inverseJoinColumns = {#JoinColumn(name = "ROLE_ID",referencedColumnName = "ID")})
private Set<Role> roles;
when I use CascadeType.ALL then it gives priority to PERSIST (save) which throws Primary Key Violation in my case. When roles assigned to the user and if it is available in the role table then I need to Merge it rather than Save. So I have changed the CascadeType from ALL to MERGE.
#ManyToMany(cascade = CascadeType.MERGE,fetch = FetchType.EAGER)
#JoinTable(name = "USER_ROLE",joinColumns = {#JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID")},
inverseJoinColumns = {#JoinColumn(name = "ROLE_ID",referencedColumnName = "ID")})
private Set<Role> roles;

Jpa join with where clause in first an second tables

I have a problem with JPA and two tables
Entity Language
public class Language {
#NotEmpty
#Id
#Column(name = "code", nullable = false, length = 5)
private String code;
#Column(nullable = false)
private Boolean active;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "language", cascade = CascadeType.MERGE, orphanRemoval = true)
private List<LanguageTranslation> languageTranslation;
#JsonPOJOBuilder(withPrefix = "")
public static class Builder {
// Lombok will add constructor, setters, build method
}
}
Entity LanguageTranslation
public class LanguageTranslation {
#EmbeddedId
private LanguageTranslationId languageTranslationId;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("languageCode")
#JoinColumn(name = "language_code", nullable = false)
#JsonIgnore
private Language language;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("languageLanguageCode")
#JoinColumn(name = "language_language_code", nullable = false)
#JsonIgnore
private Language languageLanguage;
#Column(nullable = false, length = 255)
#NotEmpty
private String name;
public LanguageTranslation(LanguageTranslationId languageTranslationId, #NotEmpty String name)
{
super();
this.languageTranslationId = languageTranslationId;
this.name = name;
}
#JsonPOJOBuilder(withPrefix = "")
public static class Builder {
// Lombok will add constructor, setters, build method
}
}
ID
#Embeddable
public class LanguageTranslationId implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "language_code")
private String languageCode;
#Column(name = "language_language_code")
private String languageLanguageCode;
#JsonPOJOBuilder(withPrefix = "")
public static class Builder {
// Lombok will add constructor, setters, build method
}
}
Query
#Query(value = "SELECT l FROM Language l " +
"JOIN LanguageTranslation lt ON l.code = lt.languageTranslationId.languageCode " +
"WHERE lt.languageTranslationId.languageLanguageCode = ?2 " +
"AND l.code = ?1 AND l.active = true")
Language findByCodeByTranslationAndActiveTrue(String id, String translation);
The problem is this WHERE lt.languageTranslationId.languageLanguageCode = ?2 it's useless. JPA doesn't execute this clause
Whith id = en_EN and translation = en_EN I have this
{
"code": "en_EN",
"languageTranslation": [
{
"languageTranslationId": {
"languageCode": "en_EN",
"languageLanguageCode": "en_EN"
},
"name": "English"
},
{
"languageTranslationId": {
"languageCode": "en_EN",
"languageLanguageCode": "es_ES"
},
"name": "Ingles"
}
]
}
And normally I would have to have this
{
"code": "en_EN",
"languageTranslation": [
{
"languageTranslationId": {
"languageCode": "en_EN",
"languageLanguageCode": "en_EN"
},
"name": "English"
}
]
}

org.hibernate.QueryException: illegal attempt to dereference collection [events0_.id.typesOfEvents] with element property reference [type_id]

I am using spring boot and jpa with restful api. when i am trying to get list of event then this type of error occurs - org.hibernate.QueryException: illegal attempt to dereference collection [events0_.id.typesOfEvents] with element property reference [type_id]
I am passing List Object to constructor as args into jpa custom query but that query will not execute it will fire error.
Event is Main entity inside this entity i have taken List of typeOfEvents with using ManyToMany relation and that two table of Id is stored into third table.
1.Event.java
#Builder
#ToString
#Entity
#Table(name = "events")
public #Data
class Events implements Comparable<Events> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column
private Long id;
#ManyToOne(cascade = CascadeType.REFRESH)
#JoinColumn(name = "site_id")
private Sites sites;
#Column
private boolean multipleDays;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinTable(name = "events_types",
joinColumns = #JoinColumn(name = "e_id"),
inverseJoinColumns = #JoinColumn(name = "t_id", unique = false))
private List<TypesOfEvents> typesOfEvents = new ArrayList<TypesOfEvents>(Arrays.asList());
#ManyToMany(cascade = CascadeType.DETACH)
#JoinTable(name = "events_format",
joinColumns = #JoinColumn(name = "e_id"),
inverseJoinColumns = #JoinColumn(name = "fs_id", unique = false))
private List<FormatStructure> formatStructure = new ArrayList<>();
#Column
private String startDate;
#Column
private String startTime;
#Column
private String endDate;
#Column
private String endTime;
#Column
private String eventName;
#Column
private String subTitle;
#Column
private String description;
#Column
private String location;
#ManyToOne(cascade = CascadeType.REFRESH)
#JoinColumn(name = "city_id")
private City city;
#ManyToOne(cascade = CascadeType.REFRESH)
#JoinColumn(name = "venue_one_off_option_id")
private VenueOneOffOptions venueOneOffOptions;
#Column
private double fee;
#Column
private boolean paid;
#Column
private String eventImg;
#ManyToOne(cascade = CascadeType.REFRESH)
#JoinColumn(name = "event_passport_option_id")
private EventPassportOptions eventPassportOptions;
#Column
private int passportTickets;
#Column
private int remainingTickets;
#Column
private boolean passport;
#Column
private int passportDiscountPercent;
#Column
private String promoCode;
#Column
private String siteUrl;
#Column
private String ticketPurchaseSite;
#Column
private String videoUrl;
#ManyToOne(cascade = CascadeType.REFRESH)
#JoinColumn(name = "primary_contact_member_id")
private Members primary_contact_member_id;
#ManyToOne(cascade = CascadeType.REFRESH)
#JoinColumn(name = "created_member_id")
private Members created_member_id;
#ManyToOne(cascade = CascadeType.REFRESH)
#JoinColumn(name = "group_id")
private Groups groups;
#Column
private String expectedAttendance;
#Column
private boolean myFavourite;
#ManyToOne(cascade = CascadeType.REFRESH)
#JoinColumn(name = "eventStatusId")
private EventStatus eventStatus;
#Column
private boolean purchasedPromotionalEmails;
#Column
private boolean featured;
#ManyToMany(cascade = CascadeType.DETACH)
#JoinTable(name = "events_audience_types",
joinColumns = #JoinColumn(name = "e_id"),
inverseJoinColumns = #JoinColumn(name = "at_id", unique = false))
private List<AudienceTypes> audienceTypes = new ArrayList<>();
#ManyToMany(cascade = CascadeType.DETACH)
#JoinTable(name = "events_food_options",
joinColumns = #JoinColumn(name = "e_id"),
inverseJoinColumns = #JoinColumn(name = "fo_id", unique = false))
private List<FoodOptions> foodOptions = new ArrayList<>();
public Events() {
}
#Override
public int compareTo(Events o) {
return (int) (this.id - o.id);
}
}
2.EventDTO.java
#ToString
#AllArgsConstructor
#NoArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonInclude(JsonInclude.Include.CUSTOM)
public #Data
class TestDTO implements Comparable<TestDTO>{
private Long id;
private boolean multipleDays;
private List<TypesOfEvents> eventType = new ArrayList<>(Arrays.asList());
private String startDate;
private String startTime;
private String endDate;
private String endTime;
private String eventName;
private String subTitle;
private String location;
private String city;
private double fee;
private boolean paid;
private String eventImg;
private Long passportOption;
private int passportTickets;
private int remainingTickets;
private boolean passport;
private String promoCode;
private String expectedAttendance;
private boolean myFavourite;
private String eventStatus;
private boolean featured;
public TestDTO(Long id, boolean multipleDays, List<TypesOfEvents> eventType, String startDate, String startTime, String endDate, String endTime, String eventName, String subTitle, String location, String city, double fee, boolean paid, String eventImg, Long passportOption, int passportTickets, int remainingTickets, boolean passport, String promoCode, String expectedAttendance, boolean myFavourite, String eventStatus, boolean featured) {
this.id = id;
this.multipleDays = multipleDays;
this.eventType = eventType;
this.startDate = startDate;
this.startTime = startTime;
this.endDate = endDate;
this.endTime = endTime;
this.eventName = eventName;
this.subTitle = subTitle;
this.location = location;
this.city = city;
this.fee = fee;
this.paid = paid;
this.eventImg = eventImg;
this.passportOption = passportOption;
this.passportTickets = passportTickets;
this.remainingTickets = remainingTickets;
this.passport = passport;
this.promoCode = promoCode;
this.expectedAttendance = expectedAttendance;
this.myFavourite = myFavourite;
this.eventStatus = eventStatus;
this.featured = featured;
}
}
3.Query
#Query("SELECT new com.test.responseDTO.TestDTO(e.id, e.multipleDays, e.typesOfEvents, e.startDate, e.startTime, e.endDate, e.endTime, e.eventName, e.subTitle, e.location, c.name, e.fee, e.paid, e.eventImg, p.event_passport_option_id, e.passportTickets, e.remainingTickets, e.passport, e.promoCode, e.expectedAttendance, e.myFavourite, s.status, e.featured)" +
" from Events e LEFT JOIN e.typesOfEvents.type_id t JOIN e.city c JOIN e.eventPassportOptions p JOIN e.eventStatus s WHERE e.startDate BETWEEN :startDate AND :endDate AND e.endDate BETWEEN :startDate AND :endDate ORDER BY e.startDate, e.id ASC")
List<TestDTO> findAllListOfEventsWithoutEventTypeIdWithOnlyTwoDateTest1(String startDate, String endDate);
4.Error
org.hibernate.QueryException: illegal attempt to dereference collection [events0_.id.typesOfEvents] with element property reference [type_id]
You reference type_id from list which its not allowed here, because query expression referring type_id to list, not to entity itself. So only replace this LEFT JOIN e.typesOfEvents.type_id t with LEFT JOIN e.typesOfEvents t and it would be work fine. And it would reference variable t to element from list(Relation).
#Query("SELECT new com.test.responseDTO.TestDTO(e.id, e.multipleDays, e.typesOfEvents, e.startDate, e.startTime, e.endDate, e.endTime, e.eventName, e.subTitle, e.location, c.name, e.fee, e.paid, e.eventImg, p.event_passport_option_id, e.passportTickets, e.remainingTickets, e.passport, e.promoCode, e.expectedAttendance, e.myFavourite, s.status, e.featured)" +
" from Events e LEFT JOIN e.typesOfEvents t /*this fixed*/ JOIN e.city c JOIN e.eventPassportOptions p JOIN e.eventStatus s WHERE e.startDate BETWEEN :startDate AND :endDate AND e.endDate BETWEEN :startDate AND :endDate ORDER BY e.startDate, e.id ASC")
List<TestDTO> findAllListOfEventsWithoutEventTypeIdWithOnlyTwoDateTest1(String startDate, String endDate);
My Query with changes into TestDTO
#Query("SELECT new com.techavidus.networkingPhoniex.responseDTO.TestDTO(e.id, e.multipleDays, t, e.startDate, e.startTime, e.endDate, e.endTime, e.eventName, e.subTitle, e.location, c.name, e.fee, e.paid, e.eventImg, p.event_passport_option_id, e.passportTickets, e.remainingTickets, e.passport, e.promoCode, e.expectedAttendance, e.myFavourite, s.status, e.featured)" +
" from Events e LEFT JOIN e.typesOfEvents t JOIN e.city c JOIN e.eventPassportOptions p JOIN e.eventStatus s WHERE e.startDate BETWEEN :startDate AND :endDate AND e.endDate BETWEEN :startDate AND :endDate ORDER BY e.startDate, e.id ASC")
List<TestDTO> findAllListOfEventsWithoutEventTypeIdWithOnlyTwoDateTest1(String startDate, String endDate);
output :
{
"success": true,
"message": "Record's find successfully.",
"data": [
{
"date": "2020/05/28",
"count": 2,
"events": [
{
"id": 1,
"multipleDays": true,
"eventType": {
"type_id": 1,
"type": "Musician1",
"paid_listing": false,
"color": "Black",
"_enabled": true
},
"startDate": "2020/05/28",
"startTime": "10:12:15",
"endDate": "2020/05/30",
"endTime": "20:18:25",
"eventName": "Test",
"subTitle": "Testing demo",
"location": "string",
"city": "Bhuj",
"fee": 49,
"paid": false,
"eventImg": "Imgage url",
"passportOption": 1,
"passportTickets": 0,
"remainingTickets": 0,
"passport": false,
"promoCode": "TEST123",
"expectedAttendance": "1500",
"myFavourite": false,
"eventStatus": "Pending Approval",
"featured": false
},
{
"id": 1,
"multipleDays": true,
"eventType": {
"type_id": 2,
"type": "Personal Growth",
"paid_listing": true,
"color": "Black",
"_enabled": true
},
"startDate": "2020/05/28",
"startTime": "10:12:15",
"endDate": "2020/05/30",
"endTime": "20:18:25",
"eventName": "Test",
"subTitle": "Testing demo",
"location": "string",
"city": "Bhuj",
"fee": 49,
"paid": false,
"eventImg": "Imgage url",
"passportOption": 1,
"passportTickets": 0,
"remainingTickets": 0,
"passport": false,
"promoCode": "TEST123",
"expectedAttendance": "1500",
"myFavourite": false,
"eventStatus": "Pending Approval",
"featured": false
}
]
}
]
}

Spring REST deserialize JSON array as a Set<?>

I'm using Spring Boot 2.2, Spring Data REST, Spring HATEOAS.
I'm facing a strange problem. I've a RestController accepting this object:
#Data
public class DocumentJSON {
#Valid
private Document document;
private List<DocumentRow> rows = new ArrayList<>();
private List<DocumentVat> vats = new ArrayList<>();
private Set<DocumentPayment> payments = new HashSet<>();
private boolean updateContactDetail = false;
}
and DocumentPayment is:
#Data
#EqualsAndHashCode(callSuper = true, onlyExplicitlyIncluded = true)
#NoArgsConstructor
#AllArgsConstructor
#Builder
#ToString(callSuper = true)
public class DocumentPayment extends AbstractEntity {
#ToString.Exclude
#JsonDeserialize(using = DocumentUriDeserializer.class)
#NotNull
#OnDelete(action = OnDeleteAction.CASCADE)
#ManyToOne(fetch = FetchType.LAZY, optional = false)
private Document document;
#NotNull
#Column(nullable = false, columnDefinition = "DATE")
private Instant date;
//Optional contact (the receipt has not a contact)
#ToString.Exclude
#ManyToOne(fetch = FetchType.LAZY)
private Contact contact;
#NotBlank
#Column(nullable = false)
private String description;
#ToString.Exclude
#JsonDeserialize(using = FinancialAccountUriDeserializer.class)
#NotNull(message = "{documentpayment.financialaccount.missing}")
#ManyToOne(fetch = FetchType.LAZY, optional = false)
private FinancialAccount financialAccount;
#Enumerated(EnumType.STRING)
#NotNull
#Column(nullable = false, length = 30)
private PaymentType paymentType;
//The amount, negative for payment to suppliers
#NotNull
#ColumnDefault("0.00")
#Column(nullable = false, scale = 2, columnDefinition = "DECIMAL(12,2)")
private BigDecimal amount = BigDecimal.ZERO;
//The amount paid
#NotNull
#ColumnDefault("0.00")
#Column(nullable = false, columnDefinition = "DECIMAL(12,2)")
private BigDecimal paid = BigDecimal.ZERO;
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
#Generated(value = GenerationTime.ALWAYS)
#Column(columnDefinition = "DECIMAL(12,2) AS (amount-paid) VIRTUAL NOT NULL")
private BigDecimal due;
#ToString.Exclude
#JsonDeserialize(using = StoreUriDeserializer.class)
//#NotNull(message = "{documentpayment.store.missing}")
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "store_id", updatable = false)
private Store store;
}
The client is sending a Json like this:
{
"document": {
"date": "2019-10-18T00:00:00.000Z",
"type": "SALES_RECEIPT",
"store": "http://95.255.117.252:8082/api/v1/stores/1",
"rounding": 0,
"amount": 19.23,
"taxAmount": 0.77,
"totalAmount": 20
},
"rows": [
{
"index": 1,
"productType": "FRAME",
"qty": 1,
"rowGroup": null,
"unitPrice": 9.615,
"percentageDiscount": 0,
"purchaseUnitPrice": null,
"amount": 9.615,
"description": "Prodotto1",
"taxRate": "http://95.255.117.252:8082/api/v1/taxRates/2",
"note": false
},
{
"index": 1,
"productType": "OPHTHALMIC_LENS",
"qty": 1,
"rowGroup": null,
"unitPrice": 9.615,
"percentageDiscount": 0,
"purchaseUnitPrice": null,
"amount": 9.615,
"description": "Lente",
"taxRate": "http://95.255.117.252:8082/api/v1/taxRates/2",
"note": false
}
],
"payments": [
{
"date": "2019-10-18T00:00:00.000Z",
"financialAccount": "http://95.255.117.252:8082/api/v1/financialAccounts/1",
"paymentType": "CASH",
"amount": "10"
},
{
"date": "2019-10-18T00:00:00.000Z",
"financialAccount": "http://95.255.117.252:8082/api/v1/financialAccounts/3",
"paymentType": "CREDIT_CARD",
"amount": "10"
}
],
"updateContactDetail": false
}
but when I debug in the first line of the REST controller I see only 1 payment.
Changing the property payments in DocumentJSON from Set<DocumentPayment> to List<DocumentPayment>, I get 2 payments as expected with the same JSON.
Am I missing something or Spring/Jackson are not able to deserialize in the right way a Set<>?

Categories

Resources