首先,我们需要知道,Spring是通过代理管理事务的,方法和方法之间的调用分为两种情况(解决办法可在下面列举的不同场景中自取):
1.不同类之间的方法调用。
如类A的方法a()调用类B的方法b(),这种情况事务是正常起作用的。只要方法a()或b()配置了事务,运行中就会产生代理,开启事务。
注意,事务正常起作用是指注解的方法内事务操作生效,下面是两种典型场景实例:
如果是在A的方法a()上加@Transactional注解,那么先正常调用b()且b()有增删改操作,但是接着执行a()的动库操作异常,则两个方法内动库操作全部回滚;
如果A的方法a()没加@Transactional注解,而类B的方法b()上加了该注解,那么如果先正常执行中a()的动库操作,然后执行类B的方法b()的动库操作发生异常,那么b()中的事务操作会全部回滚,但是并不会影响先前a()中的操作,即b()中异常b()回滚,a()不回滚。
2.同一个类的不同方法之间的调用。
以同类中方法a()调用方法b()为例:
1).方法a()开启了事务,事务正常生效
2).方法a()没有开启事务,此时调用方法b(),无论被调用的b()是否配置了事务,事务都不会生效
说明:在微服务架构中,很多方法里面会有各种跨库跨服务器操作,这时候你添加注解@Transactional就不会使事务生效了,我们会寻求Seata等其他技术方案解决分布式事务问题。
其他使用@Transactional注解失效的场景,需要注意:
1.方法是非public,其事务就会失效:
该注解一般标注在类或者public方法上,如果方法是非public,其事务就会失效
2.@Transactional 注解属性 propagation 设置错误
如果设置的事务传播行为是这三种,发生错误事务将不会回滚:
TransactionDefinition.PROPAGATION_SUPPORTS
TransactionDefinition.PROPAGATION_NOT_SUPPORTED
TransactionDefinition.PROPAGATION_NEVER
3.try catch内部消化了:
如果在该注解标注的方法或被其调用的方法中有try catch,那么想要使得事务生效,一定要在catch中把异常直接抛出,如果不处理不向上抛出,则事务不会生效。
4.没有设置属性rollbackFor = Exception.class :
@Transactional只能回滚RuntimeException和RuntimeException下面的子类抛出的异常 不能回滚Exception异常!!!如果需要支持回滚Exception异常请用@Transactional(rollbackFor = Exception.class),如果是增删改的时候我建议大家都使用@Transactional(rollbackFor = Exception.class)。
5.数据库引擎不支持事务
常用的MySQL数据库默认使用支持事务的innodb引擎。一旦数据库引擎切换成不支持事务的myisam,那事务就从根本上失效了