Skip to content

事务管理

一、事务概述

ACID 特性

┌─────────────────────────────────────────────────────────────────┐
│                        ACID 特性                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Atomic(原子性)                                                │
│  └─ 事务是最小执行单元,不可分割                                  │
│     要么全部成功,要么全部失败                                    │
│                                                                  │
│  Consistency(一致性)                                           │
│  └─ 事务执行前后,数据库状态保持一致                              │
│     约束不被破坏                                                  │
│                                                                  │
│  Isolation(隔离性)                                             │
│  └─ 并发执行的事务互不干扰                                        │
│     通过隔离级别控制                                             │
│                                                                  │
│  Durability(持久性)                                            │
│  └─ 事务提交后,结果永久保存                                      │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Spring 事务抽象

┌─────────────────────────────────────────────────────────────────┐
│                    Spring 事务抽象                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  PlatformTransactionManager(事务管理器接口)                     │
│  ├─ DataSourceTransactionManager    ← JDBC                      │
│  ├─ JpaTransactionManager           ← JPA                        │
│  ├─ HibernateTransactionManager    ← Hibernate(老)           │
│  ├─ JtaTransactionManager          ← JTA(分布式事务)         │
│  └─ RabbitTransactionManager        ← RabbitMQ                  │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

二、@Transactional

基本用法

java
@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private AccountRepository accountRepository;
    
    @Transactional  // 默认配置
    public void transfer(Long fromId, Long toId, BigDecimal amount) {
        // 扣款
        Account from = accountRepository.findById(fromId).orElseThrow();
        from.setBalance(from.getBalance().subtract(amount));
        accountRepository.save(from);
        
        // 存款
        Account to = accountRepository.findById(toId).orElseThrow();
        to.setBalance(to.getBalance().add(amount));
        accountRepository.save(to);
        
        // 如果抛出异常,事务回滚
    }
}

事务管理器

java
// 默认自动选择(根据 DataSource)
@Transactional

// 显式指定
@Transactional(transactionManager = "jpaTransactionManager")

// Spring Boot 2.x+ 配置
spring:
  transaction:
    default-timeout: 30  # 全局超时(秒)

三、事务传播行为

7 种传播级别

┌─────────────────────────────────────────────────────────────────┐
│                    事务传播行为                                   │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  REQUIRED(默认)                                                │
│  ├─ 有事务 → 加入当前事务                                       │
│  └─ 无事务 → 创建新事务                                         │
│                                                                  │
│  REQUIRES_NEW                                                   │
│  ├─ 总是创建新事务                                             │
│  └─ 挂起当前事务(如果有)                                       │
│                                                                  │
│  SUPPORTS                                                       │
│  ├─ 有事务 → 加入当前事务                                       │
│  └─ 无事务 → 非事务执行                                         │
│                                                                  │
│  NOT_SUPPORTED                                                  │
│  ├─ 非事务执行                                                 │
│  └─ 挂起当前事务(如果有)                                       │
│                                                                  │
│  MANDATORY                                                       │
│  ├─ 有事务 → 加入当前事务                                       │
│  └─ 无事务 → 抛异常                                            │
│                                                                  │
│  NEVER                                                          │
│  ├─ 非事务执行                                                 │
│  └─ 有事务 → 抛异常                                            │
│                                                                  │
│  NESTED(嵌套事务)                                              │
│  ├─ 有事务 → 创建嵌套事务(savepoint)                          │
│  └─ 无事务 → 创建新事务(等同 REQUIRED)                        │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

常用场景

java
@Service
public class OrderService {
    
    @Transactional
    public void createOrder(Order order) {
        // 保存订单
        orderRepository.save(order);
        
        // 扣减库存(REQUIRES_NEW:新事务,独立提交/回滚)
        inventoryService.reduceStock(order.getItems());
        
        // 发送消息(REQUIRES_NEW:不影响主事务)
        messageService.sendOrderCreated(order);
    }
}

@Service
public class InventoryService {
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void reduceStock(List<OrderItem> items) {
        // 库存扣减逻辑
        // 即使这里失败,订单已创建不会回滚
    }
}

传播行为对比

传播行为有事务无事务
REQUIRED加入创建
REQUIRES_NEW挂起,创建新事务创建
SUPPORTS加入非事务
NOT_SUPPORTED挂起非事务
MANDATORY加入抛异常
NEVER抛异常非事务
NESTED嵌套(savepoint)创建

四、隔离级别

4 种隔离级别

┌─────────────────────────────────────────────────────────────────┐
│                        隔离级别                                   │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  READ_UNCOMMITTED(读未提交)                                    │
│  ├─ 最低隔离级别                                                │
│  ├─ 可能脏读、不可重复读、幻读                                   │
│  └─ 性能最好                                                    │
│                                                                  │
│  READ_COMMITTED(读已提交)                                      │
│  ├─ Oracle 默认级别                                             │
│  ├─ 防止脏读                                                    │
│  ├─ 不可重复读、幻读可能                                        │
│  └─ 性能较好                                                    │
│                                                                  │
│  REPEATABLE_READ(可重复读)                                    │
│  ├─ MySQL InnoDB 默认级别                                      │
│  ├─ 防止脏读、不可重复读                                        │
│  ├─ 幻读可能(InnoDB 通过 MVCC 优化)                           │
│  └─ 性能一般                                                    │
│                                                                  │
│  SERIALIZABLE(串行化)                                         │
│  ├─ 最高隔离级别                                                │
│  ├─ 防止所有并发问题                                            │
│  └─ 性能最差,可能死锁                                          │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

并发问题

问题说明
脏读读取到其他事务未提交的数据
不可重复读同一事务内两次读取数据不同(其他事务提交了)
幻读同一事务内查询结果条数不同(其他事务插入/删除了)

配置隔离级别

java
@Service
public class UserService {
    
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public User findById(Long id) {
        return userRepository.findById(id).orElseThrow();
    }
}

五、回滚规则

默认回滚

java
@Transactional
public void transfer(...) {
    // 默认只对 RuntimeException 和 Error 回滚
    // Checked Exception 不回滚
    throw new RuntimeException("余额不足");   // 回滚 ✅
    throw new SQLException("数据库错误");    // 回滚 ✅
    throw new IOException("IO错误");         // 回滚 ✅
    throw new Exception("其他异常");         // 不回滚 ❌
}

配置回滚

java
@Transactional(rollbackFor = Exception.class)
// 所有 Exception 都回滚(包括 Checked Exception)

@Transactional(noRollbackFor = RuntimeException.class)
// RuntimeException 不回滚(一般不这样用)

正确使用

java
@Transactional(rollbackFor = Exception.class)
public void transfer(Long fromId, Long toId, BigDecimal amount) {
    try {
        // 业务逻辑
        accountRepository.save(from);
        accountRepository.save(to);
        
    } catch (BusinessException e) {
        // 业务异常,回滚
        throw e;
    } catch (Exception e) {
        // 系统异常,回滚
        throw new RuntimeException("转账失败", e);
    }
}

六、编程式事务

TransactionTemplate

java
@Service
public class UserService {
    
    @Autowired
    private TransactionTemplate transactionTemplate;
    
    public void transfer(Long fromId, Long toId, BigDecimal amount) {
        transactionTemplate.executeWithoutResult(status -> {
            try {
                // 业务逻辑
                Account from = accountRepository.findById(fromId).orElseThrow();
                from.setBalance(from.getBalance().subtract(amount));
                accountRepository.save(from);
                
                Account to = accountRepository.findById(toId).orElseThrow();
                to.setBalance(to.getBalance().add(amount));
                accountRepository.save(to);
                
            } catch (Exception e) {
                status.setRollbackOnly();  // 标记回滚
                throw new RuntimeException("转账失败", e);
            }
        });
    }
}

返回值

java
public User findById(Long id) {
    return transactionTemplate.execute(status -> {
        return userRepository.findById(id).orElseThrow();
    });
}

public BigDecimal calculate(Long userId) {
    return transactionTemplate.execute(status -> {
        // 复杂计算
        return accountRepository.sumBalance(userId);
    });
}

七、嵌套事务

savepoint 机制

┌─────────────────────────────────────────────────────────────────┐
│                        嵌套事务                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Parent Transaction ─────────────────────────────────────────▶││
│       │                                                          │
│       ├─ savepoint: sp1                                         │
│       │                                                          │
│       ├─ Nested Transaction ──────────────────────────────▶│││
│       │       │                                                │││
│       │       └─ rollback to sp1 ──────────────────────▶│││││
│       │                                                          │
│       └─ commit ───────────────────────────────────────────────▶│
│                                                                  │
│  外层回滚 → 全部回滚                                             │
│  内层回滚 → 只回滚到 savepoint,外层继续                          │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

示例

java
@Service
public class OrderService {
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Transactional
    public void createOrder(Order order) {
        // 保存主订单
        orderRepository.save(order);
        
        try {
            // 嵌套事务:处理每个订单项
            for (OrderItem item : order.getItems()) {
                processItem(order.getId(), item);
            }
        } catch (Exception e) {
            // 只回滚嵌套部分,主订单不受影响
            log.warn("部分订单项处理失败", e);
        }
    }
    
    @Transactional(propagation = Propagation.NESTED)
    public void processItem(Long orderId, OrderItem item) {
        // 每个订单项的处理
        // 失败时只回滚到 savepoint
    }
}

八、注意事项

代理问题

java
@Service
public class UserService {
    
    @Transactional
    public void save(User user) {
        userRepository.save(user);
        
        // 内部调用(绕过代理,事务不生效!)
        this.sendNotification(user);  // ❌ 事务不生效
    }
    
    // 解决方案1:注入自身
    @Autowired
    private UserService self;
    
    public void save(User user) {
        userRepository.save(user);
        self.sendNotification(user);  // ✅ 通过代理调用
    }
    
    // 解决方案2:AopContext
    ((UserService) AopContext.currentProxy()).sendNotification(user);  // ✅
}

自调用问题

java
// 问题代码
@Service
public class A {
    
    @Transactional
    public void methodA() {
        methodB();  // 内部调用,事务不生效
    }
    
    @Transactional
    public void methodB() {
        // 事务生效了吗?取决于调用方式
    }
}

// 正确做法
@Service
public class A {
    
    @Autowired
    private A self;  // 注入自身代理
    
    @Transactional
    public void methodA() {
        self.methodB();  // 通过代理调用
    }
    
    @Transactional
    public void methodB() {
        // 现在事务生效
    }
}

异常被吞

java
@Transactional
public void wrong() {
    try {
        // 业务逻辑
        doSomething();
    } catch (Exception e) {
        log.error("异常", e);
        // 异常被吞掉,事务不会回滚!
    }
}

@Transactional
public void correct() {
    try {
        doSomething();
    } catch (Exception e) {
        log.error("异常", e);
        throw e;  // 重新抛出,事务才会回滚
    }
}

九、Spring Boot 事务配置

默认配置

yaml
spring:
  transaction:
    # 默认超时(秒)
    default-timeout: 30
    
    # 默认回滚异常
    rollback-on-commit-failures: true

多数据源事务

java
@Configuration
public class TransactionConfig {
    
    @Bean
    public PlatformTransactionManager txManager1(EntityManagerFactory emf1) {
        return new JpaTransactionManager(emf1);
    }
    
    @Bean
    public PlatformTransactionManager txManager2(EntityManagerFactory emf2) {
        return new JpaTransactionManager(emf2);
    }
}

@TransactionalOnBean

java
@Bean
@Transactional(transactionManager = "txManager2")
public UserService userService2() {
    return new UserService();
}

十、面试高频问题

Q1: @Transactional 哪些情况会失效?

  1. 非 public 方法(Spring AOP 限制)
  2. 内部调用(绕过代理)
  3. 异常被 catch 吞掉
  4. 传播行为设置错误
  5. Checked Exception 未配置 rollbackFor

Q2: REQUIRED 和 REQUIRES_NEW 区别?

  • REQUIRED:加入当前事务,共享同一个事务
  • REQUIRES_NEW:创建新事务,挂起当前事务

Q3: NESTED 和 REQUIRED 区别?

  • REQUIRED:共享事务
  • NESTED:使用 savepoint,嵌套部分失败只回滚到 savepoint

Q4: 事务隔离级别和传播行为区别?

  • 隔离级别:并发事务之间如何隔离
  • 传播行为:事务和方法之间的关系(新建/加入/挂起)

Q5: 如何选择隔离级别?

  • 读频繁:READ_COMMITTED
  • 数据一致性要求高:REPEATABLE_READ
  • 写入频繁且一致性要求极高:SERIALIZABLE

十一、下一章预告

下一章我们将学习 Spring Security

  • 认证与授权流程
  • JWT 实现登录
  • 权限控制 RBAC
  • OAuth2 第三方登录

基于 MIT 许可发布