JPA Pitfalls (13): EntityManager.merge Result

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 common error when using EntityManager.merge.

The EnityManager interface provides a method merge to merge the state of a detached entity into the current persistence context.

The following lines of code show an error prone usage pattern of the merge method:

Department dept = ... // detached Department instance
em.merge(dept);
dept.setFirstname("New name");

Please note, the code ignores the instance being returned by the merge method. It returns the managed instance the state was merged to. There are two cases:

  1. The current persistence context does not include an entity with the id of the instance passed to the merge call. Then the instance is stored into the persistence context and returned by the merge method.
  2. The current persistence context already has an entity with the id of the instance passed to the merge call. The state of the passed instance is merged into the existing entity. The method merge returns the existing entity. That means the instance passed as an argument is still in the detached state.

The correct code used the return instance for any other changes:

Department dept = ... // detached Department instance
dept = em.merge(dept);
dept.setFirstname("New name");

The above error is very tricky, because it depends on the state of the current persistence context. Furthermore, it does not throw an exception in the error case, instead the update statement changes the detached instance, so nothing get store in the database. 

Recommendation: Always use the return value of EnityManager.merge.

You can find an example of this pitfall and its solution in the class DuplicatesInQueryResultExperiment in this project: https://github.com/akquinet/jpapitfalls.git.