事務傳播行為


參考:https://segmentfault.com/a/1190000013341344

 

 

 例子:

准備 對日志log表的service層操作

package com.wing.my.cloud.system.modular.system.service;

import com.wing.my.cloud.system.modular.system.definedLog.entity.DefineLogEntity;
import com.wing.my.cloud.system.modular.system.mapper.DefineLogMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
 * <p>
 *  測試事務A
 * </p>
 *
 */
@Service
public class Transaction1Service {

    @Resource
    DefineLogMapper defineLogMapper;

    @Transactional(propagation = Propagation.REQUIRED)
    public void addLog_Required(){
        DefineLogEntity defineLogEntity = new DefineLogEntity();
        defineLogEntity.setClassName("Transaction1Service");
        defineLogEntity.setMethodName("addLogRequired");
        defineLogMapper.insert(defineLogEntity);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addLog_REQUIRES_NEW(){
        DefineLogEntity defineLogEntity = new DefineLogEntity();
        defineLogEntity.setClassName("Transaction1Service");
        defineLogEntity.setMethodName("addLogREQUIRES_NEW");
        defineLogMapper.insert(defineLogEntity);
    }

    @Transactional(propagation = Propagation.NESTED)
    public void addLog_NESTED(){
        DefineLogEntity defineLogEntity = new DefineLogEntity();
        defineLogEntity.setClassName("Transaction1Service");
        defineLogEntity.setMethodName("addLogNESTED");
        defineLogMapper.insert(defineLogEntity);
    }
}
View Code

對user表的service層的操作

package com.wing.my.cloud.system.modular.system.service;

import com.wing.my.cloud.system.modular.system.entity.User;
import com.wing.my.cloud.system.modular.system.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
 * <p>
 *  測試事務B
 * </p>
 *

 */
@Service
@Slf4j
public class Transaction2Service {

    @Resource
    UserMapper userMapper;

    @Transactional(propagation = Propagation.REQUIRED)
    public void addUser_REQUIRED(){
        User user = User.builder()
                .account("張三REQUIRED")
                .status("ENABLE")
                .build();
        userMapper.insert(user);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void addUser_REQUIRED_RuntimeException(){
        User user = User.builder()
                .account("張三REQUIRED運行時異常").status("ENABLE").build();
        userMapper.insert(user);
        throw new RuntimeException("RuntimeException是運行時異常,事務自動回滾");
    }

    /**
     * 因為異常被吃掉,所以走不成事務,不會回滾
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void addUser_REQUIRED_RuntimeException_Try(){
        User user = User.builder()
                .account("張三REQUIRED運行時異常被cacth掉").status("ENABLE").build();
        userMapper.insert(user);
        try {
            throw new RuntimeException("RuntimeException是運行時異常,事務自動回滾");
        } catch (Exception e) {
            log.error("異常被吃掉");
        }
    }

    @Transactional(noRollbackFor = RuntimeException.class,propagation = Propagation.REQUIRED)
    public void addUser_REQUIRED_RuntimeException_NoRollbackFor(){
        User user = User.builder()
                .account("張三REQUIRED運行時異常").status("ENABLE").build();
        userMapper.insert(user);
        throw new RuntimeException("加上noRollbackFor也不會走事務。");
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void addUser_REQUIRED_Exception() throws Exception{
        User user = User.builder()
                .account("張三REQUIRED非運行時異常").status("ENABLE").build();
        userMapper.insert(user);
        throw new Exception("Exception是非運行時異常,事務失效");
    }

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    public void addUser_REQUIRED_Exception_RollbackFor() throws Exception{
        User user = User.builder()
                .account("張三REQUIRED非運行時異常").status("ENABLE").build();
        userMapper.insert(user);
        throw new Exception("Exception是非運行時異常,事務失效");
    }



    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addUser_REQUIRES_NEW(){
        User user = User.builder()
                .account("李四REQUIRES_NEW")
                .status("ENABLE")
                .build();
        userMapper.insert(user);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addUser_REQUIRES_NEW_Exception(){
        User user = User.builder()
                .account("李四REQUIRES_NEW運行時異常").build();
        userMapper.insert(user);
        throw new RuntimeException("RuntimeException是運行時異常,事務自動回滾");
    }

    @Transactional(propagation = Propagation.NESTED)
    public void addUser_NESTED(){
        User user = User.builder()
                .account("李四NESTED")
                .status("ENABLE")
                .build();
        userMapper.insert(user);
    }

    @Transactional(propagation = Propagation.NESTED)
    public void addUser_NESTED_Exception(){
        User user = User.builder()
                .account("李四NESTED運行時異常").status("ENABLE").build();
        userMapper.insert(user);
        throw new RuntimeException("RuntimeException是運行時異常,事務自動回滾");
    }

}
View Code

對事務的操作

package com.wing.my.cloud.system.modular.system.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
 * <p>
 *  事務嵌套
 * </p>
 *

 */
@Service
@Slf4j
public class Transaction3Service {

    @Resource
    Transaction1Service transaction1Service;
    @Resource
    Transaction2Service transaction2Service;

    /**
     * log添加一條數據
     * user添加一條數據
     * @throws Exception
     */
    public void exception失效() throws Exception{
        transaction1Service.addLog_Required();
        transaction2Service.addUser_REQUIRED_Exception();
        log.info("只有在返回的是不受檢查異常才會有效,exception是檢查異常。不會走事務");
    }

    /**
     * log添加一條數據
     * @throws Exception
     */
    public void exception失效但是事務_加上rollbackFor可以走事務() throws Exception{
        transaction1Service.addLog_Required();
        transaction2Service.addUser_REQUIRED_Exception_RollbackFor();
        log.info("Exception加上rollbackFor會走事務");
    }

    /**
     * log添加一條數據
     */
    public void RuntimeException可以走事務(){
        transaction1Service.addLog_Required();
        transaction2Service.addUser_REQUIRED_RuntimeException();
        log.info("只有在返回的是不受檢查異常才會有效,RuntimeException是非檢查異常。會走事務");
    }

    /**
     * log添加一條數據
     * user添加一條數據
     */
    public void RuntimeException可以走事務_加上noRollbackFor不會走事務(){
        transaction1Service.addLog_Required();
        transaction2Service.addUser_REQUIRED_RuntimeException_NoRollbackFor();
        log.info("noRollbackFor不會走事務");
    }


    public void 異常被吃掉(){
        transaction2Service.addUser_REQUIRED_RuntimeException_Try();
    }
    /**
     * REQUIRED
     * 如果當前沒有事務,就新建一個事務。已經存在事務,就加入到這個事務中
     * 外層沒有事務,內層Log,內層User有事務。
     * 內層事務沒有異常。外層有異常。
     * 外層回滾,內層不會回滾。內層的事務是單獨運行的。
     *
     * log添加一條數據
     * user添加一條數據
     */
    public void REQUIRED_外層無事務_兩個內層事務獨立_都無異常(){
        transaction1Service.addLog_Required();
        transaction2Service.addUser_REQUIRED();
        log.info("兩個事務" +
                "外層沒有事務,內層有事務.內層都沒有異常。外層有異常。外層回滾,內層不會回滾。因為不在一個事務中");
        throw new RuntimeException();
    }

    /**
     * log添加一條數據
     */
    public void REQUIRED_外層無事務_兩個內層事務獨立_USER有異常(){
        transaction1Service.addLog_Required();
        transaction2Service.addUser_REQUIRED_RuntimeException();
        log.info("兩個事務" +
                "外層沒有事務,內層有事務.內層事務User有異常。外層有異常。外層回滾,內層Log沒有異常,不會回滾。內層User有異常,回滾.因為不在一個事務中");
        throw new RuntimeException();
    }
    //-----外層不開事務。REQUIRED修飾的內層事務會新開自己的事務。互相獨立,互不干擾。
    /**
     * 都不添加數據
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void REQUIRED_外層有事務_兩個內層事務加入到外層事務() {
        transaction1Service.addLog_Required();
        transaction2Service.addUser_REQUIRED();
        log.info("一個事務" +
                "外層有事務,內層兩個都有事務,都沒有異常.事務Log和事務User都加入到外層的事務中。外層回滾,內層也回滾");
        throw new RuntimeException();
    }

    /**
     * 都不添加數據
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void REQUIRED_外層有事務_兩個內層事務加入到外層事務_事務USER有異常_外層感知到(){
        transaction1Service.addLog_Required();
        transaction2Service.addUser_REQUIRED_RuntimeException();
        log.info("一個事務" +
                "外層有事務,內層加入外層事務。內層拋出異常回滾,外層感知到異常使整個事務都回滾");
        throw new RuntimeException();
    }

    /**
     * 都不添加數據
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void REQUIRED_外層有事務_兩個內層事務加入到外層事務_外層有異常_事務USER有異常_被catch吃掉_外層感知不到(){
        transaction1Service.addLog_Required();
        try {
            transaction2Service.addUser_REQUIRED_RuntimeException();
        } catch (Exception e) {
            log.error("一共一個事務" +
                    "內層事務的異常被吃掉,外層感知不到異常。但是本身外層有異常,外層回滾。內層也回滾");
        }
        throw new RuntimeException();
    }

    //-----外層開啟事務。REQUIRED修飾的內層事務會加入到外層事務中。所有的事務都在同一個事務中了。只要有一個方法回滾。所有的都回滾
    /**
     * 都不添加數據
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void REQUIRED_外層有事務_兩個內層事務加入到外層事務_事務USER有異常_被catch吃掉_外層感知不到(){
        transaction1Service.addLog_Required();
        try {
            transaction2Service.addUser_REQUIRED_RuntimeException();
        } catch (Exception e) {
            log.error("一共一個事務" +
                    "內層事務的異常被吃掉,外層感知不到異常。但是當前事務已經被標記為rollbackOnly了,所以無法提交");
        }
    }
    //-----外層開啟事務后,內層事務加入到外層事務。內層方法拋出異常回滾,即使被trycatch吃掉。不被外層方法感知到。整個事務依舊回滾。
    // ------因為在同一個事務中,只要有異常,都會被察覺到。然后執行回滾。


    /**
     * REQUIRES_NEW
     * 新建事務,如果當前存在事務,就把當前事務掛起
     *
     * 外層沒有事務,兩個內層都是在自己的事務中 。外層拋出異常回滾不會影響內層的方法。
     * log插入一條數據
     * user插入一條數據
     */
    public void REQUIRES_NEW_兩個內層事務獨立(){
        transaction1Service.addLog_REQUIRES_NEW();
        transaction2Service.addUser_REQUIRES_NEW();
        log.info("一共兩個事務" +
                "外層沒有事務,內層有事務。兩個內層都沒有異常。外層有異常。外層回滾,內層不會有回滾");
        throw new RuntimeException();
    }

    /**
     * log插入一條數據
     */
    public void REQUIRES_NEW_兩個內層事務獨立_USER有異常_不會影響LOG事務() {
        transaction1Service.addLog_REQUIRES_NEW();
        transaction2Service.addUser_REQUIRES_NEW_Exception();
        log.info("一共兩個事務。" +
                "外層沒有事務,內層兩個都有事務,logRequires_new事務沒有異常,插入成功" +
                "userRequires_new有異常,回滾");
        throw new RuntimeException();
    }
    //外層未開啟事務的情況下,REQUIRES_NEW修飾的內層事務 新開自己的事務。互相獨立。互不干擾。


    /**
     * log_requires_new插入一條
     * user_requires_new插入一條
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void REQUIRES_NEW_內層事務獨立存在不加入到外層事務中(){
        transaction1Service.addLog_Required();
        transaction1Service.addLog_REQUIRES_NEW();
        transaction2Service.addUser_REQUIRES_NEW();
        log.info("一共3個事務。" +
                "logRequired會加入到外層的事務中。logRequires_new和userRequires_new都是單獨的事務。外層事務有異常,回滾。不會插入數據。" +
                "兩個requires_new都是單獨的事務,沒有異常,插入成功。");
        throw new RuntimeException();
    }


    /**
     * log_requires_new插入一條數據
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void REQUIRES_NEW_內層事務獨立不會加入到外層事務中_USER事務有異常不會影響到外層和log事務(){
        transaction1Service.addLog_Required();
        transaction1Service.addLog_REQUIRES_NEW();
        transaction2Service.addUser_REQUIRES_NEW_Exception();
        log.info("一共三個事務" +
                "logRequired會加入到外層的事務中。userRequiresNew返回異常,外層捕獲到異常。logRequired回滾" +
                "logrequires_new是單獨的事務,沒有異常,插入成功。" +
                "userRequires_new是單獨的事務,有異常,回滾。");
    }

    /**
     * logRequired插入成功
     * logRequires_new插入成功
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void REQUIRES_NEW_內層事務獨立_內層USER事務被吃掉(){
        transaction1Service.addLog_Required();
        transaction1Service.addLog_REQUIRES_NEW();
        try {
            transaction2Service.addUser_REQUIRES_NEW_Exception();
        } catch (Exception e) {
            log.error("一共有三個事務。" +
                    "內層logRequired加入到外層事務中。因為userRequires_new返回的異常被吃掉。外層感知不到異常。沒有回滾。插入成功" +
                    "內層userRequires_new是單獨的事務。沒有異常,插入成功" +
                    "內層userRequires_newException是單獨的事務,有異常,進行回滾。不會插入成功");
        }
    }

    /**
     * logRequires_new插入成功
     * userRequires_new插入成功
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void REQUIRES_NEW新開事務_REQUIRED加入外層事務(){
        transaction1Service.addLog_Required();
        transaction1Service.addLog_REQUIRES_NEW();
        transaction2Service.addUser_REQUIRES_NEW();
        log.info("一共3個事務。" +
                "外層一個。logRequired加入到外層事務。logRequires_new新創建一個事務.userRequires_new新創建一個事務" +
                "3個內層事務沒有異常。但是logRequired加入到外層事務中了,外層事務有異常,不會插入成功。另外兩個內層事務插入成功。" );
        throw new RuntimeException();
    }
    //-----外層開啟事務。內層用REQUIRES_NEW修飾的方法依舊會開啟獨立事務。且與外層事務也獨立,互相獨立,互不干擾。

    /**
     * 不添加
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void NESTED_內層事務是外層的子事務_外層有異常外層回滾_兩個子事務都回滾(){
        transaction1Service.addLog_NESTED();
        transaction2Service.addUser_NESTED();
        log.info("兩個事務。" +
                "logNested是外部事務的子事務" +
                "userNested是外部事務的子事務" +
                "外層事務有異常,回滾。兩個子事務都回滾");
        throw new RuntimeException();
    }

    /**
     * 不添加
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void NESTED_內層事務是外層事務的子事務_子事務有異常被外層感知到所有子事務都回滾(){
        transaction1Service.addLog_NESTED();
        transaction2Service.addUser_NESTED_Exception();
        log.info("兩個事務。" +
                "logNested是外部事務的子事務" +
                "userNested是外部事務的子事務" +
                "userNested有異常,外層感知到異常。外層回滾,兩個子事務都回滾" );
    }

    /**
     * log_nested添加成功
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void NESTED_內層方法是外層事務的子事務_子事務異常不被外層感知到有異常的子事務回滾_沒有異常的子事務提交(){
        transaction1Service.addLog_NESTED();
        try {
            transaction2Service.addUser_NESTED_Exception();
        } catch (Exception e) {
            log.error("兩個事務。" +
                    "logNested是外部事務的子事務" +
                    "userNested是外部事務的子事務" +
                    "userNested有異常,但是被catch捕獲到。外層感知不到異常。userNested回滾" +
                    "logNested沒有異常。提交");
        }
    }
    //------NESTED修飾的內層方法是外部事務的子事務,外層回滾。內層的都回滾。內層的獨立存在,互不干擾
    //--------外層沒有事務的時候。也是新開事務,互相獨立。互不干擾。

    /**
     * userRequires_new插入成功
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void 組合(){
        transaction1Service.addLog_Required();
        transaction2Service.addUser_REQUIRES_NEW();
        transaction2Service.addUser_NESTED();
        transaction2Service.addUser_NESTED_Exception();
        log.info("兩個事務" +
                "logRequired加入到外層事務,外層捕獲到userNested返回的異常,回滾" +
                "userRequires_new新開事務。與外層沒有影響" +
                "userNested和userNestedException都是外層的子事務,外層捕獲到異常,進行回滾,兩個子事務也進行回滾");
    }

}
View Code

總結:

一:分析事務

看外層。如果外層沒有事務。那么去分析包含的方法中有沒有加上事務,有幾個方法加上了事務就開啟了幾個事務。這幾個事務是互相獨立,互不干擾的。

如果有事務。子事務中用REQUIRED 修飾的會加入到外層事務中。

子事務用REQUIRES_NEW 修飾的不會去搭理外層的事務。自己新開事務。

子事務用NESTED 修飾的是外層的子事務。如果外層事務回滾。外層事務下是所有子事務也回滾。

二:分析事務是否失效

2.1:拋出的是非運行時異常。

比如拋出Exception

但是可以加上rollbackFor 

@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)

拋出RunTimeException。可以走事務,如果不想走事務的話可以加上noRollbackFor

@Transactional(noRollbackFor = RuntimeException.class,propagation = Propagation.REQUIRED)

2.2:異常被catch掉

/**
     * 因為異常被吃掉,所以走不成事務,不會回滾
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void addUser_REQUIRED_RuntimeException_Try(){
        User user = User.builder()
                .account("張三REQUIRED運行時異常被cacth掉").status("ENABLE").build();
        userMapper.insert(user);
        try {
            throw new RuntimeException("RuntimeException是運行時異常,事務自動回滾");
        } catch (Exception e) {
            log.error("異常被吃掉");
        }
    }
public void 異常被吃掉(){
        transaction2Service.addUser_REQUIRED_RuntimeException_Try();
    }

異常被吃掉解決方案。

 @Transactional(propagation = Propagation.REQUIRED)
    public void addUser_REQUIRED_RuntimeException_Try(){
        User user = User.builder()
                .account("張三REQUIRED運行時異常被cacth掉").status("ENABLE").build();
        userMapper.insert(user);
        try {
            throw new RuntimeException("RuntimeException是運行時異常,事務自動回滾");
        } catch (Exception e) {
            log.error("異常被吃掉");
            //解決方案一:在catch中加上 throw new RuntimeException() 把異常給拋出去。
            //throw new RuntimeException();
            //解決方案二: 在catch中手動回滾
            //TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }

    }

 

2.3:事務要用public修飾。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM