@ApplicationScoped CDI bean and @PersistenceContext - is this safe?
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.
Related videos on Youtube
wrschneider
Updated on June 05, 2022Comments
-
wrschneider almost 2 years
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 underlyingEntityManager
, 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
injectedEntityManager
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. -
Aksel Willgert over 11 yearsisnt there some restriction on injecting request scope in app scope?
-
Arjan Tijms over 11 years@AkselWillgert >isnt there some restriction on injecting request scope in app scope? - Nope, the native JSF managed beans where CDI partially draws its inspiration from had that limitation. CDI fixed this, and this is expressed by the C in CDI -> injections (when managed by CDI) are context aware. In this case, even though the bean is application scoped, the proxy that is injected will resolve to an instance in the request scope of the caller.
-
wrschneider over 11 years@ArjanTijms - thanks, I think I see my problem. I'm conflating thread-awareness with thread-safety. I feel like the
@PersistenceContext
wrapper must be aware of the current thread to join or start a new JTA transaction appropriately. But that doesn't mean that the resulting object's state is thread-safe. Does it sound like I got that right? -
Vsevolod Golovanov over 6 years
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.
This isn't true, Stateless beans often are pooled. "Stateless" refers not to the lifetime of the bean itself, but to the "state of the dialog" between a client and the bean, specifically the lack of such state. It's stateless like HTTP is stateless. -
Vsevolod Golovanov over 6 yearsTo quote EJB 3.2 spec 4.3.9.2 Stateless Session Beans:
Since stateless session bean instances are typically pooled, the time of the client’s invocation of the create method need not have any direct relationship to the container’s invocation of the PostConstruct/ejbCreate method on the stateless session bean instance.
-
Arjan Tijms over 6 years>
but to the "state of the dialog" between a client and the bean, specifically the lack of such state. It's stateless like HTTP is stateless.
– Indeed, I didn't intend to state anything to the contrary ;) But requests that are all coming in at the same time will be routed to different instances. In no circumstance will two threads (serving requests) be active in the same bean instance at the same time. -
guest over 5 years@ArjanTijms I believe that injection of EntityManager in ApplicationScoped bean is thread safe (at least in most application servers). I checked source code of WildFly and it looks that injected EntityManager is a proxy. This proxy stores real EntityManagers in ThreadLocal. Moreover this proxy is needed, because different beans can share the same persistence context if they are in the same transaction.