注解@Transactional事务失效的常见场景


在《》中,小编介绍了注解@Transactional的基本属性和使用方法,这里介绍事务失效的八种场景,使大家对注解@Transactional有一个更深刻的认知。

??被注解修饰的方法为非public类型。之所以会失效,是因为在Spring AOP 代理中,TransactionInterceptor (事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute 方法,获取Transactional 注解的事务配置信息。

protected TransactionAttribute computeTransactionAttribute(Method method,
    Class<?> targetClass) {
        // Don't allow no-public methods as required.
        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
        return null;
}

computeTransactionAttribute方法会检查目标方法的修饰符是否为 public,不是 public则不会获取@Transactional 的属性配置信息。

??异常被catch块吃掉,导致回滚失败。

??异常抛出类型错误,例如属性rollbackFor默认回滚的是RuntimeException,而代码抛出了其父类的异常Exception,这时是不可以回滚的。如果抛出了RuntimeException的子类异常,如NullPointerException,则可以回滚。

??本类方法直接调用。比如有一个类Test,它里面有两个方法,方法A没有声明注解事务,而public类型的B方法有,而且是A调用了B。则外部调用方法A之后,方法B的事务是不会起作用的。此时会用this关键字直接请求B方法,没有经过 Spring AOP的代理类去调用方法,从而没有开启事务管理,默认只有在外部调用事务才会生效。这也是经常犯错误的一个地方。

??Bean没有被spring容器管理

??数据库引擎不支持事务。以 MySQL 为例,其 MyISAM 引擎是不支持事务操作的,InnoDB 才支持,一般要支持事务都会使用 InnoDB。

??数据源没有配置事务管理器。实现接口方法,使得返回数据库事务管理器:

@Bean 
PlatformTransactionManager transactionManager(DataSource dataSource) {
       return new DataSourceTransactionManager(dataSource);
}

??方法事务传播行为设置不支持事务:propagation = Propagation.NOT_SUPPORTED or TransactionDefinition.PROPAGATION_NEVER。

TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。所以,在当前没有事务的场景下使用此传播行为属性,则以非事务的方式继续运行。

Reference