Spring的@Transactional嵌套解讀
事務(wù)嵌套和局部回滾的問題,很是費解。
本文將做一個詳細的測試,加強對Spring的@Transactional 理解和使用
1、兩個單獨不干擾事務(wù)
@RequestMapping("/test") public void test() { LoveFile test1 = new LoveFile(); test1.setFileUuid(get32Uuid()); test1.setFileName("test1"); loveFileService.test1(test1); LoveFile test2 = new LoveFile(); test2.setFileUuid(null); test2.setFileName("test2"); loveFileService.test2(test2); }
@Override @Transactional public void test1(LoveFile loveFile) { mapper.insertSelective(loveFile); } @Override @Transactional public void test2(LoveFile loveFile) { mapper.insertSelective(loveFile); }
由于我給第二個test2插入主鍵為空,報錯。因此庫里只有一條
總結(jié):兩個單獨事務(wù)互不干擾。錯了就是錯了,很好理解
2、普通嵌套事務(wù)
@RequestMapping("/test") @Transactional public void test() { LoveFile test1 = new LoveFile(); test1.setFileUuid(get32Uuid()); test1.setFileName("test1"); loveFileService.test1(test1); LoveFile test2 = new LoveFile(); test2.setFileUuid(null); test2.setFileName("test2"); loveFileService.test2(test2); }
子事務(wù)不變,刪除剛才的數(shù)據(jù)后,測試。
庫里空空。
總結(jié):加在外層的事務(wù)起了作用,在test2報錯時回滾了test1
3、嵌套事務(wù)、三個子事務(wù)中間一個加(propagation = Propagation.REQUIRES_NEW)
Propagation取值:
REQUIRED
(默認值):在有transaction狀態(tài)下執(zhí)行;如當(dāng)前沒有transaction,則創(chuàng)建新的transaction;SUPPORTS
:如當(dāng)前有transaction,則在transaction狀態(tài)下執(zhí)行;如果當(dāng)前沒有transaction,在無transaction狀態(tài)下執(zhí)行;MANDATORY
:必須在有transaction狀態(tài)下執(zhí)行,如果當(dāng)前沒有transaction,則拋出異常IllegalTransactionStateException;REQUIRES_NEW
:創(chuàng)建新的transaction并執(zhí)行;如果當(dāng)前已有transaction,則將當(dāng)前transaction掛起;NOT_SUPPORTED
:在無transaction狀態(tài)下執(zhí)行;如果當(dāng)前已有transaction,則將當(dāng)前transaction掛起;NEVER
:在無transaction狀態(tài)下執(zhí)行;如果當(dāng)前已有transaction,則拋出異常IllegalTransactionStateException。
本文重點研究 REQUIRES_NEW
@RequestMapping("/test") @Transactional public void test() { LoveFile test1 = new LoveFile(); test1.setFileUuid(get32Uuid()); test1.setFileName("test1"); loveFileService.test1(test1); LoveFile test3 = new LoveFile(); test3.setFileUuid(get32Uuid()); test3.setFileName("test3"); loveFileService.test3(test3); LoveFile test2 = new LoveFile(); test2.setFileUuid(null); test2.setFileName("test2"); loveFileService.test2(test2); }
@Override @Transactional public void test1(LoveFile loveFile) { mapper.insertSelective(loveFile); } @Override @Transactional public void test2(LoveFile loveFile) { mapper.insertSelective(loveFile); } @Override @Transactional(propagation = Propagation.REQUIRES_NEW) public void test3(LoveFile loveFile) { mapper.insertSelective(loveFile); }
REQUIRES_NEW官方文檔解釋:
Create a new transaction, and suspend the current transaction if one exists.
意思是,創(chuàng)建一個新事務(wù),如果當(dāng)前存在事務(wù),將這個事務(wù)掛起。
也就是說test3,是獨立于外層事務(wù)的單獨的事務(wù),他已經(jīng)掛起的外層事務(wù),他要把自己的問題處理完再去管別人。
因此結(jié)果是
總結(jié):不給子事務(wù)加(propagation = Propagation.REQUIRES_NEW),相當(dāng)于加入了外層事務(wù)。加了則當(dāng)做新的子事務(wù)。
test2報錯了,影響了test1 ,卻不能影響test3,已經(jīng)說明了此問題
4、嵌套事務(wù)、三個子事務(wù)中間一個加(propagation = Propagation.REQUIRES_NEW),偏偏出錯了
@RequestMapping("/test") @Transactional public void test() { LoveFile test1 = new LoveFile(); test1.setFileUuid(get32Uuid()); test1.setFileName("test1"); loveFileService.test1(test1); LoveFile test3 = new LoveFile(); test3.setFileUuid(null); test3.setFileName("test3"); loveFileService.test3(test3); LoveFile test2 = new LoveFile(); test2.setFileUuid(get32Uuid()); test2.setFileName("test2"); loveFileService.test2(test2); }
我把test3主鍵為空,因此他會有SQLException。來研究下,他會不會影響整體。
總結(jié): 雖然他是開辟的新事物,但是出錯了,還是會牽連整體的
至此,(propagation = Propagation.REQUIRES_NEW) 已經(jīng)解釋清楚了。
我覺得子事務(wù),不加這個東西的話不叫子事務(wù)。不加的話直接加入了外層事務(wù)。那還要個單單 @Transactional何用?
建議大家對子事務(wù)都加上這條件。
rollbackFor官方文檔解釋:
在@Transactional注解中如果不配置rollbackFor屬性,那么事物只會在遇到RuntimeException的時候才會回滾,加上rollbackFor=Exception.class,可以讓事物在遇到非運行時異常時也回滾
5、rollbackFor了一個異常,卻會報另外異常
@RequestMapping("/test") @Transactional public void test() { LoveFile test1 = new LoveFile(); test1.setFileUuid(get32Uuid()); test1.setFileName("test1"); loveFileService.test1(test1); LoveFile test3 = new LoveFile(); test3.setFileUuid(null); test3.setFileName("test3"); loveFileService.test3(test3); LoveFile test2 = new LoveFile(); test2.setFileUuid(get32Uuid()); test2.setFileName("test2"); loveFileService.test2(test2); }
@Override @Transactional public void test1(LoveFile loveFile) { mapper.insertSelective(loveFile); } @Override @Transactional public void test2(LoveFile loveFile) { mapper.insertSelective(loveFile); } @Override @Transactional(rollbackFor=SQLException.class,propagation = Propagation.REQUIRES_NEW) public void test3(LoveFile loveFile) { int a = 1/0; // mapper.insertSelective(loveFile); }
這個test3,一定會報 by zero,他會報錯,我來測試下,他會不會回滾,影響他人。
如此是影響了。
因為外層事務(wù)沒有聲明異常,他看到一個內(nèi)部報by zero 了,而自己是RuntimeException,包含在內(nèi)。因此自己回滾了。
test1 是直接加入整體的。沒加出數(shù)據(jù),就說明了此問題。
我其實是像測試,test3是否回滾,這個測試案例不合適。
@RequestMapping("/test") @Transactional public void test() { LoveFile test1 = new LoveFile(); test1.setFileUuid(get32Uuid()); test1.setFileName("test1"); loveFileService.test1(test1); LoveFile test3 = new LoveFile(); test3.setFileUuid(get32Uuid()); test3.setFileName("test3"); loveFileService.test3(test3); LoveFile test2 = new LoveFile(); test2.setFileUuid(get32Uuid()); test2.setFileName("test2"); loveFileService.test2(test2); }
@Override @Transactional public void test1(LoveFile loveFile) { mapper.insertSelective(loveFile); } @Override @Transactional public void test2(LoveFile loveFile) { mapper.insertSelective(loveFile); } @Override @Transactional(rollbackFor=SQLException.class,propagation = Propagation.REQUIRES_NEW) public void test3(LoveFile loveFile) { mapper.insertSelective(loveFile); int a = 1/0; }
我給了test3 正常的主鍵,讓他添加成功,卻在后面 by zero .因為我認為他不會回滾,這次應(yīng)該是test3加進去了,test1和test2被外層事務(wù)回滾。
但是結(jié)果是:
我又做了一些測試,發(fā)現(xiàn)rollbackFor=SQLException.class 這樣的聲明一個特定的異常沒有任何效果。
rollbackFor 用途不大,就是聲明rollbackFor=Exception.class 時比 RuntimeException 廣一些。
其他時用途不大。
如果想讓特定異常不回滾,還不如用 try catch。只要異常被catch住,不被方法知道,就不會出現(xiàn)error ,也就不會回滾。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Jmeter后置處理器實現(xiàn)過程及方法應(yīng)用
這篇文章主要介紹了Jmeter后置處理器實現(xiàn)過程及方法應(yīng)用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-09-09SSH框架網(wǎng)上商城項目第26戰(zhàn)之訂單支付后發(fā)送短信提醒
這篇文章主要為大家詳細介紹了SSH框架網(wǎng)上商城項目第26戰(zhàn)之訂單支付后發(fā)送短信提醒,感興趣的小伙伴們可以參考一下2016-06-06Java之關(guān)于基本數(shù)據(jù)類型和引用數(shù)據(jù)類型的存放位置
這篇文章主要介紹了Java之關(guān)于基本數(shù)據(jù)類型和引用數(shù)據(jù)類型的存放位置,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07Java中遍歷數(shù)組使用foreach循環(huán)還是for循環(huán)?
這篇文章主要介紹了Java中遍歷數(shù)組使用foreach循環(huán)還是for循環(huán)?本文著重講解for語句的語法并給出使用實例,同時總結(jié)出盡量使用foreach語句遍歷數(shù)組,需要的朋友可以參考下2015-06-06Mybatis?TypeHandler接口及繼承關(guān)系示例解析
這篇文章主要為大家介紹了Mybatis?TypeHandler接口及繼承關(guān)系示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02