Full Stack • Java • System Design • Cloud • AI Engineering

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.