@ApplicationScoped CDI bean and @PersistenceContext - is this safe?

10,541

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.

Share:
10,541

Related videos on Youtube

wrschneider
Author by

wrschneider

Updated on June 05, 2022

Comments

  • wrschneider
    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 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.

  • Aksel Willgert
    Aksel Willgert over 11 years
    isnt there some restriction on injecting request scope in app scope?
  • Arjan Tijms
    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
    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
    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
    Vsevolod Golovanov over 6 years
    To 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
    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
    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.