Administrator
发布于 2026-03-16 / 1 阅读
0
0

Spring @Transactional 事务传播机制完全指南

# Spring @Transactional 事务传播机制完全指南

> 🎯 面向面试 + 企业实战  
> 包含:原理讲解 + 代码示例 + 面试高频问题 + 踩坑经验

---

## 📚 目录

1. [事务基础回顾](#一事务基础回顾)
2. [七种传播行为详解](#二七种传播行为详解)
3. [企业级实战:电商下单场景](#三企业级实战电商下单场景)
4. [事务失效的8种场景](#四事务失效的8种场景)
5. [面试高频问题](#五面试高频问题)
6. [最佳实践总结](#六最佳实践总结)

---

## 一、事务基础回顾

### 1.1 什么是事务传播?

**事务传播(Propagation)**:当一个事务方法调用另一个事务方法时,事务应该如何传递的行为定义。

```
简单理解:
方法A(有事务) → 调用 → 方法B(有事务)

问题:方法B是加入A的事务?还是新开一个事务?还是不用事务?
答案:由传播行为决定!
```

### 1.2 @Transactional 基本用法

```java
@Transactional(
    propagation = Propagation.REQUIRED,     // 传播行为
    isolation = Isolation.DEFAULT,          // 隔离级别
    timeout = -1,                           // 超时时间(秒)
    readOnly = false,                       // 是否只读
    rollbackFor = Exception.class,          // 哪些异常回滚
    noRollbackFor = {}                      // 哪些异常不回滚
)
public void method() {
    // 业务逻辑
}
```

### 1.3 事务的ACID特性(面试必背)

| 特性 | 含义 | 举例 |
|------|------|------|
| **A**tomicity(原子性) | 全部成功或全部失败 | 转账:扣款和加款必须同时成功 |
| **C**onsistency(一致性) | 事务前后数据状态一致 | 转账前后总金额不变 |
| **I**solation(隔离性) | 事务之间互不干扰 | 并发转账不会出错 |
| **D**urability(持久性) | 提交后永久保存 | 系统崩溃后数据不丢失 |

---

## 二、七种传播行为详解

### 2.1 传播行为一览表

| 传播行为 | 含义 | 外部有事务 | 外部无事务 | 使用频率 |
|----------|------|-----------|-----------|---------|
| **REQUIRED** | 需要事务(默认) | 加入 | 新建 | ⭐⭐⭐⭐⭐ |
| **SUPPORTS** | 支持事务 | 加入 | 无事务运行 | ⭐⭐ |
| **MANDATORY** | 强制事务 | 加入 | 抛异常 | ⭐ |
| **REQUIRES_NEW** | 新建事务 | 挂起+新建 | 新建 | ⭐⭐⭐⭐ |
| **NOT_SUPPORTED** | 不支持事务 | 挂起+无事务 | 无事务运行 | ⭐⭐ |
| **NEVER** | 禁止事务 | 抛异常 | 无事务运行 | ⭐ |
| **NESTED** | 嵌套事务 | 嵌套(保存点) | 新建 | ⭐⭐⭐ |

### 2.2 REQUIRED(默认,最常用)

**含义**:当前有事务就加入,没有就新建

```java
@Service
public class OrderService {
    
    @Autowired
    private StockService stockService;
    
    @Transactional(propagation = Propagation.REQUIRED)  // 可省略,默认就是REQUIRED
    public void createOrder(Order order) {
        // 1. 保存订单
        orderMapper.insert(order);
        
        // 2. 扣减库存(会加入当前事务)
        stockService.deductStock(order.getProductId(), order.getQuantity());
        
        // 如果这里抛异常,订单和库存都会回滚
    }
}

@Service
public class StockService {
    
    @Transactional(propagation = Propagation.REQUIRED)
    public void deductStock(Long productId, Integer quantity) {
        // 加入OrderService的事务
        stockMapper.deduct(productId, quantity);
    }
}
```

**执行流程图**:
```
createOrder()
    │
    ├─ 开启事务A ──────────────────────────┐
    │                                      │
    ├─ 保存订单                            │
    │                                      │ 同一个事务
    ├─ 调用 deductStock()                  │
    │      └─ 检测到外部有事务A,加入      │
    │      └─ 扣减库存                     │
    │                                      │
    └─ 提交/回滚事务A ─────────────────────┘
```

**使用场景**:
- 大部分业务场景的默认选择
- 需要保证多个操作在同一事务中

---

### 2.3 REQUIRES_NEW(新建独立事务)

**含义**:无论外部有没有事务,都新建一个独立事务

```java
@Service
public class OrderService {
    
    @Autowired
    private LogService logService;
    
    @Transactional
    public void createOrder(Order order) {
        // 1. 保存订单
        orderMapper.insert(order);
        
        // 2. 记录操作日志(独立事务,不受主事务影响)
        logService.saveLog("创建订单: " + order.getOrderNo());
        
        // 3. 模拟异常
        if (true) {
            throw new RuntimeException("模拟异常");
        }
        // 结果:订单回滚,但日志已提交!
    }
}

@Service
public class LogService {
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveLog(String content) {
        // 新开独立事务,不管外部事务成功还是失败
        // 日志都会保存
        logMapper.insert(new Log(content));
    }
}
```

**执行流程图**:
```
createOrder()
    │
    ├─ 开启事务A ─────────────────────────────────┐
    │                                             │
    ├─ 保存订单                                   │
    │                                             │
    ├─ 调用 saveLog()                             │
    │      ├─ 挂起事务A                           │
    │      ├─ 开启新事务B ─────┐                  │
    │      │   └─ 保存日志     │ 独立事务          │
    │      ├─ 提交事务B ───────┘                  │
    │      └─ 恢复事务A                           │
    │                                             │
    ├─ 抛出异常                                   │
    │                                             │
    └─ 回滚事务A(订单回滚,日志已提交)──────────┘
```

**使用场景**:
- ✅ 操作日志记录(主业务失败也要记录)
- ✅ 发送消息/通知(不影响主流程)
- ✅ 审计记录
- ✅ 异步任务创建

**⚠️ 注意事项**:
```java
// ❌ 错误:同一个类中调用,事务不生效!
@Service
public class OrderService {
    
    @Transactional
    public void createOrder() {
        // ...
        this.saveLog("xxx");  // ❌ 事务不生效!
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveLog(String content) {
        // 不会新开事务,因为是this调用
    }
}

// ✅ 正确:注入自己或使用AopContext
@Service
public class OrderService {
    
    @Autowired
    private OrderService self;  // 注入自己
    
    @Transactional
    public void createOrder() {
        // ...
        self.saveLog("xxx");  // ✅ 通过代理调用,事务生效
    }
}
```

---

### 2.4 NESTED(嵌套事务)

**含义**:在当前事务中创建一个保存点(Savepoint),如果嵌套事务失败,只回滚到保存点

```java
@Service
public class OrderService {
    
    @Autowired
    private PointService pointService;
    
    @Transactional
    public void createOrder(Order order) {
        // 1. 保存订单(必须成功)
        orderMapper.insert(order);
        
        // 2. 赠送积分(失败不影响订单)
        try {
            pointService.addPoints(order.getUserId(), 100);
        } catch (Exception e) {
            // 积分失败了,只回滚积分,订单保留
            log.warn("积分赠送失败,但订单已创建成功");
        }
        
        // 3. 订单创建成功
    }
}

@Service
public class PointService {
    
    @Transactional(propagation = Propagation.NESTED)
    public void addPoints(Long userId, Integer points) {
        // 嵌套事务:失败只回滚这部分
        pointMapper.addPoints(userId, points);
        
        if (积分系统异常) {
            throw new RuntimeException("积分服务不可用");
            // 只回滚积分操作,不影响外部订单
        }
    }
}
```

**执行流程图**:
```
createOrder()
    │
    ├─ 开启事务A ─────────────────────────────────┐
    │                                             │
    ├─ 保存订单                                   │
    │                                             │
    ├─ 调用 addPoints()                           │
    │      ├─ 创建保存点 Savepoint1              │
    │      ├─ 执行积分操作                        │
    │      ├─ 如果失败 → 回滚到 Savepoint1        │
    │      └─ 继续执行                            │
    │                                             │
    └─ 提交事务A(订单成功)─────────────────────┘
```

**NESTED vs REQUIRES_NEW 对比**:

| 对比项 | NESTED | REQUIRES_NEW |
|--------|--------|--------------|
| 事务关系 | 子事务,共用连接 | 独立事务,新连接 |
| 外部回滚 | 子事务也回滚 | 不受影响 |
| 子事务回滚 | 可只回滚子事务 | 不影响外部 |
| 性能 | 较好(共用连接) | 较差(新建连接) |
| 使用场景 | 部分失败可接受 | 必须独立提交 |

**使用场景**:
- ✅ 积分、优惠券等附属操作(失败不影响主流程)
- ✅ 批量操作中的单条失败处理
- ✅ 需要部分回滚的场景

---

### 2.5 SUPPORTS(支持事务)

**含义**:有事务就加入,没有就以非事务方式运行

```java
@Service
public class UserService {
    
    @Transactional(propagation = Propagation.SUPPORTS)
    public User getUser(Long userId) {
        // 如果调用方有事务,就加入
        // 如果调用方没有事务,就以非事务方式查询
        return userMapper.selectById(userId);
    }
}
```

**使用场景**:
- ✅ 纯查询方法
- ✅ 可以在事务中也可以独立运行的操作

---

### 2.6 NOT_SUPPORTED(不支持事务)

**含义**:以非事务方式运行,如果当前有事务则挂起

```java
@Service
public class ReportService {
    
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public Report generateReport() {
        // 生成报表是只读操作,不需要事务
        // 如果外部有事务,会挂起,避免长事务
        return reportMapper.generateComplexReport();
    }
}
```

**使用场景**:
- ✅ 耗时的只读操作(避免长事务)
- ✅ 调用外部服务
- ✅ 不需要事务保证的操作

---

### 2.7 MANDATORY(强制事务)

**含义**:必须在事务中运行,否则抛异常

```java
@Service
public class AccountService {
    
    @Transactional(propagation = Propagation.MANDATORY)
    public void transfer(Long fromId, Long toId, BigDecimal amount) {
        // 转账必须在事务中执行
        // 如果调用方没有事务,直接抛异常
        accountMapper.deduct(fromId, amount);
        accountMapper.add(toId, amount);
    }
}

// 调用方
@Service
public class PayService {
    
    @Transactional  // ✅ 必须有事务
    public void pay() {
        accountService.transfer(1L, 2L, new BigDecimal("100"));
    }
    
    public void payWithoutTransaction() {
        // ❌ 会抛异常:IllegalTransactionStateException
        accountService.transfer(1L, 2L, new BigDecimal("100"));
    }
}
```

**使用场景**:
- ✅ 强制要求调用方必须有事务的核心操作
- ✅ 金融转账、资金操作等关键业务

---

### 2.8 NEVER(禁止事务)

**含义**:必须以非事务方式运行,如果当前有事务则抛异常

```java
@Service
public class CacheService {
    
    @Transactional(propagation = Propagation.NEVER)
    public void refreshCache() {
        // 刷新缓存不允许在事务中执行
        // 防止事务未提交就刷新缓存导致数据不一致
        cacheManager.refresh();
    }
}
```

**使用场景**:
- ✅ 不允许在事务中执行的操作
- ✅ 缓存刷新等可能导致数据不一致的操作

---

## 三、企业级实战:电商下单场景

### 3.1 业务场景分析

```
用户下单流程:
1. 创建订单(核心,必须成功)
2. 扣减库存(核心,必须成功)
3. 使用优惠券(重要,失败需回滚)
4. 赠送积分(次要,失败不影响订单)
5. 发送通知(异步,失败不影响订单)
6. 记录操作日志(审计,必须记录)
```

### 3.2 事务设计方案

```java
@Service
@RequiredArgsConstructor
@Slf4j
public class OrderService {
    
    private final OrderMapper orderMapper;
    private final StockService stockService;
    private final CouponService couponService;
    private final PointService pointService;
    private final NotifyService notifyService;
    private final LogService logService;
    
    /**
     * 创建订单 - 主事务
     * 
     * 事务策略:
     * - 订单+库存+优惠券:同一事务(REQUIRED)
     * - 积分:嵌套事务(NESTED),失败不影响订单
     * - 通知:异步处理,不在事务中
     * - 日志:独立事务(REQUIRES_NEW),主事务失败也要记录
     */
    @Transactional(rollbackFor = Exception.class)
    public OrderResult createOrder(CreateOrderRequest request) {
        log.info("开始创建订单,用户:{}", request.getUserId());
        
        // ========== 1. 创建订单(REQUIRED,加入当前事务) ==========
        Order order = buildOrder(request);
        orderMapper.insert(order);
        log.info("订单创建成功:{}", order.getOrderNo());
        
        // ========== 2. 扣减库存(REQUIRED,加入当前事务) ==========
        // 库存扣减失败,整个订单回滚
        stockService.deductStock(order.getProductId(), order.getQuantity());
        log.info("库存扣减成功");
        
        // ========== 3. 使用优惠券(REQUIRED,加入当前事务) ==========
        if (request.getCouponId() != null) {
            couponService.useCoupon(request.getUserId(), request.getCouponId());
            log.info("优惠券使用成功");
        }
        
        // ========== 4. 赠送积分(NESTED,嵌套事务) ==========
        // 积分失败不影响订单
        try {
            pointService.addPoints(request.getUserId(), calculatePoints(order));
            log.info("积分赠送成功");
        } catch (Exception e) {
            // 积分失败,只记录警告,不影响订单
            log.warn("积分赠送失败,但订单已创建:{}", e.getMessage());
        }
        
        // ========== 5. 异步发送通知(事务外,不影响主流程) ==========
        // 使用@Async或消息队列
        notifyService.sendOrderNotification(order);
        
        // ========== 6. 记录操作日志(REQUIRES_NEW,独立事务) ==========
        // 无论订单成功还是失败,都要记录日志
        logService.recordOrderLog(order, "CREATE");
        
        return OrderResult.success(order);
    }
}
```

### 3.3 各服务的事务配置

```java
// ==================== 库存服务 ====================
@Service
@RequiredArgsConstructor
public class StockService {
    
    /**
     * 扣减库存
     * 传播行为:REQUIRED(默认)
     * 原因:库存必须和订单在同一事务,要么都成功,要么都失败
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void deductStock(Long productId, Integer quantity) {
        // 1. 查询库存
        Stock stock = stockMapper.selectByProductId(productId);
        
        if (stock == null || stock.getQuantity() < quantity) {
            throw new BusinessException("库存不足");
        }
        
        // 2. 扣减库存(使用乐观锁)
        int rows = stockMapper.deductWithOptimisticLock(productId, quantity, stock.getVersion());
        
        if (rows == 0) {
            throw new BusinessException("库存扣减失败,请重试");
        }
    }
}

// ==================== 优惠券服务 ====================
@Service
@RequiredArgsConstructor
public class CouponService {
    
    /**
     * 使用优惠券
     * 传播行为:REQUIRED
     * 原因:优惠券使用必须和订单在同一事务
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void useCoupon(Long userId, Long couponId) {
        // 1. 校验优惠券
        UserCoupon coupon = couponMapper.selectByUserAndCoupon(userId, couponId);
        
        if (coupon == null || coupon.getStatus() != 0) {
            throw new BusinessException("优惠券不可用");
        }
        
        // 2. 标记为已使用
        coupon.setStatus(1);
        coupon.setUseTime(new Date());
        couponMapper.updateById(coupon);
    }
}

// ==================== 积分服务 ====================
@Service
@RequiredArgsConstructor
public class PointService {
    
    /**
     * 赠送积分
     * 传播行为:NESTED(嵌套事务)
     * 原因:积分是附属功能,失败不应影响订单
     */
    @Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
    public void addPoints(Long userId, Integer points) {
        // 调用积分系统
        pointMapper.addPoints(userId, points);
        
        // 记录积分流水
        pointRecordMapper.insert(new PointRecord(userId, points, "ORDER_REWARD"));
    }
}

// ==================== 日志服务 ====================
@Service
@RequiredArgsConstructor
public class LogService {
    
    /**
     * 记录操作日志
     * 传播行为:REQUIRES_NEW(独立事务)
     * 原因:无论主事务成功还是失败,日志都必须记录(审计需求)
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void recordOrderLog(Order order, String action) {
        OrderLog log = new OrderLog();
        log.setOrderNo(order.getOrderNo());
        log.setAction(action);
        log.setOperator(SecurityUtils.getCurrentUser());
        log.setCreateTime(new Date());
        
        orderLogMapper.insert(log);
    }
}

// ==================== 通知服务 ====================
@Service
@RequiredArgsConstructor
public class NotifyService {
    
    /**
     * 发送订单通知
     * 不使用事务注解,异步处理
     * 原因:通知是辅助功能,不影响主流程
     */
    @Async  // 异步执行
    public void sendOrderNotification(Order order) {
        try {
            // 发送短信
            smsService.send(order.getPhone(), "您的订单已创建成功");
            
            // 发送微信通知
            wechatService.sendTemplateMessage(order.getOpenId(), buildMessage(order));
            
        } catch (Exception e) {
            // 通知失败只记录日志,不抛异常
            log.error("发送通知失败:{}", e.getMessage());
        }
    }
}
```

### 3.4 事务流程图

```
createOrder() 主事务
    │
    ├─ BEGIN TRANSACTION ─────────────────────────────────┐
    │                                                      │
    ├─ 1. 创建订单 ✓                                       │
    │                                                      │
    ├─ 2. 扣减库存 ✓                                       │ 同一事务
    │      └─ REQUIRED → 加入主事务                        │
    │                                                      │
    ├─ 3. 使用优惠券 ✓                                     │
    │      └─ REQUIRED → 加入主事务                        │
    │                                                      │
    ├─ 4. 赠送积分                                         │
    │      └─ NESTED → 创建保存点                          │
    │          ├─ 成功 → 继续                              │
    │          └─ 失败 → 回滚到保存点,主事务继续          │
    │                                                      │
    ├─ 5. 发送通知                                         │
    │      └─ @Async → 异步执行,不在事务中                │
    │                                                      │
    ├─ 6. 记录日志                                         │
    │      └─ REQUIRES_NEW ──────┐                        │
    │          挂起主事务         │ 独立事务               │
    │          保存日志           │                        │
    │          提交              ─┘                        │
    │          恢复主事务                                   │
    │                                                      │
    └─ COMMIT ────────────────────────────────────────────┘
```

---

## 四、事务失效的8种场景

### 4.1 同类中方法自调用(最常见!)

```java
@Service
public class OrderService {
    
    public void createOrder() {
        // 业务逻辑...
        this.sendNotification();  // ❌ 事务不生效!
    }
    
    @Transactional
    public void sendNotification() {
        // 这里的事务不会生效,因为是this调用
    }
}

// ✅ 解决方案1:注入自己
@Service
public class OrderService {
    
    @Autowired
    private OrderService self;
    
    public void createOrder() {
        self.sendNotification();  // ✅ 通过代理调用
    }
}

// ✅ 解决方案2:使用AopContext
@Service
public class OrderService {
    
    public void createOrder() {
        ((OrderService) AopContext.currentProxy()).sendNotification();
    }
}

// ✅ 解决方案3:拆分到不同的类
@Service
public class OrderService {
    @Autowired
    private NotificationService notificationService;
    
    public void createOrder() {
        notificationService.sendNotification();  // ✅
    }
}
```

### 4.2 方法不是public

```java
@Service
public class OrderService {
    
    // ❌ 事务不生效!Spring AOP要求方法必须是public
    @Transactional
    private void createOrder() {
        // ...
    }
    
    // ❌ 事务不生效!
    @Transactional
    protected void createOrder() {
        // ...
    }
    
    // ✅ 正确:public方法
    @Transactional
    public void createOrder() {
        // ...
    }
}
```

### 4.3 异常被catch吃掉

```java
@Service
public class OrderService {
    
    @Transactional
    public void createOrder() {
        try {
            orderMapper.insert(order);
            stockService.deductStock(productId, quantity);
            
            throw new RuntimeException("模拟异常");
            
        } catch (Exception e) {
            // ❌ 异常被吃掉,事务不会回滚!
            log.error("创建订单失败", e);
        }
    }
    
    // ✅ 正确做法1:不catch或重新抛出
    @Transactional
    public void createOrder() {
        try {
            // ...
        } catch (Exception e) {
            log.error("创建订单失败", e);
            throw e;  // ✅ 重新抛出
        }
    }
    
    // ✅ 正确做法2:手动标记回滚
    @Transactional
    public void createOrder() {
        try {
            // ...
        } catch (Exception e) {
            log.error("创建订单失败", e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();  // ✅
        }
    }
}
```

### 4.4 异常类型不对

```java
@Service
public class OrderService {
    
    // ❌ 默认只回滚RuntimeException和Error
    @Transactional
    public void createOrder() throws IOException {
        orderMapper.insert(order);
        
        throw new IOException("文件读取失败");  // ❌ 不会回滚!
    }
    
    // ✅ 正确:指定rollbackFor
    @Transactional(rollbackFor = Exception.class)
    public void createOrder() throws IOException {
        orderMapper.insert(order);
        
        throw new IOException("文件读取失败");  // ✅ 会回滚
    }
}
```

### 4.5 数据库引擎不支持事务

```sql
-- ❌ MyISAM不支持事务
CREATE TABLE orders (
    id INT PRIMARY KEY
) ENGINE=MyISAM;

-- ✅ InnoDB支持事务
CREATE TABLE orders (
    id INT PRIMARY KEY
) ENGINE=InnoDB;
```

### 4.6 没有被Spring管理

```java
// ❌ 没有@Service等注解,不是Spring Bean
public class OrderService {
    
    @Transactional
    public void createOrder() {
        // 事务不生效
    }
}

// ❌ 手动new的对象
OrderService orderService = new OrderService();
orderService.createOrder();  // 事务不生效

// ✅ 正确:通过Spring注入
@Service
public class OrderService {
    @Transactional
    public void createOrder() {
        // 事务生效
    }
}
```

### 4.7 多线程调用

```java
@Service
public class OrderService {
    
    @Transactional
    public void batchCreate(List<Order> orders) {
        // ❌ 多线程中事务不生效!
        orders.parallelStream().forEach(order -> {
            orderMapper.insert(order);  // 每个线程独立,不在事务中
        });
    }
    
    // ✅ 正确:单线程处理
    @Transactional
    public void batchCreate(List<Order> orders) {
        for (Order order : orders) {
            orderMapper.insert(order);
        }
    }
}
```

### 4.8 传播行为配置错误

```java
@Service
public class OrderService {
    
    @Transactional
    public void createOrder() {
        stockService.deductStock(productId, quantity);
    }
}

@Service
public class StockService {
    
    // ❌ NOT_SUPPORTED会挂起外部事务,导致库存操作不在事务中
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void deductStock(Long productId, Integer quantity) {
        stockMapper.deduct(productId, quantity);
    }
}
```

---

## 五、面试高频问题

### Q1:Spring事务的传播行为有哪些?默认是哪个?

**答**:
Spring定义了7种传播行为:
1. **REQUIRED**(默认):有事务加入,没有新建
2. **SUPPORTS**:有事务加入,没有非事务运行
3. **MANDATORY**:必须在事务中,否则抛异常
4. **REQUIRES_NEW**:总是新建事务,挂起当前事务
5. **NOT_SUPPORTED**:非事务运行,挂起当前事务
6. **NEVER**:非事务运行,有事务抛异常
7. **NESTED**:嵌套事务,使用保存点

默认是 **REQUIRED**。

---

### Q2:REQUIRED和REQUIRES_NEW的区别?

**答**:

| 对比项 | REQUIRED | REQUIRES_NEW |
|--------|----------|--------------|
| 外部有事务 | 加入外部事务 | 挂起外部,新建事务 |
| 外部回滚 | 一起回滚 | 不受影响 |
| 内部回滚 | 外部也回滚 | 不影响外部 |
| 使用场景 | 业务一致性 | 独立提交(如日志) |

**代码示例**:
```java
// REQUIRED:A回滚,B也回滚
@Transactional
void methodA() {
    methodB();  // REQUIRED
    throw new RuntimeException();  // A和B都回滚
}

// REQUIRES_NEW:A回滚,B不回滚
@Transactional
void methodA() {
    methodB();  // REQUIRES_NEW,已独立提交
    throw new RuntimeException();  // 只有A回滚,B已提交
}
```

---

### Q3:NESTED和REQUIRES_NEW的区别?

**答**:

| 对比项 | NESTED | REQUIRES_NEW |
|--------|--------|--------------|
| 本质 | 保存点,同一事务 | 独立事务 |
| 数据库连接 | 共用 | 新建 |
| 外部回滚 | 子事务也回滚 | 子事务不受影响 |
| 子事务回滚 | 可只回滚子事务 | 不影响外部 |
| 性能 | 较好 | 较差(新连接) |

**选择建议**:
- 需要部分失败可接受 → NESTED
- 必须独立提交,不管外部成功失败 → REQUIRES_NEW

---

### Q4:事务失效的场景有哪些?

**答**:
1. **同类方法自调用**:this调用绕过代理
2. **方法非public**:AOP要求public
3. **异常被catch**:没有抛出或没有手动回滚
4. **异常类型不匹配**:默认只回滚RuntimeException
5. **数据库引擎不支持**:MyISAM不支持事务
6. **没有被Spring管理**:不是Spring Bean
7. **多线程调用**:事务绑定在当前线程
8. **传播行为配置错误**

---

### Q5:@Transactional注解可以用在哪里?

**答**:
1. **类上**:对所有public方法生效
2. **方法上**:只对该方法生效
3. **接口上**:不推荐,可能失效

**优先级**:方法级 > 类级

```java
@Transactional(readOnly = true)  // 类级:只读
@Service
public class OrderService {
    
    // 继承类级配置,只读
    public Order getOrder(Long id) { }
    
    @Transactional  // 方法级覆盖,可读写
    public void createOrder(Order order) { }
}
```

---

### Q6:如何保证分布式事务?

**答**:
Spring的@Transactional只能保证单数据源的本地事务。分布式事务需要:

1. **2PC(两阶段提交)**:XA协议,强一致性
2. **TCC(Try-Confirm-Cancel)**:业务侵入大
3. **SAGA**:补偿事务
4. **消息最终一致性**:RocketMQ事务消息
5. **Seata**:阿里开源的分布式事务框架

```java
// 使用Seata的全局事务
@GlobalTransactional
public void createOrder() {
    orderService.create();      // 订单服务
    stockService.deduct();      // 库存服务(可能是另一个数据库)
    accountService.deduct();    // 账户服务
}
```

---

### Q7:@Transactional的实现原理?

**答**:
基于**Spring AOP**和**动态代理**实现:

1. Spring启动时扫描@Transactional注解
2. 为目标类创建代理对象(JDK动态代理或CGLIB)
3. 调用方法时,代理拦截
4. 代理中通过TransactionManager管理事务
5. 方法执行前开启事务
6. 方法正常结束提交事务
7. 方法抛异常回滚事务

```
调用流程:
Client → Proxy → TransactionInterceptor → 开启事务 → 目标方法 → 提交/回滚
```

---

### Q8:readOnly = true有什么用?

**答**:
1. **性能优化**:数据库可能会优化只读事务
2. **防止误操作**:执行写操作会报错
3. **主从分离**:配合框架路由到从库

```java
@Transactional(readOnly = true)
public List<Order> listOrders() {
    return orderMapper.selectList(null);  // ✅ 查询
    // orderMapper.insert(order);  // ❌ 会报错
}
```

---

## 六、最佳实践总结

### 6.1 事务注解规范

```java
// ✅ 最佳实践模板
@Transactional(
    propagation = Propagation.REQUIRED,  // 明确指定传播行为
    rollbackFor = Exception.class,        // 所有异常都回滚
    timeout = 30                          // 设置超时时间
)
public void businessMethod() {
    // 业务逻辑
}
```

### 6.2 事务粒度控制

```java
// ❌ 错误:事务粒度太大
@Transactional
public void bigMethod() {
    // 查询(不需要事务)
    List<Order> orders = orderMapper.selectList();
    
    // 调用外部服务(可能很慢)
    httpClient.callExternalApi();
    
    // 实际需要事务的操作
    orderMapper.update(order);
}

// ✅ 正确:只给需要的部分加事务
public void bigMethod() {
    // 查询(不需要事务)
    List<Order> orders = orderMapper.selectList();
    
    // 调用外部服务
    httpClient.callExternalApi();
    
    // 实际需要事务的操作
    updateOrderWithTransaction(order);
}

@Transactional
public void updateOrderWithTransaction(Order order) {
    orderMapper.update(order);
}
```

### 6.3 异常处理规范

```java
// ✅ 推荐的异常处理方式
@Transactional(rollbackFor = Exception.class)
public void createOrder(Order order) {
    try {
        orderMapper.insert(order);
        stockService.deductStock(order.getProductId(), order.getQuantity());
        
    } catch (BusinessException e) {
        // 业务异常:直接抛出,事务回滚
        throw e;
        
    } catch (Exception e) {
        // 系统异常:记录日志后抛出
        log.error("创建订单异常", e);
        throw new SystemException("系统繁忙,请稍后重试", e);
    }
}
```

### 6.4 选择传播行为的决策树

```
需要事务吗?
├─ 不需要
│   └─ 外部有事务时需要挂起吗?
│       ├─ 需要 → NOT_SUPPORTED
│       └─ 不需要 → SUPPORTS
│
└─ 需要
    └─ 需要独立于外部事务吗?
        ├─ 需要(无论如何都要提交)
        │   └─ REQUIRES_NEW
        │
        └─ 不需要(和外部同生共死)
            └─ 外部必须有事务吗?
                ├─ 必须 → MANDATORY
                └─ 不必须
                    └─ 失败时只回滚自己吗?
                        ├─ 是 → NESTED
                        └─ 否 → REQUIRED(默认)
```

### 6.5 常用组合场景

| 场景 | 推荐传播行为 | 原因 |
|------|-------------|------|
| 核心业务操作 | REQUIRED | 保证一致性 |
| 操作日志记录 | REQUIRES_NEW | 主事务失败也要记录 |
| 附属功能(积分等) | NESTED | 失败不影响主流程 |
| 只读查询 | SUPPORTS + readOnly | 灵活 + 优化 |
| 发送通知 | @Async(异步) | 不阻塞主流程 |
| 金融转账 | MANDATORY | 强制要求调用方有事务 |

---

## 📝 面试答题模板

当面试官问到事务相关问题时,可以用这个结构回答:

```
1. 先解释概念(是什么)
2. 说明使用场景(什么时候用)
3. 给出代码示例(怎么用)
4. 补充注意事项(踩坑点)
5. 结合实际经验(加分项)
```

**示例回答**:
> 问:说说REQUIRES_NEW的作用?
> 
> 答:REQUIRES_NEW表示总是新建一个独立事务,如果当前存在事务会先挂起。
> 
> 在实际项目中,我们主要用在**操作日志记录**场景。比如下单流程中,即使订单创建失败回滚了,操作日志也要保留用于审计。
> 
> 需要注意的是,REQUIRES_NEW会创建新的数据库连接,如果使用过多会影响性能。另外,同类中自调用不会生效,需要通过代理调用。

---

**文档版本**:v1.0  
**创建时间**:2026-01-19  
**适用场景**:Java后端面试、企业级开发


评论