我總結(jié)的幾種@Transactional失效原因說(shuō)明
總結(jié)幾種@Transactional失效原因
非public方法
spring事務(wù)是通過(guò)動(dòng)態(tài)代理的方法來(lái)實(shí)現(xiàn)的,有兩種實(shí)現(xiàn)動(dòng)態(tài)代理的方式,jdk動(dòng)態(tài)代理方式是將目標(biāo)對(duì)象放入代理對(duì)象內(nèi)部,通過(guò)代理對(duì)象來(lái)訪問(wèn)目標(biāo)對(duì)象;cglib字節(jié)碼生成是通過(guò)生成目標(biāo)對(duì)象的子類(lèi),通過(guò)重寫(xiě)的方式來(lái)完成對(duì)父類(lèi)的增強(qiáng)。
但是它倆實(shí)際上可以為非public方法生成代理對(duì)象,只不過(guò)spring在調(diào)用動(dòng)態(tài)代理之前,會(huì)過(guò)濾掉非public方法。如果修改spring的源碼就可以為非public方法生成代理對(duì)象了。
自調(diào)用問(wèn)題
若同一類(lèi)中的沒(méi)有@Transactional注解的方法內(nèi)部調(diào)用有@Transactional注解的方法,那么該事務(wù)會(huì)被忽略。
原因是Spring事務(wù)是通過(guò)Spring AOP完成的,如果從外部直接訪問(wèn)使用@Transactional注解的方法,那么spring實(shí)際上會(huì)調(diào)用代理對(duì)象上的方法,在代理對(duì)象中完成事務(wù)的邏輯;
但是如果從目標(biāo)對(duì)象內(nèi)部調(diào)用了使用@Transactional注解的方法,比如在method1方法中調(diào)用了method2方法,method1沒(méi)有加@Transactional 注解,就算method2加了@Transactional 注解也沒(méi)用。因?yàn)檫@時(shí)會(huì)直接調(diào)用目標(biāo)對(duì)象中的method1方法,進(jìn)而再調(diào)用目標(biāo)對(duì)象的method2方法,并沒(méi)有走代理對(duì)象導(dǎo)致代理失效。
異常相關(guān)問(wèn)題
內(nèi)部捕捉了異常,沒(méi)有拋出新的異常,導(dǎo)致事務(wù)操作不會(huì)進(jìn)行回滾:
原因是spring事務(wù)源碼中是通過(guò)有沒(méi)有出現(xiàn)異常來(lái)判斷是否回滾的。
拋出非運(yùn)行時(shí)異常
所以最好給@Transactional添加上rollbackFor=Exception.class
傳播機(jī)制配置錯(cuò)誤
錯(cuò)誤地使用傳播機(jī)制也會(huì)導(dǎo)致事務(wù)失效。如果使用了NOT_SUPPORTED和NEVER傳播機(jī)制,那么事務(wù)機(jī)會(huì)失效,如果使用了SUPPORTS傳播機(jī)制并且當(dāng)前不存在事務(wù)那么事務(wù)也會(huì)失效。
TransactionDefinition.PROPAGATION_SUPPORTS
: 如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒(méi)有事務(wù),則以非事務(wù)的方式繼續(xù)運(yùn)行。TransactionDefinition.PROPAGATION_NOT_SUPPORTED
: 以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。TransactionDefinition.PROPAGATION_NEVER
: 以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則拋出異常。
@Transactional事務(wù)失效場(chǎng)景類(lèi)內(nèi)部調(diào)用實(shí)測(cè)
環(huán)境springboot2.7,mysql5.7
demo1
@Component @Order(6) @Slf4j public class TestRunner implements ApplicationRunner { @Autowired TestService testService; @Override public void run(ApplicationArguments args) throws Exception { testService.insertAndUpdate(); } }
@Service("testService") public class TestServiceImpl extends ServiceImpl<TestMapper, Test> implements TestService { @Override public boolean updateByIdOne() { LambdaUpdateWrapper<Test> lambdaUpdateWrapper = new UpdateWrapper<Test>().lambda(); lambdaUpdateWrapper.eq(Test::getId,1); lambdaUpdateWrapper.set(Test::getName,"test"); return this.update(lambdaUpdateWrapper); } @Transactional(rollbackFor = RuntimeException.class) @Override public boolean insertAndUpdate() { boolean b = this.updateByIdOne(); Test test = new Test(); test.setName("2222"); boolean save = this.save(test); if(b){ throw new RuntimeException("ts"); } return save; } }
以上代碼先跑一遍,看看拋出異常情況,能不能回滾
看庫(kù) 毫無(wú)變化
看主鍵遞增量其實(shí)是插入過(guò)了,我覺(jué)得事務(wù)還是生效了
demo2
@Service("testService") public class TestServiceImpl extends ServiceImpl<TestMapper, Test> implements TestService { @Transactional(rollbackFor = RuntimeException.class) @Override public boolean updateByIdOne() { LambdaUpdateWrapper<Test> lambdaUpdateWrapper = new UpdateWrapper<Test>().lambda(); lambdaUpdateWrapper.eq(Test::getId,1); lambdaUpdateWrapper.set(Test::getName,"test"); boolean update = this.update(lambdaUpdateWrapper); if(update){ throw new RuntimeException("updateByIdOne"); } LambdaUpdateWrapper<Test> lambdaUpdateWrapper2 = new UpdateWrapper<Test>().lambda(); lambdaUpdateWrapper2.eq(Test::getId,2); lambdaUpdateWrapper2.set(Test::getName,"test"); boolean update1 = this.update(lambdaUpdateWrapper2); return update; } @Transactional(rollbackFor = RuntimeException.class) @Override public boolean insertAndUpdate() { Test test = new Test(); test.setName("2222"); boolean save = this.save(test); boolean b = this.updateByIdOne(); return save; } }
執(zhí)行結(jié)果
子父方法都有事務(wù)注解,事務(wù)生效
demo3
@Service("testService") public class TestServiceImpl extends ServiceImpl<TestMapper, Test> implements TestService { @Override public boolean updateByIdOne() { LambdaUpdateWrapper<Test> lambdaUpdateWrapper = new UpdateWrapper<Test>().lambda(); lambdaUpdateWrapper.eq(Test::getId,1); lambdaUpdateWrapper.set(Test::getName,"test"); boolean update = this.update(lambdaUpdateWrapper); if(update){ throw new RuntimeException("updateByIdOne"); } return update; } @Transactional(rollbackFor = RuntimeException.class) @Override public boolean insertAndUpdate() { Test test = new Test(); test.setName("2222"); boolean save = this.save(test); boolean b = this.updateByIdOne(); return b; } }
insertAndUpdate插入成功后又回滾,update 更新成功也回滾,事務(wù)生效
demo4
@Service("testService") public class TestServiceImpl extends ServiceImpl<TestMapper, Test> implements TestService { @Transactional(rollbackFor = RuntimeException.class) @Override public boolean updateByIdOne() { LambdaUpdateWrapper<Test> lambdaUpdateWrapper = new UpdateWrapper<Test>().lambda(); lambdaUpdateWrapper.eq(Test::getId,1); lambdaUpdateWrapper.set(Test::getName,"test"); boolean update = this.update(lambdaUpdateWrapper); if(update){ throw new RuntimeException("updateByIdOne"); } LambdaUpdateWrapper<Test> lambdaUpdateWrapper2 = new UpdateWrapper<Test>().lambda(); lambdaUpdateWrapper2.eq(Test::getId,2); lambdaUpdateWrapper2.set(Test::getName,"test"); boolean update1 = this.update(lambdaUpdateWrapper2); return update; } @Override public boolean insertAndUpdate() { boolean b = this.updateByIdOne(); return b; } }
以上代碼一跑,結(jié)果就很清楚了。
1、在同類(lèi)中調(diào)用,二個(gè)方法都有加上事務(wù)注解,生效
2、同類(lèi)中,子方法有事務(wù)注解,父類(lèi)方法無(wú)事務(wù)注解,在controller層調(diào)用父類(lèi)方法,子方法事務(wù)不生效
3、同類(lèi)中,子方法無(wú)事務(wù)注解,父類(lèi)方法有事務(wù)注解,在controller層調(diào)用父類(lèi)方法,之方法事務(wù)生效
這些僅為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
springboot2如何集成ElasticSearch6.4.3
這篇文章主要介紹了springboot2如何集成ElasticSearch6.4.3問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07JavaWeb實(shí)現(xiàn)裁剪圖片上傳完整代碼
這篇文章主要為大家詳細(xì)介紹了javaWeb實(shí)現(xiàn)裁剪圖片上傳完整代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09Java基于PDFbox實(shí)現(xiàn)讀取處理PDF文件
PDFbox是一個(gè)開(kāi)源的、基于Java的、支持PDF文檔生成的工具庫(kù),它可以用于創(chuàng)建新的PDF文檔,修改現(xiàn)有的PDF文檔,還可以從PDF文檔中提取所需的內(nèi)容。本文將具體介紹一下PDFbox讀取處理PDF文件的示例代碼,感興趣的可以學(xué)習(xí)一下2022-02-02Java實(shí)現(xiàn)簡(jiǎn)單的迷宮游戲詳解
迷宮游戲作為經(jīng)典的小游戲,一直深受大家的喜愛(ài)。本文小編將為大家詳細(xì)介紹一下如何用Java實(shí)現(xiàn)一個(gè)簡(jiǎn)單的迷宮小游戲,感興趣的可以動(dòng)手試一試2022-02-02SpringBoot整合sharding-jdbc實(shí)現(xiàn)自定義分庫(kù)分表的實(shí)踐
本文主要介紹了SpringBoot整合sharding-jdbc實(shí)現(xiàn)自定義分庫(kù)分表的實(shí)踐,將通過(guò)自定義算法來(lái)實(shí)現(xiàn)定制化的分庫(kù)分表來(lái)擴(kuò)展相應(yīng)業(yè)務(wù),感興趣的可以了解一下2021-11-11Java內(nèi)存區(qū)域與內(nèi)存溢出異常詳解
這篇文章主要介紹了Java內(nèi)存區(qū)域與內(nèi)存溢出異常詳解的相關(guān)資料,需要的朋友可以參考下2017-03-03JAVA 根據(jù)設(shè)置的概率生成隨機(jī)數(shù)的方法
本篇文章主要介紹了JAVA 根據(jù)設(shè)置的概率生成隨機(jī)數(shù)的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08