aus*_*ser 23 java cdi java-ee-6
在muliple类threadsafe中,EntityManager @Inject [ed]如下所示?
@PersistenceContext(unitName="blah")
private EntityManager em;
Run Code Online (Sandbox Code Playgroud)
Tom*_*icz 19
令我惊讶的是(在春天使用jpa多年后)并不是线程安全的.如果你更深入地思考它,这实际上是可以理解的:它只是本机JPA实现的包装器,例如Hibernate中的会话,它反过来是jdbc连接的包装器.所说的不能是线程安全的,因为它代表一个数据库连接/事务.EntityManager
EntityManager
EntityManager
那为什么它在Spring中有用呢?因为它EntityManager
在代理中包装目标,原则上ThreadLocal
用于保持每个线程的本地引用.这是必需的,因为Spring应用程序构建在单例之上而EJB使用对象池.
你怎么能在你的情况下处理这个问题?我不知道cdi,但在EJB中,每个无状态和有状态会话bean都是池化的,这意味着你无法在同一时间从多个线程中真正调用同一个EJB的方法.因此EntityManager
从不同时使用.话虽这么说,注入EntityManager
是安全的,至少是无状态和有状态会话bean.
但是注入EntityManager
servlet和singleton bean并不安全,因为几个线程可能同时访问它们,弄乱了相同的JDBC连接.
小智 11
尽管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
}
}
Run Code Online (Sandbox Code Playgroud)
doSomething-> doSomethingAgainInTheSameTransaction调用在单个事务中发生,因此bean必须共享相同的EntityManager.实际上,它们共享相同的代理EntityManager,它将调用委托给相同的持久化上下文.
所以你合法使用单例bean中的EntityManager,如下所示:
@Singleton
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class Repository {
@PersistenceContext(unitName="blah", type = PersistenceContextType.TRANSACTION)
private EntityManager em;
}
Run Code Online (Sandbox Code Playgroud)
另一个证据是EntityManager javadoc中没有提及线程安全性.因此,当您留在Java EE容器中时,您不应该关心对EntityManager的并发访问.
小智 8
我觉得我需要深入研究这个因为我的第一个答案并非绝对正确.
我将参考JSR-220.在5.2节获取EntityManager中,您可能会发现:
实体管理器可能不会在多个并发执行的线程之间共享.只能以单线程方式访问实体管理器.
那就是它.您可以在这里停止阅读,除非正确同步,否则永远不要在单例bean中使用EntityManager.
但我相信规范存在混淆.实际上有两种不同的EntityManager实现.第一个是提供程序实现(说Hibernate),它没有义务是线程安全的.
另一方面,有一个EntityManager的容器实现.根据以上内容,这也不应该是线程安全的.但容器的实现充当代理并将所有调用委托给真实提供者的EntityManager.
因此,容器和持久性提供程序之间的5.9运行时契约中的规范进一步说明:
对于事务范围的持久化上下文的管理,如果没有已与JTA事务关联的EntityManager:当第一次调用具有Persistence-ContextType.TRANSACTION的实体管理器时,容器通过调用EntityManagerFactory.createEntityManager来创建新的实体管理器在JTA事务中执行的业务方法的范围内.
这意味着每个启动的事务都会有不同的EntityManager实例.根据5.3,创建EntityManager的代码是安全的:
EntityManagerFactory接口的方法是线程安全的.
但是如果有一个与JTA事务关联的EntityManager呢?根据规范,绑定与当前JTA事务关联的EntityManager的代码可能不是线程安全的.
但我真的不能想到一个应用程序服务器实现可以正确地使用注入无状态bean的EntityManager而不能在单例中正确运行.
所以我的结论是:
归档时间: |
|
查看次数: |
9625 次 |
最近记录: |