spring事务的原理是AOP,进行了切面增强,那么失效的根本原因是这个AOP不起作用了,常见情况如下:
- 抛出检查异常导致事务不能正确回滚
spring事务默认只对非检查异常进行回滚,即运行时异常。检查异常就是程序编译时必须处理的。
解决办法:可以通过配置@Transcational的属性rollbackFor来解决,将rollbackFor设置为Exception.class,即对所以异常都进行回滚。 - 业务方法内通过try-catch将异常捕获后没有再抛出,导致spring事务通知不能捕捉到异常,也就不能进行后面的回滚操作。
解决办法:
- 在catch中继续将异常抛出。
- 在catch中手动设置事务回滚
TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
- aop切面顺序导致事务不能回滚
正常情况事务切面的优先级是最低的,是包围在目标方法外的。
自定义切面的优先级也是最低的,和事务切面一样,如果没有指定自定义切面的顺序,那么它会在事务切面的内层,它包围着目标方法,这样目标方法抛出异常后首先被自定义切面捕获到,外层的事务切面的不知道的,如果自定义切面捕获到异常后没有继续抛出,那么事务就不能正确回滚。
解决办法:同情况2 - 非public方法导致的事务失效
@Transcational注解加在public方法上才会生效
Spring为方法创建代理、添加事务通知,前提条件都是该方法是public的 - 调用本类方法导致传播行为失效
调用本类方法是不经过代理的,因此无法增强
解决办法:
- 依赖注入自己(代理)来调用
- 通过AppContext拿到代理对象来调用
- @Transcational方法导致的synchronized失效
synchronized保证的仅是目标方法的原子性,环绕目标方法的还有commit等操作,它们并未处一起sync块内
解决办法:synchronized范围扩大至代理方法调用
使用select ... fro update 替换 select - @Transcational没有保证原子行为
事务的原子性仅涵盖insert,update,delete,select ... for update语句,select方法并不阻塞