Spring的@Transactional嵌套解讀
事務(wù)嵌套和局部回滾的問題,很是費(fèi)解。
本文將做一個(gè)詳細(xì)的測(cè)試,加強(qiáng)對(duì)Spring的@Transactional 理解和使用
1、兩個(gè)單獨(dú)不干擾事務(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);
}由于我給第二個(gè)test2插入主鍵為空,報(bào)錯(cuò)。因此庫(kù)里只有一條

總結(jié):兩個(gè)單獨(dú)事務(wù)互不干擾。錯(cuò)了就是錯(cuò)了,很好理解
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ù)后,測(cè)試。
庫(kù)里空空。

總結(jié):加在外層的事務(wù)起了作用,在test2報(bào)錯(cuò)時(shí)回滾了test1
3、嵌套事務(wù)、三個(gè)子事務(wù)中間一個(gè)加(propagation = Propagation.REQUIRES_NEW)
Propagation取值:
REQUIRED(默認(rèn)值):在有transaction狀態(tài)下執(zhí)行;如當(dāng)前沒有transaction,則創(chuàng)建新的transaction;SUPPORTS:如當(dāng)前有transaction,則在transaction狀態(tài)下執(zhí)行;如果當(dāng)前沒有transaction,在無(wú)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:在無(wú)transaction狀態(tài)下執(zhí)行;如果當(dāng)前已有transaction,則將當(dāng)前transaction掛起;NEVER:在無(wú)transaction狀態(tài)下執(zhí)行;如果當(dāng)前已有transaction,則拋出異常IllegalTransactionStateException。
本文重點(diǎn)研究 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)建一個(gè)新事務(wù),如果當(dāng)前存在事務(wù),將這個(gè)事務(wù)掛起。
也就是說test3,是獨(dú)立于外層事務(wù)的單獨(dú)的事務(wù),他已經(jīng)掛起的外層事務(wù),他要把自己的問題處理完再去管別人。
因此結(jié)果是

總結(jié):不給子事務(wù)加(propagation = Propagation.REQUIRES_NEW),相當(dāng)于加入了外層事務(wù)。加了則當(dāng)做新的子事務(wù)。
test2報(bào)錯(cuò)了,影響了test1 ,卻不能影響test3,已經(jīng)說明了此問題
4、嵌套事務(wù)、三個(gè)子事務(wù)中間一個(gè)加(propagation = Propagation.REQUIRES_NEW),偏偏出錯(cuò)了
@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主鍵為空,因此他會(huì)有SQLException。來研究下,他會(huì)不會(huì)影響整體。

總結(jié): 雖然他是開辟的新事物,但是出錯(cuò)了,還是會(huì)牽連整體的
至此,(propagation = Propagation.REQUIRES_NEW) 已經(jīng)解釋清楚了。
我覺得子事務(wù),不加這個(gè)東西的話不叫子事務(wù)。不加的話直接加入了外層事務(wù)。那還要個(gè)單單 @Transactional何用?
建議大家對(duì)子事務(wù)都加上這條件。
rollbackFor官方文檔解釋:
在@Transactional注解中如果不配置rollbackFor屬性,那么事物只會(huì)在遇到RuntimeException的時(shí)候才會(huì)回滾,加上rollbackFor=Exception.class,可以讓事物在遇到非運(yùn)行時(shí)異常時(shí)也回滾
5、rollbackFor了一個(gè)異常,卻會(huì)報(bào)另外異常
@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);
}這個(gè)test3,一定會(huì)報(bào) by zero,他會(huì)報(bào)錯(cuò),我來測(cè)試下,他會(huì)不會(huì)回滾,影響他人。

如此是影響了。
因?yàn)橥鈱邮聞?wù)沒有聲明異常,他看到一個(gè)內(nèi)部報(bào)by zero 了,而自己是RuntimeException,包含在內(nèi)。因此自己回滾了。
test1 是直接加入整體的。沒加出數(shù)據(jù),就說明了此問題。
我其實(shí)是像測(cè)試,test3是否回滾,這個(gè)測(cè)試案例不合適。
@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àn)槲艺J(rèn)為他不會(huì)回滾,這次應(yīng)該是test3加進(jìn)去了,test1和test2被外層事務(wù)回滾。
但是結(jié)果是:

我又做了一些測(cè)試,發(fā)現(xiàn)rollbackFor=SQLException.class 這樣的聲明一個(gè)特定的異常沒有任何效果。
rollbackFor 用途不大,就是聲明rollbackFor=Exception.class 時(shí)比 RuntimeException 廣一些。
其他時(shí)用途不大。
如果想讓特定異常不回滾,還不如用 try catch。只要異常被catch住,不被方法知道,就不會(huì)出現(xiàn)error ,也就不會(huì)回滾。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringCloud Bus消息總線的實(shí)現(xiàn)
消息總線是一種通信工具,可以在機(jī)器之間互相傳輸消息、文件等,這篇文章主要介紹了SpringCloud Bus消息總線的實(shí)現(xiàn),Spring cloud bus 通過輕量消息代理連接各個(gè)分布的節(jié)點(diǎn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05
自定義log4j2中的Appender來獲取日志內(nèi)容的示例代碼
在 Log4j2 中,Appender 是負(fù)責(zé)將日志事件輸出到目標(biāo)地點(diǎn)的組件,本文講述的是通過 log4j 中自定義的 Appender 來獲取需要打印的日志信息,文中有詳細(xì)的代碼示例供大家參考,需要的朋友可以參考下2024-02-02
spring-boot使用Admin監(jiān)控應(yīng)用的方法
本篇文章主要介紹了spring-boot使用Admin監(jiān)控應(yīng)用的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-09-09
mybatis框架order by作為參數(shù)傳入時(shí)失效的解決
這篇文章主要介紹了mybatis框架order by作為參數(shù)傳入時(shí)失效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
JAVA獲取當(dāng)前項(xiàng)目和文件所在路徑的實(shí)例代碼
這篇文章主要介紹了JAVA獲取當(dāng)前項(xiàng)目和文件所在路徑的實(shí)例代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03
解決mybatisplus插入報(bào)錯(cuò)argument type mismatch的問題
這篇文章主要介紹了解決mybatisplus插入報(bào)錯(cuò)argument type mismatch的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-11-11
java jni調(diào)用c函數(shù)實(shí)例分享(java調(diào)用c函數(shù))
Java代碼中調(diào)用C/C++代碼,當(dāng)然是使用JNI,JNI是Java native interface的簡(jiǎn)寫,可以譯作Java原生接口,下面看實(shí)例吧2013-12-12

