zoukankan      html  css  js  c++  java
  • 事务传播行为

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

  • 相关阅读:
    升级windows 11小工具
    windows 10更新升级方法
    您需要了解的有关 Oracle 数据库修补的所有信息
    Step by Step Apply Rolling PSU Patch In Oracle Database 12c RAC Environment
    Upgrade Oracle Database Manually from 12.2.0.1 to 19c
    如何应用版本更新 12.2.0.1.210420(补丁 32507738 – 2021 年 4 月 RU)
    xtrabackup 安装、备份和恢复
    Centos_Lvm expand capacity without restarting CentOS
    Centos_Lvm_Create pv vg lv and mount
    通过全备+relaylog同步恢复被drop的库或表
  • 原文地址:https://www.cnblogs.com/bulrush/p/12166587.html
Copyright © 2011-2022 走看看