spring的事務傳播機制

源灬點發表於2023-04-23

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後處理不當。如吞了異常。

  • 設定了錯誤的傳播行為

相關文章