2012-06-16 51 views

回答

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

doSomething-> doSomethingAgainInTheSameTransaction通话发生在一个单一的交易,因此,豆必须共享相同的EntityManager。实际上它们共享相同的代理EntityManager,它将调用委托给相同的持久化上下文。

所以你在单身豆类合法使用EntityManager的象下面这样:

@Singleton 
@ConcurrencyManagement(ConcurrencyManagementType.BEAN) 
public class Repository { 
    @PersistenceContext(unitName="blah", type = PersistenceContextType.TRANSACTION) 
    private EntityManager em; 
} 

另一个证据是,有没有在的EntityManager的javadoc线程安全的任何提及。所以,当你留在Java EE容器中时,你不应该关心并发访问EntityManager

+3

请注意,这个答案是(尽管被接受)其实不是真的,因为polbotinka在另一个答案中提到。如果您对'Java EE EntityManager'感兴趣'线程安全性',请继续阅读。 – Aquillo

16

令我大为吃惊的(经过多年使用)的EntityManager不是线程安全。如果您仔细考虑它,这实际上是可以理解的:EntityManager只是本地JPA实现的一个包装,例如,在Hibernate中进行会话,这反过来又是围绕连接的封装。这就是说EntityManager不能是线程安全的,因为它代表了一个数据库连接/事务。

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

你如何处理你的情况呢?我不知道,但是在EJB中,每个无状态和有状态会话bean都会被合并,这意味着您不能在同一时间从多个线程真正调用同一个EJB的方法。因此EntityManager从不同时使用。也就是说,注入EntityManager是安全的,至少到无状态和有状态会话bean。

注入EntityManager到servlet和singleton bean不安全因为可能有几个线程同时访问它们,搞乱了相同的JDBC连接。

又见

+2

很好的解释,但是在“在EJB中记录每个会话bean被合并时,这意味着你不能在同一时间从多个线程调用同一个EJB的方法” - @Singleton EJB或带有池的EJB具有bean管理并发性的大小1的多个线程可以同时执行EJB逻辑。 –

+0

@StevoSlavić:好吧,我实际上说“*注入EntityManager到单身豆不安全*”。如果单身人士也被视为会话豆,我会澄清那部分。但是,你真的可以禁用无状态和有状态会话bean的容器管理同步吗?我知道你可以做到这一点只对单身... –

8

我觉得我需要深入研究,因为我的第一个答案并不完全正确。我想参考JSR-220。在部分5.2获取一个EntityManager你会发现:

的实体管理器可能无法多个同时 执行的线程之间共享。只能以单线程方式访问实体管理器。

就是这样。您可能会停止阅读此处,并且绝不会在单身Bean中使用EntityManager,除非正确同步。

但我相信在规范中存在混淆。实际上有两种不同的EntityManager实现。第一个是提供者实现(称为Hibernate),它不一定是线程安全的。

另一方面,有一个容器实现EntityManager。根据上述,这也不应该是线程安全的。但容器的实现充当代理,并将所有调用委托给实际提供商的EntityManager

所以进一步在容器和持久性之间5.9运行时合同 提供规范:

对于事务范围的持久化上下文的管理,如果 没有的EntityManager已经与相关JTA事务: 容器通过调用 EntityManagerFactory.createEntityManager创建一个新的实体管理器,当第一次调用 带有Persistence- ContextType.TRANSACTION的实体管理器发生在 范围内时业务方法在JTA 事务中执行。

这意味着将会有不同的EntityManager实例为每个事务启动。创建一个的EntityManager代码根据5.3是安全的:

EntityManagerFactory的界面

方法是线程安全的。

但是如果有一个EntityManager与JTA事务关联?根据规范,绑定与当前JTA事务关联的EntityManager的代码可能不是线程安全的。

但我真的不能想到一个应用程序服务器实现可以正常工作与EntityManager注入到无状态bean和不正确的单身人士。

所以,我的结论是:

  1. 如果你想跟着JSR-220严格然后从不使用的EntityManager在单身,直到同步访问。
  2. 我个人将继续使用EntityManager的在单身,因为我的应用程序服务器实现与它完美的作品。在这之前你可能想检查你的实现。
+0

你在结论2中提到的应用程序服务器是什么? –

+0

的Apache TomEE 1.5 – polbotinka

+0

或许你可以用完美,因为你离开它的所有方法(Lock.Write),让所有到达的方法,因为他们有一个同步修改的基本辛格尔顿行为是线程安全的时间在辛格尔顿EJB。 –