JPA 2 | EntityManagers,事务及其周围的一切
介紹
 對我來說,最令人困惑和不清楚的事情之一是,作為Java開發人員,一直是圍繞事務管理的謎團,尤其是JPA如何處理事務管理。 事務什么時候開始,什么時候結束,實體的持久化方式,持久性上下文等等。 諸如Spring之類的框架也無助于理解概念,因為它們提供了另一層抽象,這使事情難以理解。 在今天的帖子中,我將嘗試揭露JPA關于實體管理的規范,其事務規范以及如何更好地理解該概念如何幫助我們有效地設計和編碼的某些秘密。 我們將努力保持討論 
盡管我們將同時研究Java SE(其中Java EE容器不可用)和基于Java EE的示例。 
基本概念
在深入探討更多細節之前,讓我們快速遍歷一些基礎課程及其在JPA中的含義。
從上面的第一點和第三點,我們可以推斷出實體管理器總是管理持久性上下文。 因此,如果我們了解持久性上下文,那么我們將了解EntityManager。
細節
JPA中的EntityManager
JPA中定義了EntityManager的三種主要類型。
- 容器管理和交易范圍的實體管理器
- 容器管理和擴展范圍實體管理器
- 應用程序管理的實體管理器
現在,我們將更詳細地介紹其中的每一個。
容器管理的實體管理器
當應用程序的一個容器(例如Java EE容器或任何其他自定義容器,例如Spring)管理實體管理器的生命周期時,該實體管理器被稱為“容器管理”。 獲取容器管理的EntityManager的最常見方法是在EntityManager屬性上使用@PersistenceContext批注。 這是定義EntityManager的示例。
public class EmployeeServiceImpl implements EmployeeService { @PersistenceContext(unitName="EmployeeService") EntityManager em; public void assignEmployeeToProject(int empId, int projectId) { Project project = em.find(Project.class, projectId); Employee employee = em.find(Employee.class, empId); project.getEmployees().add(employee); employee.getProjects().add(project); }在上面的示例中,我們在EntityManager類型實例變量上使用了@PersistenceContext批注。 PersistenceContext批注具有屬性“ unitName”,用于標識該上下文的持久性單元。
容器管理的實體管理器有兩種形式:
請注意,上述范圍實際上是指實體管理器管理的持久性上下文的范圍。 它不是EntityManager本身的范圍。
讓我們依次查看它們中的每一個。
交易范圍實體管理器
這是應用程序中最常用的實體管理器。 同樣在上面的示例中,我們實際上是在創建事務作用域實體管理器。 每當解析由@PersistenceContext創建的引用時,都會返回事務作用域實體管理器。
使用事務作用域實體管理器的最大好處是它是無狀態的。 這也使事務范圍的EntityManager線程安全,因此實際上無需維護。 但是我們只是說EntityManager管理實體的持久性狀態,而實體的持久性狀態是注入EntityManager的持久性上下文的一部分。 那么,上述關于無國籍的說法如何呢?
答案在于所有容器管理的實體管理器都依賴于JTA事務。 每次在實體管理器上調用操作時,容器代理(容器在實例化時在實體管理器周圍創建一個代理)都會檢查JTA事務上是否存在任何持久性上下文。 如果找到一個,則實體管理器將使用此持久性上下文。 如果找不到,則將創建一個新的持久性上下文并將其與事務關聯。
讓我們以上面討論的相同示例來了解實體管理器和事務創建的概念。
public class EmployeeServiceImpl implements EmployeeService { @PersistenceContext(unitName="EmployeeService") EntityManager em; public void assignEmployeeToProject(int empId, int projectId) { Project project = em.find(Project.class, projectId); Employee employee = em.find(Employee.class, empId); project.getEmployees().add(employee); employee.getProjects().add(project); }在上面的示例中,assignEmployeeToProject方法的第一行正在EntityManager上調用find方法。 調用find將強制容器檢查現有交易。 是否存在事務(例如,對于Java EE中的無狀態會話Bean,容器在每次調用Bean上的方法時都保證事務可用)。 如果事務不存在,它將拋出異常。 如果存在,它將檢查持久性上下文是否存在。 自從首次調用EntityManager的任何方法以來,持久性上下文尚不可用。 然后,實體管理器將創建一個并使用它來查找項目bean實例。
在下一個查找調用中,實體管理器已經具有關聯的事務以及與之關聯的持久性上下文。 它使用相同的事務來查找員工實例。 在該方法的第二行末尾,將同時管理項目和員工實例。 在方法調用結束時,將提交事務,并保留人員和員工的托管實例。 要記住的另一件事是,當事務結束時,持久性上下文消失了。
擴展范圍實體管理器
如果并且當您希望持久性上下文在方法范圍之外可用時,請使用具有擴展范圍的實體管理器。 理解擴展范圍實體管理器的最好方法是以一個類為例,該類需要維護某種狀態(該狀態是由于諸如myEntityManager.find(“ employeeId”)之類的事務請求而創建的,然后使用該雇員),并且通過各種業務方法共享狀態。
因為Persistence Context在方法調用之間共享并且用于維護狀態,所以除非您在有狀態會話Bean中使用它們(容器負責使其變為線程安全),否則它通常不是線程安全的。 重申一下,如果您使用的是Java EE容器,則將在Stateful Session Bean(帶有@Stateful注釋的類)內使用擴展范圍實體管理器。 如果您決定在有狀態bean之外使用它,則該容器不能保證您可以安全地執行線程操作,而必須自己處理。 如果您使用像Spring這樣的第三方容器,情況也是如此。
讓我們看一下使用有狀態會話Bean時Java EE環境中擴展作用域實體管理器的示例。
該示例中的目標是創建一個業務類,該業務類具有在LibraryUser Entity實例上工作的業務方法。 讓我們將此業務類稱為LibraryUserManagementService,它具有業務接口UserManagementService。 LibraryUserManagementService在LibraryUsers實體實例上工作。 圖書館可以將多本書借給LibraryUser。
這是描述上述情況的有狀態會話Bean的示例。
@Stateful public class LibraryUserManagementService implements UserManagementService { @PersistenceContext(unitName="UserService") EntityManager em; LibraryUser user; public void init(String userId) { user = em.find(LibraryUser.class, userId); } public void setUserName(String name) { user.setName(name); } public void borrowBookFromLibrary(BookId bookId) { Book book = em.find(Book.class, bookId); user.getBooks().add(book); book.setLendingUser(user); } // ... @Remove public void finished() { } }在上面使用用戶實例的情況下,更自然的是先獲得一個實例,然后逐步進行操作,只有完成后,我們才應保留用戶實例。 但是,問題在于實體管理器是事務范圍的。 這意味著init將在其自己的事務中運行(因此具有其自己的持久性上下文),而roweBookFromLibrary將在其自己的事務中運行。 結果,init方法一結束,用戶對象就變得不受管理。
為了確切地解決此類問題,我們使用PersistenceContextType.EXTENDED類型的實體管理器。
這是帶有PersistenceContextType EXTENDED的修改后的示例,可以很好地工作。
@Stateful public class LibraryUserManagementService implements UserManagementService { @PersistenceContext(unitName="UserService" , type=PersistenceContextType.EXTENDED) EntityManager em;LibraryUser user; public void init(String userId) { user = em.find(LibraryUser.class, userId); } public void setUserName(String name) { user.setName(name); } public void borrowBookFromLibrary(BookId bookId) { Book book = em.find(Book.class, bookId); user.getBooks().add(book); book.setLendingUser(user); } // ... @Remove public void finished() { } }在上述場景中,用于管理用戶實例的PersistenceContext是由Java EE容器在Bean初始化時創建的,并且在調用完成的方法(在該時間提交事務)之前,該持久性一直可用。
應用范圍的實體管理器
不是由容器而是由應用程序本身創建的實體管理器是應用程序范圍的實體管理器。 為了使定義更清晰,每當我們通過在EntityManagerFactory實例上調用createEntityManager來創建實體管理器時,實際上是在創建應用程序范圍的實體管理器。 所有基于Java SE的應用程序實際上都使用應用程序范圍的實體管理器。 JPA為我們提供了一個Persistence類,該類用于最終創建應用程序范圍的實體管理器。
以下是如何創建應用程序范圍的EM的示例:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("myPersistenceUnit"); EntityManager em = emf.createEntityManager();請注意,要創建應用程序范圍的EntityManager,在應用程序的META-INF文件夾中需要有一個persistence.xml文件。
 EntityManager可以通過兩種方式創建。 上面已經顯示了一個。 創建EntityManager的另一種方法是將一組屬性作為參數傳遞給 
 createEntityManagerFactory方法。 
如果要創建自己的應用程序托管實體管理器,請確保在每次使用完它后都將其關閉。 這是必需的,因為您現在正在管理應如何以及何時創建和使用EntityManager。
交易管理
交易與實體直接相關。 實質上,管理事務意味著要管理實體生命周期(創建,更新,刪除)的管理方式。 理解事務管理的另一個關鍵是要了解持久性上下文如何與事務交互。 值得注意的是,從最終用戶的角度來看,即使我們使用EntityManager的實例,EntityManager的唯一作用是確定持久性上下文的生存期。 它在決定持久化上下文的行為時不起作用。 重申一下,持久性上下文是一組實體實例的托管集合。 每當事務開始時,Persistence Context實例都會與之關聯。 當事務結束時(例如,提交),持久性上下文將被刷新并與事務解除關聯。
JPA支持兩種類型的事務管理類型。
- 資源本地交易
- JTA或全球交易
 資源本地事務是指JDBC驅動程序的本機事務,而JTA事務是指JEE服務器的事務。 資源本地事務涉及單個事務資源,例如JDBC連接。 每當在單個事務中需要兩個或多個資源(例如JMS連接和JDBC連接)時,都可以使用JTA事務。 
 容器管理的實體管理器始終使用JTA事務,因為容器負責事務生命周期管理并在多個事務資源中生成事務。 應用程序管理的實體管理器可以使用資源本地事務或JTA事務。 
通常,在JTA或全局事務中,第三方事務監視器會在事務中獲取不同的事務資源,為提交做準備,最后提交事務。 首先準備事務資源(通過空運行)然后提交(或回滾)的過程稱為兩階段提交。
有關XA協議的附帶說明 –在全球交易中,交易監視器必須不斷與不同的交易資源進行對話。 不同的交易資源會說不同的語言,因此交易監視器可能無法理解。 XA是一個協議規范,為事務監視器與不同的事務資源進行交互提供了通用基礎。 JTA是使用XA的全球事務監控器規范,因此能夠管理多個事務資源。 兼容Java EE的服務器具有內置的JTA實現。其他容器(例如Spring)可以自己編寫或使用其他實現(例如Java Open Transaction Manager,JBoss TS等)來支持JTA或全局事務。
持久性上下文,事務和實體管理器
持久性上下文可以與單個或多個事務關聯,也可以與多個實體管理器關聯。 持久性上下文已向事務注冊,以便在提交事務時可以刷新持久性上下文。 事務啟動時,實體管理器將查找活動的持久性上下文實例。 如果不可用,它將創建一個并將其綁定到事務。 通常,持久性上下文的范圍與事務緊密相關。 當事務結束時,與該事務關聯的持久性上下文實例也結束。 但是有時,在大多數情況下,在Java EE世界中,我們需要事務傳播,這是在單個事務中的不同實體管理器之間共享單個持久性上下文的過程。
持久性上下文可以有兩個范圍:
- 事務范圍的持久性上下文
- 擴展范圍的持久性上下文
我們已經討論了事務/擴展范圍的實體管理器,并且我們也知道實體管理器可以是事務或擴展范圍的。 關系不是偶然的。 事務范圍的實體管理器創建事務范圍的持久性上下文。 擴展范圍實體管理器使用擴展持久性上下文。 擴展持久性上下文的生命周期與Java EE環境中的有狀態會話Bean有關。
讓我們簡要討論一下這些持久性上下文
事務范圍的持久性上下文
TSPC僅在需要時由實體管理器創建。 僅當首次調用實體管理器上的方法時,事務作用域實體管理器才創建TSPC。 因此,持久性上下文的創建是懶惰的。 如果已經存在傳播的持久性上下文,則實體管理器將使用該持久性上下文。
了解持久性上下文傳播對于識別和調試代碼中與事務相關的問題非常重要。 讓我們看一個如何傳播事務范圍的持久性上下文的示例。
ItemDAOImpl.java:
public class ItemDAOImpl implements ItemDAO { @PersistenceContext(unitName="ItemService") EntityManager em; LoggingService ls; @TransactionAttribute()public void createItem(Item item) { em.persist(item); ls.log(item.getId(), "created item"); } // ... }LoggingService.java:
public class LoggingService implements AuditService { @PersistenceContext(unitName="ItemService") EntityManager em; @TransactionAttribute()public void log(int itemId, String action) { // verify item id is valid if (em.find(Item.class, itemId) == null) { throw new IllegalArgumentException("Unknown item id"); } LogRecord lr = new LogRecord(itemId, action); em.persist(lr); } } 調用ItemDAOImpl的createItem方法時,將在實體管理器實例上調用persist方法。 假設這是對實體管理器方法的第一次調用。 實體管理器將查找單元名稱為“ ItemService”的任何傳播的持久性上下文。 它找不到一個,因為這是對實體管理器的第一個調用。 因此,它創建了一個新的持久性上下文實例并將其附加到自身。 然后,它繼續保存Item對象。 持久化項目對象后,我們調用以記錄剛剛持久化的項目信息。 請注意,LoggingService有其自己的EnitityManager實例,方法日志中具有注釋@TransactionAttribute(如果在Java EE envt中并且將bean聲明為EJB,則不需要此注釋)。 
 由于TransactionAttribute的默認TransactionAttributeType為REQUIRED,因此LoggingService中的實體管理器將查找以前的事務中可能可用的任何持久性上下文。 它找到一個在ItemDAOImpl的createItem方法內部創建的對象,并使用相同的對象。 這就是為什么即使實際項目尚未持久到數據庫(因為尚未提交事務),LoggingService中的實體管理器仍能夠找到它,因為持久性上下文已從ItemDAOImpl傳播到LoggingService 。 
擴展持久性上下文
事務范圍持久性上下文是為每個事務創建的(在不傳播的情況下),而擴展持久性上下文僅創建一次,并由管理擴展持久性上下文的生命周期的類范圍內的所有事務使用。 對于Java EE,由狀態會話Bean管理擴展的持久性上下文的生命周期。 有狀態會話bean的創建是EAGER。 如果是容器托管事務,則在類上的方法被調用后立即創建它。 對于應用程序管理的事務,將在調用userTransaction.begin()時創建。
摘要
在這篇博客文章中,已經討論了很多東西,實體管理器,事務管理,持久性上下文,所有這些東西如何相互作用和相互配合。
 我們討論了容器管理的和應用程序管理的實體管理器,事務范圍和擴展范圍持久性上下文,事務傳播之間的區別。 該博客的大部分材料是閱讀精彩書籍: Pro JPA 2的結果 。 如果您想更深入地了解JPA的工作原理,我建議您閱讀它。 
翻譯自: https://www.javacodegeeks.com/2013/06/jpa-2-entitymanagers-transactions-and-everything-around-it.html
總結
以上是生活随笔為你收集整理的JPA 2 | EntityManagers,事务及其周围的一切的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 建筑企业入川备案(建筑入川备案)
- 下一篇: 安卓手机查找对方手机位置(安卓手机查找)
