This blog article is part of a “series of blog articles” about common pitfalls using JPA and ways to avoid them. In this article, we describe a problem with entites not beeing removed altough EntityManager.remove
succeeds.
When calling EntityManager.remove
on an entity referenced by a relationship, the EntityManager.remove
call may succeed, but the related entity is not removed from the database. The following statement tries to remove the related insurance instance of an emplyoee entity:
em.remove(emp.getInsurance());
Entity classes Emplyoee
and Insurance
have a bidirectional relationship annotated with cascade=PERSIST
:
Emplyoee.java:
@OneToOne(mappedBy = "employee",
cascade = CascadeType.PERSIST)
private Insurance insurance;
Insurance.java:
@OneToOne(cascade = CascadeType.PERSIST)
private Employee employee;
At commit or flush all managed entities are synchronized to the database. This includes applying the persist operation to all relationships of the managed entities that are annotated with cascade=PERSIST
. For the above case this means EM.persist is called for the removed Insurance entity. As a result the Insurace entity becomes managed again and is not removed from the database.
W/o the cascade=PERSIST
annotation the persistent Employee entity would reference a non-persistent Insurance instance object at commit or flush. This results in an IllegalStateException
and the transaction will fail on commit.
The correct code breaks the relationship by nullifying the insurance field in Employee
entity and then remove the Insurance
entity:
Insurance ins = emp.getInsurance();
emp.setInsurance(null);
em.remove(ins);
You can find an example of this pitfall and its solution in the class RemoveExperiment in this project: https://github.com/akquinet/jpapitfalls.git.