淺析Spring事務傳播機制

在這裏插入圖片描述

基本概念

Spring的事務傳播機制有以下七種

PROPAGATION_REQUIRED:Spring的默認傳播級別,如果上下文中存在事務則加入當前事務,如果不存在事務則新建事務執行。

PROPAGATION_SUPPORTS:如果上下文中存在事務則加入當前事務,如果沒有事務則以非事務方式執行。

PROPAGATION_MANDATORY:該傳播級別要求上下文中必須存在事務,否則拋出異常。

PROPAGATION_REQUIRES_NEW:該傳播級別每次執行都會創建新事務,並同時將上下文中的事務掛起,執行完當前線程後再恢復上下文中事務。(子事務的執行結果不影響父事務的執行和回滾)

PROPAGATION_NOT_SUPPORTED:當上下文中有事務則掛起當前事務,執行完當前邏輯後再恢復上下文事務。(降低事務大小,將非核心的執行邏輯包裹執行。)

PROPAGATION_NEVER:該傳播級別要求上下文中不能存在事務,否則拋出異常。

PROPAGATION_NESTED:嵌套事務,如果上下文中存在事務則嵌套執行,如果不存在則新建事務。(save point概念)

接下來分析兩種 PROPAGATION_REQUIREDPROPAGATION_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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章