jpa 事务嵌套事务_JPA 2 | EntityManagers,事务及其周围的一切
jpa 事務(wù)嵌套事務(wù)
介紹
對我來說,最令人困惑和不清楚的事情之一是,作為Java開發(fā)人員,一直是圍繞事務(wù)管理的謎團(tuán),尤其是JPA如何處理事務(wù)管理。 事務(wù)什么時(shí)候開始,什么時(shí)候結(jié)束,實(shí)體的持久化方式,持久性上下文等等。 諸如Spring之類的框架也無助于理解概念,因?yàn)樗鼈兲峁┝肆硪粚映橄?#xff0c;這使事情難以理解。 在今天的帖子中,我將嘗試揭露JPA關(guān)于實(shí)體管理的規(guī)范,其事務(wù)規(guī)范以及如何更好地理解該概念如何幫助我們有效地設(shè)計(jì)和編碼的某些秘密。 我們將努力保持討論
盡管我們將同時(shí)研究Java SE(其中沒有Java EE容器)和基于Java EE的示例。
基本概念
在深入探討更多細(xì)節(jié)之前,讓我們快速完成一些基礎(chǔ)課程及其在JPA中的含義。
從上面的第一點(diǎn)和第三點(diǎn),我們可以推斷出實(shí)體管理器總是管理持久性上下文。 因此,如果我們了解持久性上下文,我們將了解EntityManager。
細(xì)節(jié)
JPA中的EntityManager
JPA中定義了EntityManager的三種主要類型。
- 容器管理和交易范圍的實(shí)體管理器
- 容器管理和擴(kuò)展范圍實(shí)體管理器
- 應(yīng)用程序管理的實(shí)體管理器
現(xiàn)在,我們將更詳細(xì)地介紹其中的每一個(gè)。
容器管理的實(shí)體管理器
當(dāng)應(yīng)用程序的一個(gè)容器(例如Java EE容器或任何其他自定義容器,例如Spring)管理實(shí)體管理器的生命周期時(shí),該實(shí)體管理器被稱為“容器管理”。 獲取容器管理的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類型實(shí)例變量上使用了@PersistenceContext批注。 PersistenceContext批注具有屬性“ unitName”,用于標(biāo)識(shí)該上下文的持久性單元。
容器管理的實(shí)體管理器有兩種形式:
請注意,上述范圍實(shí)際上是指實(shí)體管理器管理的持久性上下文的范圍。 它不是EntityManager本身的范圍。
讓我們依次查看它們中的每一個(gè)。
交易范圍實(shí)體管理器
這是應(yīng)用程序中最常用的實(shí)體管理器。 同樣在上面的示例中,我們實(shí)際上是在創(chuàng)建事務(wù)作用域?qū)嶓w管理器。 每當(dāng)解析由@PersistenceContext創(chuàng)建的引用時(shí),都會(huì)返回事務(wù)作用域?qū)嶓w管理器。
使用事務(wù)作用域?qū)嶓w管理器的最大好處是它是無狀態(tài)的。 這也使事務(wù)范圍的EntityManager線程安全,因此實(shí)際上無需維護(hù)。 但是我們只是說EntityManager管理實(shí)體的持久狀態(tài),而實(shí)體的持久狀態(tài)是注入EntityManager的持久性上下文的一部分。 那么,上述關(guān)于無國籍狀態(tài)的說法又如何呢?
答案在于所有容器管理的實(shí)體管理器都依賴于JTA事務(wù)。 每次在實(shí)體管理器上調(diào)用操作時(shí),容器代理(容器在實(shí)例化時(shí)在實(shí)體管理器周圍創(chuàng)建一個(gè)代理)都會(huì)檢查JTA事務(wù)上是否存在任何持久性上下文。 如果找到一個(gè),則實(shí)體管理器將使用此持久性上下文。 如果找不到,則將創(chuàng)建一個(gè)新的持久性上下文并將其與事務(wù)關(guān)聯(lián)。
讓我們以上面討論的相同示例來了解實(shí)體管理器和事務(wù)創(chuàng)建的概念。
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上調(diào)用find方法。 查找調(diào)用將強(qiáng)制容器檢查現(xiàn)有交易。 是否存在事務(wù)(例如,對于Java EE中的無狀態(tài)會(huì)話Bean,容器在每次調(diào)用Bean上的方法時(shí)都保證事務(wù)可用)或不存在。 如果事務(wù)不存在,它將拋出異常。 如果存在,它將檢查持久性上下文是否存在。 自從第一次調(diào)用EntityManager的任何方法以來,持久性上下文尚不可用。 然后,實(shí)體管理器將創(chuàng)建一個(gè)并使用它來查找項(xiàng)目bean實(shí)例。
在下一個(gè)查找調(diào)用中,實(shí)體管理器已經(jīng)具有關(guān)聯(lián)的事務(wù)以及與其關(guān)聯(lián)的持久性上下文。 它使用相同的事務(wù)來查找員工實(shí)例。 在方法的第二行結(jié)束時(shí),將同時(shí)管理項(xiàng)目和員工實(shí)例。 在方法調(diào)用結(jié)束時(shí),將提交事務(wù),并保留人員和員工的托管實(shí)例。 要記住的另一件事是,當(dāng)事務(wù)結(jié)束時(shí),持久性上下文消失了。
擴(kuò)展范圍實(shí)體管理器
如果并且當(dāng)您希望持久性上下文在方法范圍之外可用時(shí),請使用具有擴(kuò)展范圍的實(shí)體管理器。 理解擴(kuò)展范圍實(shí)體管理器的最好方法是以一個(gè)類為例,該類需要維護(hù)某種狀態(tài)(該狀態(tài)是由于諸如myEntityManager.find(“ employeeId”)之類的事務(wù)請求而創(chuàng)建的,然后使用該雇員),并且通過各種業(yè)務(wù)方法共享狀態(tài)。
因?yàn)镻ersistence Context在方法調(diào)用之間共享并且用于維護(hù)狀態(tài),所以除非您在有狀態(tài)會(huì)話Bean中使用它們(容器負(fù)責(zé)使其成為線程安全的),否則它通常不是線程安全的。 重申一下,如果您使用的是Java EE容器,則將在Stateful Session Bean(帶有@Stateful注釋的類)內(nèi)使用擴(kuò)展范圍實(shí)體管理器。 如果您決定在有狀態(tài)bean之外使用它,則該容器不能保證您可以安全地執(zhí)行線程操作,而必須自己處理。 如果您使用的是像Spring這樣的第三方容器,情況也是如此。
讓我們看一下使用狀態(tài)會(huì)話Bean時(shí)Java EE環(huán)境中擴(kuò)展作用域?qū)嶓w管理器的示例。
該示例中的目標(biāo)是創(chuàng)建一個(gè)業(yè)務(wù)類,該業(yè)務(wù)類具有在LibraryUser Entity實(shí)例上工作的業(yè)務(wù)方法。 讓我們將此具有業(yè)務(wù)接口UserManagementService的業(yè)務(wù)類LibraryUserManagementService命名。 LibraryUserManagementService在LibraryUsers實(shí)體實(shí)例上工作。 圖書館可以將多本書借給LibraryUser。
這是描述上述情況的有狀態(tài)會(huì)話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() { } }在上面使用用戶實(shí)例的情況下,更自然的是先獲得一個(gè)實(shí)例,然后逐步進(jìn)行操作,只有完成后,我們才應(yīng)保留用戶實(shí)例。 但是,問題在于實(shí)體管理器是事務(wù)范圍的。 這意味著init將在其自身的事務(wù)中運(yùn)行(因此具有其自身的持久性上下文),而roweBookFromLibrary將在其自身的事務(wù)中運(yùn)行。 結(jié)果,init方法一結(jié)束,用戶對象就變得不受管理。
為了確切地解決此類問題,我們使用PersistenceContextType.EXTENDED類型的實(shí)體管理器。
這是帶有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() { } }在上述場景中,用于管理用戶實(shí)例的PersistenceContext是由Java EE容器在Bean初始化時(shí)創(chuàng)建的,并且在調(diào)用完成的方法(在該時(shí)間提交事務(wù))之前可用。
應(yīng)用范圍的實(shí)體管理器
不是由容器而是由應(yīng)用程序本身創(chuàng)建的實(shí)體管理器是應(yīng)用程序范圍的實(shí)體管理器。 為了使定義更清晰,每當(dāng)我們通過在EntityManagerFactory實(shí)例上調(diào)用createEntityManager來創(chuàng)建實(shí)體管理器時(shí),實(shí)際上是在創(chuàng)建應(yīng)用程序范圍的實(shí)體管理器。 所有基于Java SE的應(yīng)用程序?qū)嶋H上都使用應(yīng)用程序范圍的實(shí)體管理器。 JPA為我們提供了一個(gè)Persistence類,該類用于最終創(chuàng)建應(yīng)用程序范圍的實(shí)體管理器。
以下是如何創(chuàng)建應(yīng)用程序范圍的EM的示例:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("myPersistenceUnit"); EntityManager em = emf.createEntityManager();請注意,要?jiǎng)?chuàng)建應(yīng)用程序范圍的EntityManager,在應(yīng)用程序的META-INF文件夾中需要有一個(gè)persistence.xml文件。
EntityManager可以通過兩種方式創(chuàng)建。 上面已經(jīng)顯示了一個(gè)。 創(chuàng)建EntityManager的另一種方法是將一組屬性作為參數(shù)傳遞給
createEntityManagerFactory方法。
如果要?jiǎng)?chuàng)建自己的應(yīng)用程序托管實(shí)體管理器,請確保在每次使用完它后都將其關(guān)閉。 這是必需的,因?yàn)槟F(xiàn)在正在管理應(yīng)如何以及何時(shí)創(chuàng)建和使用EntityManager。
交易管理
交易與實(shí)體直接相關(guān)。 實(shí)質(zhì)上,管理事務(wù)意味著要管理實(shí)體生命周期(創(chuàng)建,更新,刪除)的管理方式。 理解事務(wù)管理的另一個(gè)關(guān)鍵是要了解持久性上下文如何與事務(wù)交互。 值得注意的是,從最終用戶的角度來看,即使我們使用EntityManager的實(shí)例,EntityManager的唯一作用是確定持久性上下文的生存期。 它在決定持久化上下文應(yīng)如何表現(xiàn)方面沒有任何作用。 重申一下,持久性上下文是一組實(shí)體實(shí)例的托管集合。 每當(dāng)事務(wù)開始時(shí),Persistence Context實(shí)例都會(huì)與之關(guān)聯(lián)。 當(dāng)事務(wù)結(jié)束時(shí)(例如,提交),持久性上下文將被刷新并與事務(wù)解除關(guān)聯(lián)。
JPA支持兩種類型的事務(wù)管理類型。
- 資源本地交易
- JTA或全球交易
資源本地事務(wù)是指JDBC驅(qū)動(dòng)程序的本機(jī)事務(wù),而JTA事務(wù)是指JEE服務(wù)器的事務(wù)。 資源本地事務(wù)涉及單個(gè)事務(wù)資源,例如JDBC連接。 每當(dāng)在單個(gè)事務(wù)中需要兩個(gè)或更多資源(例如JMS連接和JDBC連接)時(shí),都可以使用JTA事務(wù)。
容器管理的實(shí)體管理器始終使用JTA事務(wù),因?yàn)槿萜髫?fù)責(zé)事務(wù)生命周期管理并在多個(gè)事務(wù)資源中產(chǎn)生事務(wù)。 應(yīng)用程序管理的實(shí)體管理器可以使用資源本地事務(wù)或JTA事務(wù)。
通常,在JTA或全局事務(wù)中,第三方事務(wù)監(jiān)控器會(huì)在事務(wù)中獲取不同的事務(wù)資源,為提交做準(zhǔn)備,最后提交事務(wù)。 首先準(zhǔn)備事務(wù)資源(通過空運(yùn)行)然后提交(或回滾)的過程稱為兩階段提交。
有關(guān)XA協(xié)議的附帶說明 –在全球交易中,交易監(jiān)視器必須不斷與不同的交易資源進(jìn)行對話。 不同的交易資源會(huì)說不同的語言,因此交易監(jiān)視器可能無法理解。 XA是一個(gè)協(xié)議規(guī)范,為事務(wù)監(jiān)視器與不同的事務(wù)資源進(jìn)行交互提供了通用基礎(chǔ)。 JTA是說XA的全球事務(wù)監(jiān)控器規(guī)范,因此能夠管理多個(gè)事務(wù)資源。 兼容Java EE的服務(wù)器具有內(nèi)置的JTA實(shí)現(xiàn)。其他容器(例如Spring)可以自己編寫或使用其他實(shí)現(xiàn)(例如Java Open Transaction Manager,JBoss TS等)來支持JTA或全局事務(wù)。
持久性上下文,事務(wù)和實(shí)體管理器
持久性上下文可以與單個(gè)或多個(gè)事務(wù)關(guān)聯(lián),也可以與多個(gè)實(shí)體管理器關(guān)聯(lián)。 持久性上下文已向事務(wù)注冊,以便在提交事務(wù)時(shí)可以刷新持久性上下文。 事務(wù)開始時(shí),實(shí)體管理器將查找活動(dòng)的持久性上下文實(shí)例。 如果不可用,它將創(chuàng)建一個(gè)并將其綁定到事務(wù)。 通常,持久性上下文的范圍與事務(wù)緊密相關(guān)。 當(dāng)事務(wù)結(jié)束時(shí),與該事務(wù)關(guān)聯(lián)的持久性上下文實(shí)例也結(jié)束。 但是有時(shí),在大多數(shù)情況下,在Java EE世界中,我們需要事務(wù)傳播,這是在單個(gè)事務(wù)中的不同實(shí)體管理器之間共享單個(gè)持久性上下文的過程。
持久性上下文可以有兩個(gè)范圍:
- 事務(wù)范圍的持久性上下文
- 擴(kuò)展范圍的持久性上下文
我們已經(jīng)討論了事務(wù)/擴(kuò)展范圍的實(shí)體管理器,并且我們也知道實(shí)體管理器可以是事務(wù)或擴(kuò)展范圍的。 關(guān)系不是偶然的。 事務(wù)范圍的實(shí)體管理器創(chuàng)建事務(wù)范圍的持久性上下文。 擴(kuò)展范圍實(shí)體管理器使用擴(kuò)展持久性上下文。 擴(kuò)展持久性上下文的生命周期與Java EE環(huán)境中的有狀態(tài)會(huì)話Bean相關(guān)聯(lián)。
讓我們簡要地討論這些持久性上下文
事務(wù)范圍的持久性上下文
TSPC僅在需要時(shí)由實(shí)體管理器創(chuàng)建。 僅當(dāng)首次調(diào)用實(shí)體管理器上的方法時(shí),事務(wù)作用域?qū)嶓w管理器才創(chuàng)建TSPC。 因此,持久性上下文的創(chuàng)建是懶惰的。 如果已經(jīng)存在傳播的持久性上下文,則實(shí)體管理器將使用該持久性上下文。
了解持久性上下文傳播對于識(shí)別和調(diào)試代碼中與事務(wù)相關(guān)的問題非常重要。 讓我們看一個(gè)如何傳播事務(wù)范圍的持久性上下文的示例。
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); } } 調(diào)用ItemDAOImpl的createItem方法時(shí),將在實(shí)體管理器實(shí)例上調(diào)用persist方法。 假設(shè)這是對實(shí)體管理器方法的第一次調(diào)用。 實(shí)體管理器將查找單元名稱為“ ItemService”的任何傳播的持久性上下文。 它找不到一個(gè),因?yàn)檫@是對實(shí)體管理器的第一個(gè)調(diào)用。 因此,它創(chuàng)建了一個(gè)新的持久性上下文實(shí)例并將其附加到自身。 然后,它繼續(xù)保存Item對象。 持久化項(xiàng)目對象后,我們將調(diào)用以記錄剛剛持久化的項(xiàng)目信息。 請注意,LoggingService有其自己的EnitityManager實(shí)例,方法日志中具有注釋@TransactionAttribute(如果在Java EE envt中并且將bean聲明為EJB,則不需要此注釋)。
由于TransactionAttribute的默認(rèn)TransactionAttributeType為REQUIRED,因此LoggingService中的實(shí)體管理器將查找以前的事務(wù)中可能可用的任何持久性上下文。 它找到一個(gè)在ItemDAOImpl的createItem方法內(nèi)部創(chuàng)建的對象,并使用相同的對象。 這就是為什么即使實(shí)際項(xiàng)目尚未持久到數(shù)據(jù)庫(因?yàn)樯形刺峤皇聞?wù)),LoggingService中的實(shí)體管理器仍能夠找到它,因?yàn)槌志眯陨舷挛囊褟腎temDAOImpl傳播到LoggingService 。
擴(kuò)展持久性上下文
事務(wù)范圍持久性上下文是為每個(gè)事務(wù)創(chuàng)建的(在不傳播的情況下),而擴(kuò)展持久性上下文僅創(chuàng)建一次,并由管理擴(kuò)展持久性上下文生命周期的類范圍內(nèi)的所有事務(wù)使用。 對于Java EE,由狀態(tài)會(huì)話Bean管理擴(kuò)展的持久性上下文的生命周期。 有狀態(tài)會(huì)話bean的創(chuàng)建是EAGER。 如果是容器托管事務(wù),則在類上的方法被調(diào)用后立即創(chuàng)建它。 對于應(yīng)用程序管理的事務(wù),將在調(diào)用userTransaction.begin()時(shí)創(chuàng)建。
摘要
在這篇博客文章中,已經(jīng)討論了很多東西,實(shí)體管理器,事務(wù)管理,持久性上下文,所有這些東西如何相互作用以及如何相互配合。
我們討論了容器管理的實(shí)體管理器和應(yīng)用程序管理的實(shí)體管理器,事務(wù)范圍和擴(kuò)展范圍持久性上下文,事務(wù)傳播之間的區(qū)別。 該博客的大部分材料是閱讀了精彩的書: Pro JPA 2的結(jié)果 。 如果您想更深入地了解JPA的工作原理,我建議您閱讀它。
翻譯自: https://www.javacodegeeks.com/2013/06/jpa-2-entitymanagers-transactions-and-everything-around-it.html
jpa 事務(wù)嵌套事務(wù)
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的jpa 事务嵌套事务_JPA 2 | EntityManagers,事务及其周围的一切的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 重构字符串型系统
- 下一篇: 暗黑2快捷键技巧大全(暗黑2实用快捷键)