# 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后端面试、企业级开发