2012-06-16 58 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。 –