Is in an EJB injected EntityManager thread-safe?

You can inject EntityManager directly into EJBs. But: is it thread safe?:


@Stateless
public class BookServiceBean implements BookService {


  @PersistenceContext EntityManager em;

  public void create(Book book) { this.em.persist(book);}

 } 

It is, the EJB 3.1 specification, JSR-318, page 82 explains the reasons:

"…The container serializes calls to each stateful and stateless session bean instance. Most containers will support many instances of a session bean executing concurrently; however, each instance sees only a serialized sequence of method calls. Therefore, a stateful or stateless session bean does not have to be coded as reentrant…"

"…The container must serialize all the container-invoked callbacks (that is, the business method interceptor methods, lifecycle callback interceptor methods, timeout callback methods, beforeCompletion, and so on), and it must serialize these callbacks with the client-invoked business method calls..."

Working with EJBs without any further configuration is thread-safe regardless whether you are invoking one method or multiple methods concurrently. The container cares about the serialization of calls.

Big thanks to Dean for this question!

Comments:

You might also explain _why_ it is that way.

For @Stateless EJBs it's a no-brainer anyway. Each thread will take a single instance from the EJB-Pool and returns it back to the pool if it's not needed anymore.

But what about injecting @PersistenceContext into a JSF managed bean or a Stateful Session bean?

In EE Servers an injected @PersistenceContext will _never_ give you a _native_ EntityManager but always only a 'Facade' or 'Proxy'. Internally this EntityManager-Facade might e.g. keep a ThreadLocal<EntityManager>. That's the reason why you can use the injected EntityManager in parallel: because you will just get different instances for each thread anyway.

Btw, this is exactly the reason why the EntityManager interfaces has a getDeletate() method. This will give you access to the 'native' EM...

LieGrue,
strub

Posted by struberg on June 25, 2012 at 05:15 PM CEST #

There's more to the story , however. If a SessionBean is also a Singleton then concurrency control comes back into play. From the spec mentioned above:

"It is legal to store Java EE objects that do not support concurrent access (e.g. Entity Managers, Stateful
Session Bean references) within Singleton bean instance state. However, it is the responsibility of the
Bean Developer to ensure such objects are not accessed by more than one thread at a time."

Posted by Dean Schulze on June 26, 2012 at 07:13 PM CEST #

@Struberg, You make it sound as if an injected EntityManager is actually thread-safe.

Is there a difference an injected EntityManager (class-level) and a local EntityManager obtained from and EntityManagerFactory? I know their lifecycle management will be different, but as far as thread-safety goes aren't they both not-thread-safe?

Posted by Dean Schulze on June 27, 2012 at 03:50 AM CEST #

Howdy,

Knowing that the App Server has its proxy for the "real", underlying EntityManager and it can e.g. wrap it in ThreadLocal, I wonder why this approach can't be applied to every usage of @PersistenceContext.

In other words, why can't we have always-thread-safe EntityManager obtained by @PersistenceContext injection which under the hood is achieved with ThreadLocal<EntityManager> for EVERY injection point (doesn't matter if its SLSB, SFSB, EJB Singleton, Servlet, CDI Bean, JSF Backing bean, etc.)?

What is the rationale behind the actual approach? Wouldn't it simplify things to have always-thread-safe EM?

Cheers!
Piotr

Posted by Piotr Nowicki on June 28, 2012 at 08:44 PM CEST #

Hey guys,

Does this mean that this code snippet would be legit?

@Stateless
public class BookServiceBean implements BookService {

@PersistenceContext EntityManager em;

public List<Book> findAll(Date d1, Date d2) {
List<Book> books = Collections.synchronizedList(new ArrayList<>());
CompletableFuture<Void> f1 = CompletableFuture.runAsync(()-> {
books.addAll((List<Book>) em.createQuery("select b from Book b where date = :D1").getResultList());
}
CompletableFuture<Void> f2 = CompletableFuture.runAsync(()-> {
books.addAll(((List<Book>) em.createQuery("select b from Book b where date = :D2").getResultList()));
}
CompletableFuture<Void> future = CompletableFuture.allOf(f1, f2);
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
// do something...
}
return books;
}

}

Thanks

Posted by Christian on January 13, 2018 at 12:02 AM CET #

The closest I have been able to come to finding a spec regarding this is https://docs.oracle.com/javaee/7/tutorial/persistence-intro003.htm :

With a container-managed entity manager, an EntityManager instance's persistence context is automatically propagated by the container to all application components that use the EntityManager instance within a single Java Transaction API (JTA) transaction.

JTA transactions usually involve calls across application components. To complete a JTA transaction, these components usually need access to a single persistence context. This occurs when an EntityManager is injected into the application components by means of the javax.persistence.PersistenceContext annotation. The persistence context is automatically propagated with the current JTA transaction, and EntityManager references that are mapped to the same persistence unit provide access to the persistence context within that transaction. By automatically propagating the persistence context, application components don't need to pass references to EntityManager instances to each other in order to make changes within a single transaction. The Java EE container manages the lifecycle of container-managed entity managers.

To obtain an EntityManager instance, inject the entity manager into the application component:

@PersistenceContext
EntityManager em;

This would suggest that injecting an EntityManager in this manner, even into a Singleton session bean with bean-managed concurrency would be *safe*.

Posted by Frans on May 21, 2018 at 05:04 PM CEST #

The closest I have been able to come to finding a spec regarding this is https://docs.oracle.com/javaee/7/tutorial/persistence-intro003.htm :

"With a container-managed entity manager, an EntityManager instance's persistence context is automatically propagated by the container to all application components that use the EntityManager instance within a single Java Transaction API (JTA) transaction."

This would suggest that injecting an EntityManager with @PersistenceContext, even into a Singleton session bean with bean-managed concurrency, would be *safe*.

Posted by Frans on May 21, 2018 at 05:05 PM CEST #

Post a Comment:
  • HTML Syntax: NOT allowed
...the last 150 posts
...the last 10 comments
License