I have three entity classes.
Product, Category, and SubCategory.
A Category has a OneToMany relation with SubCategory
#Entity
#Table(name = "CATEGORY")
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "category_id")
private Long categoryId;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
#JoinColumn(name = "category_id")
private List<SubCategory> subCategories;
}
The product is assocciated with a Category and one of its SubCategories
#Entity
#Table(name = "PRODUCTS")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = ("id"))
private Long id;
#ManyToOne
#JoinColumn(name = "category", unique = false, nullable = true, insertable = true, updatable = true)
private Category category;
#ManyToOne
#JoinColumn(name = "sub_category", unique = false, nullable = true, insertable = true, updatable = true)
private SubCategory subCategory;
}
now if I delete a Category, all its SubCatogries are deleted, but I also want the associations in Product to be updated to null. I thought of manually fetching all the products with the associated deleted Category and updating them manually, but is there a way to handle this with JPA annotations?
This update from JPA will be very inefficient in performance perspective.
Your table PRODUCTS has columns category abd sub_category which linked with correspond tables by foreign keys. Add to end of definition of each of these columns string 'ON DELETE SET NULL' and what you want will be done by database automatically.
The Product entity has a ManyToOne relationship with SubCategory that means that the SubCategory entity has a OneToMany relationship with Product. So, in the SubCategory class, where you have defined the OneToMany relationship, you need to mention the cascadetype = remove
#manytoone(cascade = cascadetype.remove)
Hope it solves your problem
Related
I have got two different rest endpoints to insert data into DB where:
/api1 will insert data into Table1 and Table3
where as /api2 will insert data into Table Table1 and Table3 but facing this error while inserting the data into db.
I have got three tables: Table1, Table2, Table3
Table1 and Table3's association is #OneToMany
Table2 and Table3's association is ALSO #OneToMany
The solution that I am getting is: (should be mapped with insert="false" update="false") but this will prevent the insertion and updating of the data into database for these columns.
How do I use these
Sharing snippet of the code block where I'm facing the issue. Please let me know if you want me to share the whole code
#Data #EqualsAndHashCode(callSuper = true) #Entity #Table(name = "[dbo].[LAInstructions]")
public class LaInstructionsEntity extends AuditableFields<Long> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "Id")
private Long id;
#JsonManagedReference
#**OneToMany**(mappedBy = "laInstructionsEntity", cascade = CascadeType.ALL)
private List<LaAssociateGridEntity> laAssociateGridEntities;
#Column(name = "Active")
private Boolean active;
}
Table 2:
#Data
#EqualsAndHashCode(callSuper = true)
#Entity
#Table(name = "[dbo].[LALegalCreditCheck]")
public class LALegalCreditCheckEntity extends AuditableFields<Long> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "Id")
private Long id;
#JsonManagedReference
#**OneToMany**(mappedBy = "laLegalCreditCheckEntity", cascade = CascadeType.ALL, targetEntity = LaAssociateGridEntity.class)
List<LaAssociateGridEntity> laAssociateGridEntities;
#Column(name = "Active")
Boolean active;
}
Table 3:
#Data #EqualsAndHashCode(callSuper = true) #Entity #Table(name = "[dbo].[LAAssociateGrid]")
public class LaAssociateGridEntity extends AuditableFields<Long> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "Id")
private Long id;
#Column(name = "Active")
private Boolean active;
#JsonBackReference
#**ManyToOne**(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "AssociateId", referencedColumnName = "Id")
private LaInstructionsEntity laInstructionsEntity;
#**ManyToOne**(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "AssociateId", referencedColumnName = "Id")
private LALegalCreditCheckEntity laLegalCreditCheckEntity;
}
This error says that in LaAssociateGridEntity , both laInstructionsEntity and laLegalCreditCheckEntity fields are also mapped to the same column (i.e. AssociateId) , so Hibernate does not know which fields should it consider when mapping this column.
I believe there is a typo in one of them. LaInstructionsEntity and LALegalCreditCheckEntity are different entities. And without another discriminator column , how can you differentiate for the same AssociateId in LaAssociateGridEntity , its parent is LaInstructionsEntity or LALegalCreditCheckEntity ?
Have 2 Entities: Orders and Products. 1 Order can have Many Products and Many Products can belong to 1 Order (Each Product only belongs to 1 Order).
With unidirectional association at Order Entity, I am able to retrieve product details when performing orderRepo.findAll(); In similar fashion, need order details when performing productRepo.findAll();
Tried code:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "order_details")
public class OrderData {
#Id
#Column(name = "order_id", nullable = false, unique = true)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long orderId;
#NotNull
#Column(name = "customer_name", nullable = false)
private String customerName;
#OneToMany(mappedBy = "productId", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<ProductData> products;
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "product_details")
public class ProductData {
#Id
#Column(name = "product_id", nullable = false, unique = true)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long productId;
#NotNull
#Column(name = "product_name", nullable = false)
private String productName;
#ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "order_id", nullable = false)
private OrderData orderData;
}
While inserting at products; we are getting error: "insert or update on table violates foreign key constraint jpa"
While performing productRep.findAll(): infinite loop for hibernate select queries
Tried #JsonIgnore. This not returning child or parent elements.
Tried #JsonManagedReference vs #JsonBackReference - still no luck.
Please guide me on this
The mappedBy attribute points to the wrong field:
#OneToMany(mappedBy = "productId", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<ProductData> products;
This must be the back reference:
#OneToMany(mappedBy = "orderData", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<ProductData> products;
I would like to ignore #OnetoMany field in my entity. fetch data need to get actual fields but don't want to fire query to dependent table. But deleting data from parent table needs deletion from dependent table
I have tried #Transient that ignores but the delete is also being ignored. Is there any other option to tell JPA not to fetch data from childs table when i call the parent entity?
#Entity
Table(name = "User")
public class UserEntity implements Serializable {
#Id
#Column(name = "id")
private int id;
#Column(name = "SERIAL", unique = true, nullable = false)
private String serial;
#OneToMany(mappedBy = "serialBySerialId", cascade = CascadeType.ALL)
private Set<UserActionEntity> userActionsById;
}
#Table(name = "user_action")
public class UserActionEntity implements Serializable {
#Id
#Column(name = "id")
private int id;
#Column(name = "action")
private String action;
#ManyToOne
#JoinColumn(name = "USER_ID", referencedColumnName = "ID", nullable = false)
private UserEntity userByUserId;
If you don't want to fire query to dependent table, you can use (fetch = FetchType.LAZY) on UserActionEntity property.
#OneToMany(mappedBy = "serialBySerialId", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<UserActionEntity> userActionsById;
I have Product entity and ProductRating entity, each Product can have many ProductRatings. When Product is deleted I want to have associated ratings deleted too, but nothing works so far (also orphanRemoval set to true)...
Classes:
#Getter
#Setter
#Entity
#Table(name = "PRODUCT")
public class Product extends AbstractEntity<Long> {
#Column(nullable = false)
private String name;
private String description;
#Column(nullable = false)
#Min(value = 0)
private Float cost;
#OneToMany(mappedBy = "product",
orphanRemoval = true, cascade = CascadeType.PERSIST,
fetch = FetchType.EAGER)
//#OnDelete(action = OnDeleteAction.CASCADE)
#Fetch(value = FetchMode.SELECT)
private Set<ProductRating> productRatings;
}
#Getter
#Setter
#Entity
#Table(name = "PRODUCT_RATING")
public class ProductRating extends Rating {
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "product_id")
#NotNull(message = "Rating must be in context of Product")
private Product product;
}
After Product deletion ratings stay with deleted Product's ID
AbstractEntity implementation:
#Getter
#Setter
#MappedSuperclass
public abstract class AbstractEntity<I> implements Serializable {
private static final long serialVersionUID = 1700166770839683115L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID", unique = true, nullable = false)
private I id;
}
In the #OneToMany relation you need to add the cascade type delete: cascade = {CascadeType.PERSIST, CascadeType.REMOVE}
Or if you don't mind having all cascade types you can just put: cascade = CascadeType.ALL
EDIT:
Also check the name of the Product primary key in the database.
It should match the defined in the #JoinColumn annotation of ProductRating
The default database field for the attribute id of the Product class would be product_id.
However you have defined the id in AbstractEntity as name = "ID" so the #JoinColumn should be something like: #JoinColumn(name = "ID")
My alternative approach to fix this problem is to:
On parent-side relation create method with #PreRemove annotation
in this method iterate over collection with #[One/Many]ToMany annotation and call delete(obj) method for corresponding repository on child
On child-side relation create method with #PreRemove annotation
In this method set parent to null
I have entity object which has relationship with itself in two forms:
entity refer to list of that entities
entity refer to entity (mainOrder), which is superior current enitity
Java class for order:
#Entity
#Table(name = "shop_order")
public class ShopOrder {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", columnDefinition = "serial")
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "main_id",
nullable = true)
private ShopOrder mainOrder;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "id") // I am not sure here should be id or mainOrder
private Set<ShopOrder> subOrders = new HashSet<>();
// some others columns, and getters and setters
}
Can you tell me how to ensure that entity for cascade remove? I need if some order will be removed, also will be removed its suborders (it means orders which have set that removed order as mainOrder)
mappedBy is the attribute name in the class ShopOrder that is the back reference to ShopOrder.
Cascade can be defined in the annotation attribute cascade
#OneToMany(fetch = FetchType.LAZY, mappedBy = "mainOrder", cascade = CascadeType.REMOVE)
private Set<ShopOrder> subOrders = new HashSet<>();
Read more about Cascading in the Hibernate manual:
http://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#pc-cascade