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 issues of EM.persist with respect to relationships.
When persisting an entity with EntityManager.persist the operation is not automatically propagated to associated entities. As a result associated entities might not be stored in the database and the transaction fails on commit or flush. In the following example the employee passed to the EntityManager.persist call references a non-persistent department entity:
Department dept = new Department(...); Employee emp = new Employee(...); dept.addEmployee(emp); // Associated employee is not automatically persisted em.persist(dept);
If the department is still not persistent a flush operation tries to store a persistent department referring a non persistent employee. This will result in an IllegalStateException and the transaction commit will fail.
JPA supports reachability by setting the cascade attribute on the relationship annotation @OneToOne
, @OneToMany
, @ManyToOne
and @ManyToMany
to PERSIST
or ALL
:
@OneToMany(mappedBy="department", cascade=CascadeType.PERSIST) private Set<Employee> employees;
With the cascade setting the persist operation is automatically applied to the associated entities. So in the example above the associated employee will also be persistet as a result of the em.persist(dept) call.
Recommendation: Set the cascade=CascadeType.PERSIST
attribute on the relationship annotation to avoid transaction commit issues.
You can find an example of this pitfall and its solution in the class CascadePersistExperiment in this project: https://github.com/akquinet/jpapitfalls.git.