Java CDI@PersistenceContext和线程安全
在muliple类中,EntityManager@Inject[ed]是否是线程安全的Java CDI@PersistenceContext和线程安全,java,java-ee-6,cdi,jpa,spring,jdbc,Java,Java Ee 6,Cdi,Jpa,Spring,Jdbc,在muliple类中,EntityManager@Inject[ed]是否是线程安全的 @PersistenceContext(unitName="blah") private EntityManager em; 这个问题似乎是针对Spring的。我正在使用Jave EE CDI服务(在中使用多年后)令我大吃一惊的是,EntityManager不是线程安全的。如果您更深入地思考,这实际上是可以理解的:EntityManager只是本地JPA实现的包装,例如Hibernate中的会话,它反过来又
@PersistenceContext(unitName="blah")
private EntityManager em;
这个问题似乎是针对Spring的。我正在使用Jave EE CDI服务(在中使用多年后)令我大吃一惊的是,EntityManager
不是线程安全的。如果您更深入地思考,这实际上是可以理解的:EntityManager
只是本地JPA实现的包装,例如Hibernate中的会话,它反过来又是连接的包装。也就是说,EntityManager不能是线程安全的,因为它代表一个数据库连接/事务
那么为什么它在春天起作用呢?因为它将targetEntityManager
封装在代理中,原则上使用ThreadLocal
保留每个线程的本地引用。这是必需的,因为Spring应用程序构建在单例之上,而EJB使用对象池
在你的情况下,你如何处理?我不知道,但在EJB中,每个无状态和有状态会话bean都是池式的,这意味着您不能同时从多个线程调用同一EJB的方法。因此,EntityManager
永远不会同时使用。也就是说,将EntityManager
注入到无状态和有状态会话bean中是安全的
但是,将EntityManager
注入servlet和单例bean是不安全的,因为可能有多个线程可以同时访问它们,从而破坏了同一个JDBC连接
另见
@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->doSomethingaain发生在单个事务中,因此bean必须共享相同的EntityManager。实际上,它们共享同一个代理EntityManager,该代理将调用委托给相同的持久性上下文
因此,您在singleton Bean中合法使用EntityManager,如下所示:
@Singleton
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class Repository {
@PersistenceContext(unitName="blah", type = PersistenceContextType.TRANSACTION)
private EntityManager em;
}
另一个证据是,EntityManagerjavadoc中没有提到线程安全。因此,当您呆在JavaEE容器中时,您不应该关心对EntityManager的并发访问我觉得我需要更深入地了解这一点,因为我的第一个答案不是绝对正确的 我将参考JSR-220(ejb3.0)。在第5.2节“获取实体管理器”中,您可以找到: 实体管理器不能在多个实体之间同时共享 执行线程。实体管理器只能在数据库中访问 单线程方式 就这样。除非正确同步,否则您可以停止阅读这里的内容,并且永远不要在单例bean中使用EntityManager 但我认为规范中存在一个混乱。实际上有两种不同的EntityManager实现。第一个是提供程序实现(称为Hibernate),它不必是线程安全的
另一方面,存在EntityManager的容器实现。根据上述规定,它也不应该是线程安全的。但容器的实现充当代理,并将所有调用委托给真正提供者的EntityManager 因此,在5.9规范中,容器和持久性之间的运行时契约更进一步 提供者: 用于管理事务范围的持久性上下文,如果 没有EntityManager已与JTA事务关联: 容器通过调用 EntityManagerFactory.createEntityManager首次调用 发生持久性为-ContextType.TRANSACTION的实体管理器 在JTA中执行的业务方法的范围内 交易 这意味着,对于每个启动的事务,将有一个不同的EntityManager实例。根据5.3,创建实体管理器的代码是安全的: EntityManagerFactory接口的方法是线程安全的 但是,如果存在与JTA事务相关联的EntityManager呢?根据规范,绑定与当前JTA事务关联的EntityManager的代码可能不是线程安全的 但是我真的想不出一个应用服务器实现能够正确地与注入无状态bean的EntityManager一起工作,而不能正确地在单例中工作 因此,我的结论是:
很好的解释,但是当您声明“在EJB中,每个会话bean都是池的,这意味着您不能同时从多个线程调用同一EJB的方法”-@Singleton EJB或池大小为1的EJB(具有bean管理的并发性)可以有多个线程同时执行EJB逻辑。@StevoSlavić:好,我实际上是在说“将EntityManager注入[…]单例bean是不安全的”。如果是单身人士,我会澄清这一部分