EntityManager @Inject [ed]在多个类的线程安全中如下所示?
@PersistenceContext(unitName="blah")
private EntityManager em;
答案 0 :(得分:18)
令我惊讶的是(在jpa中使用spring多年后)EntityManager
并非线程安全。如果你仔细考虑一下,这实际上是可以理解的:EntityManager
只是本机JPA实现的包装,例如: Hibernate中的会话,它反过来是jdbc连接的包装。据说EntityManager
不能是线程安全的,因为它代表一个数据库连接/事务。
那为什么它在Spring工作?因为它将目标EntityManager
包装在代理中,原则上使用ThreadLocal
来保持每个线程的本地引用。这是必需的,因为Spring应用程序构建在单例之上,而EJB使用对象池。
你怎么能处理你的情况呢?我不知道cdi但是在EJB中,每个无状态和有状态会话bean都是池化的,这意味着您无法在同一时间从多个线程中真正调用同一EJB的方法。因此EntityManager
永远不会同时使用。话虽这么说,注入EntityManager
是安全的,至少是无状态和有状态会话bean。
然而向servlet和singleton bean注入EntityManager
并不安全,因为可能有多个线程同时访问它们,弄乱了相同的JDBC连接。
答案 1 :(得分:12)
虽然 EntityManager 实现本身不是线程安全的,但 Java EE 容器会注入一个代理,该代理将所有方法调用委托给绑定了 EntityManager 的事务。因此,每个事务都使用它自己的 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 的并发访问。
答案 2 :(得分:8)
我觉得我需要深入研究这个问题,因为我的第一个答案并非绝对正确。
我将参考 JSR-220 。在 5.2获取EntityManager 部分,您可以找到:
实体经理可能不会同时在多个人之间共享 执行线程。实体经理只能在a中访问 单线程方式。
那就是它。你可以在这里停止阅读,除非正确同步,否则永远不要在单例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中而在单例中不正确。
所以我的结论是: