準備
建兩張表,模擬兩個數據操作
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;
測試:
根據排列組合原理,我們進行四種測試:
1、無try catch、無嵌套;2、有try catch、無嵌套;3、無try catch、有嵌套;4、都有。
最簡單測試
如果我們單純@Transactional,事務可以正常回滾嗎?
@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();
}
如果事務內報了RuntimeException錯誤,事務可以回滾。
@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();
}
如果事務內報了Exception錯誤(非RuntimeException錯誤),事務不可以回滾。
@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錯誤(非RuntimeException),加上 rollbackFor = Exception.class 參數也可以實現回滾。
結論一:對於@Transactional可以保證RuntimeException錯誤的回滾,如果想保證非RuntimeException錯誤的回滾,需要加上rollbackFor = Exception.class 參數。
try catch 影響
經過博主多種情況測試,發現try catch對回滾這個事本身沒有什麼影響,結論一照樣成立。try catch只是對異常是否可以被@Transactional 感知 到有影響。如果錯誤拋到切面可以感知到的地步,那就可以起作用。
@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中的錯誤不繼續網上拋,切面無法感知到錯誤,無法進行處理,那麼事務就無法回滾了。
結論二:try catch只是對異常是否可以被@Transactional 感知 到有影響。如果錯誤拋到切面可以感知到的地步,那就可以起作用。
事務嵌套 影響
首先經過實驗,結論一仍然成立,即,當不加上rollbackFor = Exception.class 的時候,無論內外報RuntimeException,都會回滾;無論內外報 非RuntimeException 錯誤,都不會回滾。如果加上rollbackFor = Exception.class,無論內外怎麼報錯,都會回滾。這些代碼就不給出了。接下來,試下下面兩種情況:
@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);
}
情況一,外面事務加上rollbackFor = Exception.class,裏面事務不加,測試內外分別報錯的情況(爲了簡化代碼量,只給出了外面報錯的代碼),都可以回滾。因爲,無論如何,錯誤都拋給了外面那個事務進行處理,而外面那個加上了rollbackFor = Exception.class,具備處理非RuntimeException錯誤的能力,所以都可以讓事務進行正常回滾。
下面看情況二,裏面的事務加上rollbackFor = Exception.class,外面不加,外面報錯。
@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);
}
事務都無法回滾,這是我們有個疑問,裏面的事務明明有很強的處理能力啊,爲什麼和外面一起回滾失敗呢,彆着急,等等聊這個。
然後試下里面報錯:
@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();
}
咦,這回都進行了正常的回滾。我的天,這回外面沒有處理能力,爲什麼接受裏面拋出來的錯誤,也進行了回滾!!!看上去,就好像裏外事務總是同生共死的對不對?原來,@Transactional還有個參數,看下源碼,這個註解還有默認值:
Propagation propagation() default Propagation.REQUIRED;
REQUIRED的意思是說,事務嵌套的時候,如果發現已經有事務存在了,就加入這個事務,而不是新建一個事務,所以根本就不存在兩個事務,一直只有一個!至於,此參數其他值,本文不進行測試。回到上面的問題,當外面報錯的時候,此時查看事務,沒有增加rollbackFor = Exception.class參數,即沒有處理非RuntimeException能力,所以代碼走完,貌似“兩個事務”,都回滾失敗了。當裏面報錯的時候,事務已經添加上了處理非RuntimeException能力,所以,代碼走完就回滾成功了。
結論三:由於REQUIRED屬性,“兩個事務”其實是一個事務,處理能力看報錯時刻,是否添加了處理非RuntimeException的能力。
try catch和事務嵌套 共同影響
在結論一二三成立的條件下,探索共同影響的問題就簡單多了,由於情況太多,就不進行過多的代碼展示了。
結論
結論一:對於@Transactional可以保證RuntimeException錯誤的回滾,如果想保證非RuntimeException錯誤的回滾,需要加上rollbackFor = Exception.class 參數。
結論二:try catch只是對異常是否可以被@Transactional 感知 到有影響。如果錯誤拋到切面可以感知到的地步,那就可以起作用。
結論三:由於REQUIRED屬性,“兩個事務”其實是一個事務,處理能力看報錯時刻,是否添加了處理非RuntimeException的能力。
轉載自:
作者:啤酒就辣條
來源:CSDN
原文:https://blog.csdn.net/qq_19006223/article/details/90550455
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!