@OnDelete vs JPA Cascade: Persistence Context Consistency Issue
Problem
While migrating the Gift API to JPA at Kakao Tech Campus, I needed to delete options when a product is deleted.
But the entity structure was unidirectional.
1
2
3
4
5
@Entity
public class Option {
@ManyToOne(fetch = FetchType.LAZY)
private Product product; // Only Option → Product reference
}
Product had no reference to Option. So how do you delete related options when deleting a product?
What Is Persistence Context?
One of the most important concepts in JPA. The persistence context is an environment for persistently storing entities, acting as a first-level cache between the app and the database.
Key Features:
- First-level cache: Within the same transaction, querying the same entity returns from cache, not DB
- Dirty checking: Automatically detects entity changes and syncs to DB on commit
- Identity guarantee: Same PK in the same transaction always returns the same instance
Solution 1: @OnDelete (Hibernate)
I used Hibernate’s @OnDelete annotation.
1
2
3
@ManyToOne(fetch = FetchType.LAZY)
@OnDelete(action = OnDeleteAction.CASCADE)
private Product product;
This sets ON DELETE CASCADE at the DB level—when a product is deleted, related options are automatically deleted.
Mentor Feedback
But my mentor flagged this:
“@OnDelete is Hibernate-specific, and since the DB directly executes the cascade delete, it’s not reflected in JPA’s persistence context. This can cause consistency issues with JPA.”
Problems:
@OnDeleteis handled by the DB directly- JPA’s persistence context has no idea
- Accessing deleted entities in the same transaction can cause unexpected behavior
For example, Options get deleted from the DB via @OnDelete, but they might still exist in the persistence context, behaving as if they’re still there.
Solution 2: JPA Cascade (Requires Bidirectional)
Using JPA’s cascade requires a bidirectional relationship.
1
2
3
4
5
@Entity
public class Product {
@OneToMany(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Option> options = new ArrayList<>();
}
This way, JPA manages deletions and keeps the persistence context consistent. When Product is deleted, JPA removes related Options from the persistence context too.
Trade-off
| Method | Pros | Cons |
|---|---|---|
| @OnDelete | Can stay unidirectional, simple setup | Inconsistent with JPA persistence context |
| JPA Cascade | Persistence context consistency guaranteed | Requires bidirectional relationship |
I ended up adding the bidirectional relationship and using JPA Cascade.
Lessons Learned
- Hibernate-specific features differ from JPA standard
- You need to understand the difference between DB-level and application-level handling
- Persistence context consistency is always a concern when using JPA
From Kakao Tech Campus 3rd cohort Gift API clone coding, summarizing mentor feedback.