基本概念
Spring的事務傳播機制有以下七種
PROPAGATION_REQUIRED:Spring的默認傳播級別,如果上下文中存在事務則加入當前事務,如果不存在事務則新建事務執行。
PROPAGATION_SUPPORTS:如果上下文中存在事務則加入當前事務,如果沒有事務則以非事務方式執行。
PROPAGATION_MANDATORY:該傳播級別要求上下文中必須存在事務,否則拋出異常。
PROPAGATION_REQUIRES_NEW:該傳播級別每次執行都會創建新事務,並同時將上下文中的事務掛起,執行完當前線程後再恢復上下文中事務。(子事務的執行結果不影響父事務的執行和回滾)
PROPAGATION_NOT_SUPPORTED:當上下文中有事務則掛起當前事務,執行完當前邏輯後再恢復上下文事務。(降低事務大小,將非核心的執行邏輯包裹執行。)
PROPAGATION_NEVER:該傳播級別要求上下文中不能存在事務,否則拋出異常。
PROPAGATION_NESTED:嵌套事務,如果上下文中存在事務則嵌套執行,如果不存在則新建事務。(save point概念)
接下來分析兩種 PROPAGATION_REQUIRED
和 PROPAGATION_REQUIRES_NEW
案例分析
案例一:常規情況
這種也是大家最常見的情況
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
@Override
public void Example1(DemoEntity demoEntity) {
demoMapper.insert(demoEntity);
propagationService.required();
}
@Transactional
@Override
public void required() {
throw new NullPointerException("肥朝假裝拋出了異常");
}
測試
@Test
public void testExample1() {
DemoEntity user = new DemoEntity();
user.setName("batman");
demoService.Example1(user);
}
案例二:try-required
開始敲黑板劃重點了,這個情況也是大家最常見的情況之一,但是由於這個try起來了,那麼,這個insert能否插入數據呢?
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
@Override
public void Example2(DemoEntity demoEntity) {
demoMapper.insert(demoEntity);
try {
propagationService.required();
} catch (Exception e) {
e.printStackTrace();
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
@Override
public void requiresNew() {
throw new NullPointerException("假裝拋出了異常");
}
測試
@Test
public void testExample2() {
DemoEntity user = new DemoEntity();
user.setName("batman");
demoService.Example2(user);
}
案例三:try-requiresNew
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
@Override
public void Example3(DemoEntity demoEntity) {
demoMapper.insert(demoEntity);
try {
propagationService.requiresNew();
} catch (Exception e) {
e.printStackTrace();
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
@Override
public void requiresNew() {
throw new NullPointerException("假裝拋出了異常");
}
測試
@Test
public void testExample3() {
DemoEntity user = new DemoEntity();
user.setName("batman");
demoService.Example3(user);
}
案例四:常規情況
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
@Override
public void Example4(DemoEntity demoEntity) {
demoMapper.insert(demoEntity);
propagationService.requiresNew();
}
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
@Override
public void requiresNew() {
throw new NullPointerException("假裝拋出了異常");
}
@Test
public void testExample4() {
DemoEntity user = new DemoEntity();
user.setName("batman");
demoService.Example4(user);
}
解密
案例一
這個不用說,稍微有點Java常識的人都知道,異常必然會導致回滾,數據庫不會插入數據。
案例二
這個到底會不會插入數據呢?畢竟這個異常被try起來了。這個時候,正常的思維都會認爲,能正常插入數據,但是答案是,不會插入數據,並且拋出異常
案例三
這個和案例二很像,因爲有了案例二的陰影,這個時候你就變得不確定了。答案是,能正常插入數據。
案例四
這個和案例一很像,本來你是很確定能不能插入數據的,但是有了案例三的陰影之後,這個時候你又變得不確定了。答案是,不會插入數據。
原理
org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
我們重點解析案例二和案例三的情況。我們再把那兩個事務傳播機制的意思來解讀一下:
PROPAGATION_REQUIRED:Spring的默認傳播級別,如果上下文中存在事務則加入當前事務,如果不存在事務則新建事務執行。
PROPAGATION_REQUIRES_NEW:該傳播級別每次執行都會創建新事務,並同時將上下文中的事務掛起,執行完當前線程後再恢復上下文中事務。
首先來看案例二,執行Example2方法的時候,由上文得知,將開啓一個事務,再執行到required方法時,此時,因爲用得的默認的隔離級別,因此,這個時候,會加入到剛纔的事務之中,然後required方法中,出現了異常,我們來看
completeTransactionAfterThrowing(txInfo, ex);
中的核心方法
從doSetRollbackOnly(status)這個單詞就知道,required的時候,已經把這個事務設置成RollbackOnly,因此,雖然try住了,但是Example2執行完提交的時候,卻發現無法提交,所以異常信息如下:
Transaction rolled back because it has been marked as rollback-only
踩坑
@Transactional
有很多注意點
- 在同個類中A方法調用B方法,B方法是不會開啓事務,自然也就不會用到事務的傳播機制。(因爲不會進入aop)!
- @Transactional默認情況下,只回滾RuntimeException。如果你拋出的異常不是RuntimeException,可能導致在默認情況下和本文有所偏差
github地址: https://github.com/fafeidou/fast-cloud-nacos/tree/master/fast-common/fast-common-mybatis