Cascade Types in Hibernate and JPA
Learn Cascade Types in Hibernate and JPA with CascadeType.PERSIST, MERGE, REMOVE, REFRESH, DETACH, ALL, orphanRemoval, parent-child relationships, SQL examples, Spring Boot code examples, and interview questions.
What You Will Learn
- What is Cascade?
- Why Cascade is needed
- Parent and child relationship
- CascadeType.PERSIST
- CascadeType.MERGE
- CascadeType.REMOVE
- CascadeType.REFRESH
- CascadeType.DETACH
- CascadeType.ALL
- orphanRemoval
- Real code examples
- Best practices
- Interview questions
Introduction
In Hibernate and JPA, entities often have relationships.
Example:
Customer
has many
Orders
When we save a customer, should Hibernate also save the orders?
When we delete a customer, should Hibernate also delete the orders?
This behavior is controlled by:
Cascade Types
What is Cascade?
Cascade means:
Apply parent entity operation to child entities automatically
Example:
Save Customer
Automatically Save Orders
Without cascade, you must save parent and child separately.
Parent Child Example
flowchart LR
Customer --> Order
Customer is parent.
Order is child.
Database Tables
CREATE TABLE customers (
id BIGINT PRIMARY KEY,
name VARCHAR(100)
);
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
order_number VARCHAR(100),
amount DECIMAL(10,2),
customer_id BIGINT
);
Entity Relationship
Customer Entity
import jakarta.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "customers")
public class Customer {
@Id
private Long id;
private String name;
@OneToMany(
mappedBy = "customer",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<Order> orders = new ArrayList<>();
public Customer() {
}
public Customer(Long id, String name) {
this.id = id;
this.name = name;
}
public void addOrder(Order order) {
orders.add(order);
order.setCustomer(this);
}
public void removeOrder(Order order) {
orders.remove(order);
order.setCustomer(null);
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public List<Order> getOrders() {
return orders;
}
}
Order Entity
import jakarta.persistence.*;
@Entity
@Table(name = "orders")
public class Order {
@Id
private Long id;
private String orderNumber;
private Double amount;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "customer_id")
private Customer customer;
public Order() {
}
public Order(
Long id,
String orderNumber,
Double amount) {
this.id = id;
this.orderNumber = orderNumber;
this.amount = amount;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public Long getId() {
return id;
}
public String getOrderNumber() {
return orderNumber;
}
public Double getAmount() {
return amount;
}
}
Repository
import org.springframework.data.jpa.repository.JpaRepository;
public interface CustomerRepository
extends JpaRepository<Customer, Long> {
}
Service Layer
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class CustomerService {
private final CustomerRepository customerRepository;
public CustomerService(
CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
}
Cascade Types List
JPA supports:
CascadeType.PERSIST
CascadeType.MERGE
CascadeType.REMOVE
CascadeType.REFRESH
CascadeType.DETACH
CascadeType.ALL
CascadeType.PERSIST
CascadeType.PERSIST means:
When parent is saved for first time, child is also saved
Example Mapping
@OneToMany(
mappedBy = "customer",
cascade = CascadeType.PERSIST
)
private List<Order> orders = new ArrayList<>();
Code Example
@Transactional
public void createCustomerWithOrders() {
Customer customer =
new Customer(1L, "Venu");
Order order1 =
new Order(101L, "ORD-101", 500.0);
Order order2 =
new Order(102L, "ORD-102", 900.0);
customer.addOrder(order1);
customer.addOrder(order2);
customerRepository.save(customer);
}
What Happens?
Hibernate saves:
Customer
Order 101
Order 102
Generated SQL:
INSERT INTO customers VALUES (1, 'Venu');
INSERT INTO orders VALUES (101, 'ORD-101', 500.0, 1);
INSERT INTO orders VALUES (102, 'ORD-102', 900.0, 1);
Without Cascade PERSIST
If cascade is not configured:
customerRepository.save(customer);
Only customer is saved.
Orders may not be saved automatically.
CascadeType.MERGE
CascadeType.MERGE means:
When parent is updated, child updates are also merged
Example Mapping
@OneToMany(
mappedBy = "customer",
cascade = CascadeType.MERGE
)
private List<Order> orders = new ArrayList<>();
Code Example
@Transactional
public void updateCustomerAndOrder() {
Customer customer =
customerRepository.findById(1L)
.orElseThrow();
Order order =
customer.getOrders().get(0);
order.setCustomer(customer);
customerRepository.save(customer);
}
If child entity changes and merge cascade is enabled, Hibernate can merge child changes.
Better Update Example
Add setter in Order:
public void updateAmount(Double amount) {
this.amount = amount;
}
Service:
@Transactional
public void updateOrderAmount() {
Customer customer =
customerRepository.findById(1L)
.orElseThrow();
Order order =
customer.getOrders().get(0);
order.updateAmount(1200.0);
customerRepository.save(customer);
}
Generated SQL:
UPDATE orders
SET amount = 1200.0
WHERE id = 101;
CascadeType.REMOVE
CascadeType.REMOVE means:
When parent is deleted, child entities are also deleted
Example Mapping
@OneToMany(
mappedBy = "customer",
cascade = CascadeType.REMOVE
)
private List<Order> orders = new ArrayList<>();
Code Example
@Transactional
public void deleteCustomer() {
Customer customer =
customerRepository.findById(1L)
.orElseThrow();
customerRepository.delete(customer);
}
Generated SQL:
DELETE FROM orders
WHERE customer_id = 1;
DELETE FROM customers
WHERE id = 1;
Warning
Be careful with CascadeType.REMOVE.
Bad example:
@ManyToOne(cascade = CascadeType.REMOVE)
private Customer customer;
This is dangerous.
Deleting one order may delete the customer.
Dangerous Flow
flowchart LR
DeleteOrder --> DeleteCustomer
DeleteCustomer --> DeleteOtherOrders
Avoid CascadeType.REMOVE on ManyToOne.
CascadeType.REFRESH
CascadeType.REFRESH means:
When parent is refreshed from database, child is also refreshed
Use Case
If database values changed outside Hibernate, refresh reloads latest data.
entityManager.refresh(customer);
If cascade refresh is enabled, orders are also refreshed.
Example Mapping
@OneToMany(
mappedBy = "customer",
cascade = CascadeType.REFRESH
)
private List<Order> orders = new ArrayList<>();
Code Example
@Transactional
public void refreshCustomer(Long id) {
Customer customer =
entityManager.find(Customer.class, id);
entityManager.refresh(customer);
}
CascadeType.DETACH
CascadeType.DETACH means:
When parent is detached from persistence context, child is also detached
Example
entityManager.detach(customer);
If cascade detach is enabled, orders also become detached.
Example Mapping
@OneToMany(
mappedBy = "customer",
cascade = CascadeType.DETACH
)
private List<Order> orders = new ArrayList<>();
Detached Flow
flowchart LR
ManagedParent --> DetachedParent
ManagedChild --> DetachedChild
CascadeType.ALL
CascadeType.ALL means:
Apply all cascade operations
Equivalent to:
PERSIST
MERGE
REMOVE
REFRESH
DETACH
Example Mapping
@OneToMany(
mappedBy = "customer",
cascade = CascadeType.ALL
)
private List<Order> orders = new ArrayList<>();
Full Example With Cascade ALL
@Transactional
public void createFullCustomer() {
Customer customer =
new Customer(1L, "Venu");
customer.addOrder(
new Order(101L, "ORD-101", 500.0)
);
customer.addOrder(
new Order(102L, "ORD-102", 900.0)
);
customerRepository.save(customer);
}
Only one save call.
Hibernate saves parent and children.
orphanRemoval
orphanRemoval is not exactly cascade type, but often used with cascade.
It means:
If child is removed from parent collection, delete child from database
Mapping
@OneToMany(
mappedBy = "customer",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<Order> orders = new ArrayList<>();
Code Example
@Transactional
public void removeOrderFromCustomer() {
Customer customer =
customerRepository.findById(1L)
.orElseThrow();
Order order =
customer.getOrders().get(0);
customer.removeOrder(order);
}
Generated SQL:
DELETE FROM orders
WHERE id = 101;
Cascade REMOVE vs orphanRemoval
| Feature | Cascade REMOVE | orphanRemoval |
|---|---|---|
| Trigger | Parent Deleted | Child Removed From Collection |
| Deletes Child | Yes | Yes |
| Parent Must Be Deleted | Yes | No |
Example Difference
Cascade REMOVE:
customerRepository.delete(customer);
Deletes customer and orders.
orphanRemoval:
customer.removeOrder(order);
Deletes only removed order.
Customer remains.
OneToMany Best Practice
Recommended:
@OneToMany(
mappedBy = "customer",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<Order> orders = new ArrayList<>();
Use when child cannot exist without parent.
ManyToOne Best Practice
Usually avoid cascade:
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "customer_id")
private Customer customer;
Why?
Many orders share one customer.
Deleting one order should not delete customer.
OneToOne Example
User and Profile.
@OneToOne(
mappedBy = "user",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private UserProfile profile;
If user is deleted, profile is deleted.
ManyToMany Warning
Avoid CascadeType.REMOVE in ManyToMany.
Example:
@ManyToMany(cascade = CascadeType.REMOVE)
private Set<Role> roles;
Danger:
Deleting one user may delete shared roles
Better:
@ManyToMany
private Set<Role> roles;
Banking Example
Account and Transactions.
@OneToMany(
mappedBy = "account",
cascade = CascadeType.PERSIST
)
private List<Transaction> transactions;
When account is created, initial transactions can be saved.
But be careful with REMOVE because transaction history may need to be retained.
Insurance Example
Policy and Beneficiaries.
@OneToMany(
mappedBy = "policy",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<Beneficiary> beneficiaries;
If beneficiary is removed from policy, delete it from database.
E-Commerce Example
Order and OrderItems.
Best mapping:
@OneToMany(
mappedBy = "order",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<OrderItem> items = new ArrayList<>();
Why?
OrderItem should not exist without Order
Order Entity Example
@Entity
@Table(name = "orders")
public class PurchaseOrder {
@Id
private Long id;
@OneToMany(
mappedBy = "order",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<OrderItem> items =
new ArrayList<>();
public void addItem(OrderItem item) {
items.add(item);
item.setOrder(this);
}
public void removeItem(OrderItem item) {
items.remove(item);
item.setOrder(null);
}
}
OrderItem Entity Example
@Entity
@Table(name = "order_items")
public class OrderItem {
@Id
private Long id;
private String productName;
private Integer quantity;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id")
private PurchaseOrder order;
public void setOrder(PurchaseOrder order) {
this.order = order;
}
}
Service Example
@Transactional
public void createOrder() {
PurchaseOrder order =
new PurchaseOrder();
OrderItem item1 =
new OrderItem();
OrderItem item2 =
new OrderItem();
order.addItem(item1);
order.addItem(item2);
purchaseOrderRepository.save(order);
}
Hibernate saves:
Order
OrderItem 1
OrderItem 2
Cascade Flow
flowchart LR
Parent --> ChildOne
Parent --> ChildTwo
Parent --> ChildThree
Common Mistakes
❌ Using CascadeType.ALL everywhere
❌ Using CascadeType.REMOVE on ManyToOne
❌ Using CascadeType.REMOVE on ManyToMany
❌ Forgetting helper methods like addOrder
❌ Not setting both sides of bidirectional relationship
Correct Bidirectional Relationship Handling
public void addOrder(Order order) {
orders.add(order);
order.setCustomer(this);
}
public void removeOrder(Order order) {
orders.remove(order);
order.setCustomer(null);
}
Always update both sides.
Best Practices
✅ Use cascade from parent to child
✅ Avoid cascade from child to parent
✅ Use orphanRemoval for dependent children
✅ Avoid REMOVE cascade on shared entities
✅ Prefer explicit cascade types when possible
✅ Use CascadeType.ALL only when lifecycle is fully owned
Interview Questions
What is Cascade in Hibernate?
Cascade means applying parent entity operations to related child entities automatically.
What is CascadeType.PERSIST?
It saves child entities when parent is persisted.
What is CascadeType.MERGE?
It merges child entities when parent is merged.
What is CascadeType.REMOVE?
It deletes child entities when parent is deleted.
What is CascadeType.ALL?
It applies all cascade operations.
What is orphanRemoval?
It deletes child entity when removed from parent collection.
Difference Between Cascade REMOVE and orphanRemoval?
Cascade REMOVE works when parent is deleted.
orphanRemoval works when child is removed from collection.
Should We Use Cascade REMOVE On ManyToOne?
No.
It can delete parent unexpectedly.
Should We Use Cascade REMOVE On ManyToMany?
Usually no.
Shared entities can be deleted accidentally.
Key Takeaways
- Cascade controls how operations move from parent to child.
- CascadeType.PERSIST saves children with parent.
- CascadeType.MERGE updates children with parent.
- CascadeType.REMOVE deletes children with parent.
- CascadeType.ALL applies all cascade operations.
- orphanRemoval deletes children removed from parent collection.
- Use cascade carefully in enterprise systems.
- Never blindly use CascadeType.ALL everywhere.
- Cascade should follow ownership and lifecycle rules.