• 事务传播行为


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

  • 相关阅读:
    虚拟机字节码执行引擎 —— 方法调用
    虚拟机字节码执行引擎 —— 运行时栈帧
    Java 虚拟机类加载机制
    MySQL提升笔记(1):MySQL逻辑架构
    【JVM进阶之路】十:JVM调优总结
    【JVM进阶之路】九:性能监控工具-可视化工具篇
    SpingCloud Alibaba实战(1:微服务与SpringCloud Alibaba)
    【JVM进阶之路】八:性能监控工具-命令行篇
    【JVM进阶之路】七:垃圾收集器盘点
    【JVM进阶之路】六:垃圾收集理论和算法
  • 原文地址:https://www.cnblogs.com/bulrush/p/12166587.html
Copyright © 2020-2023  润新知