Java 基础知识总结 -- 事务失效的 12 种场景
一 常见问题
1 访问权限问题
private 的方法,导致事务失效,spring 要求被代理方法必须是 public 的。
不加权限的时候,方法默认的是??
2 方法用 final 修饰
spring 事务底层使用了 aop,也就是通过 jdk 动态代理或者 cglib,帮我们生成了代理类,在代理类中实现的事务功能。
final 的方法,在它的代理类中,无法重写该方法,无法添加事务功能。
3 方法内部调用
内部调用其实是通过 this 来调用的。如果要解决,就将这个 Service 本身进行注入
4 未被 spring 管理
通常情况下,通过 @Controller、@Service、@Component、@Repository 等注解,可以自动实现 bean 实例化和依赖注入的功能。
如果忘记加这些注解,这个类就不会交给 spring 管理,就事务失效。
5 多线程调用
spring 的事务是通过数据库连接来实现的。当前线程中保存了一个 map<数据源,数据库连接>。
线程间的数据是隔离的,如果用多线程去调用一个方法的话,就会产生多份 ThreadLocal,从而产生多个数据库连接,数据库也就不一样了。
同一个事务,是指同一个数据库连接,只有拥有同一个数据库连接,才能同时提交和回滚。
6 表不支持事务
引擎是 myisam 的就不支持事务。
7 未开启事务
spring 是需要通过 xml 去配置的。springboot 是默认开启事务的。
二 事务不回滚
1 错误的传播特性
@Transcational(propagation=传播特性),该参数指定事务的传播特性。Spring 目前有 7 种。
REQUIRED 如果当前上下文中存在事务,那么加入该事务,如果不存在事务,创建一个事务,这是默认的传播属性值。
SUPPORTS 如果当前上下文存在事务,则支持事务加入事务,如果不存在事务,则使用非事务的方式执行。
MANDATORY 如果当前上下文中存在事务,否则抛出异常。
REQUIRES_NEW 每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。
NOT_SUPPORTED 如果当前上下文中存在事务,则挂起当前事务,然后新的方法在没有事务的环境中执行。
NEVER 如果当前上下文中存在事务,则抛出异常,否则在无事务环境上执行代码。
NESTED 如果当前上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。
目前能创建新事务的三种传播特性:Required、requires_new、nested。
2 自己吞了异常
事务不会回滚,开发者在代码中手动 try…catch 了异常
3 手动抛了别的异常
开发者没有手动捕获异常,但是抛得异常不正确,spring 事务也不会回滚
如上:开发捕获异常,又手动抛出了异常:Exception,事务也不会回滚。
spring 事务,默认情况只回滚 RuntimeException 和 Error,对于普通的 Exception(非运行时异常)不会进行回滚。
4 自定义了回滚异常
spring 通过 rollbackFor 参数,支持自定义回滚的异常。
但是,设置的是 BusinessException 异常,但是报错不属于其,比如是:SqlException 或其他的,事务也不会回滚。
虽然 rollbackFor 参数有默认值,但是阿里的规范手册中还是要求重新指定该参数。
rollbackFor 默认值为:UncheckedException,包括了 RuntimeException 和 Error,当知己使用 @Transcational 不指定 rollbackFor 时,Exception 及其子类都不会出发回滚。
所以,一般建议写成:@Transcational(rollBackFor = Exception.class)
5 嵌套事务回滚多了
版权声明: 本文为 InfoQ 作者【阿婷】的原创文章。
原文链接:【http://xie.infoq.cn/article/6a5cd1418ca37e19a3e259ee3】。文章转载请联系作者。
评论