Let's start with these two items:
- Divergent instances of objects in instances of
EntityManager
different.
- Concurrent operations with
EntityManager
different generating persistence error.
NEVER use the same EntityManager
on different threads. EntityManager
is designed to live and die confined within the thread that created it.
I recommend studying how to do lock
" of entities. It pays to take a look at the concepts of optimistic lock and pessimistic lock.
Wherever you prefer optimistic locking, remember the @Version
annotation. . I will not go into more detail here, because this would give me some book chapters and the idea is just to show you 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 between several EntityManager
s, which also means that you have to be very careful if you want to share them in more than one thread. One way to do this is something 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
EntityManager
.
The most obvious is to use the old try-finally
, but maybe you want something more magical. In this case I can suggest starting from something 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 should not be too difficult to adapt this code to your needs. If you intend to make your transact
method throw any exceptions checked, then the best thing to do is to use a custom functional interface.
This code above can be improved to prevent EntityManager
from being recreated multiple times in the same Thread
if you do not close it and use em.clear()
before starting any operation and / or after it finishes them. However, if this is not done in a very precise way, you can introduce competing bugs into the application, while opening and closing several times the way you are, at the most, slows down performance a little.
In addition, you may notice that the above code emulates the required transaction scope semantics of 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 , mandatory , etc.