javascript
Spring管理的交易说明-第2部分(JPA)
在本系列的第一部分中 ,我展示了事務(wù)如何在普通JDBC中工作 。 然后,我展示了Spring如何管理基于JDBC的事務(wù)。 在本系列的第二部分中,我將首先展示事務(wù)如何在普通的JPA中工作。 然后展示Spring如何管理基于JPA的事務(wù)。
資金轉(zhuǎn)移
為了幫助說明交易,我將使用同一案例研究,將資金從一個銀行帳戶轉(zhuǎn)移到另一個銀行帳戶。 在這里,我們顯示了借方,貸方和轉(zhuǎn)賬方法的代碼片段。
... 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上調(diào)用getTransaction().begin()來啟動事務(wù)。 下面的代碼段對此進行了說明。
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(); }從技術(shù)上講, EntityManager從創(chuàng)建時就處于事務(wù)中。 因此,調(diào)用begin()有點多余。 在調(diào)用begin()不能調(diào)用某些操作,例如persist , merge , remove 。 查詢?nèi)匀豢梢詧?zhí)行(例如find() )。
從查詢返回的對象可以更改。 盡管JPA規(guī)范尚不清楚在沒有事務(wù)開始時這些更改將發(fā)生什么。
現(xiàn)在,讓我們將JPA應(yīng)用于資金轉(zhuǎn)移案例研究。
我們定義了一個BankAccount實體來處理debit()和credit()行為。
import javax.persistence.*;@Entity ... class BankAccount {@Id ...;...public void debit(MonetaryAmount amount) {...}public void credit(MonetaryAmount amount) {...}... }我們將EntityManagerFactory添加到BankAccountService以在需要時啟用EntityManager的創(chuàng)建。
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)}} }我們的代碼要簡單一些。 由于我們沒有創(chuàng)建EntityManager ,所以不必關(guān)閉它。 但是我們?nèi)栽谡{(diào)用getTransaction().begin() 。 有沒有更好的辦法? 首先如何將EntityManager注入對象?
從本系列的前一篇文章中 ,精明的讀者可能已經(jīng)在考慮讓Spring為我們完成這項工作。 當(dāng)然是這樣!
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容器將充當(dāng)JPA容器,并從EntityManagerFactory注入EnitityManager 。
JPA和
現(xiàn)在我們有了EntityManager ,如何告訴Spring為我們開始交易?
我們告訴Spring通過將方法標(biāo)記為@Transactional (或?qū)㈩悩?biāo)記為@Transactional ,使所有公共方法都具有事務(wù)性)來開始事務(wù)。 這與Spring通過JDBC啟用事務(wù)的方式一致。
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);} }哇,真好! 我們的代碼短了很多。
就像本系列第一部分中所解釋的那樣,當(dāng)Spring遇到此注釋時,它將代理該對象(通常稱為Spring管理的Bean)。 代理為標(biāo)記為@Transactional方法啟動事務(wù)(如果沒有正在進行的事務(wù)),并在方法成功返回時結(jié)束事務(wù)。
調(diào)用debit()將使用事務(wù)。 單獨調(diào)用credit()將使用交易。 但是,當(dāng)調(diào)用transfer()時會發(fā)生什么?
由于transfer()方法被標(biāo)記為@Transactional ,Spring將啟動一個事務(wù)。 相同的事務(wù)將用于對debit()和credit()調(diào)用。 換句話說, debit(amount)和credit(amount)不會啟動新交易。 它將使用正在進行的事務(wù)(因為有一個事務(wù))。
可是等等! Spring如何知道何時注入適當(dāng)?shù)膶嶓w管理器? 它僅在調(diào)用事務(wù)方法時才注入嗎?
共享的
在我的一個培訓(xùn)課程中 ,我嘗試了以下內(nèi)容,以更好地理解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());}... }應(yīng)用程序上下文啟動后,控制臺上將顯示類似這樣的輸出。
Shared EntityManager proxy for target factory [...]那么,這個共享實體管理器是什么?
當(dāng)應(yīng)用程序上下文啟動時,Spring注入一個共享實體管理器。 共享EntityManager行為就像從JPA規(guī)范定義的從應(yīng)用程序服務(wù)器的JNDI環(huán)境中獲取的EntityManager一樣。 它將所有調(diào)用委派給當(dāng)前的事務(wù)性EntityManager (如果有); 否則,它將按操作退回到新創(chuàng)建的EntityManager 。
回到我們的問題。 Spring沒有在正確的時間注入正確的實體管理器。 它總是注入一個共享的實體管理器。 但是,該共享實體管理器是事務(wù)感知的。 如果存在正在進行的事務(wù),它將委派給當(dāng)前的事務(wù)性EntityManager 。
結(jié)論
本系列分為兩部分。 我希望通過從純文本版本的JDBC和JPA(沒有DAO和存儲庫)開始,我可以使自己更清楚地了解Spring如何在后臺管理事務(wù)。 而且,通過對Spring的幕后工作有一個更清晰的了解,您可以更好地進行故障排除,了解為什么會得到一個TransactionRequiredException說“沒有可用的事務(wù)EntityManager”,并為應(yīng)用程序添加更好的修復(fù)程序。
現(xiàn)在,該冷了。
翻譯自: https://www.javacodegeeks.com/2016/02/spring-managed-transactions-explained-part-2-jpa.html
總結(jié)
以上是生活随笔為你收集整理的Spring管理的交易说明-第2部分(JPA)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 外汇登记备案如何办理(外汇登记备案)
- 下一篇: linux目录挂载目录(linux 目录
