spring聲明式事務(wù) @Transactional 不回滾的多種情況以及解決方案
本文是基于springboot完成測試測試代碼地址如下:
https://github.com/Dr-Water/springboot-action/tree/master/springboot-shiro
一、 spring 事務(wù)原理
一、Spring事務(wù)原理
在使用JDBC事務(wù)操作數(shù)據(jù)庫時,流程如下:
//獲取連接 1.Connection con = DriverManager.getConnection() //開啟事務(wù) 2.con.setAutoCommit(true/false); 3.執(zhí)行CRUD //提交事務(wù)/回滾事務(wù) 4. con.commit() / con.rollback(); //關(guān)閉連接 5. conn.close();
Spring本身并不提供事務(wù),而是對JDBC事務(wù)通過AOP做了封裝,隱藏了2和4的操作,簡化了JDBC的應(yīng)用。
spring對JDBC事務(wù)的封裝,是通過AOP動態(tài)代理來實現(xiàn)的,在調(diào)用目標(biāo)方法(也就是第3步)前后會通過代理類來執(zhí)行事務(wù)的開啟、提交或者回滾操作。
spring事務(wù)使用的兩個不可忽略點:
注意關(guān)鍵詞 “動態(tài)代理”,這意味著要生成一個代理類,那么我們就不能在一個類內(nèi)直接調(diào)用事務(wù)方法,否則無法代理,而且該事務(wù)方法必須是public,如果定義成 protected、private 或者默認(rèn)可見性,則無法調(diào)用!
問題一、@Transactional 應(yīng)該加到什么地方,如果加到Controller會回滾嗎?
@Transactional 最好加到service層,加到Controller層也是生效的,但是為了規(guī)范起見,還是加到service層上。
下載代碼并啟動難項目進(jìn)行驗證:主要代碼如下:
Controller層代碼如下:
@Autowired private TransactionalService transactionalService; @Autowired private UserDao userDao; @Autowired private JwtUserDao jwtUserDao; /** * 測試@Transactional 注解加到service層事務(wù)是否回滾 */ @RequestMapping("/tx") public void serviceTX(){ transactionalService.controllerTX(); } /** * 測試@Transactional 注解加到Controller層事務(wù)是否回滾 */ @Transactional(rollbackFor = Exception.class) @RequestMapping("/ctx2") public void cTX2(){ userDao.update(); System.out.println(2/0); jwtUserDao.update(); } /** * 測試@Transactional 注解加到Controller層事務(wù)是否回滾 * 這里在Controller層為了方便直接調(diào)用了dao層,在實際開發(fā)中dao層即可在Controller層調(diào)用也可以在service層調(diào)用, * 比如service層只是直接調(diào)用dao層一個方法,此外沒有任何操作,那么這時候完全不用寫service層的方法,直接在Controller調(diào)用dao層即可, * 當(dāng)然如果公司有規(guī)范,必須嚴(yán)格按照mvc的模式進(jìn)行開發(fā),則另說 */ @Transactional(rollbackFor = Exception.class) @RequestMapping("/ctx2") public void cTX2(){ userDao.update(); //手動拋出一個RuntimeException System.out.println(2/0); jwtUserDao.update(); }
service層的主要代碼
@Autowired private UserDao userDao; @Autowired private JwtUserDao jwtUserDao; @Transactional(rollbackFor = Exception.class) public void controllerTX(){ userDao.update(); //手動拋出一個RuntimeException System.out.println(2/0); jwtUserDao.update(); }
dao層sql語句如下:
<update id="update"> UPDATE jwt_user SET username ='wangwuupdate' WHERE user_id= 2 </update> <update id="update"> UPDATE user SET username ='zsupdate' WHERE id= 2 </update>
數(shù)據(jù)庫原始數(shù)據(jù):
瀏覽器中輸入:http://localhost:8081/tx/tx,由于本次使用測試代碼進(jìn)行統(tǒng)一的異常處理所以瀏覽器的返回數(shù)據(jù)如下:
控制臺輸出如下:
查看數(shù)據(jù)庫中的數(shù)據(jù)并沒有被修改
瀏覽器中輸入:http://localhost:8081/tx/ctx2,
查看數(shù)據(jù)庫中的數(shù)據(jù)并沒有被修改
由此可以得出 :@Transactional 加到Controller層也是生效的,但是為了規(guī)范起見,還是加到service層上。
問題二、 @Transactional 注解中用不用加rollbackFor = Exception.class 這個屬性值
spring的api doc中有折磨一句描述:
紅框中的內(nèi)容如下:
rolling back on RuntimeException and Error but not on checked exceptions
大致意思就默認(rèn)情況下,當(dāng)程序發(fā)生 RuntimeException 和 Error 的這兩種異常的時候事務(wù)會回滾,但是如果發(fā)生了checkedExcetions ,如fileNotfundException 則不會回滾,所以 rollbackFor = Exception.class 這個一定要加!
驗證如下:
瀏覽器輸入:http://localhost:8081/tx/ctx3
控制臺輸出如下:
這時候查看數(shù)據(jù)庫中的數(shù)據(jù)并沒有被修改
瀏覽器輸入:http://localhost:8081/tx/ctx4
這時候查看數(shù)據(jù)庫數(shù)據(jù)已經(jīng)被修改:
問題三:事務(wù)調(diào)用嵌套問題具體結(jié)果如下代碼:
/** * 同類中在方法a中調(diào)用b * a沒有事務(wù),b有 ,異常發(fā)生在b中 不會回滾 */ @RequestMapping("/a1") public void a1(){ transactionalService.a1(); } /** * 同類中在方法a中調(diào)用b * a沒有事務(wù),b有 ,異常發(fā)生在a中 不會回滾 */ @RequestMapping("/a2") public void a2(){ transactionalService.a2(); } /** * 同類中在方法a中調(diào)用b * a有事務(wù),b沒有 ,異常發(fā)生在b中 會回滾 */ @RequestMapping("/a3") public void a3(){ transactionalService.a3(); } /** * 同類中在方法a中調(diào)用b * a有事務(wù),b沒有 ,異常發(fā)生在a中 會回滾 */ @RequestMapping("/a4") public void a4(){ transactionalService.a4(); } /** * 同類中在方法a中調(diào)用b * a有事務(wù),b也有 ,異常發(fā)生在b中 會回滾 */ @RequestMapping("/a5") public void a5(){ transactionalService.a5(); } /** * 同類中在方法a中調(diào)用b * a有事務(wù),b也有 ,異常發(fā)生在a中 會回滾 */ @RequestMapping("/a6") public void a6(){ transactionalService.a6(); } /** *a類中調(diào)用b類中的方法 * a中有事務(wù),b中也有 會回滾 * */ @RequestMapping("/b5") public void b5(){ transactionalService.b5(); } /** *a類中調(diào)用b類中的方法 * a中有事務(wù),b中沒有 會回滾 * */ @RequestMapping("/b6") public void b6(){ transactionalService.b6(); } /** *a類中調(diào)用b類中的方法 * a沒有事務(wù),b中有 不會回滾 * */ @RequestMapping("/b7") public void b7(){ transactionalService.b7(); } /** *a類中調(diào)用b類中的方法 * a沒有事務(wù),b中沒有 不會回滾 * */ @RequestMapping("/b8") public void b8(){ transactionalService.b8(); }
總結(jié):如果在a方法中調(diào)用b方法不管是不是a和b是不是在同一個類中,只要a方法中沒有事務(wù),則發(fā)生異常的時候不會回滾,即:當(dāng)a無事務(wù)時,則a和b均沒有事務(wù),當(dāng)a有事務(wù)時,b如果有事務(wù),則b事務(wù)會加到a事務(wù)中,二者為同一事務(wù)!
四、總結(jié)
在springboot中默認(rèn)是開啟事務(wù)的,在service層的方法加上@Transactional(rollbackFor = Exception.class) 注解即可實現(xiàn)事務(wù) 如果在方法a中調(diào)用方法b 如果要實現(xiàn)事務(wù),則只需要在方法上加上@Transactional(rollbackFor = Exception.class) 即可!
如果業(yè)務(wù)需要,一定要拋出checked異常的話,可以通過rollbackFor屬性指定異常類型即可。有興趣的可以動手驗證一下,這里不再贅述。
五、 參考鏈接
Spring Boot中的事務(wù)管理
深入理解 Spring 之 SpringBoot 事務(wù)原理
聲明式事務(wù)不回滾@Transactional的避坑正確使用
Spring聲明式事務(wù)不回滾問題
spring 事務(wù)應(yīng)用誤區(qū)總結(jié):那些導(dǎo)致事務(wù)不回滾的坑
你的Spring事務(wù)為什么不會自動回滾,包含異常的分類
Java異常之checked與unchecked
到此這篇關(guān)于spring聲明式事務(wù) @Transactional 不回滾的多種情況以及解決方案的文章就介紹到這了,更多相關(guān)spring @Transactional不回滾內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
一文吃透消息隊列RocketMQ實現(xiàn)消費冪等原理
這篇文章主要介紹了消息隊列RocketMQ實現(xiàn)消費冪等的全面講解,幫助大家吃透RocketMQ消息隊列消費冪等,更好的的應(yīng)用與工作實踐,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01SpringBoot+Ajax+redis實現(xiàn)隱藏重要接口地址的方法
這篇文章主要介紹了SpringBoot+Ajax+redis實現(xiàn)隱藏重要接口地址,本篇文章主要講訴使用SpringBoot項目配合Ajax和redis實現(xiàn)隱藏重要接口地址,這里我以隱藏秒殺地址為例,需要的朋友可以參考下2024-03-03Java中線程組ThreadGroup與線程池的區(qū)別及示例
這篇文章主要介紹了Java中線程組與線程池的區(qū)別及示例,ThreadGroup是用來管理一組線程的,可以控制線程的執(zhí)行,查看線程的執(zhí)行狀態(tài)等操作,方便對于一組線程的統(tǒng)一管理,需要的朋友可以參考下2023-05-05Maven多模塊之父子關(guān)系的創(chuàng)建
這篇文章主要介紹了Maven多模塊之父子關(guān)系的創(chuàng)建,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03spring boot高并發(fā)下耗時操作的實現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于spring boot高并發(fā)下耗時操作的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用spring boot具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11