Post

@OnDelete vs JPA Cascade: Persistence Context Consistency Issue

@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:

  • @OnDelete is 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

MethodProsCons
@OnDeleteCan stay unidirectional, simple setupInconsistent with JPA persistence context
JPA CascadePersistence context consistency guaranteedRequires 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.

This post is licensed under CC BY 4.0 by the author.