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

wrschneider picture wrschneider · Dec 14, 2012 · Viewed 9.3k times · Source

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.

Answer

Arjan Tijms picture Arjan Tijms · Dec 15, 2012

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.