美文网首页
@Transactional失效场景

@Transactional失效场景

作者: 奔向学霸的路上 | 来源:发表于2020-04-06 11:40 被阅读0次

    上一篇:事务的两种形式

    @Transactional介绍

    @Transactional注解底层使用的是动态代理来进行实现的

    Transactional注解可以作用在接口、类、类方法

    • 作用接口: 不推荐,因为这只有在使用基于接口的代理时它才会生效
    • 作用于类:表示该类的所有public方法都配置相同的事务属性信息
    • 作用于类方法:只能用于public方法上。注意:如果类于类方法都配置了@Transactional,类方法事务会覆盖类事务

    propagation属性

    propagation代表事务的传播行为,默认值为Propagation.REQUIRED

    • Propagation.REQUIRED:如果当前存在事务,则加入该事务,如果当前不存在事务,则新创建事务(也就是说如果A方法和B方法都添加了注解,默认传播模式下,A方法调用B方法,会将两个方法事务合并为一个)
    • Propagation.SUPPORTS:如果当前存在事务,则加入,如果不存在,则以非事务形式运行
    • Propagation.MANDATORY:如果当前存在事务,则加入事务,如果不存在事务,则抛异常
    • Propagation.REQUIRES_NEW:重新创建一个事务,如果当前存在事务,暂停当前事务(如果A方法默认为Propagation.REQUIRED模式,B方法为Propagation.REQUIRES_NEW,在A方法中调用B方法,A方法抛出异常后,B方法不会回滚,因为Propagation.REQUIRES_NEW会暂停A方法的事务)
    • Propagation.NOT_SUPPORTED:以非事务方法运行,如果当前存在事务,暂停当前事务
    • Propagation.NESTED :和 Propagation.REQUIRED 效果一样

    isolation属性

    isolation事务的隔离级别,默认值为Isolation.DEFAULT

    • Isolation.DEFAULT:使用底层数据库默认的隔离级别
    • Isolation.READ_UNCOMMITTED
    • Isolation.READ_COMMITTED
    • Isolation.REPEATABLE_READ
    • Isolation.SERIALIZABLE

    timeout属性

    timeout事务的超时时间,默认值:-1,如果超过该时间限制事务还未完成,则自动回滚

    readOnly属性

    readOnly指定事务是否为只读事务,默认值为false,它的存在是为了让那些不需要事务的方法被排除掉,例如:读取数据,可以设置为readOnly为true

    rollbackFor属性

    用于指定能够触发事务回滚的异常类型,可以指定多个异常类型

    noRollbackFor属性

    指定的异常类型,不回滚事务,可以指定多个异常类型

    @Transactional失效场景

    1. @Transactional注解应用在非public方法上


      image.png

      之所以会失效式因为在Spring AOP代理时,如上图所示 TransactionInterceptor (事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute 方法,获取Transactional 注解的事务配置信息。


      image.png
    2. propagation 配置错误
      其中这三种会存在以非事务形式运行
      TransactionDefinition.PROPAGATION_SUPPORTS、TransactionDefinition.PROPAGATION_NOT_SUPPORTED、
      TransactionDefinition.PROPAGATION_NEVER;
      Propagation.REQUIRES_NEW也会存在事务失效的情况,见上述Propagation.REQUIRES_NEW的举例描述

    3. rollbackFor配置错误


      image.png

    Spring默认抛出了未检查unchecked异常(继承自 RuntimeException 的异常)或者 Error才回滚事务;其他异常不会触发回滚事务。如果在事务中抛出其他类型的异常,但却期望 Spring 能够回滚事务,就需要指定 rollbackFor属性。
    希望自定义的异常可以进行回滚@Transactional(propagation= Propagation.REQUIRED,rollbackFor= MyException.class
    若在目标方法中抛出的异常是 rollbackFor 指定的异常的子类,事务同样会回滚。

    1. 同一个类中方法间的调用
      A调用本类中的B方法(不论B时public或者private),A没有声明事务,而B方法有。外部调用A方法后,方法B的事务不会起作用。
      这是由于使用Spring AOP代理造成的,因为只有当前事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。
    //@Transactional 
    @GetMapping("/test") 
    private Integer A() throws Exception { 
        CityInfoDict cityInfoDict = new CityInfoDict(); 
        cityInfoDict.setCityName("2"); 
        /** * B 插入字段为 3的数据 */ 
        this.insertB();
     /** * A 插入字段为 2的数据 */ 
        int insert = cityInfoDictMapper.insert(cityInfoDict);
        return insert; 
    } 
    @Transactional() 
    public Integer insertB() throws Exception {
        CityInfoDict cityInfoDict = new CityInfoDict();
        cityInfoDict.setCityName("3");
        cityInfoDict.setParentCityId(3);
        return cityInfoDictMapper.insert(cityInfoDict);
     }
    
    1. 异常被你的catch“吃了”
    @Transactional 
    private Integer A() throws Exception {
        int insert = 0; 
        try { 
            CityInfoDict cityInfoDict = new CityInfoDict();
            cityInfoDict.setCityName("2");
            cityInfoDict.setParentCityId(2); 
            /** * A 插入字段为 2的数据 */
            insert = cityInfoDictMapper.insert(cityInfoDict);
            /** * B 插入字段为 3的数据 */ 
            b.insertB(); 
        } catch (Exception e) {
            e.printStackTrace();
        }
     }
    

    如果B方法发生了异常,而A方法catch住了B方法的异常,那么这个事务不能正常回滚
    会抛出:org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

    因为当B方法中抛出了一个异常后,B标识当前事务需要rollback。但是A中由于收到铺货了这个异常并进行了处理,A任务当前事务应该可以正常commit。此时前后不一致,抛出UnexpectedRollbackException

    Spring事务时在调用业务方法之前开始的,业务方法执行完毕之后才执行commit or rollback,事务是否执行取决于是否抛出RuntimeException,如果抛出了并且未catch到的话,事务就会回滚。

    1. 数据库不支持事务
      mysql数据库默认使用支持事务的innodb引擎,一旦切换为不支持事务的myisam,那么事务就会失效

    原文链接:https://baijiahao.baidu.com/s?id=1661650900351466294&wfr=spider&for=pc

    相关文章

      网友评论

          本文标题:@Transactional失效场景

          本文链接:https://www.haomeiwen.com/subject/chpdphtx.html