Springboot种声明式事务(@Transactional注解)不生效的场景

  • Post author:
  • Post category:其他





一、引进来



数据库操作中保证数据ACID特性的行为。(ACID:原子性、一致性、隔离性、持久性)

spring的事务有两种:编程式事务和声明式事务。其中:

编程式事务:指通过代码实现事务管理

声明式事务:xml方式配置、方法或类中加@Transactional注解(springboot项目中会自动配置DataSourceTransactional到spring)




二、讲道理



1、判断数据库引擎支不支持事务

例如


MySQL




数据库,




其 MyISAM 引擎是不支持事务操作的,InnoDB




和NDB




才是支持事务的引擎,一般要支持事务都会使用 InnoDB

2、抛出异常类型错误时




Spring的事务管理默认是针对Error异常和RuntimeException异常以及其子类进行事务回滚。




如果代码抛出的异常在这两中范围之外,则不可回滚。





例如:





抛出个




RuntimeException




的子类异常,NullPointerException可以回滚;





抛出个这两中范围之外的异常,像




SQLTimeoutException()




则不会回滚。





处理这种情况:





只需在




@Transaction注解里面指定回滚异常类型即可









如下示例:


@Transactional(rollbackFor = Exception.class)

3、在执行事物的方法时,如果对异常进行抛出,并且手动捕获了这个异常try…catch




在需要执行的sercvice里面不应该主动捕获异常,这会导致事物不生效,应该继续往上抛,在controller层捕获即可,这样事物也生效了,异常也捕获了。





如果确实需要try…catch可以在catch中加入手动回滚动作,示例如下:


try{

    jdbcTemplate.update( updateSql)

}catch(Exception e){

    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

    e.printStackTrace();

}

4、方法名是否为pubic




@Transaction注解只对方法名为pubic的才生效,其他事物不会生效。因为只有@Transaction注解只有被其他方法调用才生效的,能被其他方法调用的方法,只能是public。





因为在SpringAOP的代理处理方式中:事务拦截器TransactionInterceptor会在目标方法执行前后进行拦截,动态通知拦截器DynamicAdvisedInterceptor(CglibAopProxy的内部类)的intercept方法或者JdkDynamicAopProxy的invoke方法会间接的调用AbstractFallbackTransactionAttributeSource的computeTransactionAttribute方法,获取Transaction注解的事务配置信息。





如果目标方法的修饰符不是public,computeTransactionAttribute方法返回null。


5、内部调用的2种场景




因为




@Transactional




要生效









需要经过Spring的代理类。所以




只有来自外部的方法调用被AOP代理




成功




捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为










自身调用




失效














该类









方法A没有




经过 Spring 的代理类



















该类自己的方法




B









默认只有在外部调用事务才会生效





如下两种场景:








当无事务的A方法内部用




this




的方法调用带事务的B方法的时候,B事务就是失效








解决方法:方法A和B都需要支持事务。







如果同类中A方法调用B方法,




被调用方法




B




使用@Transactional(propagation = Propagation.REQUIRES_NEW)注解进行修饰









REQUIRES_NEW




表示新开一个事务),那么事务也不会生效

解决方法:

一个是可以在类中自己注入自己,用注入的对象调用另一个方法。

另一个是将同一个类中的两个事务方法拆分到两个类中,A方法所在的类中注入B方法所在的类。

6、数据源是否配置管理器

Springboot项目一般在启动类中开启事务管理即可

如下:

@EnableTransactionManagement // 启注解事务管理,等同于xml配置方式的 <tx:annotation-driven />

@SpringBootApplication

public class ProfiledemoApplication {

    public static void main(String[] args) {

        SpringApplication.run(ProfiledemoApplication.class, args);

    }

}

如果需要指定使用那个事务管理器:

可参考这篇文章:https://www.cnblogs.com/weiwuxian-95/p/10429538.html

7、方法被final或者static修饰



方法使用了




final




或者




static




之后,不能被代理类重写,因此事务丢失.



版权声明:本文为weixin_45502734原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。