Careeroza
Cascade Pitfalls
Detached Entity Passed to Persist
If a child already exists in the database but the parent cascades PERSIST to it, Hibernate tries to insert it again:
Example
Category electronics = new Category();
electronics.setId(3L); // existing row, but this object is not managed
product.setCategory(electronics);
entityManager.persist(product); // with cascade = PERSIST on Product.categoryExample output
jakarta.persistence.EntityExistsException: detached entity passed to persist: com.shopease.catalog.Category
Fix: remove the cascade on @ManyToOne, and get a reference to the existing row with categoryRepository.getReferenceById(3L) (no SELECT, just a proxy with the id).
Deleting Large Collections
Cascade REMOVE loads every child and deletes them one by one. Deleting a category with 20,000 products would run 20,000 DELETE statements. For bulk deletes use a JPQL bulk statement instead (it skips cascades and the persistence context):
@Modifying
@Query("delete from OrderItem i where i.order.id = :orderId")
int deleteItemsOfOrder(@Param("orderId") Long orderId);Checklist
- Cascade only from aggregate root to its own children (
Order → OrderItem,Product → ProductImage). - No cascade on
@ManyToOne; neverREMOVEon@ManyToMany. - Use
orphanRemovalwhen "removed from the list" should mean "deleted". - Use bulk JPQL or native deletes for large data sets.