詳解Spring中的Transactional屬性
一、Transactional
聲明式事務(wù)管理建立在AOP之上的。其本質(zhì)是對(duì)方法前后進(jìn)行攔截,然后在目標(biāo)方法開(kāi)始之前創(chuàng)建或者加入一個(gè)事務(wù),在執(zhí)行完目標(biāo)方法之后根據(jù)執(zhí)行情況提交或者回滾事務(wù)。
簡(jiǎn)而言之,@Transactional注解在代碼執(zhí)行出錯(cuò)的時(shí)候能夠進(jìn)行事務(wù)的回滾。
二、使用說(shuō)明
- 在啟動(dòng)類(lèi)上添加@EnableTransactionManagement注解。
- 用于類(lèi)上時(shí),該類(lèi)的所有 public 方法將都具有該類(lèi)型的事務(wù)屬性,同時(shí),我們也可以在方法級(jí)別使用該標(biāo)注來(lái)覆蓋類(lèi)級(jí)別的定義。
- 在項(xiàng)目中,@Transactional(rollbackFor=Exception.class),如果類(lèi)加了這個(gè)注解,那么這個(gè)類(lèi)里面的方法拋出異常,就會(huì)回滾,數(shù)據(jù)庫(kù)里面的數(shù)據(jù)也會(huì)回滾。
- 在@Transactional注解中如果不配置rollbackFor屬性,那么事物只會(huì)在遇到RuntimeException的時(shí)候才會(huì)回滾,加上rollbackFor=Exception.class,可以讓事物在遇到非運(yùn)行時(shí)異常時(shí)也回滾。
而至于什么是運(yùn)行時(shí)異常(RuntimeException),什么是非運(yùn)行時(shí)異常,可通過(guò)下圖所示理解(圖片截取網(wǎng)絡(luò))
三、注解失效問(wèn)題
正常情況下,只要在方法上添加@Transactional注解就完事了,但是需要注意的是,雖然使用簡(jiǎn)單,但是如果不合理地使用注解,還是會(huì)存在注解失效的問(wèn)題。
@Transactional 應(yīng)用在非 public 修飾的方法上
事務(wù)攔截器在目標(biāo)方法執(zhí)行前后進(jìn)行攔截,內(nèi)部會(huì)調(diào)用方法來(lái)獲取Transactional 注解的事務(wù)配置信息,調(diào)用前會(huì)檢查目標(biāo)方法的修飾符是否為 public,不是 public則不會(huì)獲取@Transactional 的屬性配置信息。
@Transactional 注解屬性 rollbackFor 設(shè)置錯(cuò)誤
rollbackFor 可以指定能夠觸發(fā)事務(wù)回滾的異常類(lèi)型。Spring默認(rèn)拋出了未檢查unchecked異常(繼承自 RuntimeException 的異常)或者 Error才回滾事務(wù);其他異常不會(huì)觸發(fā)回滾事務(wù)。如果在事務(wù)中拋出其他類(lèi)型的異常,但卻期望 Spring 能夠回滾事務(wù),就需要指定rollbackFor屬性。
同一個(gè)類(lèi)中方法調(diào)用,導(dǎo)致@Transactional失效
開(kāi)發(fā)中避免不了會(huì)對(duì)同一個(gè)類(lèi)里面的方法調(diào)用,比如有一個(gè)類(lèi)Test,它的一個(gè)方法A,A再調(diào)用本類(lèi)的方法B(不論方法B是用public還是private修飾),但方法A沒(méi)有聲明注解事務(wù),而B(niǎo)方法有。則外部調(diào)用方法A之后,方法B的事務(wù)是不會(huì)起作用的。這也是經(jīng)常犯錯(cuò)誤的一個(gè)地方。
那為啥會(huì)出現(xiàn)這種情況?其實(shí)這還是由于使用Spring AOP代理造成的,因?yàn)橹挥挟?dāng)事務(wù)方法被當(dāng)前類(lèi)以外的代碼調(diào)用時(shí),才會(huì)由Spring生成的代理對(duì)象來(lái)管理。
異常被你的 catch“吃了”導(dǎo)致@Transactional失效
如果你手動(dòng)的catch捕獲這個(gè)異常并進(jìn)行處理,事務(wù)管理器會(huì)認(rèn)為當(dāng)前事務(wù)應(yīng)該正常commit,就會(huì)導(dǎo)致注解失效,如果非要捕獲且不失效,就必須在代碼塊內(nèi)throw new Exception拋出異常。
數(shù)據(jù)庫(kù)引擎不支持事務(wù)
開(kāi)啟事務(wù)的前提就是需要數(shù)據(jù)庫(kù)的支持,我們一般使用的Mysql引擎時(shí)支持事務(wù)的,所以一般不會(huì)出現(xiàn)這種問(wèn)題。
開(kāi)啟多線程任務(wù)時(shí),事務(wù)管理會(huì)受到影響
因?yàn)榫€程不屬于spring托管,故線程不能夠默認(rèn)使用spring的事務(wù),也不能獲取spring注入的bean在被spring聲明式事務(wù)管理的方法內(nèi)開(kāi)啟多線程,多線程內(nèi)的方法不被事務(wù)控制。
如下代碼,線程內(nèi)調(diào)用insert方法,spring不會(huì)把insert方法加入事務(wù)就算在insert方法上加入@Transactional注解,也不起作用。
@Service public class ServiceA { @Transactional public void threadMethod(){ this.insert(); System.out.println("main insert is over"); for(int a=0 ;a<3;a++){ ThreadOperation threadOperation= new ThreadOperation(); Thread innerThread = new Thread(threadOperation); innerThread.start(); } } public class ThreadOperation implements Runnable { public ThreadOperation(){ } @Override public void run(){ insert(); System.out.println("thread insert is over"); } } public void insert(){ //do insert...... } }
如果把上面insert方法提出到新的類(lèi)中,加入事務(wù)注解,就能成功的把insert方法加入到事務(wù)管理當(dāng)中
@Service public class ServiceA { @Autowired private ServiceB serviceB; @Transactional public void threadMethod(){ this.insert(); System.out.println("main insert is over"); for(int a=0 ;a<3;a++){ ThreadOperation threadOperation= new ThreadOperation(); Thread innerThread = new Thread(threadOperation); innerThread.start(); } } public class ThreadOperation implements Runnable { public ThreadOperation(){ } @Override public void run(){ serviceB.insert(); System.out.println("thread insert is over"); } } public void insert(){ //do insert...... } } @Service public class ServiceB { @Transactional public void insert(){ //do insert...... } }
另外,使用多線程事務(wù)的情況下,進(jìn)行回滾,比較麻煩。thread的run方法,有個(gè)特別之處,它不會(huì)拋出異常,但異常會(huì)導(dǎo)致線程終止運(yùn)行。
最麻煩的是,在線程中拋出的異常即使在主線程中使用try…catch也無(wú)法截獲這非常糟糕,我們必須要“感知”到異常的發(fā)生。比如某個(gè)線程在處理重要的事務(wù),當(dāng)thread異常終止,我必須要收到異常的報(bào)告,才能回滾事務(wù)。這時(shí)可以使用線程的UncaughtExceptionHandler進(jìn)行異常處理,UncaughtExceptionHandler名字意味著處理未捕獲的異常。更明確的說(shuō),它處理未捕獲的運(yùn)行時(shí)異常。
如下代碼
線程出要使用
①處要拋出異常
②處要捕捉異常,并且要拋出RuntimeException
③處手動(dòng)處理回滾邏輯
@Service public class ServiceA { @Autowired private ServiceB serviceB; @Transactional public void threadMethod(){ this.insert(); System.out.println("main insert is over"); for(int a=0 ;a<3;a++){ ThreadOperation threadOperation= new ThreadOperation(); Thread innerThread = new Thread(threadOperation); innerThread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread t, Throwable e) { try { serviceB.delete();③ } catch (Exception e1) { e1.printStackTrace(); } } }); innerThread.start(); } } public class ThreadOperation implements Runnable { public ThreadOperation(){ } @Override public void run(){ try { serviceB.insert(); }catch (Exception ex){ ② System.out.println(" Exception in run "); throw new RuntimeException(); } System.out.println("thread insert is over"); } } public void insert(){ //do insert...... } } @Service public class ServiceB { @Transactional public void insert() throws Exception{ ① //do insert...... } @Transactional public void delete() throws Exception{ //do delete...... } }
到此這篇關(guān)于詳解Spring中的Transactional屬性的文章就介紹到這了,更多相關(guān)Transactional屬性詳解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 解決@Transactional注解事務(wù)不回滾不起作用的問(wèn)題
- springboot中使用@Transactional注解事物不生效的坑
- Java注解@Transactional事務(wù)類(lèi)內(nèi)調(diào)用不生效問(wèn)題及解決辦法
- 帶有@Transactional和@Async的循環(huán)依賴(lài)問(wèn)題的解決
- Spring聲明式事務(wù)@Transactional知識(shí)點(diǎn)分享
- Spring @Transactional注解失效解決方案
- springboot中事務(wù)管理@Transactional的注意事項(xiàng)與使用場(chǎng)景
- spring @Transactional 無(wú)效的解決方案
- 深入學(xué)習(xí)Spring Boot排查 @Transactional 引起的 NullPointerException問(wèn)題
- spring的@Transactional注解用法解讀
- Spring @Transactional工作原理詳解
- 淺談Spring中@Transactional事務(wù)回滾及示例(附源碼)
- Spring中@Transactional用法詳細(xì)介紹
- mysql Non-Transactional Database Only(只支持MyISAM)
- Transactional replication(事務(wù)復(fù)制)詳解之如何跳過(guò)一個(gè)事務(wù)
相關(guān)文章
Java并發(fā)編程之Semaphore(信號(hào)量)詳解及實(shí)例
這篇文章主要介紹了Java并發(fā)編程之Semaphore(信號(hào)量)詳解及實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-06-06解決Maven中的依賴(lài)導(dǎo)包問(wèn)題(組合技巧)
自從我開(kāi)始接觸了以spring為框架的項(xiàng)目學(xué)習(xí)后,這個(gè)maven導(dǎo)包老是出現(xiàn)問(wèn)題,每次在這個(gè)上面花費(fèi)好多時(shí)間,于是乎打算寫(xiě)一個(gè)秘籍出來(lái),這篇文章主要介紹了解決Maven中的依賴(lài)導(dǎo)包問(wèn)題,需要的朋友可以參考下2023-11-11SpringBoot整合Guava Cache實(shí)現(xiàn)全局緩存的示例代碼
這篇文章主要介紹了SpringBoot整合Guava Cache實(shí)現(xiàn)全局緩存,Guava Cache是Google Guava庫(kù)中的一個(gè)模塊,提供了基于內(nèi)存的本地緩存實(shí)現(xiàn),文中介紹了SpringBoot整合使用Guava Cache的具體步驟,需要的朋友可以參考下2024-03-03SpringBoot集成canal實(shí)現(xiàn)示例解析
這篇文章主要為大家介紹了springboot整合canal的示例實(shí)現(xiàn)解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多多進(jìn)步,早日升職加薪2022-02-02關(guān)于Java中的dozer對(duì)象轉(zhuǎn)換問(wèn)題
Dozer是Java?Bean到Java?Bean映射器,它以遞歸方式將數(shù)據(jù)從一個(gè)對(duì)象復(fù)制到另一個(gè)對(duì)象,這篇文章主要介紹了Java中的dozer對(duì)象轉(zhuǎn)換的操作方法,需要的朋友可以參考下2022-08-08JavaWeb response和request對(duì)象原理及實(shí)例解析
這篇文章主要介紹了JavaWeb response和request對(duì)象原理及實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11