这是我参与11月更文挑战的第11天,活动详情查看:2021最后一次更文挑战
前言
事务是一个需要同时保证原子性、隔离性、一致性和持久性的一个或多个数据库操作,但是在某些情况下会存在事务失效的情况,如果你不注意,那么他就会在不经意间突然地背刺你.
事务的一些介绍可以参看前文SpringBoot基础之声明式事务和切面事务和编程式事务,包含了本文需要了解的事务的四大特征,事务的隔离级别,事务的传播行为,还有@Transactional
的参数解读
事务未生效场景
事务的正常使用依靠于是事务的正确配置和使用,如果使用错误了,则事务不会生效
数据库引擎不支持事务
对于MySQL
来说当前只有InnoDB
和NDB
引擎支持事务,其中较为常用的MyISAM
是不支持事务的.
如果使用了不支持事务的引擎,则事务不生效.(写这篇文章,做测试的时候我还出现了这个问题😂)
方法使用了final
或者static
修饰
使用了final
或者static
之后,不能被代理类重写,因此事务丢失.
IDEA会提示次错误,因此IDEA上少见,错误如下
1 | csharp复制代码Reports the cases when your code prevents a class from being subclassed by some framework (e.g. Spring or Hibernate) at runtime. |
非public
修饰的方法
具体原因是因为调用的AbstractFallbackTransactionAttributeSource
的computeTransactionAttribute
方法会判断,该方法是否是public
方法.
IDEA会提示次错误,因此IDEA上少见,错误如下
1 | csharp复制代码Reports the cases when your code prevents a class from being subclassed by some framework (e.g. Spring or Hibernate) at runtime. |
非事务方法内部调用同一个类中事务方法
当无事务的A方法内部用this
的方法调用带事务的B方法的时候,B事务就是失效
1 | typescript复制代码@Override |
直接调用B方法可以正常回滚,但是调用A方法的时候 B方法不会回滚.
回滚失败或者阻止回滚场景
事务为什么回滚,在什么情况下回滚,这些都需要参数进行定界,而大部分的参数都在@Transactional
中.
在这里集中说明一下@Transactional
的参数
参数 | 作用 |
---|---|
value 或 transactionManager | 当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器. |
propagation | 事务的传播行为,默认值为 Propagation.REQUIRED,更详细的解释请点击 |
isolation | 事务的隔离级别,默认值为 Isolation.DEFAULT ,更详细的解释请点击 |
timeout | 事务的超时时间,默认值为-1.如果超过该时间限制但事务还没有完成,则自动回滚事务. |
readOnly | 指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置read-only 为 true. |
rollbackFor 或 rollbackForClassName | 用于指定能够触发事务回滚的异常类型,可以指定多个异常类型. |
noRollbackFor 或 noRollbackForClassName | 抛出指定的异常类型,不回滚事务,也可以指定多个异常类型. |
手动try-catch
了异常
想要spring事务能够回滚,必须抛出能够捕获的异常,如果异常被try-catch
了, 则程序认为没有抛出异常,则不会回滚
1 | less复制代码@Override |
抛出的异常不属于RuntimeException
或Error
异常
JDK中 @Transactional
的rollbackFor
参数上注释了该内容
1 | less复制代码By default, a transaction will be rolling back on {@link RuntimeException} |
默认情况下只处理RuntimeException
或Error
异常,如果你抛出其他异常则不会被处理
1 | less复制代码@Override |
因此为了能过抛出所有的异常,我们通常会在@Transactional
上定义rollbackFor = Exception.class
,这样能捕获所有的异常.当然你也可以赋值Exception.class
的父类为Throwable.class
1 | less复制代码@Override |
noRollbackFor
定义错误
@Transactional
中的noRollbackFor
是 抛出指定的异常类型,不回滚事务,如果错误的设置了抛出异常则不会回滚事务
1 | less复制代码 @Override |
错误的传播属性或者错误的嵌套关系
事务的传播行为,默认值为 Propagation.REQUIRED,更详细的解释请点击
本节例子 B为主方法, C子方法, 操作B的是否有事务, 操作C的传播属性 ,这个地方的情况太多,直接在下面表格中的本文中的解释
部分说明
1 | less复制代码@Service |
1 | less复制代码@Service |
传播行为 | 本文中的解释 |
---|---|
REQUIRED | 如果B存在事务,则C加入该事务(如果发生异常,则BC一起回滚) ;如果B不存在事务,则C创建一个新的事务(B不回滚,如果C发生异常则只有C部分回滚) |
SUPPORTS | 如果B存在事务,则C加入该事务(如果发生异常,则BC一起回滚) ;如果B不存在事务,则C以非事务的方式继续运行(BC任何情况都不回滚) |
MANDATORY | 如果B存在事务,则C加入该事务(如果发生异常,则一起回滚) ;如果B不存在事务,则C抛出异常.(C直接报错,无事务B不回滚) |
REQUIRES_NEW | 如果B不存在事务,C重新创建一个新的事务(无事务B发生异常不回滚,有事C发生异常则C回滚) ;如果B存在事务,C挂起B得事务并重新创建一个新的事务(这是两个事务,自己部分有异常,则自己部分回滚) |
NOT_SUPPORTED | 如果B不存在事务,C以非事务的方式运行(任何情况都不回滚) ;如果B存在事务,C暂停当前的事务并以非事务的方式运行(B部分报错,则B部分回滚,C不回滚;非事务C部分报错,则都不会回滚) |
NEVER | 如果B不存在事务,C以非事务的方式运行(任何情况都不回滚) ,如果B存在事务,C则抛出异常(C报错,B因为异常回滚) |
NESTED | 和REQUIRED效果一样. |
在这个地方讲了事务的传播行为会影响事务的状态,在事务嵌套的情况下,如果某一部分报错回滚,根据情况可能全部回滚,也有可能部分回滚.
如果使用到了传播行为参数,则需要仔细分析,仔细测试,然后再交付
非SpringBoot场景
没有配置事务管理器或者配置错误
老SSM项目配置问题
SpringMVC扫描错误
老SSM项目配置问题
没有被spring管理
特殊情况特殊分析
1 | arduino复制代码作者:ZOUZDC |
本文转载自: 掘金