entitymanager_实体管理器的类型:应用程序管理的EntityManager
entitymanager
JPA規范定義了幾種類型的EntityManagers / Persistence Context。 我們可以有:
- 擴展和事務范圍的EntityManager,
- 容器管理或應用程序管理的EntityManager。
- JTA或本地資源EntityManager,
除了上述區別之外,我們還可以在其中存在EntityManager / Persistence Context的兩個主要上下文– Java EE和Java SE 。 并非每個選項都適用于Java EE,并且并非每個選項都適用于Java SE。 在本文的其余部分中, 我將介紹Java EE環境 。
好的,因此在繼續本文之前的主題之前(這是使用EntityManagerFactory手動創建的Java EE中EntityManager的行為),我們僅簡要介紹一下上述EM類型。
擴展與事務范圍
此功能告訴我們EntityManager的操作是否可能跨越多個事務。 默認情況下,使用事務性持久性上下文,這意味著當提交當前事務時,將清除所有更改,并分離所有受管實體。 擴展范圍僅適用于有狀態EJB。 這非常有意義,因為SFSB可以保存狀態,因此不必終止一種業務方法就意味著結束事務。 使用SLSB的情況就不同了–我們有業務方法必須在業務方法完成時結束,因為在下一次調用中,我們不知道我們將以哪個EJB實例結束。 SLSB僅允許使用事務范圍的EntityManager。 您可以控制在EntityManager注入期間EntityManager是擴展的還是事務的:
@PersistenceContext(type=javax.persistence.PersistenceContextType.EXTENDED) EntityManager em;默認情況下為javax.persistence.PersistenceContextType.TRANSACTION 。 附帶說明–使用擴展的EntityManager可能使您可以創建一些有趣的解決方案; 看看具有事務性保存方法技巧的 Adam Bien的無事務bean 。 他使用這種方法使事務開始和結束時自動清除所有更改(并且他實際上通過調用特殊的人工方法來做到這一點。)僅在容器管理的EntityManagers的情況下才允許擴展和事務范圍的PersistenceContext。
容器管理與應用程序管理
在大多數Java EE應用程序中,您只是使用@PersistenceContext注入EntityManager,如下所示:
@PersistenceContext EntityManager em;實際上,這意味著您要讓容器為您的EntityManager注入 (容器是在后臺從EntityManagerFactory創建的。)這意味著EntityManager是由容器管理的。 另外,您可以從EntityManagerFactory自己創建EntityManager。 您可以通過注入獲得它:
@PersistenceUnit EntityManagerFactory emf;然后,要獲取EntityManager,您需要調用emf.createEntityManager() 。 這就是–您現在正在使用應用程序管理的EntityManager 。 應用程序(您)負責創建和刪除EntityManager。 每個應用程序管理的持久性上下文都有擴展的范圍。
如果要控制已創建的EM,則可以使用-例如,如果要為基礎JPA實現程序設置一些屬性,或者只是在業務方法將其投入使用之前插入自己。 但是,您將需要跨事務涉及的多個bean移動創建的EntityManager –容器不會為您這樣做,并且每次調用emf.createEntityManager()您都在創建一個與新PersistenceContext連接的EntityManager。 。 您可以使用CDI進行EntityManager的共享,但這是最后一節之一的主題。
JTA與本地資源
此屬性定義您是要JTA管理EntityManager的事務,還是要使用其直接API開始和提交。 如果您使用的是容器管理的EntityManager,則自動意味著您必須使用JTA EntityManager 。 如果您使用的是應用程序管理的EntityManager,則可以使用JTA或本地資源。 在現實生活中,這意味著,如果您使用的是JTA EntityManager,則您只需照顧更高級別的事務管理:
- 聲明性地 使用注釋或XML JTA交易屬性,或
- 以編程方式 使用javax.transaction.UserTransaction 。
如果您使用的是本地資源EntityManager,則需要更深入一點,并使用EntityManager.getTransaction()返回javax.persistence.EntityTransaction并調用commit(-) , begin(-) , rollback()等。使用transaction-type屬性在persistence.xml定義此功能:
<?xml version='1.0' encoding='UTF-8'?> <persistence ...><persistence-unit transaction-type='RESOURCE_LOCAL' ... > </persistence>另一個可能的值(當未定義transaction-type時為默認值)是JTA 。
Java EE中由應用程序管理的JTA EntityManager
盡管此字幕聽起來很復雜,但了解EntityManager和PersistenceContexts的所有先前類型后,您應該確切了解if指的是什么。
- “應用程序托管”意味著我們將注入@PersistenceUnit EntityManagerFactory而不是EntityManager,
- 之所以稱為 “ Java EE”,是因為這些示例(在github上發布)僅在Java EE Application Server中使用 ,
- “ JTA”,因為我們將使用JTA事務級別,因此不會使用javax.persistence.EntityTransaction 。
現在,您需要了解的第一件事是如何使用JTA事務。 JTA事務管理有兩種類型-容器(CMT)和Bean管理(BMT)。 容器管理的JTA事務 ( CMT )意味著您使用javax.ejb.TransactionAttribute定義tx是否應處于活動狀態以及事務邊界在何處。 這是默認的JTA管理類型。 或者,您可以選擇自己劃定JTA事務的邊界。 這稱為Bean管理的JTA事務 ( BMT )。應用程序(您)負責啟動,回滾或提交事務。 您如何控制JTA交易? 您可以使用javax.transaction.UserTransaction做到這一點。
您如何獲得一個? 好吧,至少有3種方法可以做到這一點:
- 它綁定到組件私有名稱空間( java:comp/UserTransaction )中的JNDI,因此您可以使用InitialContext或SessionContext對其進行查找,
- 您可以使用SessionContext進行訪問– SessionContext#getUserTransaction() ,
- 因為它綁定在眾所周知的JNDI名稱中,所以可以讓容器使用以下方式注入它: @Resource UserTransaction utx; 。
如果您有UserTransaction ,則可以開始劃分事務中要執行的內容。 請注意,您仍在控制JTA事務 –您甚至都沒有接觸EntityManager的資源本地事務。
EntityManager何時在JTA事務中?
如果沒有前面的介紹,您可能會認為由應用程序管理的EntityManager意味著您將獨自承擔一切–創建,共享EntityManager,開始tx,提交,關閉。 但是,了解了以上所有差異后,您知道可以在應用程序管理的EntityManager中使用JTA事務 。 但是問題是–如何使它知道活動的JTA事務? 如果我們有一個容器管理的EntityManager,我們知道容器將全部管理它,但是如果我們是一個人,我們該怎么做? 實際上, 這取決于我們在哪里創建EntityManager 。 在下面找到一些示例(完整的代碼可以在我的github帳戶上找到:
情況1:我們在沒有活動交易的情況下調用以下代碼(因此,對于CMT,我們具有TransactionAttribute.NEVER或TransactionAttribute.NOT_SUPPORTED ;對于BMT,我們不調用UserTransaction.begin() :
EntityManager em = emf.createEntityManager(); em.persist(new Customer(firstName, lastName));結果: EntityManager操作在持久化時不會引發任何異常,但是所有更改都不會提交 。 沒有活動的事務,因此不會進行任何更改。
情況2:我們使用BMT調用以下代碼:
utx.begin();EntityManager em = emf.createEntityManager();em.persist(new Customer(firstName, lastName));utx.commit();結果:在JTA提交期間,新數據已正確保留(在最后一行。)
情況3:我們使用BMT調用以下代碼:
EntityManager em = emf.createEntityManager(); utx.begin();em.persist(new Customer(firstName, lastName));utx.commit();結果: EntityManager在事務之外,因為它是在啟動JTA事務之前創建的。 盡管已提交JTA事務,但更改不會持久保存。 沒有異常被拋出。
在第二個示例的情況下,您可能會問自己-是否可以首先創建EntityManager,然后開始事務,最后以某種方式使EntityManager知道周圍的TX? 如果事實是這樣,則可以執行此操作,而這正是EntityManager#joinTransaction()方法的用途。 以下兩種情況將向您展示如何使用它:
情況4:我們使用BMT調用以下代碼:
EntityManager em = emf.createEntityManager(); utx.begin(); em.joinTransaction();em.persist(new Customer(firstName, lastName));utx.commit();結果:在這里,我們明確地告訴EntityManager加入活動的JTA事務。 結果, EntityManager將在JTA commit期間刷新其所有更改 。
情況5:我們使用BMT調用以下代碼:
EntityManager em = emf.createEntityManager(); utx.begin();em.joinTransaction();em.persist(new Customer(firstName, lastName));utx.commit();utx.begin();em.joinTransaction();em.persist(new Customer(firstName, lastName));utx.commit();結果: 兩種EntityManager操作均正確存在。 在這里,我們證明了應用程序管理的持久性上下文可以跨越多個JTA事務 (請注意,我們沒有創建另一個EntityManager,而只是重用了先前事務中使用的那個)。在這里,您可以看到JPA規范(JPA規范) 2.0最終版本)告訴您有關應用程序管理的持久性上下文的信息:
7.7應用程序管理的持久性上下文
使用JTA應用程序管理的實體管理器時,如果在當前JTA事務的范圍之外創建實體管理器,則應用程序有責任通過調用EntityManager.joinTransaction將實體管理器與事務關聯(如果需要)。 。 如果實體管理器是在JTA事務范圍之外創建的,則除非調用EntityManager.joinTransaction,否則它不與事務關聯。
使用CDI共享EntityManager
如前所述,如果要在組成一個事務的組件之間共享EntityManager,則應手動傳遞它(畢竟,它是“應用程序管理的”。)CDI可能是此處的解決方案。 您可以生成請求范圍內的EntityManager并將其注入所需的任何組件中。 看起來可能像這樣(在現實生活中,您還需要注意處理EM):
public class Resources {@PersistenceUnitEntityManagerFactory emf;@Produces @RequestScopedpublic EntityManager createEntityManager() {return emf.createEntityManager();} }現在,在每個bean中我們都可以擁有:
@Stateless public class MyBean {@InjectEntityManager em; }在構成事務的不同組件之間共享應用程序管理的持久性上下文似乎是一種非常干凈的方法。 但是,我擔心的是:知道由應用程序管理的EntityManager事務性行為取決于創建它的位置 ,因此這種方法有時可能會給您帶來討厭的結果。 以下面的代碼為例( 我的Github項目中也提供了該類,該類正是在這里 ):
@Stateless @TransactionAttribute(TransactionAttributeType.NEVER) public class BeanABoundary {@Injectprivate EntityManager em;@EJBBeanB beanB;public void invoke() {em.getProperties();beanB.invoke(); }請注意,BeanA是非事務性資源。 還要注意,我們在EntityManager上注入并調用了一些操作 (這使得注入實際上得以執行。)現在,如果BeanB是事務性的,并且還注入并使用EntityManager –我們將以非事務性EntityManager結束,該操作不會拋出任何異常,并且不會將任何更改保存到數據庫 。
在舊的@PersistenceContext的情況下,我們將處于事務中,因為EntityManager將由容器管理,并且容器將知道當前處于活動狀態的事務。 容器負責在事務邊界之間共享EntityManager。 在顯示CDI生產者方法的情況下,CDI不知道運行事務,而只是共享EntityManager。
當然,可以使用CDI并創建一個@Produces @PersistenceContext EntityManager em ,然后使用@Inject EntityManager 。 這將與@PersistenceContext EntityManager完全一樣,但是允許我們在產生EntityManager的單個位置定義(例如)持久性單元的名稱。 但是,如果我們要擁有一個應用程序管理的EntityManager,則這不是一個選擇。
參考: 實體管理器的類型:由我們的JCG合作伙伴 Piotr Nowicki在Piotr Nowicki主頁博客上進行的應用程序管理的EntityManager 。
翻譯自: https://www.javacodegeeks.com/2013/03/types-of-entity-managers-application-managed-entitymanager.html
entitymanager
總結
以上是生活随笔為你收集整理的entitymanager_实体管理器的类型:应用程序管理的EntityManager的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用Payara Micro的Easy
- 下一篇: CDI中的事务异常处理