Question 20 · Section 16

How Do Cascade Operations Work

Cascading in JPA is a mechanism for automatically propagating operations performed on a parent entity to all related child entities. This allows managing entire object graphs th...

Language versions: English Russian Ukrainian

Overview

Cascading in JPA is a mechanism for automatically propagating operations performed on a parent entity to all related child entities. This allows managing entire object graphs through a single EntityManager method call.


Junior Level

What is Cascade

Cascade - automatically propagates operations from parent entity to related ones.

@Entity
public class Order {
    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
    private List<OrderItem> items;
}

Order order = new Order();
OrderItem item = new OrderItem();
order.getItems().add(item);

entityManager.persist(order);  // persist called for items too!

CascadeTypes

CascadeType.ALL       - all operations (persist, merge, remove, refresh, detach)
CascadeType.PERSIST   - persist only
CascadeType.MERGE     - merge only
CascadeType.REMOVE    - remove only
CascadeType.REFRESH   - refresh only
CascadeType.DETACH    - detach only

Example

@Entity
public class Order {
    @Id
    private Long id;

    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
    private List<OrderItem> items = new ArrayList<>();

    public void addItem(OrderItem item) {
        items.add(item);
        item.setOrder(this);
    }
}

// persist(order) -> persist for all items
Order order = new Order();
order.addItem(new OrderItem());
order.addItem(new OrderItem());
entityManager.persist(order);  // 3 INSERTs: order + 2 items

Middle Level

Cascade vs orphanRemoval

@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
private List<OrderItem> items;

// Cascade - propagates operations (persist, merge, remove)
// orphanRemoval - removes entities removed from collection

order.getItems().remove(0);  // orphanRemoval deletes item from DB!

// Without orphanRemoval:
// order.getItems().remove(0) -> item remains in DB (orphan)

// Key difference: CascadeType.REMOVE fires only when parent is deleted.
// orphanRemoval also fires when child is removed from collection
// (order.getItems().remove(item)), even if parent is not deleted.

Usage Recommendations

@OneToMany (composite):  CascadeType.ALL + orphanRemoval = true
@ManyToOne:              CascadeType.PERSIST, MERGE (or no cascade)
@OneToOne:               CascadeType.ALL
@ManyToMany:             CascadeType.PERSIST, MERGE

Common Mistakes

// Cascade.ALL for @ManyToOne
@ManyToOne(cascade = CascadeType.ALL)
private User user;

// On Order delete -> User deleted!
entityManager.remove(order);  // cascade.REMOVE -> User deleted

// Without cascade for shared entities
@ManyToOne
private User user;  // User managed separately

JPA Cascade vs DB Cascade

JPA Cascading:
- Executed by Hibernate code
- Hibernate must know about all objects in memory
- Convenient for object-oriented logic

DB Cascading (ON DELETE CASCADE):
- Executed by DBMS engine
- Happens faster
- Guarantees integrity on direct SQL queries
- Hibernate may not know about deleted objects

Senior Level

Internal Implementation

Cascade process:
1. On operation on parent (persist, merge, remove)
2. Hibernate traverses all related entities
3. For each child - applies operation
4. Recursively for nested associations
5. All operations scheduled in persistence context
6. On flush - executed in correct order

Dangers of Circular Cascading

// Circular cascade - infinite recursion
@Entity
public class A {
    @OneToOne(cascade = CascadeType.ALL)
    private B b;
}

@Entity
public class B {
    @OneToOne(cascade = CascadeType.ALL)
    private A a;
}

a.setB(b);
b.setA(a);
entityManager.persist(a);  // Hibernate detects cycle and prevents infinite recursion. However result is unpredictable: may be PersistentObjectException, extra SQL queries, or silently incorrect state. Circular cascade is an anti-pattern.

Cascading in Many-to-Many

// Cascade.REMOVE in Many-to-Many - dangerous!
@Entity
public class Author {
    @ManyToMany(cascade = CascadeType.ALL)
    private List<Book> books;
}

// Deleting Author -> deleting all books -> deleting other Authors
// Deleting Author with cascade=ALL deletes all Books of this author. If Books also have cascade=ALL to Author - second author is deleted too, and their books, and so on through the graph. Result - mass deletion.

// Without REMOVE for Many-to-Many
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private List<Book> books;

Performance Considerations

// Cascading delete of large collection - N DELETE queries
Author author = entityManager.find(Author.class, id);
entityManager.remove(author);
// DELETE for author
// DELETE for each book (if cascade = ALL)

// DB-level ON DELETE CASCADE - 1 query
// ALTER TABLE author_books ADD CONSTRAINT ... ON DELETE CASCADE
entityManager.createNativeQuery("DELETE FROM authors WHERE id = ?")
    .setParameter(1, id)
    .executeUpdate();

Best Practices

CascadeType.ALL for compositions (child has no meaning without parent)
PERSIST, MERGE for associations (shared entities)
orphanRemoval for composite children
Without REMOVE for shared entities
Understand difference between cascade and orphanRemoval
DB-level cascade for large deletions

CascadeType.ALL for @ManyToOne
Cascade.REMOVE for shared entities
Circular cascading
Cascade.REMOVE in Many-to-Many
Cascading delete of large collections via JPA

Summary for Senior

  • Cascading is a tool for reducing code when working with object graphs
  • Use CascadeType.ALL only for compositions (child object has no meaning without parent)
  • For associations (links between independent entities), delete cascades are usually harmful
  • JPA cascades work only with Managed objects
  • For mass deletions, prefer DB-level ON DELETE CASCADE

Interview Cheat Sheet

Must know:

  • Cascade automatically propagates operations (persist, merge, remove) to related entities
  • CascadeType.ALL - all operations, PERSIST/MERGE/REMOVE - individual types
  • CascadeType.ALL for compositions (Order -> OrderItem), PERSIST/MERGE for associations (Order -> User)
  • Cascade.REMOVE for @ManyToOne - dangerous: deleting Order deletes User
  • Circular cascade - antipattern: A->B->A cascade causes unpredictable behavior
  • JPA cascades work only with Managed objects

Frequent follow-up questions:

  • Cascade vs orphanRemoval? Cascade - propagates operations, orphanRemoval - removes on collection removal
  • Why is Cascade.REMOVE for Many-to-Many dangerous? Deleting Author deletes all Books, and Books may have cascade to other Author - mass deletion
  • JPA Cascade vs DB Cascade? JPA - via Hibernate code (convenient), DB - via DBMS engine (faster, guarantees integrity)
  • When is cascade NOT needed? When both entities are aggregate roots with their own lifecycle (Order and Customer)

Red flags (DO NOT say):

  • “CascadeType.ALL for @ManyToOne” - deleting child deletes parent
  • “Cascade.REMOVE for Many-to-Many” - mass deletion of related entities
  • “Circular cascade A<->B” - unpredictable behavior
  • “Cascading delete of 10k collections via JPA” - 10k DELETE queries, better DB-level CASCADE

Related topics:

  • [[21. What Cascade Types Exist]]
  • [[22. What is Orphan Removal]]
  • [[23. How to Properly Use @OneToMany and @ManyToOne]]
  • [[7. Describe the Entity Lifecycle in Hibernate]]