Java CDI @PersistenceContext and thread safety
Although EntityManager implementations itself are not thread safe the Java EE container injects a proxy which delegates all methods invocations to a transaction bound EntityManager. Therefore each transaction works with it's own EntityManager instance. This is true for at least transaction-scoped persistence context (which is default).
If container would inject a new instance of EntityManager in each bean the below wouldn't work:
@Stateless
public class Repository1 {
@EJB
private Repository2 rep2;
@PersistenceContext(unitName="blah", type = PersistenceContextType.TRANSACTION)
private EntityManager em;
@TransactionAttribute
public void doSomething() {
// Do something with em
rep2.doSomethingAgainInTheSameTransaction();
}
}
@Stateless
public class Repository2 {
@PersistenceContext(unitName="blah", type = PersistenceContextType.TRANSACTION)
private EntityManager em;
@TransactionAttribute
public void doSomethingAgainInTheSameTransaction() {
// Do something with em
}
}
doSomething->doSomethingAgainInTheSameTransaction call happens in a single transaction and therefore the beans must share the same EntityManager. Actually they share the same proxy EntityManager which delegates calls to the same persistence context.
So you are legal use EntityManager in singleton beans like below:
@Singleton
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class Repository {
@PersistenceContext(unitName="blah", type = PersistenceContextType.TRANSACTION)
private EntityManager em;
}
Another proof is that there is no any mention of thread safety in EntityManager javadoc. So while you stay inside Java EE container you shouldn't care about concurrency access to EntityManager.
I feel I need to go deeper into this because my first answer was not absolutely true.
I will refer to JSR-220 (EJB 3.0). In section 5.2 Obtaining an EntityManager you may find:
An entity manager may not be shared among multiple concurrently executing threads. Entity managers may only be accessed in a single-threaded manner.
Well that's it. You may stop reading here and never use EntityManager in singleton beans unless properly synchronized.
But I believe there is a confusion in the spec. There are actually two different EntityManager implementations. The first is one is provider implementation (saying Hibernate) which is not obliged to be threadsafe.
On the other hand there is a container implementation of EntityManager. Which is also not supposed to be threadsafe according to the above. But container's implementation acts as a proxy and delegates all calls to the real provider's EntityManager.
So further in the spec in 5.9 Runtime Contracts between the Container and Persistence Provider:
For the management of a transaction-scoped persistence context, if there is no EntityManager already associated with the JTA transaction: The container creates a new entity manager by calling EntityManagerFactory.createEntityManager when the first invocation of an entity manager with Persistence- ContextType.TRANSACTION occurs within the scope of a business method executing in the JTA transaction.
This means in turn that there will be a different EntityManager instance for each transaction started. The code that creates an EntityManager is safe according to 5.3:
Methods of the EntityManagerFactory interface are threadsafe.
But what if there is an EntityManager associated with JTA transaction? The code that binds an EntityManager associated with current JTA transaction may be not threadsafe according to the spec.
But I can't really think of an application server implementation that works correctly with EntityManager injected into stateless beans and not correctly within singletons.
So my conclusions are:
- If you want to follow the JSR-220 strictly then never use EntityManager in singletons until synchronizing the access to it.
- I personally will continue to use EntityManager in singleton because my application server implementation works perfectly with it. You may want to check your implementation before doing so.
To my great surprise (after years of using jpa in spring) EntityManager
is not thread safe. This is actually understandable if you think about it deeper: EntityManager
is just a wrapper around native JPA implementation, e.g. session in Hibernate, which in turns is a wrapper around jdbc connection. That being said EntityManager
can't be thread safe as it represents one database connection/transaction.
So why does it work in Spring? Because it wraps target EntityManager
in a proxy, in principle using ThreadLocal
to keep local reference per each thread. This is required as Spring applications are built on top of singletons while EJB uses object pool.
And how can you deal with that in your case? I don't know cdi but in EJB each stateless and stateful session bean is pooled, which means you cannot really call method of the same EJB from multiple threads in the same time. Thus EntityManager
is never used concurrently. That being said, injecting EntityManager
is safe, at least into stateless and stateful session beans.
However injecting EntityManager
to servlets and singleton beans is not safe as possibly several threads can access them at the same time, messing up with the same JDBC connection.
See also
- Mind thread-safety when injecting EntityManager
- The EntityManager is not thread-safe