spring事务失效的几个场景
1、抛出检查异常
如下代码会抛出检查异常,spring事务不会回滚
1 |
|
如果@Transactional 没有特别指定,Spring 只会在遇到运行时异常RuntimeException或者error时进行回滚,而IOException等检查异常不会影响回滚。
1 | // org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn |
2. 业务方法本身捕获了异常
spring事务管理通过AOP对方法进行增强,如果业务方法本身捕获了异常,spring事务管理不会进行回滚。
1 |
|
3. 同一类中的方法调用
这也是一个容易出错的场景。事务失败的原因也很简单,因为Spring的事务管理功能是通过动态代理实现的,而Spring默认使用JDK动态代理,而JDK动态代理采用接口实现的方式,通过反射调用目标类。简单理解,就是saveUser()方法中调用this.doInsert(),这里的this是被真实对象,所以会直接走doInsert的业务逻辑,而不会走切面逻辑,所以事务失败。
1 |
|
4. 方法使用 final 或 static关键字
如果Spring使用了Cglib代理实现(比如你的代理类没有实现接口),而你的业务方法恰好使用了final或者static关键字,那么事务也会失败。更具体地说,它应该抛出异常,因为Cglib使用字节码增强技术生成被代理类的子类并重写被代理类的方法来实现代理。如果被代理的方法的方法使用final或static关键字,则子类不能重写被代理的方法。
如果Spring使用JDK动态代理实现,JDK动态代理是基于接口实现的,那么final和static修饰的方法也就无法被代理。
总而言之,方法连代理都没有,那么肯定无法实现事务回滚了。
5. 方法不是public
如果方法不是public,Spring事务也会失败,因为Spring的事务管理源码AbstractFallbackTransactionAttributeSource中有判断computeTransactionAttribute()。如果目标方法不是公共的,则TransactionAttribute返回null。
1 | // Don't allow no-public methods as required. |
6. 多线程执行
1 |
|
我们可以看到事务方法add中,调用了事务方法doOtherThing,但是事务方法doOtherThing是在另外一个线程中调用的。
这样会导致两个方法不在同一个线程中,获取到的数据库连接不一样,从而是两个不同的事务。如果想doOtherThing方法中抛了异常,add方法也回滚是不可能的。
我们说的同一个事务,其实是指同一个数据库连接,只有拥有同一个数据库连接才能同时提交和回滚。如果在不同的线程,拿到的数据库连接肯定是不一样的,所以是不同的事务。
如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !