EntityManager 真的是线程安全的吗?

2022-09-03 05:20:26

我在这里谈论的是基本用法:

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

谷歌搜索上面的问题,StackOverflow说是,但不是 - 接受的答案说是,但后续是否;Spring.io 说“是”和“否”,而亚当·比恩(Adam Bien)似乎是Java EE专家,他给出了一个不合格的“是”。

我自己对一个简单的预定豆子的经验表明,答案是否定的:

@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;
  }
}

抽象接口:

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

开头为:

@Resource ManagedScheduledExecutorService managedExecutorService;
@EJB TimerTick myRunner;

public void startup()
{
    managedExecutorService.scheduleAtFixedRate(myRunner.runner(), 3, 40, TimeUnit.SECONDS);
}

如果我打印出 ,即使我在两次调用之间仍然在同一线程上,我得到:Thread.currentThread().getId()

SEVERE: java.lang.IllegalStateException: 试图在已关闭的 EntityManager 上执行操作

我知道我可以像自己一样做代码和管理,但我想利用所有自动事务的东西。@PersistenceUnit private EntityManagerFactory emf;EntityManager@PersistenceContext


答案 1

不,实体管理器不是线程安全的。Adam Bien虽然也是正确的。你只是没有正确地看待这个问题。他要回答的问题不是EntityManager是否是线程安全的,而是他在无状态会话Bean中使用容器托管的EntityManger是安全的,事实确实如此。然后,他解释了允许容器发挥其魔力的规范的推理和措辞 - “每个实例只能看到一个序列化的方法调用”。这允许容器注入在每个方法调用中具有不同的 EntityManager 上下文,类似于每个调用如何绑定到自己的事务和隔离的资源。

注入实际上只是注入一个EntityManager代理,该代理使容器能够控制其下JPA EntityManagers的生命周期,从而允许将其绑定到线程和事务。

因此,EntityManager 不是线程安全的,但容器注入的 EntityManager 代理需要安全才能在无状态会话 Bean 中使用。


答案 2

对于应用程序管理的实体管理器

EntityManager 实例不是线程安全的。

EntityManagerFactory 实例是线程安全的。

当应用程序需要访问未随 JTA 事务在特定持久性单元中的 EntityManager 实例之间传播的持久性上下文时,将使用应用程序管理的实体管理器。在这种情况下,每个 EntityManager 都会创建一个新的、孤立的持久性上下文。EntityManager 及其关联的持久性上下文由应用程序显式创建和销毁。当无法直接注入 EntityManager 实例时,也会使用它们,因为 EntityManager 实例不是线程安全的。EntityManagerFactory 实例是线程安全的。

有关更多详细信息,请访问此处

对于容器管理的实体管理器

EntityManager 必须安全才能在无状态会话 Bean 中使用。