完美解決Spring聲明式事務(wù)不回滾的問題
疑問,確實(shí)像往常一樣在service上添加了注解 @Transactional,為什么查詢數(shù)據(jù)庫(kù)時(shí)還是發(fā)現(xiàn)有數(shù)據(jù)不一致的情況,想想肯定是事務(wù)沒起作用,出現(xiàn)異常的時(shí)候數(shù)據(jù)沒有回滾。于是就對(duì)相關(guān)代碼進(jìn)行了一番測(cè)試,結(jié)果發(fā)現(xiàn)一下踩進(jìn)了兩個(gè)坑,確實(shí)是事務(wù)未回滾導(dǎo)致的數(shù)據(jù)不一致。
下面總結(jié)一下經(jīng)驗(yàn)教訓(xùn):
Spring事務(wù)的管理操作方法
編程式的事務(wù)管理
實(shí)際應(yīng)用中很少使用
通過使用TransactionTemplate 手動(dòng)管理事務(wù)
聲明式的事務(wù)管理
開發(fā)中推薦使用(代碼侵入最少)
Spring的聲明式事務(wù)是通過AOP實(shí)現(xiàn)的
主要掌握聲明式的事務(wù)管理。
spring事務(wù)不回滾的兩個(gè)原因
總結(jié)一下導(dǎo)致事務(wù)不回滾的兩個(gè)原因,一是Service類內(nèi)部方法調(diào)用,二是try...catch異常。
1. Service類內(nèi)部方法調(diào)用
大概就是 Service 中有一個(gè)方法 A,會(huì)內(nèi)部調(diào)用方法 B, 方法 A 沒有事務(wù)管理,方法 B 采用了聲明式事務(wù),通過在方法上聲明 Transactional 的注解來做事務(wù)管理。示例代碼如下:
@Service public class RabbitServiceImpl implements RabbitService { @Autowired private RabbitDao rabbitDao; @Autowired private TortoiseDao tortoiseDao; @Override public Rabbit methodA(String name){ return methodB(name); } @Transactional(propagation = Propagation.REQUIRED) public boolean methodB(String name){ rabbitDao.insertRabbit(name); tortoiseDao.insertTortoise(name); return true; } }
單元測(cè)試代碼如下:
public class RabbitServiceImplTest { @Autowired private RabbitService rabbitService; // 事務(wù)未開啟 @Test public void testA(){ rabbitService.methodA("rabbit"); } // 事務(wù)開啟 @Test public void testB(){ rabbitService.methodB("rabbit"); } }
從上一節(jié)中可以看到,聲明式事務(wù)是通通過AOP動(dòng)態(tài)代理實(shí)現(xiàn)的,這樣會(huì)產(chǎn)生一個(gè)代理類來做事務(wù)管理,而目標(biāo)類(service)本身是不能感知代理類的存在的。
對(duì)于加了@Transactional注解的方法來說,在調(diào)用代理類的方法時(shí),會(huì)先通過攔截器TransactionInterceptor開啟事務(wù),然后在調(diào)用目標(biāo)類的方法,最后在調(diào)用結(jié)束后,TransactionInterceptor 會(huì)提交或回滾事務(wù),大致流程如下圖:
總結(jié),在方法 A 中調(diào)用方法 B,實(shí)際上是通過“this”的引用,也就是直接調(diào)用了目標(biāo)類的方法,而非通過 Spring 上下文獲得的代理類,所以事務(wù)是不會(huì)開啟的。
2. try...catch異常
在一段業(yè)務(wù)邏輯中對(duì)數(shù)據(jù)庫(kù)異常進(jìn)行了處理,使用了try...catch子句捕獲異常并throw了一個(gè)自定義異常,這種情況導(dǎo)致了事務(wù)未回滾,示例代碼如下:
@Transactional(propagation = Propagation.REQUIRED) public boolean methodB(String name) throws BizException { try { rabbitDao.insertRabbit(name); tortoiseDao.insertTortoise(name); } catch (Exception e) { throw new BizException(ReturnCode.EXCEPTION.code, ReturnCode.EXCEPTION.msg); } return true; }
BizException的定義如下:
public class BizException extends Exception { // 自定義異常 }
上面代碼中的聲明式事務(wù)在出現(xiàn)異常的時(shí)候,事務(wù)是不會(huì)回滾的。在代碼中我雖然捕獲了異常,但是同時(shí)我也拋出了異常,為什么事務(wù)未回滾呢?猜測(cè)是異常類型不對(duì),于是開始查詢?cè)?,翻看了Spring的官方文檔,找到了答案。下面是翻譯自Spring官網(wǎng)。
17.5.3 聲明式事務(wù)的回滾
上一節(jié)中介紹了如何設(shè)置開啟Spring事務(wù),一般在你的應(yīng)用的Service層代碼中設(shè)置,這一節(jié)將介紹在簡(jiǎn)單流行的聲明式事務(wù)中如何控制事務(wù)回滾。
在Spring FrameWork 的事務(wù)框架中推薦的事務(wù)回滾方法是,在當(dāng)前執(zhí)行的事務(wù)上下文中拋出一個(gè)異常。如果異常未被處理,當(dāng)拋出異常調(diào)用堆棧的時(shí)候,Spring FrameWork 的事務(wù)框架代碼將捕獲任何未處理的異常,然后并決定是否將此事務(wù)標(biāo)記為回滾。
在默認(rèn)配置中,Spring FrameWork 的事務(wù)框架代碼只會(huì)將出現(xiàn)runtime, unchecked 異常的事務(wù)標(biāo)記為回滾;也就是說事務(wù)中拋出的異常時(shí)RuntimeException或者是其子類,這樣事務(wù)才會(huì)回滾(默認(rèn)情況下Error也會(huì)導(dǎo)致事務(wù)回滾)。在默認(rèn)配置的情況下,所有的 checked 異常都不會(huì)引起事務(wù)回滾。
注:Unchecked Exception包括Error與RuntimeException. RuntimeException的所有子類也都屬于此類。另一類就是checked Exception。
你可以精確的配置異常類型,指定此異常類事務(wù)回滾,包括 checked 異常。下面的xml代碼片段展示了如何配置checked異常引起事務(wù)回滾,應(yīng)用自定義異常類型:
<tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="get*" read-only="true" rollback-for="Exception"/> <tx:method name="*"/> </tx:attributes> </tx:advice>
與其有同等作用的注解形式如下:
@Transactional(rollbackForClassName={"Exception"}) 或者 @Transactional(rollbackFor={Exception.class})
在你遇到異常不想回滾事務(wù)的時(shí)候,同樣的你也可指定不回滾的規(guī)則,下面的一個(gè)例子告訴你,即使遇到未處理的 InstrumentNotFoundException 異常時(shí),Spring FrameWork 的事務(wù)框架同樣會(huì)提交事務(wù),而不回滾。
<tx:advice id="txAdvice"> <tx:attributes> <tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/> <tx:method name="*"/> </tx:attributes> </tx:advice>
與其有同樣作用的注解形式如下:
@Transactional(noRollbackForClassName={"InstrumentNotFoundException"}) 或者 @Transactional(noRollbackFor={InstrumentNotFoundException.class})
還有更靈活的回滾規(guī)則配置方法,同時(shí)指定什么異常回滾,什么異常不回滾。當(dāng)Spring FrameWork 的事務(wù)框架捕獲到一個(gè)異常的時(shí)候,會(huì)去匹配配置的回滾規(guī)則來決定是否標(biāo)記回滾事務(wù),使用匹配度最強(qiáng)的規(guī)則結(jié)果。因此,下面的配置例子表達(dá)的意思是,除了異常 InstrumentNotFoundException 之外的任何異常都會(huì)導(dǎo)致事務(wù)回滾。
<tx:advice id="txAdvice"> <tx:attributes> <tx:method name="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/> </tx:attributes> </tx:advice>
你也可以通過編程式的方式回滾一個(gè)事務(wù),盡管方法非常簡(jiǎn)單,但是也有非常強(qiáng)的代碼侵入性,使你的業(yè)務(wù)代碼和Spring FrameWork 的事務(wù)框架代碼緊密的綁定在一起,示例代碼如下:
public void resolvePosition() { try { // some business logic... } catch (NoProductInStockException ex) { // trigger rollback programmatically TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } }
如果可能的話,強(qiáng)烈推薦您使用聲明式事務(wù)方式回滾事務(wù),對(duì)于編程式事務(wù),如果你強(qiáng)烈需要它,也是可以使用的,but its usage flies in the face of achieving a clean POJO-based architecture.(沒懂...)
看完官方文檔這節(jié)內(nèi)容找到了問題的答案,原來是因?yàn)槲覀冏远x的異常不是 RuntimeException。我的解決辦法是,在注解@Transactional中添加 rollbackFor={BizException.class}??赡苣銜?huì)問我為什么不將自定義異常修改為繼承RuntimeException,因?yàn)槲倚枰狟izException是一個(gè)checked 異常。
以上這篇完美解決Spring聲明式事務(wù)不回滾的問題就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Eclipse 開發(fā)java 出現(xiàn)Failed to create the Java Virtual Machine錯(cuò)誤
這篇文章主要介紹了Eclipse 開發(fā)java 出現(xiàn)Failed to create the Java Virtual Machine錯(cuò)誤解決辦法的相關(guān)資料,需要的朋友可以參考下2017-04-04Java 實(shí)戰(zhàn)項(xiàng)目之誠(chéng)途旅游系統(tǒng)的實(shí)現(xiàn)流程
讀萬卷書不如行萬里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SpringBoot+Vue+maven+Mysql實(shí)現(xiàn)一個(gè)精美的物流管理系統(tǒng),大家可以在過程中查缺補(bǔ)漏,提升水平2021-11-11Java時(shí)間復(fù)雜度、空間復(fù)雜度的深入詳解
對(duì)于一個(gè)算法,其時(shí)間復(fù)雜度和空間復(fù)雜度往往是相互影響的,當(dāng)追求一個(gè)較好的時(shí)間復(fù)雜度時(shí),可能會(huì)使空間復(fù)雜度的性能變差,即可能導(dǎo)致占用較多的存儲(chǔ)空間,這篇文章主要給大家介紹了關(guān)于Java時(shí)間復(fù)雜度、空間復(fù)雜度的相關(guān)資料,需要的朋友可以參考下2021-11-11java格式化數(shù)字操作 NumberFormat及DecimalFormat
這篇文章主要介紹了java格式化數(shù)字操作 NumberFormat及DecimalFormat,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10解決IDEA?JDK9沒有module-info.java的問題
這篇文章主要介紹了解決IDEA?JDK9沒有module-info.java的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01解決spring-boot-maven-plugin報(bào)紅的問題
這篇文章主要給大家介紹一下如何解決spring-boot-maven-plugin報(bào)紅的問題,文中通過圖文講解的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下2023-08-08