原因一:是否是数据库引擎设置不对造成的。比如我们最常用的mysql,引擎MyISAM,是不支持事务操作的。需要改成InnoDB才能支持
原因二:入口的方法必须是public,否则事务不起作用(这一点由Spring的AOP特性决定的,理论上而言,不public也能切入,但spring可能是觉得private自己用的方法,应该自己控制,不应该用事务切进去吧)。另外private 方法, final 方法 和 static 方法不能添加事务,加了也不生效
原因三:Spring的事务管理默认只对出现运行期异常(java.lang.RuntimeException及其子类)进行回滚(至于为什么spring要这么设计:因为spring认为Checked的异常属于业务的,coder需要给出解决方案而不应该直接扔该框架)
原因四:请确保你的业务和事务入口在同一个线程里,否则事务也是不生效的,@Transactional的事务开启 ,或者是基于接口的 或者是基于类的代理被创建。所以在同一个类中一个无事务的方法调用另一个有事务的方法,事务是不会起作用的
比如下面代码事务不生效:
@Transactional
@Override
public void save(User user1, User user2) {
new Thread(() -> {
saveError(user1, user2);
System.out.println(1 / 0);
}).start();
}
针对于第四点,比较常见的例子
事务不生效
事务生效
如果create()抛出了异常,而addInfo()捕获了异常,也会造成事务不会滚,会抛出org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
因为当create()中抛出了一个异常以后,create()标识当前事务需要rollback。但是addInfo()中由于你手动的捕获这个异常并进行处理,addInfo()认为当前事务应该正常commit。此时就出现了前后不一致,也就是因为这样,抛出了前面的UnexpectedRollbackException异常。
spring的事务是在调用业务方法之前开始的,业务方法执行完毕之后才执行commit or rollback,事务是否执行取决于是否抛出runtime异常。如果抛出runtime exception 并在你的业务方法中没有catch到的话,事务会回滚。
在业务方法中一般不需要catch异常,如果非要catch一定要抛出throw new RuntimeException(),或者注解中指定抛异常类型@Transactional(rollbackFor=Exception.class),否则会导致事务失效,数据commit造成数据不一致,所以有些时候try catch反倒会画蛇添足
在应用系统调用声明@Transactional 的目标方法时,Spring Framework 默认使用 AOP 代理,在代码运行时生成一个代理对象,再由这个代理对象来统一管理,当在Service实现类直接调用内部方法时,其本质是通过this对象来调用的方法,而不是代理对象,因为会出现事务失效的情况
参考 https://blog.csdn.net/f641385712/article/details/80445933