@OnDelete vs JPA Cascade: Persistence Context Consistency Issue
Situation
While migrating the Gift API to JPA at Kakao Tech Campus, there was a requirement 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 entity had no reference to Option. In this situation, how do you delete associated 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 application and database.
Key Features:
- First-level cache: Within same transaction, querying the same entity returns from cache, not DB
- Dirty checking: Automatically detects entity changes and reflects to DB on transaction commit
- Identity guarantee: Same PK in same transaction always returns same instance
Solution 1: @OnDelete (Hibernate)
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 DB level, so when a product is deleted, associated options are automatically deleted.
Mentor Feedback
But mentor gave this feedback:
“@OnDelete is Hibernate-specific, and since the DB directly executes cascade delete, it’s not reflected in JPA’s persistence context. This can conflict with JPA consistency.”
Problems:
@OnDeleteis handled by DB directly- JPA’s persistence context doesn’t know about this
- Accessing deleted entities in the same transaction can cause unexpected behavior
For example, Options are deleted from DB via @OnDelete, but they might still exist in the persistence context, behaving as if they exist.
Solution 2: JPA Cascade (Requires Bidirectional)
Using JPA’s cascade requires 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, maintaining consistency with the persistence context. When Product is deleted, JPA removes associated Options from the persistence context too.
Trade-off
| Method | Pros | Cons |
|---|---|---|
| @OnDelete | Can keep unidirectional, simple setup | Inconsistent with JPA persistence context |
| JPA Cascade | Persistence context consistency guaranteed | Requires bidirectional relationship |
Ended up adding bidirectional relationship and using JPA Cascade.
Lessons Learned
- Hibernate-specific features differ from JPA standard
- Need to understand 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.