spring的事務傳播機制
嫌棄內容程式碼複雜的可直接看思維導圖大綱即可
基本概念
指的就是當一個事務方法被另一個事務方法呼叫時,這個事務方法應該如何進行
七種行為和測試
PROPAGATION_REQUIRED
預設,當前存在事務,則加入該事務;不存在事務,建立新事務。
public class PropagationService {
@Autowired
private
PropagationMapper propagationMapper;
@Autowired
@Lazy
PropagationService propagationService;
@Transactional(rollbackFor = Exception.class)
public void insertPropagationA() throws Exception {
Propagation propagationA = Propagation.builder()
.type("Propagation.REQUIRED")
.comment("propagationA").build();
propagationMapper.insert(propagationA);
propagationServiceSelf.insertPropagationB();
throw new Exception();
}
@Transactional(rollbackFor = Exception.class,propagation = org.springframework.transaction.annotation.Propagation.REQUIRED)
public void insertPropagationB() throws Exception {
Propagation propagationB = Propagation.builder()
.type("Propagation.REQUIRED")
.comment("propagationB").build();
Propagation propagationBB = Propagation.builder()
.type("Propagation.REQUIRED")
.comment("propagationBB").build();
propagationMapper.insert(propagationB);
propagationMapper.insert(prop
@Test
//Given A傳播行為required 和B為required,WHE A發生異常,THEN A插入失敗,B插入失敗
public void testRequired() throws Exception {
propagationService.insertPropagationA();
}
PROPAGATION_REQUIRED_NEW
始終以新的事務執行,當前存在事務,則掛起原事務;不存在事務,建立新事務
@Test
//Given 同樣程式碼更改B為required_new, WHE A發生異常, THEN B插入成功,A插入失敗
public void testRequiredNew() throws Exception {
propagationService.insertPropagationA();
}
PROPAGATION_SUPPORTS
支援當前事務。當前存在事務,則支援該事務;不存在事務,以非事務方式執行
@Test
//Given 更改B為required_supports, A不開啟事物, WHE B發生異常, THEN A、B插入成功BB失敗;A開啟事物因為有異常發生全失敗
public void testRequiredSupports() throws Exception {
propagationService.insertPropagationA();
}
//@Transactional(rollbackFor = Exception.class)
public void insertPropagationA() throws Exception {
Propagation propagationA = Propagation.builder()
.type("Propagation.REQUIRED")
.comment("propagationA").build();
propagationMapper.insert(propagationA);
propagationServiceSelf.insertPropagationB();
throw new Exception();
}
@Transactional(rollbackFor = Exception.class,propagation = org.springframework.transaction.annotation.Propagation.SUPPORTS)
public void insertPropagationB() throws Exception {
Propagation propagationB = Propagation.builder()
.type("Propagation.REQUIRED")
.comment("propagationB").build();
Propagation propagationBB = Propagation.builder()
.type("Propagation.REQUIRED")
.comment("propagationBB").build();
propagationMapper.insert(propagationB);
int a = 1/0;
propagationMapper.insert(propagationBB);
}
PROPAGATION_NO_SUPPORTED
非事務方式執行,當前存在事務,則掛起該事務
@Test
//Given 更改B為required_not_supports, A開啟事物, WHEN A發生異常, THEN A插入失敗、B插入成功
public void testRequiredNotSupports() throws Exception {
propagationService.insertPropagationA();
}
@Transactional(rollbackFor = Exception.class)
public void insertPropagationA() throws Exception {
Propagation propagationA = Propagation.builder()
.type("Propagation.REQUIRED")
.comment("propagationA").build();
propagationMapper.insert(propagationA);
propagationServiceSelf.insertPropagationB();
throw new Exception();
}
@Transactional(rollbackFor = Exception.class,propagation = org.springframework.transaction.annotation.Propagation.NOT_SUPPORTED)
public void insertPropagationB() throws Exception {
Propagation propagationB = Propagation.builder()
.type("Propagation.REQUIRED")
.comment("propagationB").build();
Propagation propagationBB = Propagation.builder()
.type("Propagation.REQUIRED")
.comment("propagationBB").build();
propagationMapper.insert(propagationB);
propagationMapper.insert(propagationBB);
}
PROPAGATION_NEVER
非事務方式執行,當前存在事務,則丟擲異常();
org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
at org.springframework.transaction.support.AbstractPlatformTransactionManager.handleExistingTransaction(AbstractPlatformTransactionManager.java:413)
@Transactional(rollbackFor = Exception.class)
public void insertPropagationA() throws Exception {
Propagation propagationA = Propagation.builder()
.type("Propagation.REQUIRED")
.comment("propagationA").build();
propagationMapper.insert(propagationA);
propagationServiceSelf.insertPropagationB();
//throw new Exception();
}
@Transactional(rollbackFor = Exception.class,propagation = org.springframework.transaction.annotation.Propagation.NEVER)
public void insertPropagationB() throws Exception {
Propagation propagationB = Propagation.builder()
.type("Propagation.REQUIRED")
.comment("propagationB").build();
Propagation propagationBB = Propagation.builder()
.type("Propagation.REQUIRED")
.comment("propagationBB").build();
propagationMapper.insert(propagationB);
propagationMapper.insert(propagationBB);
}
@Test
//Given 更改B為required_never, A開啟事物, 呼叫方法B時報錯
public void testRequiredNever() throws Exception {
propagationService.insertPropagationA();
}
PROPAGATION_NESTED
當前存在事務,則巢狀在該事務下起新事務執行;不存在事務,建立新事務。
@Test
//Given 更改B為required_nested, A開啟事物, 呼叫方法B,後丟擲異常,都失敗;A開啟事物,呼叫方法B,B丟擲異常,A catch,A成功,B失敗
public void testRequiredNested() throws Exception {
propagationService.insertPropagationA();
}
@Transactional(rollbackFor = Exception.class)
public void insertPropagationA() throws Exception {
Propagation propagationA = Propagation.builder()
.type("Propagation.REQUIRED")
.comment("propagationA").build();
propagationMapper.insert(propagationA);
try{
propagationServiceSelf.insertPropagationB();
}catch(Exception e){
}
}
@Transactional(rollbackFor = Exception.class,propagation = org.springframework.transaction.annotation.Propagation.NESTED)
public void insertPropagationB() throws Exception {
Propagation propagationB = Propagation.builder()
.type("Propagation.REQUIRED")
.comment("propagationB").build();
Propagation propagationBB = Propagation.builder()
.type("Propagation.REQUIRED")
.comment("propagationBB").build();
propagationMapper.insert(propagationB);
int a = 1/0;
propagationMapper.insert(propagationBB);
}
PROPAGATION_MANDATORY
事務方式執行,當前不存在事務,則丟擲異常
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:362)
//@Transactional(rollbackFor = Exception.class)
public void insertPropagationA() throws Exception {
Propagation propagationA = Propagation.builder()
.type("Propagation.REQUIRED")
.comment("propagationA").build();
propagationMapper.insert(propagationA);
propagationServiceSelf.insertPropagationB();
}
@Transactional(rollbackFor = Exception.class,propagation = org.springframework.transaction.annotation.Propagation.MANDATORY)
public void insertPropagationB() throws Exception {
Propagation propagationB = Propagation.builder()
.type("Propagation.REQUIRED")
.comment("propagationB").build();
Propagation propagationBB = Propagation.builder()
.type("Propagation.REQUIRED")
.comment("propagationBB").build();
propagationMapper.insert(propagationB);
propagationMapper.insert(propagationBB);
}
@Test
//Given 更改B為required_mandatory, WHEN A不開啟事物,呼叫方法B, THEN呼叫B丟擲異常;
public void testRequiredMandatory() throws Exception {
propagationService.insertPropagationA();
}
Transactional失效
spring的事物是基於AOP代理實現的,也就是說背@Transactional修飾的方法所在類會有一個代理類,透過這個代理類實現的。並且也需要底層資料庫支援事物,還需要在同一個庫中,多個方法執行呼叫需要在同一個執行緒中。基於這情況大概失效場景分為兩部分
非代理類方向
-
資料庫引擎不支援索引如MyISAm
-
資料來源未配置事物管理器
-
資料庫分散式部署,需要Seata技術解決
-
多執行緒,兩個事務方法不在同一個執行緒
代理類方向
-
類未交給spring,代理自然無法生成
-
自身呼叫,相當於this,代理類未生效
-
事務方法修飾如非public、final、static修飾導致不能被代理
-
異常型別錯誤,宣告式事務預設對runtimeException、Error才可以觸發回滾
-
對丟擲的異常,catch後處理不當。如吞了異常。
-
設定了錯誤的傳播行為