Let’s start with these two items:
- Instances of divergent objects in instances of
EntityManager
different.
- Competing operations with
EntityManager
different generating persistence error.
NEVER use the same EntityManager
in different threads. The EntityManager
was designed to live and die confined within the thread that created it.
I recommend studying how to make the lock
of experiences. It is worth studying the concepts of optimistic lock (optimistic lock) and pessimistic lock (pessimistic lock). In the places where you prefer the optimistic lock, remember the note @Version
. I won’t go into more detail here, because that would be enough to write a few book chapters and the idea is just to show you which is the way to find what you want.
In addition, it is worth remembering that each instance of an entity can only be managed by a single EntityManager
, and because of this it is not correct to share these instances among several EntityManager
s, which also means you have to be very careful if you’re going to share them in more than one thread. One way to do this is kind of like this:
MinhaEntidade veioDeOutraThread = ...;
MinhaEntidade minhaEntidadeNestaThread = em.find(MinhaEntidade.class, veioDeOutraThread.getId());
A safe way to work with this would be to never share any object that is touched by JPA between different threads that can access the database directly.
- Always close the
EntityManager
.
The obvious thing is to use the old try-finally
, but maybe you want something more magical. In this case I can suggest you from something more or less like this (Java 8):
private static final EntityManagerFactory EMF = ...;
private final ThreadLocal<EntityManager> ativo = new ThreadLocal<>();
public <E> E transact(Function<EntityManager, E> func) {
EntityManager em = ativo.get();
if (em != null) return func.apply(em);
em = EMF.createEntityManager();
try {
ativo.set(em);
boolean success = false;
EntityTransaction et = em.getTransaction();
try {
et.begin();
E ret = func.apply(em);
success = !et.getRollbackOnly();
return ret;
} finally {
if (et.isActive()) {
if (success) {
et.commit();
} else {
et.rollback();
}
}
}
} finally {
ativo.set(null);
em.close();
}
}
If you are using Java 7 or lower, it shouldn’t be too difficult to adapt this code to your needs. If you intend to do your method transact
throw some verified exception, so the best thing to do is to use a customized functional interface.
This above code can be improved to prevent EntityManager
be recreated multiple times in the same Thread
if you don’t close it and use a em.clear()
before starting any operation and/or after finishing them. However, if this is not done in a very precise way, you can introduce competition bugs in the application, while opening and closing several times the way it is, at most it harms a little the performance.
Also, you might notice that the above code emulates the semantics of the transaction scope required from EJB/Java EE (at least I think it emulates, unless I’ve done something stupid or forgotten something). It is not very difficult to modify it to emulate other scopes such as requires new, Mandatory, etc..