17

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.

Community
  • 1
  • 1
Ken Y-N
  • 14,644
  • 21
  • 71
  • 114
  • and what does the documentation of your JPA implementation tell you??? All docs of all implementations I've ever read says very clearly that it is NOT. IIRC The JPA spec also states this. – Neil Stockton Jul 09 '14 at 04:57
  • This isn't the documentation, but [this mailing list](http://dev.eclipse.org/mhonarc/lists/eclipselink-users/msg01977.html) and [this BBS](http://www.eclipse.org/forums/index.php/t/368811/) both say it is safe to have one EntityManager per thread from the same Factory. – Ken Y-N Jul 09 '14 at 05:38
  • 1
    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. – Neil Stockton Jul 09 '14 at 06:53

3 Answers3

21

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.

Chris
  • 20,138
  • 2
  • 29
  • 43
  • So, multiple instances of the same Bean will be thread-safe, but two different Beans will not be. Is that a correct summary? – Ken Y-N Jul 09 '14 at 02:34
6

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.

yashjain12yj
  • 723
  • 7
  • 19
1

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!

Community
  • 1
  • 1
Ken Y-N
  • 14,644
  • 21
  • 71
  • 114