1、前提:
在一个类中有方法A和方法B里面都有对数据库的增删改操作,在方法A上添加了@Transactional,并且在方法A中调用了方法B,但是方法B上没有添加@Transactional注解。
2、事务失效:
当方法A在调用方法B之后的代码(或方法B操作数据库之后的代码),出现了异常,期望的结果是方法A和方法B中的数据库操作都会回滚,但是实际情况只有方法A中的数据库操作有回滚,方法B中的数据库操作并未回滚,出现了事务失效。
3、原因
spring在对@Transactional注解的方法添加事务时,使用了动态代理,在代理对象执行方法时会看被代理方法上是否有事务注解,如果有就开启事务,在调用被代理方法的语句上添加了try-catch,然后如果执行的方法中未出现异常就提交事务,如果出现了异常就在catch中回滚事务,但是在调用方法B时前面隐藏了this,而且方法B上并未添加事务注解,所以方法B的执行并不走代理对象,而是走被代理的对象本身,所以spring的通过代理实现事务的控制对于方法B来说没有起到作用,从而出现方法A的数据库操作回滚了,但是方法B中的事务并未回滚的事务失效现象。
4、解决
1、在方法B上也添加@Transactional注解,此时根据事务的传递,会将方法B的事务合并到方法A中或者单独开启一个事务(这取决于方法B上的注解中的配置),这样就可以解决事务失效的情况。
2、也可以在类中获取该类的事务动态代理对象,然后通过代理对象调用方法B,这样方法B就不是隐式的通过this调用,而是通过代理对象调用,所以也不会出现事务失效的情况。
5、拓展
spring中开启事务的原则是调用的方法或所在的类上是否有@Transactional注解,所以如果在类上添加注解,类中的所有方法无论是增删改还是查询亦或根本没有对数据库进行操作,都会开启事务,这样就导致本不该加事务的方法也开启了事务,所以在添加事务控制注解时,尽量不要直接在类上添加,而是在需要事务控制的方法上添加。