javascript
Spring管理的交易说明-第2部分(JPA)
在本系列的第一部分中 ,我展示了事務如何在普通JDBC中工作 。 然后,我展示了Spring如何管理基于JDBC的事務。 在本系列的第二部分中,我將首先展示事務如何在普通的JPA中工作。 然后展示Spring如何管理基于JPA的事務。
資金轉移
為了幫助說明交易,我將使用同一案例研究,將資金從一個銀行帳戶轉移到另一個銀行帳戶。 在這里,我們顯示了借方,貸方和轉賬方法的代碼片段。
... class BankAccountService {public void transfer(MonetaryAmount amount, ...) {debit(amount, ...);credit(amount, ...);...}public void credit(MonetaryAmount amount, AccountId accountId) {...}public void debit(MonetaryAmount amount, AccountId accountId) {...}... }JPA交易
在普通的JPA中,通過在EntityManager上調用getTransaction().begin()來啟動事務。 下面的代碼段對此進行了說明。
import javax.persistence.*; ... EntityManagerFactory emf = ...; EntityManager em = emf.createEntityManager(); try {em.getTransaction().begin();// make changes through entitiesem.getTransaction().commit();... } catch(Exception e) {em.getTransaction().rollback();throw e; } finally {em.close(); }從技術上講, EntityManager從創建時就處于事務中。 因此,調用begin()有點多余。 在調用begin()不能調用某些操作,例如persist , merge , remove 。 查詢仍然可以執行(例如find() )。
從查詢返回的對象可以更改。 盡管JPA規范尚不清楚在沒有事務開始時這些更改將發生什么。
現在,讓我們將JPA應用于資金轉移案例研究。
我們定義了一個BankAccount實體來處理debit()和credit()行為。
import javax.persistence.*;@Entity ... class BankAccount {@Id ...;...public void debit(MonetaryAmount amount) {...}public void credit(MonetaryAmount amount) {...}... }我們將EntityManagerFactory添加到BankAccountService以在需要時啟用EntityManager的創建。
import javax.persistence.*;... class BankAccountService {private EntityManagerFactory emf; // injected via constructor...public void transfer(MonetaryAmount amount, ...) ... {EntityManager em = emf.createEntityManager();try {em.getTransaction().begin();BankAccount fromAccount = em.find(BankAccount.class, ...);BankAccount toAccount = em.find(BankAccount.class, ...);fromAccount.debit(amount);toAccount.credit(amount);em.getTransaction().commit();...} catch(Exception e) {em.getTransaction().rollback();// handle exception (possibly rethrowing it)} finally {em.close();}}public void credit(MonetaryAmount amount, AccountId ...) ... {EntityManager em = emf.createEntityManager();try {em.getTransaction().begin();BankAccount theAccount = em.find(BankAccount.class, ...);theAccount.credit(amount);em.getTransaction().commit();...} catch(Exception e) {em.getTransaction().rollback();// handle exception (possibly rethrowing it)} finally {em.close();}}public void debit(MonetaryAmount amount, AccountId ...) ... {EntityManager em = emf.createEntityManager();try {em.getTransaction().begin();BankAccount theAccount = em.find(BankAccount.class, ...);theAccount.debit(amount);em.getTransaction().commit();...} catch(Exception e) {em.getTransaction().rollback();// handle exception (possibly rethrowing it)} finally {em.close();}} }Spring管理的JPA交易
transfer , credit和debit方法肯定可以使用模板類(類似于JdbcTemplate )來刪除所有樣板代碼。 Spring以前提供了JpaTemplate類,但是從Spring 3.1開始不推薦使用,而推薦使用本機EntityManager用法(通常通過@PersistenceContext獲得)。
因此,讓我們做到這一點-使用通過@PersistenceContext獲得的EntityManager 。
import javax.persistence.*;... class BankAccountService {@PersistenceContextprivate EntityManager em;...public void transfer(MonetaryAmount amount, ...) ... {try {em.getTransaction().begin();BankAccount fromAccount = em.find(BankAccount.class, ...);BankAccount toAccount = em.find(BankAccount.class, ...);fromAccount.debit(amount);toAccount.credit(amount);em.getTransaction().commit();...} catch(Exception e) {em.getTransaction().rollback();// handle exception (possibly rethrowing it)}}public void credit(MonetaryAmount amount, AccountId ...) ... {try {em.getTransaction().begin();BankAccount theAccount = em.find(BankAccount.class, ...);theAccount.credit(amount);em.getTransaction().commit();...} catch(Exception e) {em.getTransaction().rollback();// handle exception (possibly rethrowing it)}}public void debit(MonetaryAmount amount, AccountId ...) ... {try {em.getTransaction().begin();BankAccount theAccount = em.find(BankAccount.class, ...);theAccount.debit(amount);em.getTransaction().commit();...} catch(Exception e) {em.getTransaction().rollback();// handle exception (possibly rethrowing it)}} }我們的代碼要簡單一些。 由于我們沒有創建EntityManager ,所以不必關閉它。 但是我們仍在調用getTransaction().begin() 。 有沒有更好的辦法? 首先如何將EntityManager注入對象?
從本系列的前一篇文章中 ,精明的讀者可能已經在考慮讓Spring為我們完成這項工作。 當然是這樣!
EntityManager和@PersistenceContext
我們告訴Spring通過添加PersistenceAnnotationBeanPostProcessor (通過XML <bean>或通過通過AnnotationConfigApplicationContext加載的@Configuration類使用基于Java的配置)從EntityManagerFactory注入EntityManager 。
- 使用基于XML的配置時, PersistenceAnnotationBeanPostProcessor由<context:annotation-config />元素透明地激活。 并且<context:component-scan />也透明地激活了此元素。
- 使用基于Java的@Configuration ,將使用AnnotationConfigApplicationContext 。 并使用它始終注冊注釋配置處理器(其中之一是上述PersistenceAnnotationBeanPostProcessor )。
通過添加單個bean定義,Spring容器將充當JPA容器,并從EntityManagerFactory注入EnitityManager 。
JPA和
現在我們有了EntityManager ,如何告訴Spring為我們開始交易?
我們告訴Spring通過將方法標記為@Transactional (或將類標記為@Transactional ,使所有公共方法都具有事務性)來開始事務。 這與Spring通過JDBC啟用事務的方式一致。
import javax.persistence.*; import org.springframework.transaction.annotation.Transactional;@Transactional ... class BankAccountService {@PersistenceContextprivate EntityManager em;...public void transfer(MonetaryAmount amount, ...) ... {BankAccount fromAccount = em.find(BankAccount.class, ...);BankAccount toAccount = em.find(BankAccount.class, ...);fromAccount.debit(amount);toAccount.credit(amount);}public void credit(MonetaryAmount amount, AccountId ...) ... {BankAccount theAccount = em.find(BankAccount.class, ...);theAccount.credit(amount);}public void debit(MonetaryAmount amount, AccountId ...) ... {BankAccount theAccount = em.find(BankAccount.class, ...);theAccount.debit(amount);} }哇,真好! 我們的代碼短了很多。
就像本系列第一部分中所解釋的那樣,當Spring遇到此注釋時,它將代理該對象(通常稱為Spring管理的Bean)。 代理為標記為@Transactional方法啟動事務(如果沒有正在進行的事務),并在方法成功返回時結束事務。
調用debit()將使用事務。 單獨調用credit()將使用交易。 但是,當調用transfer()時會發生什么?
由于transfer()方法被標記為@Transactional ,Spring將啟動一個事務。 相同的事務將用于對debit()和credit()調用。 換句話說, debit(amount)和credit(amount)不會啟動新交易。 它將使用正在進行的事務(因為有一個事務)。
可是等等! Spring如何知道何時注入適當的實體管理器? 它僅在調用事務方法時才注入嗎?
共享的
在我的一個培訓課程中 ,我嘗試了以下內容,以更好地理解Spring如何通過@PersistenceContext注入EntityManager 。 而且我相信它也會幫助其他人。 因此,這是我嘗試的方法:
import javax.persistence.*; import org.springframework.transaction.annotation.Transactional; import org.springframework.beans.factory.InitializingBean;@Transactional ... class BankAccountService implements InitializingBean {@PersistenceContextprivate EntityManager em;...@Overridepublic void afterPropertiesSet() {System.out.println(em.toString());}... }應用程序上下文啟動后,控制臺上將顯示類似這樣的輸出。
Shared EntityManager proxy for target factory [...]那么,這個共享實體管理器是什么?
當應用程序上下文啟動時,Spring注入一個共享實體管理器。 共享EntityManager行為就像從JPA規范定義的從應用程序服務器的JNDI環境中獲取的EntityManager一樣。 它將所有調用委派給當前的事務性EntityManager (如果有); 否則,它將按操作退回到新創建的EntityManager 。
回到我們的問題。 Spring沒有在正確的時間注入正確的實體管理器。 它總是注入一個共享的實體管理器。 但是,該共享實體管理器是事務感知的。 如果存在正在進行的事務,它將委派給當前的事務性EntityManager 。
結論
本系列分為兩部分。 我希望通過從純文本版本的JDBC和JPA(沒有DAO和存儲庫)開始,我可以使自己更清楚地了解Spring如何在后臺管理事務。 而且,通過對Spring的幕后工作有一個更清晰的了解,您可以更好地進行故障排除,了解為什么會得到一個TransactionRequiredException說“沒有可用的事務EntityManager”,并為應用程序添加更好的修復程序。
現在,該冷了。
翻譯自: https://www.javacodegeeks.com/2016/02/spring-managed-transactions-explained-part-2-jpa.html
總結
以上是生活随笔為你收集整理的Spring管理的交易说明-第2部分(JPA)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 外汇登记备案如何办理(外汇登记备案)
- 下一篇: linux目录挂载目录(linux 目录