一、异常捕获的原因
- 这里Exception异常,他又分为运行时异常RuntimeException和非运行时异常
- 可查的异常(checked exceptions):Exception下除了RuntimeException外的异常
- 不可查的异常(unchecked exceptions):RuntimeException及其子类和错误(Error)
- 异常checked例外也回滚:在整个方法前加上 @Transactional(rollbackFor=Exception.class)
- 异常unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)
- 如果异常被try{}catch{}了,事务就不回滚了,如果想让事务回滚必须再往外抛try{}catch{throw Exception}
二、数据库引擎不支持回滚(使用MYSQL就很可能是这个原因)
- Mysql数据库有两种引擎,注意要使用支持事务的引擎,比如innodb,如果是MyISAM,事务是不起作用的。
- 使用springboot的jpa自动创建库表的时候,默认使用MyISAM引擎,可以检查库表查看引擎。
- 修改配置:
spring: jpa: hibernate: ddl-auto: update naming: physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl #按字段名字建表 show-sql: true database: mysql database-platform: org.hibernate.dialect.MySQL5InnoDBDialect #使用innodb引擎建表
三、发生了自调用情况
spring的数据库事务调用的实现原理是AOP,而AOP的原理是动态代理,
在自调用的过程中,是类自身的调用,而不是代理对象去调用,那么不会产生AOP,没有AOP,意味着@Transactional不会被切面捕获,
这样spring就不能把你的代码植入到约定的流程中,于是就产生了事务回滚失败。
解决方案:
1、将涉及到事务的处理都放入一个方法中,由其他类调用。
2、如果非要在本类中调用,那么需要在本类中生成本类的bean对象,由这个bean对象来调用,实现方式也有多种:
- 自注入
- 通过SpringContextHolder获取bean,即注入ApplicationContext,从ApplicationContext获取bean
- 通过AopContext获取当前类的代理对象,注意此种方式需要开启expose-proxy="true",可通过注解开启@EnableAspectJAutoProxy(exposeProxy =true)
最后一种方式的伪代码:
@Service public class OrderService { public void insert() { OrderService proxy = (OrderService) AopContext.currentProxy(); proxy.insertOrder(); } @Transactional public void insertOrder() { //SQL操作 } }
因为AopContext默认是不暴露当前代理类的,所以要@EnableAspectJAutoProxy(exposeProxy =true)
或者<aop:aspectj-autoproxy expose-proxy="true"/>:
四、补充:
在Spring 的AOP实现有两种代理方式:
- Java动态代理 :通过反射生成一个实现了代理方法的匿名类来完成代理
- cglib代理 :通过Asm修改字节码文件,生成一个子类来完成代理
Spring在项目中会根据被代理对象是否实现了接口来自动切换上述两种代理方式
所以private方法也不能实现事务