zoukankan      html  css  js  c++  java
  • 事务传播机制,搞懂。

    使用:

    1、引入spring的jdbc、数据库驱动,数据源

    2、配置数据源,注入JdbcTemplate,启用事务管理,注入DataSourceTransactionManager

    3、传播机制

    @see Propagation#REQUIRED 支持当前事务,如果没有则新建一个事务,
    例:a方法调用b方法,如果a方法有事务,则b加入a事务,如果a没有,则b新建一个事务
    @see Propagation#SUPPORTS 支持当前事务,如果没有则以非事务运行
    例:a方法调用b方法,如果a方法有事务,则b加入a事务,如果a没有,则b以非事务运行
    @see Propagation#MANDATORY 支持当前事务,如果没有则抛出异常
    例:a方法调用b方法,如果a方法有事务,则b加入a事务,如果a没有,则b抛出异常
    @see Propagation#REQUIRES_NEW 创建一个新的事务,如果当前有事务,则挂起
    例:a方法调用b方法,不论a有没有事务,b都会新建一个事务
    @see Propagation#NOT_SUPPORTED 不支持事务,如果当前有事务则挂起
    例:a方法调用b方法,不论a有没有事务,b都会以非事务方式运行
    @see Propagation#NEVER 不支持事务,如果有则抛出异常
    例:a方法调用b方法,如果a有事务,b会抛出异常
    @see Propagation#NESTED 嵌套事务
    例:a方法调用b方法,如果a有事务,b会开启一个内嵌事务。如果a没有事务,则b开启一个新的事务

    4、代码测试

    服务类:

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    
    import java.util.Random;
    
    /**
     * @author liangjunhui
     * @date Created in 2020-08-12 11:25
     */
    @Service
    public class UsersService {
        @Autowired
        private JdbcTemplate jdbcTemplate;
        @Autowired
        UsersService usersService;
        private int num = 9;
    
        @Transactional(rollbackFor = Exception.class,timeout = 10)
        public void requiredA(int a, int b, Propagation propagation) {
            try {
                insertUser(a);
            } finally {
                invoke(b, propagation);
            }
        }
        @Transactional(rollbackFor = Exception.class)
        public void requiredACatchB(int a, int b, Propagation propagation) {
            try {
                insertUser(a);
            } finally {
                try {
                    invoke(b, propagation);
                } catch (Exception e) {
                }
            }
        }
        public void invoke(int b, Propagation propagation) {
            switch (propagation) {
                case REQUIRED:
                    usersService.requiredB(b);
                    break;
                case SUPPORTS:
                    usersService.supportsB(b);
                    break;
                case MANDATORY:
                    usersService.mandatoryB(b);
                    break;
                case REQUIRES_NEW:
                    usersService.requiresNewB(b);
                    break;
                case NOT_SUPPORTED:
                    usersService.notSupportedB(b);
                    break;
                case NEVER:
                    usersService.neverB(b);
                    break;
                case NESTED:
                    usersService.nestedB(b);
                    break;
    
                default:
                    break;
            }
        }
    
        @Transactional(rollbackFor = Exception.class)
        public void requiredB(int b) {
            insertUser(b);
        }
    
        @Transactional(rollbackFor = Exception.class, propagation = Propagation.SUPPORTS)
        public void supportsB(int b) {
            insertUser(b);
        }
        @Transactional(rollbackFor = Exception.class, propagation = Propagation.MANDATORY)
        public void mandatoryB(int b) {
            insertUser(b);
        }
        @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
        public void requiresNewB(int b) {
            insertUser(b);
        }
        @Transactional(rollbackFor = Exception.class, propagation = Propagation.NOT_SUPPORTED)
        public void notSupportedB(int b) {
            insertUser(b);
        }
        @Transactional(rollbackFor = Exception.class, propagation = Propagation.NEVER)
        public void neverB(int b) {
            insertUser(b);
        }
        @Transactional(rollbackFor = Exception.class, propagation = Propagation.NESTED)
        public void nestedB(int b) {
            insertUser(b);
        }
    
    
        /**
         * 当i为0的时候,会发生异常
         * @param i
         * @return
         */
        private int insertUser(int i) {
            String methodName = Thread.currentThread().getStackTrace()[2].getMethodName();
            String name = methodName + new Random().nextInt();
            int update = jdbcTemplate.update("insert into users(`userName`,`passWord`) values (?,?)", new Object[]{name, "123456"});
            int res = num / i;
            return update;
        }
    
    }
    service

    注意:有多个事务注解标注的方法相互调用的时候,一定要从IOC容器中取对象,否则不生效。原因是apc默认创建的对象调用目标方法是通过实例化初始化之后的bean,这个bean是未代理增强的,创建代理是BeanPostProcessor实现类负责的,通过初始化的后置处理器包装,然后将包装好的bean放入IOC容器中,这个代理对象持有未代理之前的引用,所以说要从容器中取。

    测试类:

    /**
     * @author liangjunhui
     * @date Created in 2020-08-12 10:58
     */
    public class TestTx extends BaseTest {
    
    
        /**
         * 测试所有的事务传播,该情况调用方不处理被调用方的异常
         * 测试结果:
         * ----------------------测试REQUIRED开始-------------------------
         * 当前有事务,a失败,b失败共执行2次,成功0次
         * 当前有事务,a成功,b成功共执行2次,成功2次
         * 当前有事务,a成功,b失败共执行2次,成功0次
         * 当前有事务,a失败,b成功共执行2次,成功0次
         * 当前无事务,失败共执行1次,成功0次
         * 当前无事务,成功共执行1次,成功1次
         * ----------------------测试REQUIRED结束-------------------------
         * ----------------------测试SUPPORTS开始-------------------------
         * 当前有事务,a失败,b失败共执行2次,成功0次
         * 当前有事务,a成功,b成功共执行2次,成功2次
         * 当前有事务,a成功,b失败共执行2次,成功0次
         * 当前有事务,a失败,b成功共执行2次,成功0次
         * 当前无事务,失败共执行1次,成功1次
         * 当前无事务,成功共执行1次,成功1次
         * ----------------------测试SUPPORTS结束-------------------------
         * ----------------------测试MANDATORY开始-------------------------
         * 当前有事务,a失败,b失败共执行2次,成功0次
         * 当前有事务,a成功,b成功共执行2次,成功2次
         * 当前有事务,a成功,b失败共执行2次,成功0次
         * 当前有事务,a失败,b成功共执行2次,成功0次
         * 发生异常:No existing transaction found for transaction marked with propagation 'mandatory'
         * 当前无事务,失败共执行1次,成功0次
         * 发生异常:No existing transaction found for transaction marked with propagation 'mandatory'
         * 当前无事务,成功共执行1次,成功0次
         * ----------------------测试MANDATORY结束-------------------------
         * ----------------------测试REQUIRES_NEW开始-------------------------
         * 当前有事务,a失败,b失败共执行2次,成功0次
         * 当前有事务,a成功,b成功共执行2次,成功2次
         * 当前有事务,a成功,b失败共执行2次,成功0次
         * 当前有事务,a失败,b成功共执行2次,成功1次
         * 当前无事务,失败共执行1次,成功0次
         * 当前无事务,成功共执行1次,成功1次
         * ----------------------测试REQUIRES_NEW结束-------------------------
         * ----------------------测试NOT_SUPPORTED开始-------------------------
         * 当前有事务,a失败,b失败共执行2次,成功1次
         * 当前有事务,a成功,b成功共执行2次,成功2次
         * 当前有事务,a成功,b失败共执行2次,成功1次
         * 当前有事务,a失败,b成功共执行2次,成功1次
         * 当前无事务,失败共执行1次,成功1次
         * 当前无事务,成功共执行1次,成功1次
         * ----------------------测试NOT_SUPPORTED结束-------------------------
         * ----------------------测试NEVER开始-------------------------
         * 发生异常:Existing transaction found for transaction marked with propagation 'never'
         * 当前有事务,a失败,b失败共执行2次,成功0次
         * 发生异常:Existing transaction found for transaction marked with propagation 'never'
         * 当前有事务,a成功,b成功共执行2次,成功0次
         * 发生异常:Existing transaction found for transaction marked with propagation 'never'
         * 当前有事务,a成功,b失败共执行2次,成功0次
         * 发生异常:Existing transaction found for transaction marked with propagation 'never'
         * 当前有事务,a失败,b成功共执行2次,成功0次
         * 当前无事务,失败共执行1次,成功1次
         * 当前无事务,成功共执行1次,成功1次
         * ----------------------测试NEVER结束-------------------------
         * ----------------------测试NESTED开始-------------------------
         * 当前有事务,a失败,b失败共执行2次,成功0次
         * 当前有事务,a成功,b成功共执行2次,成功2次
         * 当前有事务,a成功,b失败共执行2次,成功0次
         * 当前有事务,a失败,b成功共执行2次,成功0次
         * 当前无事务,失败共执行1次,成功0次
         * 当前无事务,成功共执行1次,成功1次
         * ----------------------测试NESTED结束-------------------------
         */
        @Test
        public void testTx(){
            for (Propagation propagation:Propagation.values()){
                System.out.println("----------------------测试"+propagation.name()+"开始-------------------------");
                testTxCommon(propagation);
                System.out.println("----------------------测试"+propagation.name()+"结束-------------------------");
            }
        }
    
        /**
         * 测试所有的事务传播,该情况调用方处理被调用方的异常
         * 测试结果:
         * ----------------------测试REQUIRED开始-------------------------
         * 当前有事务,a失败,b失败共执行2次,成功0次
         * 当前有事务,a成功,b成功共执行2次,成功2次
         * 发生异常:Transaction rolled back because it has been marked as rollback-only
         * 当前有事务,a成功,b失败共执行2次,成功0次
         * 当前有事务,a失败,b成功共执行2次,成功0次
         * ----------------------测试REQUIRED结束-------------------------
         * ----------------------测试SUPPORTS开始-------------------------
         * 当前有事务,a失败,b失败共执行2次,成功0次
         * 当前有事务,a成功,b成功共执行2次,成功2次
         * 发生异常:Transaction rolled back because it has been marked as rollback-only
         * 当前有事务,a成功,b失败共执行2次,成功0次
         * 当前有事务,a失败,b成功共执行2次,成功0次
         * ----------------------测试SUPPORTS结束-------------------------
         * ----------------------测试MANDATORY开始-------------------------
         * 当前有事务,a失败,b失败共执行2次,成功0次
         * 当前有事务,a成功,b成功共执行2次,成功2次
         * 发生异常:Transaction rolled back because it has been marked as rollback-only
         * 当前有事务,a成功,b失败共执行2次,成功0次
         * 当前有事务,a失败,b成功共执行2次,成功0次
         * ----------------------测试MANDATORY结束-------------------------
         * ----------------------测试REQUIRES_NEW开始-------------------------
         * 当前有事务,a失败,b失败共执行2次,成功0次
         * 当前有事务,a成功,b成功共执行2次,成功2次
         * 当前有事务,a成功,b失败共执行2次,成功1次
         * 当前有事务,a失败,b成功共执行2次,成功1次
         * ----------------------测试REQUIRES_NEW结束-------------------------
         * ----------------------测试NOT_SUPPORTED开始-------------------------
         * 当前有事务,a失败,b失败共执行2次,成功1次
         * 当前有事务,a成功,b成功共执行2次,成功2次
         * 当前有事务,a成功,b失败共执行2次,成功2次
         * 当前有事务,a失败,b成功共执行2次,成功1次
         * ----------------------测试NOT_SUPPORTED结束-------------------------
         * ----------------------测试NEVER开始-------------------------
         * 当前有事务,a失败,b失败共执行2次,成功0次
         * 当前有事务,a成功,b成功共执行2次,成功1次
         * 当前有事务,a成功,b失败共执行2次,成功1次
         * 当前有事务,a失败,b成功共执行2次,成功0次
         * ----------------------测试NEVER结束-------------------------
         * ----------------------测试NESTED开始-------------------------
         * 当前有事务,a失败,b失败共执行2次,成功0次
         * 当前有事务,a成功,b成功共执行2次,成功2次
         * 当前有事务,a成功,b失败共执行2次,成功1次
         * 当前有事务,a失败,b成功共执行2次,成功0次
         * ----------------------测试NESTED结束-------------------------
         */
        @Test
        public void testTxCacheB(){
            for (Propagation propagation:Propagation.values()){
                System.out.println("----------------------测试"+propagation.name()+"开始-------------------------");
                testTxCommonCacheB(propagation);
                System.out.println("----------------------测试"+propagation.name()+"结束-------------------------");
            }
        }
    
        private void testTxCommon(Propagation propagation) {
            AllStatus[] values = AllStatus.values();
            for (AllStatus status : values) {
                String[] strArr = status.getStatus().split("");
                int before = testCount();
                int a = Integer.parseInt(strArr[0]);
                try {
                    if (strArr.length > 1) {
                        int b = Integer.parseInt(strArr[1]);
                        usersService.requiredA(a, b, propagation);
                    } else {
                        usersService.invoke(a, propagation);
                    }
                }catch (ArithmeticException ae){
                    // e.printStackTrace();
                }catch (Exception e) {
                    System.out.println("发生异常:"+e.getMessage());
                }
                int after = testCount();
                System.out.println(status.getMsg() + "共执行" + strArr.length + "次,成功" + (after - before) + "次");
    
            }
        }
    
        private void testTxCommonCacheB(Propagation propagation) {
            AllStatus[] values = AllStatus.values();
            for (AllStatus status : values) {
                String[] strArr = status.getStatus().split("");
                int before = testCount();
                int a = Integer.parseInt(strArr[0]);
                try {
                    if (strArr.length > 1) {
                        int b = Integer.parseInt(strArr[1]);
                        usersService.requiredACatchB(a, b, propagation);
                    } else {
                        continue;
                    }
                }catch (ArithmeticException ae){
                    // e.printStackTrace();
                }catch (Exception e) {
                    System.out.println("发生异常:"+e.getMessage());
                }
                int after = testCount();
                System.out.println(status.getMsg() + "共执行" + strArr.length + "次,成功" + (after - before) + "次");
    
            }
        }
    
        public int testCount() {
            JdbcTemplate jdbcTemplate = context.getBean(JdbcTemplate.class);
            Map<String, Object> countMap = jdbcTemplate.queryForMap("select count(1) as count from users");
            return Integer.parseInt(countMap.get("count").toString());
        }
    
        @Before
        public void before() {
            getContext(TxConfig.class);
            usersService = context.getBean(UsersService.class);
        }
    
        private UsersService usersService;
        enum AllStatus {
            ALL_FAILE("00", "当前有事务,a失败,b失败"),
            ALL_SUCCESS("11", "当前有事务,a成功,b成功"),
            SUCCESS_FAILE("10", "当前有事务,a成功,b失败"),
            FAILE_SUCCESS("01", "当前有事务,a失败,b成功"),
            FAILE("0", "当前无事务,失败"),
            SUCCESS("1", "当前无事务,成功"),;
            private String status;
            private String msg;
    
            AllStatus(String status, String msg) {
                this.status = status;
                this.msg = msg;
            }
    
            public String getStatus() {
                return status;
            }
    
            public String getMsg() {
                return msg;
            }
        }
    }
    测试类

    5、REQUIRED、REQUIRES_NEW、NESTED 之间的区别:

    REQUIRED是a,b公用一个事务,a、b任一一个发生异常,事务都会回滚,即使是a未发生异常且处理了b的异常;
    NESTED是a、b为父子事务,如果b发生了异常,a处理了b的异常,这样只有b会回滚,如果a发生了异常,则回滚。
    父影响子,子不影响父。
    REQUIREs_NEW是a、b只会影响到自己,即只要a或b执行完没有发生异常,事务就回提交。

     

  • 相关阅读:
    JS创建类的方法--简单易懂有实例
    CommonJS, AMD, CMD是什么及区别--简单易懂有实例
    JS回调函数--简单易懂有实例
    单链表应用(2)--使用快慢指针,如何判断是否有环,环在哪个节点
    单链表应用(1)--使用快慢指针,找链表中间值
    自定义线性结构-有序Map
    C++中final和override
    双向链表翻转
    检查“()”是否匹配并返回深度
    是否存在K
  • 原文地址:https://www.cnblogs.com/mao-yan/p/13522548.html
Copyright © 2011-2022 走看看