Cascading Operations
Understand cascading in Hibernate — CascadeType.ALL, PERSIST, MERGE, and REMOVE — and exactly what happens to related entities when you save or delete a parent.
Introduction
When you save a Department, should its Employees be saved automatically? When you delete a Department, should its Employees be deleted too, or should the delete fail because they still exist? These are exactly the questions cascading answers. Cascading tells Hibernate which operations performed on a parent entity should automatically propagate to its related entities.
- What cascading means and why it matters
- The individual cascade types: PERSIST, MERGE, REMOVE, REFRESH, DETACH, and ALL
- What actually happens to child entities under each cascade type
- The difference between CascadeType.REMOVE and orphanRemoval
What Is Cascading?
Without cascading, every entity in a relationship must be persisted, merged, or removed individually and explicitly. That gets tedious fast — imagine having to manually save every Employee separately every time you create a new Department. Cascading, configured via the cascade attribute on relationship annotations like @OneToMany, @OneToOne, and @ManyToMany, lets an operation on the parent "cascade" down to its associated entities automatically.
@OneToMany(mappedBy = "department", cascade = CascadeType.ALL)private List<Employee> employees = new ArrayList<>();With this configuration, calling session.persist(department) also persists every Employee in the employees list, and session.remove(department) also removes every associated Employee row.
Cascade Types
| CascadeType | What It Propagates |
|---|---|
| PERSIST | Saving the parent also saves any new, unsaved child entities. |
| MERGE | Merging a detached parent also merges its child entities. |
| REMOVE | Removing the parent also removes its child entities from the database. |
| REFRESH | Refreshing the parent from the database also refreshes its children. |
| DETACH | Detaching the parent from the persistence context also detaches its children. |
| ALL | Shorthand for PERSIST, MERGE, REMOVE, REFRESH, and DETACH combined. |
You can also specify multiple, specific cascade types instead of ALL, which is often the safer choice for relationships where you do not want every operation to propagate.
@ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE })@JoinTable( name = "student_course", joinColumns = @JoinColumn(name = "student_id"), inverseJoinColumns = @JoinColumn(name = "course_id"))private Set<Course> courses = new HashSet<>();CascadeType.PERSIST in Action
Session session = sessionFactory.openSession();Transaction tx = session.beginTransaction();
Department hr = new Department("Human Resources");hr.addEmployee(new Employee("Nikhil Bansal"));hr.addEmployee(new Employee("Divya Kapoor"));
session.persist(hr); // both Employee rows are inserted automatically
tx.commit();session.close();Click Run to see what this code prints.
CascadeType.REMOVE in Action
Session session = sessionFactory.openSession();Transaction tx = session.beginTransaction();
Department hr = session.get(Department.class, 1L);session.remove(hr); // also removes every Employee in hr.getEmployees()
tx.commit();session.close();Click Run to see what this code prints.
CascadeType.REMOVE (and CascadeType.ALL, which includes it) permanently deletes every related entity. Never apply it to a relationship where the child entity might legitimately be shared or should outlive the parent — for example, do not cascade REMOVE from Course to Student in a many-to-many enrollment relationship.
orphanRemoval vs CascadeType.REMOVE
CascadeType.REMOVE only deletes children when the parent itself is explicitly removed. orphanRemoval = true goes a step further: it deletes a child the moment it is taken out of the parent's collection, even if the parent itself is never deleted.
@OneToMany(mappedBy = "department", cascade = CascadeType.ALL, orphanRemoval = true)private List<Employee> employees = new ArrayList<>();
// Elsewhere, within a transaction:Department department = session.get(Department.class, 1L);Employee toRemove = department.getEmployees().get(0);
department.getEmployees().remove(toRemove); // orphanRemoval deletes this row on flushtoRemove.setDepartment(null);Click Run to see what this code prints.
Use orphanRemoval = true for genuine parent-owns-child relationships, like Department/Employee or Order/OrderItem, where a child removed from the collection has no reason to keep existing on its own.
Common Mistakes
- Using CascadeType.ALL by default on every relationship without considering whether REMOVE is actually safe there.
- Applying cascading REMOVE to a many-to-many relationship, deleting a shared entity still referenced elsewhere.
- Confusing orphanRemoval with CascadeType.REMOVE — orphanRemoval also triggers on collection removal, not just parent deletion.
- Forgetting that cascading only applies in the direction it is declared — cascading from Department to Employee does not automatically cascade back from Employee to Department.
Best Practices
- Use CascadeType.ALL only for true parent-child ownership, such as Order and OrderItem.
- Avoid cascading REMOVE across many-to-many relationships or wherever child entities can be shared.
- Combine orphanRemoval = true with CascadeType.ALL specifically when children should never exist without their parent.
- Be explicit about individual cascade types (PERSIST, MERGE) rather than reaching for ALL when only some behaviors are actually needed.
Frequently Asked Questions
No. Cascading only applies to operations performed through the Session on managed entities (persist, merge, remove). Bulk HQL UPDATE and DELETE statements bypass the persistence context entirely and do not trigger cascading.
No. CascadeType.ALL controls what happens when you call operations on the parent explicitly. orphanRemoval is a separate, additional setting that also deletes children removed from a collection without deleting the parent.
Without cascading, you must persist, merge, or remove each related entity individually and explicitly — saving a parent will not automatically save or delete its children.
Key Takeaways
- Cascading propagates operations (persist, merge, remove, etc.) from a parent to its related entities.
- CascadeType.ALL bundles PERSIST, MERGE, REMOVE, REFRESH, and DETACH.
- CascadeType.REMOVE deletes children only when the parent itself is removed.
- orphanRemoval = true additionally deletes a child the moment it is removed from the parent's collection.
Summary
Cascading determines exactly how much work Hibernate does on your behalf when you touch a parent entity, and getting it wrong can either force tedious extra code or accidentally delete data. Next, you will look at fetch types — controlling when related data is actually loaded from the database in the first place.