Is it safe to do something like this with CDI?
@Named
@ApplicationScoped
public class DAO {
@PersistenceContext
private EntityManager entityManager;
}
I understand that EntityManager
itself is not thread-safe, and therefore should not be used in a shared global context like @ApplicationScoped
. However, since the injected object with @PersistenceContext
is actually a thread-aware wrapper around an underlying EntityManager
, does that make this ok?
I've seen other posts on the subject but haven't been able to figure out an authoritative answer for this specific case. For example:
Java CDI @PersistenceContext and thread safety
It looks like it's safe to use with @Stateless
, for instance - but I'm not sure if that's because of the way @Stateless
works, or because of something intrinsic to @PersistenceContext
itself.
EDIT
The source of my confusion is that the @PersistenceContext
injected EntityManager
wrapper seems to be aware of the current thread, in order to figure out whether there's already a transaction in progress. So perhaps I'm confusing thread-awareness with thread-safety and they're two different things.
I'm pretty sure that in this case CDI does not create a contextual proxy for the entity manager. After all, what scope would it be in? You may want something akin to a hypothetical @ThreadScoped
or just @RequestScoped
, but @PersistenceContext
is not a CDI annotation and CDI does not modify its semantics.
So what's happening here is the Java EE 6 platform "managed bean" injection, which is similar to injecting the entity manager in a Servlet. Both cases give you an instance that is not thread-safe to use directly.
It looks like it's safe to use with @Stateless, for instance - but I'm not sure if that's because of the way @Stateless works, or because of something intrinsic to @PersistenceContext itself.
It's because of the way @Stateless
works. Every request (call) to a method on a stateless bean is routed by the container to a unique instance. The container guarantees that no two threads are ever active in the same bean.
With CDI you can get a similar effect per request by encapsulating the entity manager in a request scoped bean and injecting that into the application scoped one:
import javax.enterprise.context.RequestScoped;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@RequestScoped
public class EntityManagerProvider {
@PersistenceContext
private EntityManager entityManager;
public EntityManager getEntityManager() {
return entityManager;
}
}
Inject this into the bean where you previously injected the entity manager:
@Named
@ApplicationScoped
public class DAO {
@Inject
private EntityManagerProvider entityManagerProvider;
}
This will give you a unique entity manager per request. You can easily turn this into a producer method as well, so you won't have to call getEntityManager()
on the injected provider.