@Transactional失效场景

2020-04-06  本文已影响0人  奔向学霸的路上

上一篇:事务的两种形式

@Transactional介绍

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

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

propagation属性

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

isolation属性

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

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

上一篇下一篇

猜你喜欢

热点阅读