使用@Transactional 設(shè)置嵌套事務(wù)不回滾
@Transactional 設(shè)置嵌套事務(wù)不回滾
@Transactional(rollbackFor = Exception.class)
public void testA(RequestSchedulingVO requestSchedulingVO) {
...業(yè)務(wù)...
BService.testB(param);
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = false, noRollbackFor = BusinessException.class)
// @Transactional(propagation = Propagation.NOT_SUPPORTED)
public void testB(Long param) throws BusinessException {
...業(yè)務(wù)...
try{
}cach(Exception e){
throw new BusinessException(BaiduFaceErrorCode.FACE_CREATE_CONVERSATION_ERROR);
}
}
這里的事務(wù)要求是方法testB報(bào)錯(cuò)(BusinessException異常)時(shí),testB事務(wù)不回滾,testA事務(wù)回滾.
方案一:
testB設(shè)置事務(wù)為REQUIRES_NEW(創(chuàng)建一個(gè)新的事務(wù),如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起),并設(shè)置不回滾的異常類
方案二:
testB設(shè)置事務(wù)為NOT_SUPPORTED(以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起)。
@Transactional回滾問題(try catch、嵌套)
Spring 事務(wù)注解 @Transactional 本來可以保證原子性,如果事務(wù)內(nèi)有報(bào)錯(cuò)的話,整個(gè)事務(wù)可以保證回滾,但是加上try catch或者事務(wù)嵌套,可能會(huì)導(dǎo)致事務(wù)回滾失敗。測(cè)試一波。
準(zhǔn)備
建兩張表,模擬兩個(gè)數(shù)據(jù)操作
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(20) DEFAULT NULL, `age` smallint(3) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; CREATE TABLE `role` ( `id` int(11) NOT NULL AUTO_INCREMENT, `role_name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
測(cè)試
根據(jù)排列組合原理,我們進(jìn)行四種測(cè)試:1、無try catch、無嵌套;2、有try catch、無嵌套;3、無try catch、有嵌套;4、都有。
最簡(jiǎn)單測(cè)試
如果我們單純@Transactional,事務(wù)可以正常回滾嗎?
@GetMapping("/saveNormal0")
@Transactional
public void saveNormal0() throws Exception {
int age = random.nextInt(100);
User user = new User().setAge(age).setName("name:"+age);
userService.save(user);
throw new RuntimeException();
}
如果事務(wù)內(nèi)報(bào)了RuntimeException錯(cuò)誤,事務(wù)可以回滾。
@GetMapping("/saveNormal0")
@Transactional
public void saveNormal0() throws Exception {
int age = random.nextInt(100);
User user = new User().setAge(age).setName("name:"+age);
userService.save(user);
throw new Exception();
}
如果事務(wù)內(nèi)報(bào)了Exception錯(cuò)誤(非RuntimeException錯(cuò)誤),事務(wù)不可以回滾。
@GetMapping("/saveNormal0")
@Transactional( rollbackFor = Exception.class)
public void saveNormal0() throws Exception {
int age = random.nextInt(100);
User user = new User().setAge(age).setName("name:"+age);
userService.save(user);
throw new Exception();
}
如果是Exception錯(cuò)誤(非RuntimeException),加上 rollbackFor = Exception.class 參數(shù)也可以實(shí)現(xiàn)回滾。
結(jié)論一:對(duì)于@Transactional可以保證RuntimeException錯(cuò)誤的回滾,如果想保證非RuntimeException錯(cuò)誤的回滾,需要加上rollbackFor = Exception.class 參數(shù)。
try catch 影響
經(jīng)過博主多種情況測(cè)試,發(fā)現(xiàn)try catch對(duì)回滾這個(gè)事本身沒有什么影響,結(jié)論一照樣成立。try catch只是對(duì)異常是否可以被@Transactional 感知 到有影響。如果錯(cuò)誤拋到切面可以感知到的地步,那就可以起作用。
@GetMapping("/saveTryCatch")
@Transactional( rollbackFor = Exception.class)
public void saveTryCatch() throws Exception{
try{
int age = random.nextInt(100);
User user = new User().setAge(age).setName("name:"+age);
userService.save(user);
throw new Exception();
}catch (Exception e){
throw e;
}
}
比如上面一段代碼就回滾了。
@GetMapping("/saveTryCatch")
@Transactional( rollbackFor = Exception.class)
public void saveTryCatch() throws Exception{
try{
int age = random.nextInt(100);
User user = new User().setAge(age).setName("name:"+age);
userService.save(user);
throw new Exception();
}catch (Exception e){
}
}
然而,將catch中的錯(cuò)誤不繼續(xù)網(wǎng)上拋,切面無法感知到錯(cuò)誤,無法進(jìn)行處理,那么事務(wù)就無法回滾了。
結(jié)論二:try catch只是對(duì)異常是否可以被@Transactional 感知 到有影響。如果錯(cuò)誤拋到切面可以感知到的地步,那就可以起作用。
事務(wù)嵌套 影響
首先經(jīng)過實(shí)驗(yàn),結(jié)論一仍然成立,即,當(dāng)不加上rollbackFor = Exception.class 的時(shí)候,無論內(nèi)外報(bào)RuntimeException,都會(huì)回滾;無論內(nèi)外報(bào) 非RuntimeException 錯(cuò)誤,都不會(huì)回滾。如果加上rollbackFor = Exception.class,無論內(nèi)外怎么報(bào)錯(cuò),都會(huì)回滾。這些代碼就不給出了。接下來,試下下面兩種情況:
@GetMapping("/out")
@Transactional( rollbackFor = Exception.class)
public void out() throws Exception{
innerService.inner();
int age = random.nextInt(100);
User user = new User().setAge(age).setName("name:" + age);
userService.save(user);
throw new Exception();
}
@Transactional
public void inner() throws Exception{
Role role = new Role();
role.setRoleName("roleName:"+new Random().nextInt(100));
roleService.save(role);
// throw new Exception();
}
情況一,外面事務(wù)加上rollbackFor = Exception.class,里面事務(wù)不加,測(cè)試內(nèi)外分別報(bào)錯(cuò)的情況(為了簡(jiǎn)化代碼量,只給出了外面報(bào)錯(cuò)的代碼),都可以回滾。因?yàn)?,無論如何,錯(cuò)誤都拋給了外面那個(gè)事務(wù)進(jìn)行處理,而外面那個(gè)加上了rollbackFor = Exception.class,具備處理非RuntimeException錯(cuò)誤的能力,所以都可以讓事務(wù)進(jìn)行正?;貪L。
下面看情況二,里面的事務(wù)加上rollbackFor = Exception.class,外面不加,外面報(bào)錯(cuò)。
@GetMapping("/out")
@Transactional
public void out() throws Exception{
innerService.inner();
int age = random.nextInt(100);
User user = new User().setAge(age).setName("name:" + age);
userService.save(user);
throw new Exception();
}
@Transactional( rollbackFor = Exception.class)
public void inner() throws Exception{
Role role = new Role();
role.setRoleName("roleName:"+new Random().nextInt(100));
roleService.save(role);
}
事務(wù)都無法回滾,這是我們有個(gè)疑問,里面的事務(wù)明明有很強(qiáng)的處理能力啊,為什么和外面一起回滾失敗呢,別著急,等等聊這個(gè)。
然后試下里面報(bào)錯(cuò):
@GetMapping("/out")
@Transactional
public void out() throws Exception{
innerService.inner();
int age = random.nextInt(100);
User user = new User().setAge(age).setName("name:" + age);
userService.save(user);
}
@Transactional( rollbackFor = Exception.class)
public void inner() throws Exception{
Role role = new Role();
role.setRoleName("roleName:"+new Random().nextInt(100));
roleService.save(role);
throw new Exception();
}
咦,這回都進(jìn)行了正常的回滾。我的天,這回外面沒有處理能力,為什么接受里面拋出來的錯(cuò)誤,也進(jìn)行了回滾!?。】瓷先?,就好像里外事務(wù)總是同生共死的對(duì)不對(duì)?原來,@Transactional還有個(gè)參數(shù),看下源碼,這個(gè)注解還有默認(rèn)值:
Propagation propagation() default Propagation.REQUIRED;
REQUIRED的意思是說,事務(wù)嵌套的時(shí)候,如果發(fā)現(xiàn)已經(jīng)有事務(wù)存在了,就加入這個(gè)事務(wù),而不是新建一個(gè)事務(wù),所以根本就不存在兩個(gè)事務(wù),一直只有一個(gè)!至于,此參數(shù)其他值,本文不進(jìn)行測(cè)試?;氐缴厦娴膯栴},當(dāng)外面報(bào)錯(cuò)的時(shí)候,此時(shí)查看事務(wù),沒有增加rollbackFor = Exception.class參數(shù),即沒有處理非RuntimeException能力,所以代碼走完,貌似“兩個(gè)事務(wù)”,都回滾失敗了。當(dāng)里面報(bào)錯(cuò)的時(shí)候,事務(wù)已經(jīng)添加上了處理非RuntimeException能力,所以,代碼走完就回滾成功了。
結(jié)論三:由于REQUIRED屬性,“兩個(gè)事務(wù)”其實(shí)是一個(gè)事務(wù),處理能力看報(bào)錯(cuò)時(shí)刻,是否添加了處理非RuntimeException的能力。
try catch和事務(wù)嵌套 共同影響
在結(jié)論一二三成立的條件下,探索共同影響的問題就簡(jiǎn)單多了,由于情況太多,就不進(jìn)行過多的代碼展示了。
結(jié)論
結(jié)論一:
對(duì)于@Transactional可以保證RuntimeException錯(cuò)誤的回滾,如果想保證非RuntimeException錯(cuò)誤的回滾,需要加上rollbackFor = Exception.class 參數(shù)。
結(jié)論二:
try catch只是對(duì)異常是否可以被@Transactional 感知 到有影響。如果錯(cuò)誤拋到切面可以感知到的地步,那就可以起作用。
結(jié)論三:
由于REQUIRED屬性,“兩個(gè)事務(wù)”其實(shí)是一個(gè)事務(wù),處理能力看報(bào)錯(cuò)時(shí)刻,是否添加了處理非RuntimeException的能力。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Python如何使用@property @x.setter及@x.deleter
這篇文章主要介紹了Python如何使用@property @x.setter及@x.deleter,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05
java如何在項(xiàng)目中實(shí)現(xiàn)excel導(dǎo)入導(dǎo)出功能
這篇文章主要介紹了java如何在項(xiàng)目中實(shí)現(xiàn)excel導(dǎo)入導(dǎo)出功能的相關(guān)資料,EasyExcel是一個(gè)基于Apache?POI開發(fā)的開源Java庫,用于簡(jiǎn)化Excel文件的讀寫操作,文中將用法介紹的非常詳細(xì),需要的朋友可以參考下2024-10-10
Java實(shí)現(xiàn)在PPT中創(chuàng)建SmartArt圖形的示例代碼
SmartArt其實(shí)就是一個(gè)文字的可視化工具,用戶可在PowerPoint,Word,Excel中使用該特性創(chuàng)建各種圖形圖表。本文就將為您介紹如何通過Java應(yīng)用程序在PPT中創(chuàng)建SmartArt圖形,需要的可以參考一下2023-04-04
學(xué)習(xí)不同 Java.net 語言中類似的函數(shù)結(jié)構(gòu)
這篇文章主要介紹了學(xué)習(xí)不同 Java.net 語言中類似的函數(shù)結(jié)構(gòu),函數(shù)式編程語言包含多個(gè)系列的常見函數(shù)。但開發(fā)人員有時(shí)很難在語言之間進(jìn)行切換,因?yàn)槭煜さ暮瘮?shù)具有不熟悉的名稱。函數(shù)式語言傾向于基于函數(shù)范例來命名這些常見函數(shù)。,需要的朋友可以參考下2019-06-06
一文了解SpringBoot是如何連接數(shù)據(jù)庫的
Spring Boot提供了一系列的開箱即用的功能和特性,使得開發(fā)人員可以快速構(gòu)建和部署應(yīng)用程序,下面這篇文章主要給大家介紹了關(guān)于SpringBoot是如何連接數(shù)據(jù)庫的相關(guān)資料,需要的朋友可以參考下2023-06-06
Java中l(wèi)ist.contains()的用法及拓展
List集合相信大家在開發(fā)過程中幾乎都會(huì)用到,有時(shí)候難免會(huì)遇到集合里的數(shù)據(jù)是重復(fù)的,需要進(jìn)行去除,下面這篇文章主要給大家介紹了關(guān)于Java中l(wèi)ist.contains()的用法及拓展的相關(guān)資料,需要的朋友可以參考下2023-03-03

