Java CDI @PersistenceContext和线程安全

问题描述 投票:23回答:3

在多个类线程安全中,如下所示是EntityManager @Inject [ed]吗?

@PersistenceContext(unitName="blah")
private EntityManager em;

This问题和this one似乎是特定于Spring的。我正在使用Jave EE CDI服务

java java-ee-6 cdi
3个回答
11
投票

尽管EntityManager实现本身不是线程安全的,但Java EE容器注入了一个代理,该代理将所有方法调用委派给绑定了事务[的事务。因此,每个事务都使用其自己的EntityManager实例。至少对于事务范围的持久性上下文而言,这是正确的(默认设置)。

如果容器将在每个bean中注入

EntityManager

的新实例,则以下操作将无效:@Stateless public class Repository1 { @EJB private Repository2 rep2; @PersistenceContext(unitName="blah", type = PersistenceContextType.TRANSACTION) private EntityManager em; @TransactionAttribute public void doSomething() { // Do something with em rep2.doSomethingAgainInTheSameTransaction(); } } @Stateless public class Repository2 { @PersistenceContext(unitName="blah", type = PersistenceContextType.TRANSACTION) private EntityManager em; @TransactionAttribute public void doSomethingAgainInTheSameTransaction() { // Do something with em } }

doSomething-> doSomethingAgainInTheSameTransaction调用发生在单个事务中,因此Bean必须共享相同的EntityManager

。实际上,它们共享同一个代理EntityManager,该代理将调用委派给相同的持久性上下文。因此您可以在单例bean中合法使用

EntityManager

,如下所示:@Singleton @ConcurrencyManagement(ConcurrencyManagementType.BEAN) public class Repository { @PersistenceContext(unitName="blah", type = PersistenceContextType.TRANSACTION) private EntityManager em; }
另一个证明是,在[[EntityManager
javadoc中没有提及线程安全性。因此,当您留在

Java EE容器中时,您无需关心对EntityManager的并发访问。


20
投票
不是线程安全的。如果您考虑得更深,这实际上是可以理解的:EntityManager只是本机JPA实现的包装,例如Hibernate中的会话,这反过来又是连接的包装器。就是说EntityManager不能是线程安全的,因为它表示一个数据库连接/事务。

那么为什么它在Spring中起作用?因为它将目标EntityManager包装在代理中,所以原则上使用ThreadLocal保留每个线程的本地引用。这是必需的,因为Spring应用程序建立在单例之上,而EJB使用对象池。

您如何处理您的情况?我不知道,但是在EJB中,每个无状态和有状态会话bean都被池化,这意味着您不能真正从多个线程中同时调用同一EJB的方法。因此,EntityManager决不能同时使用。话虽如此,

注入EntityManager是安全的

,至少到无状态和有状态会话Bean中。

但是但是将EntityManager注入到servlet和单例bean中并不安全,因为可能有多个线程可以同时访问它们,并弄乱了相同的JDBC连接。

另请参见

Mind thread-safety when injecting EntityManager


8
投票
我将参考

JSR-220(EJB 3.0)。在

5.2获取EntityManager部分中,您可以找到:

[实体管理器不得同时在多个实体之间共享执行线程。实体管理器只能在单线程方式。
就是这样。您可能会在这里停止阅读,并且除非正确同步,否则不要在单例bean中使用

EntityManager

但是我相信规范中存在混淆。实际上有两种不同的EntityManager

实现。第一个是提供程序实现(例如Hibernate),它不一定是线程安全的。

另一方面,有一个EntityManager

的容器实现。根据以上所述,也不应该认为它是线程安全的。但是容器的实现充当代理,并将所有调用委派给真实提供者的

EntityManager。因此在5.9容器与持久性之间的运行时合同提供者

用于管理事务范围的持久性上下文,如果没有与JTA事务关联的EntityManager:容器通过调用创建新的实体管理器首次调用EntityManagerFactory.createEntityManager时发生具有Persistence- ContextType.TRANSACTION的实体管理器在JTA中执行的业务方法范围内交易。

这意味着依次启动的每个事务都有一个不同的

EntityManager

实例。根据

5.3:,创建EntityManager的代码是安全的EntityManagerFactory接口的方法是线程安全的。

但是,如果有与JTA事务关联的

EntityManager

怎么办?根据规范,绑定与当前JTA事务关联的

EntityManager的代码可能不是线程安全的。 但是我真的想不起来一个应用服务器实现,它可以正确地将EntityManager

注入到无状态Bean中,并且不能在单例中正确运行。

所以我的结论是:

如果要严格遵循

    JSR-220,则在同步对其的访问之前,切勿单独使用
  1. EntityManager
我个人将继续在单例中使用EntityManager,因为我的应用程序服务器实现与之完美配合。您可能需要先检查实施情况。
最新问题
© www.soinside.com 2019 - 2024. All rights reserved.