Is EntityManager really thread-safe?

14,071

Solution 1

No, an EntityManager is NOT thread safe. Adam Bien though is also correct. You are just not looking at the question correctly. The question he is answering isn't if an EntityManager is thread safe, he is stating that using container managed EntityManger in a stateless session bean is safe, which it is. He then explains the reasoning and wording of the spec that allows the container to work its magic - "each instance sees only a serialized sequence of method calls". That allows container injection to have different EntityManager contexts per method invocation, similar to how each invocation can be tied to their own transaction and isolated resources.

Injection is really just injecting an EntityManager proxy that gives the container control over the lifecycle of the JPA EntityManagers underneath, allowing it to be tied to the thread and the transaction.

So an EntityManager is NOT thread safe, but the container injected EntityManager proxies are required to be safe to use within stateless session beans.

Solution 2

For Application-Managed Entity Managers:

EntityManager instances are not thread-safe.

EntityManagerFactory instances are thread-safe.

Application-managed entity managers are used when applications need to access a persistence context that is not propagated with the JTA transaction across EntityManager instances in a particular persistence unit. In this case, each EntityManager creates a new, isolated persistence context. The EntityManager and its associated persistence context are created and destroyed explicitly by the application. They are also used when directly injecting EntityManager instances can’t be done because EntityManager instances are not thread-safe. EntityManagerFactory instances are thread-safe.

For more details visit here

For Container-Managed Entity Managers:

EntityManager is required to be safe to use within stateless session beans.

Solution 3

Although @Chris' answer helped me understand a bit more about EntityManager (thus the tick), I worked out the real problem - ManagedScheduledExecutorService does not necessarily auto-kill its threads on application termination - this appears to be true for Glassfish 4.0 also. So, although my underlying application had terminated, the thread still had an EntityManager from the dead app's EntityManagerFactory (I think), so when the timer ticked it spewed out the error I described above.

I fixed the problem by getting the ScheduledFuture and calling future.cancel(false) in my contextDestroyed().


Furthermore, it seems that Timer Tick beans and EntityManager don't mix, so I had to do this:

@Stateless
public class TimerTick implements TimerTickAbs, Runnable {
  @EJB private RealTimerTick realTick;
  @Override
  public void run() {
    realTick.run();
  }
  @Override
  public Runnable runner() {
    return this;
  }
}

and:

@Stateless
public class RealTimerTick implements TimerTickAbs, Runnable {
  @PersistenceContext private EntityManager entityManager;
  @Override
  public void run() {
    Query q = entityManager.createQuery("SELECT blah...");
  }
  @Override
  public Runnable runner() {
    return this;
  }
}

This now works perfectly, including automatic persistence of changes made to values returned from the Query, as far as I can tell, but I cannot explain it!

Share:
14,071

Related videos on Youtube

Ken Y-N
Author by

Ken Y-N

Hello sailor! Avatar built at https://chanrio.com and is (c), (TM) and whatever of Sanrio.

Updated on September 16, 2022

Comments

  • Ken Y-N
    Ken Y-N over 1 year

    I'm talking about the basic usage here:

    @Stateless
    public class BookServiceBean implements BookService {
      @PersistenceContext EntityManager em;
      public void create(Book book) { this.em.persist(book);}
    } 
    

    Googling the above question, StackOverflow says yes, but no - the accepted answer says Yes, but a followup is No; Spring.io says both yes and no, and Adam Bien, who seems to be a Java EE expert, gives an unqualified yes.

    My own experience with a simple scheduled bean suggests the answer is NO:

    @Stateless
    public class TimerTick implements TimerTickAbs, Runnable {
      @PersistenceContext private EntityManager entityManager;
      @Override
      public void run() {
        Query q = entityManager.createQuery("SELECT blah...");
      }
      @Override
      public Runnable runner() {
        return this;
      }
    }
    

    Abstract interface:

    @Local
    public interface TimerTickAbs {
      public Runnable runner();
    }
    

    Started with:

    @Resource ManagedScheduledExecutorService managedExecutorService;
    @EJB TimerTick myRunner;
    
    public void startup()
    {
        managedExecutorService.scheduleAtFixedRate(myRunner.runner(), 3, 40, TimeUnit.SECONDS);
    }
    

    If I print out the Thread.currentThread().getId(), even though I am still on the same thread between calls, I get:

    SEVERE: java.lang.IllegalStateException: Attempting to execute an operation on a closed EntityManager

    I know I can do code like @PersistenceUnit private EntityManagerFactory emf; and manage the EntityManager myself, but I'd like to take advantage of all the automatic transaction stuff that @PersistenceContext gives me.

    • Neil Stockton
      Neil Stockton over 9 years
      PER THREAD if the key phrase there. They are not "thread-safe" ... i.e cannot be operated on by multiple threads at the same time.
  • Ken Y-N
    Ken Y-N over 9 years
    So, multiple instances of the same Bean will be thread-safe, but two different Beans will not be. Is that a correct summary?