【SpringBoot】事务传播行为,怎么看是否会回滚

事务(Transaction)

一. Java 事务的核心概念

  1. 原子性:事务中的所有操作要么全部完成,要么全部不完成
  2. 一致性:事务执行前后,数据库状态保持一致
  3. 隔离性:多个事务的执行不受其他事务干扰
  4. 持久性:事务完成后,修改永久保存到数据库

1. Spring 声明事务

@Service
public class TransferService {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    // 声明式事务,默认发生异常时回滚
    @Transactional
    public void transferMoney(Long fromId, Long toId, double amount) {
        // 转出
        jdbcTemplate.update(
            "UPDATE account SET balance = balance - ? WHERE id = ?",
            amount, fromId
        );
        
        // 转入
        jdbcTemplate.update(
            "UPDATE account SET balance = balance + ? WHERE id = ?",
            amount, toId
        );
    }
}

2. Spring 事务注解的常用属性

@Transactional(
    propagation = Propagation.REQUIRED,  // 事务传播行为
    isolation = Isolation.READ_COMMITTED, // 事务隔离级别
    readOnly = false,  // 是否为只读事务
    timeout = 30,  // 超时时间(秒)
    rollbackFor = {Exception.class},  // 指定回滚的异常类型
    noRollbackFor = {RuntimeException.class}  // 指定不回滚的异常类型
)

2.1. 事务传播行为 Propagation

当一个事务方法调用另一个事务方法时,传播行为定义了如何处理事务:

  • REQUIRED:如果当前有事务,加入该事务;否则新建事务
  • REQUIRES_NEW:无论当前是否有事务,都新建一个事务
  • SUPPORTS:如果当前有事务,加入该事务;否则以非事务方式执行
  • NOT_SUPPORTED:以非事务方式执行,如果当前有事务,暂停该事务
  • MANDATORY:必须在事务中执行,否则抛出异常
  • NEVER:必须在非事务中执行,否则抛出异常
  • NESTED:如果当前有事务,则嵌套在该事务中执行

2.2. 事务隔离级别

Java 定义了 5 种事务隔离级别:

  1. DEFAULT:使用数据库默认隔离级别
  2. READ_UNCOMMITTED:最低级别,允许读取未提交的数据
  3. READ_COMMITTED:保证读取已提交的数据,避免脏读
  4. REPEATABLE_READ:保证多次读取同一数据结果一致,避免不可重复读
  5. SERIALIZABLE:最高级别,完全隔离,避免幻读

-----------------------------------------------------------------------------------------

二.事务的回滚行为主要由事务传播行为(Propagation) 决定

-----------------------------------------------------------------------------------------

场景 1:事务方法 A 调用事务方法 B(两个都有@Transactional注解)

假设方法 A 和方法 B 都有@Transactional注解,当方法 A 调用方法 B 时:

  • 关键因素:两个方法的propagation属性配置(默认是REQUIRED
  • 回滚规则
    1. 若 B 抛出异常,且异常满足回滚条件(默认是RuntimeException及其子类):
      • 如果 B 的传播行为是REQUIRED(默认):B 会加入 A 的事务,A 和 B 会一起回滚(因为在同一个事务中)。
      • 如果 B 的传播行为是REQUIRES_NEW:B 会开启新事务,只有 B 回滚,A 的事务不受影响(除非 A 也捕获并抛出异常)。
    2. 若 A 在调用 B 之后抛出异常:
      • 无论 B 的传播行为如何,A 的事务会回滚,但REQUIRES_NEW的 B 事务不会回滚(已独立提交)。

看回滚规则,是否还是同一个事务

场景 2:普通函数调用事务方法 B(只有 B 有@Transactional

普通函数(无@Transactional)调用事务方法 B 时:

  • B 的事务会正常生效(独立事务)。
  • 若 B 内部抛出异常并满足回滚条件,只有 B 的事务回滚,调用者的普通函数无事务,不存在回滚概念。
  • 若 B 成功执行后,调用者的普通函数后续逻辑出错,B 的事务不会回滚(已提交)。

事务提交了,不会回滚

场景 3:事务方法 A 调用普通函数 C(只有 A 有@Transactional

事务方法 A 调用无事务的普通函数 C 时:

  • C 的逻辑属于 A 的事务范围(因为没有开启新事务)。
  • 若 C 中抛出异常且被 A 捕获:若 A 不再次抛出异常,事务不会回滚;若 A 重新抛出异常,整个 A 的事务(包括 C 的操作)会回滚
  • 若 C 中抛出异常未被 A 捕获:异常向上传播到 A,A 的事务会回滚(包括 C 的操作)。

捕获异常没有抛出不回滚,抛出了就回滚

### Spring Boot事务传播行为及其用法 #### 什么是事务传播行为事务传播行为定义了一个方法如何参与事务。当调用带有 `@Transactional` 注解的方法时,Spring 根据指定的传播行为决定是否创建事务或加入现有事务。 常见的事务传播行为有以下几种: - **REQUIRED**: 如果当前存在事务,则加入该事务;如果不存在则创建一个事务。 - **REQUIRES_NEW**: 创建一个事务,无论当前是否存在事务。如果有当前事务,将其挂起。 - **SUPPORTS**: 如果当前存在事务,则加入该事务;如果没有事务,则以非事务方式执行操作。 - **NOT_SUPPORTED**: 不支持事务上下文,暂停任何现有的事务。 - **MANDATORY**: 当前必须有一个活动的事务,否则抛出异常。 - **NEVER**: 当前不能有任何事务,否则抛出异常。 - **NESTED**: 如果当前存在事务,则在嵌套事务中运行;如果不存在事务,则表现如同 REQUIRED。 这些传播行为可以通过 `propagation` 属性来设置[^2]。 --- #### 使用示例 下面是一个基于不同传播行为的例子,展示它们的行为差异。 ##### 示例代码 假设我们有两个服务类 `ServiceA` 和 `ServiceB`,分别实现不同的业务逻辑并涉及事务处理。 ```java @Service public class ServiceA { @Autowired private ServiceB serviceB; @Transactional(propagation = Propagation.REQUIRED) public void methodInRequired() { System.out.println("Executing in REQUIRED"); serviceB.methodInRequiresNew(); } @Transactional(propagation = Propagation.NOT_SUPPORTED) public void methodNotSupported() { System.out.println("Executing without transaction support."); } } @Service public class ServiceB { @Transactional(propagation = Propagation.REQUIRES_NEW) public void methodInRequiresNew() { System.out.println("Executing in REQUIRES_NEW (new transaction)"); } } ``` 在这个例子中: - 方法 `methodInRequired()` 的传播行为为 `REQUIRED`,它会尝试加入已有的事务或者创建事务。 - 方法 `methodInRequiresNew()` 的传播行为为 `REQUIRES_NEW`,即使被其他事务中的方法调用,也会启动自己的独立事务。 - 方法 `methodNotSupported()` 则完全不使用事务支持。 --- #### 运行流程分析 1. 调用 `serviceA.methodInRequired()` 时,由于其传播行为为 `REQUIRED`,因此会一个事务。 2. 接着调用了 `serviceB.methodInRequiresNew()`,因为它的传播行为为 `REQUIRES_NEW`,所以会在原有事务的基础上挂起当前事务,并创建一个事务。 3. 完成后返回到原来的事务继续执行剩余部分。 对于 `methodNotSupported()`,尽管可能处于某个事务范围内,但它不会参与到事务管理之中。 --- #### 数据库交互与事务控制 实际应用中,事务不仅依赖于 Java 层面的配置,还需要底层数据库的支持。例如,在 MySQL 中启用事务模式通常需要调整隔离级别以及关闭自动提交功能[^3]。 以下是手动设置事务参数的一个简单案例: ```java @Transactional(isolation = Isolation.SERIALIZABLE, propagation = Propagation.REQUIRED) public void updateData(String dataId) throws Exception { try { // 执行更操作 repository.update(dataId); // 模拟错误情况 if ("error".equals(dataId)) { throw new RuntimeException("Simulated error occurred!"); } } catch (Exception e) { log.error("Transaction rolled back due to exception", e); throw e; // 确保回滚发生 } } ``` 在此处设置了较高的隔离等级 (`SERIALIZABLE`) 来防止并发问题,同时也指定了默认的传播行为 `REQUIRED`。 --- #### 总结 通过合理选择和组合各种事务传播行为,可以灵活应对复杂的业务需求。每种传播行为都有特定的应用场景,发者应根据实际情况权衡选用合适的选项。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值