天天看点

Java-SpringBoot-@Transactional-事务注解的失效和不回滚的场景

Java-SpringBoot-@Transactional-事务注解的失效和不回滚的场景

前言

事务(Transaction)指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。

SpringBoot 通过 @Transactional 注解大大简化了开发时事务使用的复杂度,但同时也引入了不少隐藏的坑。

使用不当会引起事务失效、不能回滚等情况,从而破坏记录的完整性。

我们梳理下导致事务失效的常见错误避免踩坑。

环境

Spring Boot (v2.6.7)

ORM JPA

数据库驱动 rg.postgresql.Driver

事务不回滚

1. 类内调用

外部类调用没有加 @Transactional 注解的方法。

public void fx1_test(RuntimeException ex) {
    fx1_test_2(ex);
}
@Transactional
public void fx1_test_2(RuntimeException ex) {
    aService.save();
    bService.save();
    if(ex!=null)
        throw ex;
}      

外部类通过调用fx1_test(),间接调用了使用事务的fx1_test_2()方法,发生异常时事务不会回滚。

2. 多线程调用

如果业务和事务入口不在同一个线程里,事务也是不生效的。

3. 错误的传播特性

  • Propagation.NOT_SUPPORTED

    声明方法不需要事务。如果方法没有关联到一个事务,容器不会为他开启事务,如果方法在一个事务中被调用,当前事务会被挂起,调用结束后,原先的事务会恢复执行。

  • Propagation.REQUIRESNEW

    不管是否存在事务,该方法总会为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务挂起,新的事务被创建。

  • Propagation.NEVER

    该方法绝对不能在事务范围内执行。如果在就抛例外。只有该方法没有关联到任何事务,才正常执行。

4. 自己吞了异常

使用try…catch 语句块将异常吞掉,没有向外层抛出,事务不回滚。

5. 自定义了回滚异常

‘’’

@Transactional(noRollbackFor = MyException.class)

设置了noRollbackFor属性值,并抛出了该异常时,事务不回滚。

‘’’

事务失效

1. 方法非public修饰,访问权限问题

private方法,protected和默认可见性方法不能添加事务,加了不生效;

2. 方法用final修饰

private方法,final方法和static方法不能添加事务,加了也不生效;

3. 未被spring管理

4. 手动抛了别的异常

事务的四大特性(ACID)

  • 原子性(Atomicity)

    原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

  • 一致性(Consistency)

    事务必须使数据库从一个一致性状态变换到另外一个一致性状态。(数据不被破坏

  • 隔离性(Isolation)

    事务的隔离性是指一个事务的执行不能被其他事务干扰.

  • 持久性(Durability)

    持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的.即使系统重启也不会丢失.

事务的七种传播模式

  • 1、REQUIRED(默认模式):业务方法需要在一个容器里运行。如果方法运行时,已经处在一个事务中,那么加入到这个事务,否则自己新建一个新的事务。
  • 2、NOT_SUPPORTED:声明方法不需要事务。如果方法没有关联到一个事务,容器不会为他开启事务,如果方法在一个事务中被调用,当前事务会被挂起,调用结束后,原先的事务会恢复执行。
  • 3、REQUIRESNEW:不管是否存在事务,该方法总会为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务挂起,新的事务被创建。
  • 4、 MANDATORY:该方法只能在一个已经存在的事务中执行,此方法不能发起自己的事务。如果在没有事务的环境下被调用,容器抛出例外。
  • 5、SUPPORTS:该方法在某个事务范围内被调用,则方法成为该事务的一部分。如果方法在该事务范围外被调用,该方法就在没有事务的环境下执行。
  • 6、NEVER:该方法绝对不能在事务范围内执行。如果在就抛例外。只有该方法没有关联到任何事务,才正常执行。
  • 7、NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行。它使用了一个单独的事务,这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效。

总结

  • 项目结构层次清晰(controller、service、repository);
  • 数据层保证单一职责(一个类操作一个数据表);
  • 操作多表的方法,封装在 service 层;
  • 事务注解 @Transaction 加在 serivce 层的pulbic方法上;
  • controller 直接调用 service 层加注解的方法;