一、事务传播行为概览
1. 7种传播行为总览
java
复制
下载
// Propagation枚举定义
public enum Propagation {
REQUIRED(0), // 支持当前事务,不存在则新建(默认)
SUPPORTS(1), // 支持当前事务,不存在则以非事务运行
MANDATORY(2), // 支持当前事务,不存在则抛出异常
REQUIRES_NEW(3), // 新建事务,当前事务挂起
NOT_SUPPORTED(4), // 非事务执行,当前事务挂起
NEVER(5), // 非事务执行,存在事务则抛出异常
NESTED(6); // 嵌套事务,不存在则新建
private final int value;
}
2. 传播行为决策矩阵
text
复制
下载
┌─────────────────────────────────────────────────────┐
│ 当前是否存在事务? │ 传播行为 │ 结果 │
├───────────────────┼───────────────┼─────────────────┤
│ 无 │ REQUIRED │ 创建新事务 │
│ 有 │ REQUIRED │ 加入当前事务 │
│ 无 │ SUPPORTS │ 无事务运行 │
│ 有 │ SUPPORTS │ 加入当前事务 │
│ 无 │ MANDATORY │ 抛出异常 │
│ 有 │ MANDATORY │ 加入当前事务 │
│ 无 │ REQUIRES_NEW │ 创建新事务 │
│ 有 │ REQUIRES_NEW │ 挂起并新建 │
│ 无 │ NOT_SUPPORTED │ 无事务运行 │
│ 有 │ NOT_SUPPORTED │ 挂起并无事务 │
│ 无 │ NEVER │ 无事务运行 │
│ 有 │ NEVER │ 抛出异常 │
│ 无 │ NESTED │ 创建新事务 │
│ 有 │ NESTED │ 嵌套事务 │
└───────────────────┴───────────────┴─────────────────┘
二、每种传播行为深度解析
1. REQUIRED(默认传播行为)
场景分析
java
复制
下载
@Service
public class OrderService {
@Transactional(propagation = Propagation.REQUIRED)
public void createOrder(Order order) {
// 业务逻辑1
orderRepository.save(order);
// 调用其他事务方法
inventoryService.deductStock(order); // REQUIRED
couponService.useCoupon(order); // REQUIRED
// 异常模拟
if (order.getAmount() > 10000) {
throw new RuntimeException("金额过大");
}
}
}
@Service
public class InventoryService {
@Transactional(propagation = Propagation.REQUIRED)
public void deductStock(Order order) {
// 减库存逻辑
inventoryRepository.updateStock(order);
}
}
// 执行流程:
// 1. OrderService.createOrder()开启事务T1
// 2. InventoryService.deductStock()加入T1事务
// 3. 如果InventoryService抛出异常,整个T1回滚
// 4. OrderService.createOrder()抛出异常,T1回滚
关键特性
java
复制
下载
// 1. 事务共享
// 所有REQUIRED方法共享同一个物理连接
Connection conn = DataSourceUtils.getConnection(dataSource);
// 2. 异常传播
// 内层方法异常会传播到外层,导致整个事务回滚
try {
methodA(); // REQUIRED
} catch (Exception e) {
// methodA异常会导致外层事务回滚
transactionManager.rollback(status);
}
// 3. 使用建议
// 适用于业务逻辑紧密关联的场景
// 如:订单创建 → 减库存 → 用优惠券
2. REQUIRES_NEW(新建独立事务)
场景分析
java
复制
下载
@Service
public class OrderService {
@Transactional(propagation = Propagation.REQUIRED)
public void processOrder(Order order) {
// 主事务逻辑
orderRepository.save(order); // T1事务
try {
// 记录操作日志(独立事务)
logService.addOperationLog(order); // REQUIRES_NEW
} catch (Exception e) {
// 日志失败不影响主事务
logger.error("记录日志失败", e);
}
// 发送消息(独立事务)
messageService.sendOrderMessage(order); // REQUIRES_NEW
}
}
@Service
public class LogService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addOperationLog(Order order) {
// 始终在新事务中执行
operationLogRepository.save(buildLog(order));
// 即使抛出异常,也不会影响外层事务
if (System.currentTimeMillis() % 100 == 0) {
throw new RuntimeException("随机日志失败");
}
}
}
// 执行流程:
// 1. OrderService.processOrder()开启事务T1
// 2. LogService.addOperationLog()挂起T1,开启新事务T2
// 3. T2提交或回滚后,恢复T1
// 4. T2的回滚不会影响T1
关键特性
java
复制
下载
// 1. 事务隔离
// 每个REQUIRES_NEW方法有独立的事务和连接
// 物理上:每个事务使用不同的数据库连接
// 2. 事务挂起与恢复
TransactionStatus suspended = transactionManager.suspend(existing);
try {
// 新事务逻辑
TransactionStatus newStatus = transactionManager.begin(newDefinition);
transactionManager.commit(newStatus);
} finally {
transactionManager.resume(suspended);
}
// 3. 使用场景
// 日志记录、消息发送、异步任务提交
// 需要独立提交,失败不影响主业务的场景
3. NESTED(嵌套事务)
场景分析
java
复制
下载
@Service
public class OrderService {
@Transactional(propagation = Propagation.REQUIRED)
public void batchCreateOrders(List<Order> orders) {
for (Order order : orders) {
try {
// 嵌套事务处理单个订单
createSingleOrder(order); // NESTED
} catch (Exception e) {
// 单个订单失败不影响其他订单
logger.error("订单创建失败: {}", order.getId(), e);
}
}
}
@Transactional(propagation = Propagation.NESTED)
public void createSingleOrder(Order order) {
orderRepository.save(order);
inventoryService.deductStock(order); // REQUIRED
// 模拟业务异常
if (order.getUserId() == null) {
throw new RuntimeException("用户ID不能为空");
}
}
}
// 执行流程(Savepoint机制):
// 1. 外层事务T1开始
// 2. 每个createSingleOrder()设置保存点
// 3. 内层方法异常 → 回滚到保存点
// 4. 外层事务继续执行后续订单
// 5. 最终T1提交或回滚
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
关键特性
java
复制
下载
// 1. 保存点机制
// 基于JDBC的Savepoint实现
Savepoint savepoint = connection.setSavepoint("nested_tx");
try {
// 嵌套事务逻辑
executeNestedOperation();
connection.releaseSavepoint(savepoint);
} catch (Exception e) {
connection.rollback(savepoint); // 回滚到保存点
throw e;
}
// 2. 部分提交
// 内层事务的提交是"部分提交"
// 最终提交依赖外层事务
// 3. 支持数据库
// MySQL的InnoDB支持保存点
// 部分数据库可能不支持NESTED传播行为
// 4. 与REQUIRES_NEW的区别
// NESTED: 共享连接,使用保存点
// REQUIRES_NEW: 新连接,完全独立
4. SUPPORTS(支持当前事务)
场景分析
java
复制
下载
@Service
public class QueryService {
// 查询服务通常不需要事务,但如果有事务就加入
@Transactional(propagation = Propagation.SUPPORTS)
public OrderDTO getOrderDetail(Long orderId) {
// 复杂查询逻辑
Order order = orderRepository.findById(orderId);
User user = userRepository.findById(order.getUserId());
return assembleOrderDTO(order, user);
}
}
@Service
public class OrderService {
@Transactional(propagation = Propagation.REQUIRED)
public void updateOrder(Order order) {
// 更新前查询(加入当前事务)
OrderDTO detail = queryService.getOrderDetail(order.getId());
// 基于查询结果更新
orderRepository.update(order);
// 更新后查询(同一事务,看到更新结果)
OrderDTO updated = queryService.getOrderDetail(order.getId());
}
}
// 行为特点:
// 1. 在OrderService.updateOrder()中调用getOrderDetail()
// → 加入REQUIRED事务,看到未提交的更新
// 2. 单独调用getOrderDetail()
// → 无事务运行,查询已提交的数据
5. MANDATORY(强制要求事务)
场景分析
java
复制
下载
@Service
public class AuditService {
// 审计操作必须在一个事务中执行
@Transactional(propagation = Propagation.MANDATORY)
public void auditOperation(OperationLog log) {
// 审计逻辑必须看到完整的事务视图
auditRepository.save(log);
}
}
@Service
public class UserService {
// 正确的调用方式
@Transactional(propagation = Propagation.REQUIRED)
public void updateUser(User user) {
userRepository.update(user);
// 在事务内调用审计
auditService.auditOperation(buildAuditLog(user));
}
// 错误的调用方式
public void getUserInfo(Long userId) {
User user = userRepository.findById(userId);
// 这里没有事务,会抛出异常!
auditService.auditOperation(buildAuditLog(user));
// 抛出IllegalTransactionStateException
}
}
设计意图
java
复制
下载
// 1. 强制事务上下文
// 确保某些关键操作必须在事务保护下执行
// 2. 设计约束
// 防止开发人员无意中在非事务环境下调用关键方法
// 3. 使用场景
// 审计日志、重要数据修改、资金操作
6. NOT_SUPPORTED(非事务执行)
场景分析
java
复制
下载
@Service
public class FileService {
// 文件操作通常不需要事务
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void uploadFile(MultipartFile file) {
// 文件存储逻辑(非事务)
String path = fileStorage.save(file);
// 记录元数据(可能需要事务)
fileMetadataService.saveMetadata(path, file.getSize());
}
}
@Service
public class TransactionalService {
@Transactional(propagation = Propagation.REQUIRED)
public void businessProcess() {
// 主事务逻辑
businessRepository.save(data);
// 调用非事务方法(当前事务被挂起)
fileService.uploadFile(file); // NOT_SUPPORTED
// 恢复事务,继续执行
businessRepository.update(data);
}
}
// 关键点:
// 1. 当前有事务 → 挂起事务,非事务执行
// 2. 当前无事务 → 直接非事务执行
// 3. 挂起的事务在方法执行完后恢复
适用场景
java
复制
下载
// 1. 耗时操作
// 避免长时间占用数据库连接
public void generateReport() { // NOT_SUPPORTED
// 复杂计算,耗时较长
Report report = computeComplexReport();
// 不占用事务连接
}
// 2. 外部系统调用
public void callExternalAPI() { // NOT_SUPPORTED
// HTTP调用外部服务
ResponseEntity response = restTemplate.post(...);
// 不受事务管理,失败不回滚
}
// 3. 文件/IO操作
public void processFile() { // NOT_SUPPORTED
// 读写文件系统
Files.copy(source, target);
// 文件操作不支持事务回滚
}
7. NEVER(禁止事务)
场景分析
java
复制
下载
@Service
public class CacheService {
// 缓存操作禁止在事务中执行
@Transactional(propagation = Propagation.NEVER)
public void updateCache(String key, Object value) {
// 缓存更新应该立即生效
redisTemplate.opsForValue().set(key, value);
// 如果在事务中执行,缓存更新可能延迟
// 导致缓存与数据库不一致
}
}
@Service
public class UserService {
@Transactional(propagation = Propagation.REQUIRED)
public void updateUserWithCache(User user) {
// 更新数据库
userRepository.update(user);
// 错误:在事务中更新缓存
// cacheService.updateCache("user:"+user.getId(), user);
// 抛出IllegalTransactionStateException
}
public void updateUserWithoutCache(User user) {
// 更新数据库(无事务)
userRepository.update(user);
// 正确:无事务时更新缓存
cacheService.updateCache("user:"+user.getId(), user);
}
}
设计哲学
java
复制
下载
// 1. 强制分离
// 明确区分事务性操作和非事务性操作
// 2. 防止误用
// 避免在事务中执行不应该在事务中的操作
// 3. 性能考虑
// 确保某些操作不会受到事务开销影响
// 4. 一致性保证
// 防止缓存/外部系统与数据库状态不一致
三、实战场景与组合使用
场景1:订单支付流程
java
复制
下载
@Service
public class PaymentService {
@Transactional(propagation = Propagation.REQUIRED)
public PaymentResult payOrder(PaymentRequest request) {
// 1. 检查订单状态(加入事务)
Order order = orderService.getOrder(request.getOrderId());
// 2. 扣款(独立事务,支付必须提交)
Payment payment = paymentGateway.deduct(request); // REQUIRES_NEW
// 3. 更新订单状态(当前事务)
orderService.updateOrderStatus(order.getId(), OrderStatus.PAID);
// 4. 记录支付日志(独立事务,失败不影响)
try {
paymentLogService.logPayment(payment); // REQUIRES_NEW
} catch (Exception e) {
logger.error("支付日志记录失败", e);
}
// 5. 发送支付成功消息(独立事务)
messageService.sendPaymentSuccess(payment); // REQUIRES_NEW
return buildResult(payment);
}
}
@Service
public class PaymentGatewayService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Payment deduct(PaymentRequest request) {
// 第三方支付调用
ThirdPartyResponse response = callPaymentAPI(request);
// 本地支付记录
Payment payment = savePaymentRecord(response);
// 支付必须独立提交,即使后续步骤失败
return payment;
}
}
场景2:批量数据处理
java
复制
下载
@Service
public class BatchDataProcessor {
@Transactional(propagation = Propagation.REQUIRED)
public void processBatch(List<DataItem> items) {
int successCount = 0;
int failCount = 0;
for (DataItem item : items) {
try {
// 每个item独立处理,失败不影响其他
processSingleItem(item); // NESTED
successCount++;
} catch (Exception e) {
failCount++;
errorHandler.handleError(item, e);
}
}
// 记录处理统计(独立事务)
statisticService.recordProcessResult(
successCount, failCount); // REQUIRES_NEW
// 如果整体失败率过高,回滚整个批处理
if (failCount > items.size() * 0.3) {
throw new BatchProcessException("失败率过高");
}
}
@Transactional(propagation = Propagation.NESTED)
public void processSingleItem(DataItem item) {
// 复杂的数据处理逻辑
validate(item);
transform(item);
persist(item);
}
}
场景3:缓存与数据库同步
java
复制
下载
@Service
public class UserService {
// 主业务方法 - 使用事务
@Transactional(propagation = Propagation.REQUIRED)
public User updateUser(User user) {
// 1. 更新数据库
User updated = userRepository.save(user);
// 2. 更新缓存(必须无事务)
// 使用编程式事务管理
TransactionTemplate transactionTemplate =
new TransactionTemplate(transactionManager);
transactionTemplate.setPropagationBehavior(
Propagation.NOT_SUPPORTED.value());
transactionTemplate.execute(status -> {
cacheService.updateUserCache(updated);
return null;
});
return updated;
}
// 查询方法 - 根据情况决定是否使用事务
@Transactional(propagation = Propagation.SUPPORTS)
public User getUser(Long userId) {
// 先查缓存
User cached = cacheService.getUserFromCache(userId);
if (cached != null) {
return cached;
}
// 查数据库
User user = userRepository.findById(userId);
// 异步更新缓存(无事务)
cacheService.asyncUpdateCache(userId, user);
return user;
}
}
四、源码实现解析
1. 传播行为处理入口
java
复制
下载
// AbstractPlatformTransactionManager
public final TransactionStatus getTransaction(
@Nullable TransactionDefinition definition) {
// 获取当前事务
Object transaction = doGetTransaction();
// 检查当前是否存在事务
boolean existing = isExistingTransaction(transaction);
// 根据传播行为处理
if (definition.getPropagationBehavior() ==
TransactionDefinition.PROPAGATION_REQUIRED) {
if (existing) {
// 存在事务 → 加入
return handleExistingTransaction(definition, transaction, existing);
}
// 不存在事务 → 新建
return startTransaction(definition, transaction, debugEnabled, null);
}
if (definition.getPropagationBehavior() ==
TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
// 挂起当前事务(如果存在)
SuspendedResourcesHolder suspended = null;
if (existing) {
suspended = suspend(transaction);
}
// 创建新事务
return startTransaction(definition, transaction, debugEnabled, suspended);
}
// ... 其他传播行为的处理
}
2. REQUIRES_NEW实现细节
java
复制
下载
// DataSourceTransactionManager
protected Object doSuspend(Object transaction) {
DataSourceTransactionObject txObject =
(DataSourceTransactionObject) transaction;
// 释放当前连接
txObject.setConnectionHolder(null);
// 从ThreadLocal中解绑连接
return TransactionSynchronizationManager.unbindResource(this.dataSource);
}
protected void doResume(@Nullable Object transaction,
Object suspendedResources) {
// 重新绑定连接到ThreadLocal
TransactionSynchronizationManager.bindResource(
this.dataSource, suspendedResources);
}
3. NESTED实现细节
java
复制
下载
// DataSourceTransactionManager (JDBC实现)
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject =
(DataSourceTransactionObject) transaction;
Connection con = txObject.getConnectionHolder().getConnection();
if (definition.getPropagationBehavior() ==
TransactionDefinition.PROPAGATION_NESTED) {
// 设置保存点
if (this.savepointAllowed) {
// 使用JDBC保存点
Savepoint savepoint = con.setSavepoint();
txObject.setSavepoint(savepoint);
} else {
// 不支持保存点则使用新事务(REQUIRES_NEW)
// 实际上Spring会降级处理
}
}
// ... 其他初始化逻辑
}
五、常见陷阱与最佳实践
⚠️ 常见陷阱
陷阱1:REQUIRES_NEW导致死锁
java
复制
下载
// 错误示例
@Service
public class AccountService {
@Transactional(propagation = Propagation.REQUIRED)
public void transfer(Account from, Account to, BigDecimal amount) {
// 先扣款
debit(from, amount); // REQUIRES_NEW
// 再存款(等待扣款事务提交)
credit(to, amount); // REQUIRES_NEW
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void debit(Account account, BigDecimal amount) {
// 持有账户A锁
account.setBalance(account.getBalance().subtract(amount));
accountRepository.save(account);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void credit(Account account, BigDecimal amount) {
// 等待账户B锁(可能被其他事务持有)
account.setBalance(account.getBalance().add(amount));
accountRepository.save(account);
}
}
// 可能产生死锁:多个转账同时进行时
陷阱2:NESTED不支持时降级问题
java
复制
下载
// 数据库不支持保存点时
@Service
public class BatchService {
@Transactional
public void batchProcess() {
for (Data data : dataList) {
// 如果数据库不支持NESTED,Spring会使用REQUIRED
// 导致第一个失败后整个batch回滚
processItem(data); // 期望NESTED
}
}
@Transactional(propagation = Propagation.NESTED)
public void processItem(Data data) {
// 处理逻辑
}
}
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
✅ 最佳实践
实践1:合理选择传播行为
java
复制
下载
// 选择指南
public class PropagationSelectionGuide {
// 1. 默认使用REQUIRED
// 大多数业务方法
@Transactional // 默认就是REQUIRED
public void businessMethod() {}
// 2. 独立操作使用REQUIRES_NEW
// 日志、消息、外部调用
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void independentOperation() {}
// 3. 查询服务使用SUPPORTS
// 复杂查询,可加入事务也可不加入
@Transactional(propagation = Propagation.SUPPORTS)
public QueryResult complexQuery() {}
// 4. 必须事务时使用MANDATORY
// 审计、关键数据修改
@Transactional(propagation = Propagation.MANDATORY)
public void criticalOperation() {}
// 5. 禁止事务使用NEVER
// 缓存操作、文件操作
@Transactional(propagation = Propagation.NEVER)
public void nonTransactionalOp() {}
}
实践2:传播行为与隔离级别配合
java
复制
下载
@Service
public class FinancialService {
// 财务操作:需要强一致性
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.SERIALIZABLE // 最高隔离级别
)
public void financialTransfer() {
// 资金转移逻辑
}
// 日志记录:可以接受脏读
@Transactional(
propagation = Propagation.REQUIRES_NEW,
isolation = Isolation.READ_COMMITTED // 较低隔离级别
)
public void auditLog() {
// 审计日志记录
}
// 统计查询:可重复读
@Transactional(
propagation = Propagation.SUPPORTS,
isolation = Isolation.REPEATABLE_READ
)
public Statistics generateReport() {
// 报表生成
}
}
六、面试深度问题
1. 源码级问题
java
复制
下载
// Q1: Spring如何实现事务传播?
// A: 通过AbstractPlatformTransactionManager的getTransaction方法,
// 根据当前事务状态和传播行为决定是加入、新建还是挂起事务。
// Q2: REQUIRES_NEW是如何挂起和恢复事务的?
// A: 1. suspend()方法:解绑当前线程的资源(如数据库连接)
// 2. 开始新事务,使用新的连接
// 3. resume()方法:重新绑定之前挂起的资源
// Q3: NESTED在MySQL和Oracle中的实现差异?
// A: MySQL(InnoDB): 使用SAVEPOINT实现
// Oracle: 原生支持嵌套事务
// 不支持SAVEPOINT的数据库会降级为REQUIRED
2. 场景设计问题
java
复制
下载
// Q1: 设计一个需要部分提交的批量处理系统
// 要求:单个失败不影响整体,但失败过多需要回滚
// 解决方案:
@Transactional
public void batchProcess() {
for (Item item : items) {
try {
// 使用NESTED实现部分提交
processItemWithSavepoint(item);
} catch (Exception e) {
handleError(item, e);
}
}
}
@Transactional(propagation = Propagation.NESTED)
public void processItemWithSavepoint(Item item) {
// 业务逻辑
}
3. 性能优化问题
java
复制
下载
// Q1: 大量使用REQUIRES_NEW有什么性能影响?
// A: 1. 频繁创建/提交事务开销
// 2. 数据库连接数增加
// 3. 锁竞争可能加剧
// 优化:批量操作、连接池调优
// Q2: 如何监控事务传播行为的影响?
// A: 1. 使用Spring的TransactionSynchronization
// 2. 监控事务创建/提交/回滚次数
// 3. 跟踪事务执行时间
// 4. 分析死锁和锁等待
七、总结对比表
传播行为对比矩阵
| 传播行为 | 存在事务 | 不存在事务 | 异常回滚影响 | 适用场景 |
|---|---|---|---|---|
| REQUIRED | 加入 | 新建 | 全部回滚 | 默认选择,业务主流程 |
| SUPPORTS | 加入 | 非事务 | 看情况 | 查询服务,可事务可非事务 |
| MANDATORY | 加入 | 抛异常 | 全部回滚 | 必须事务的操作(审计) |
| REQUIRES_NEW | 挂起后新建 | 新建 | 独立回滚 | 独立操作(日志、消息) |
| NOT_SUPPORTED | 挂起后非事务 | 非事务 | 不影响事务 | 非事务操作(文件、缓存) |
| NEVER | 抛异常 | 非事务 | 无影响 | 禁止事务(缓存更新) |
| NESTED | 嵌套事务 | 新建 | 部分回滚 | 批量处理,部分提交 |
选择决策树
text
复制
下载
需要事务吗?
├── 是 → 当前有事务吗?
│ ├── 是 → 需要独立提交吗?
│ │ ├── 是 → REQUIRES_NEW(完全独立)
│ │ └── 否 → 需要部分提交吗?
│ │ ├── 是 → NESTED(支持保存点)
│ │ └── 否 → REQUIRED(默认加入)
│ └── 否 → REQUIRED(新建事务)
└── 否 → 禁止事务吗?
├── 是 → NEVER(强制非事务)
└── 否 → 有事务就加入吗?
├── 是 → SUPPORTS(支持事务)
└── 否 → NOT_SUPPORTED(非事务)
记住 :传播行为的选择本质是事务边界和异常处理策略的设计。理解每种行为背后的设计意图,才能在实际业务中做出合理的选择。