事务传播行为

参考: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修饰。

原文地址:https://www.cnblogs.com/bulrush/p/12166587.html