欢迎光临
我们一直在努力

Spring 传播行为(Propagation)

Spring 事务传播行为(Propagation)

一、概述

Spring 事务传播行为(Propagation)是指当一个事务方法被另一个事务方法调用时,当前方法应该如何执行事务的规则。Spring 在 Propagation 枚举中定义了 7 种传播行为。

二、传播行为一览表

传播行为说明是否有事务?
REQUIRED (默认) 如果当前有事务则加入,否则新建一个 ✅ 一定有
SUPPORTS 当前有事务则加入,否则以无事务方式执行 ❓ 可有可无
MANDATORY 必须已有事务,否则抛异常 ✅ 必须存在外部事务
REQUIRES_NEW 总是新建事务,如有当前事务则挂起 ✅ 一定新建
NOT_SUPPORTED 总以无事务方式执行,如有当前事务则挂起 ❌ 无事务
NEVER 总以无事务方式执行,如有当前事务则抛异常 ❌ 必须无事务
NESTED 在有事务的情况下创建嵌套事务(Savepoint) 有/嵌套

三、详细说明及代码示例

3.1 REQUIRED(默认的,最常用)

@Service
public class UserService {

@Autowired
private UserDao userDao;

@Autowired
private LogService logService;

@Transactional(propagation = Propagation.REQUIRED)
public void createUser(User user) {
userDao.insert(user);
// 调用其他事务方法
logService.log("User created: " + user.getName());
}
}

@Service
public class LogService {

@Transactional(propagation = Propagation.REQUIRED)
public void log(String message) {
// 这里会加入 createUser 方法的事务
System.out.println("Log: " + message);
}
}

特点: 如果当前存在事务,则加入该事务 如果当前没有事务,则创建新事务 异常回滚:所有操作在同一个事务中,任何异常都会导致全部回滚

3.2 SUPPORTS

@Service
public class OrderService {

@Autowired
private PaymentService paymentService;

@Transactional(propagation = Propagation.REQUIRED)
public void processOrder(Order order) {
// 有事务环境
paymentService.recordPayment(order);
// 其他操作…
}

@Transactional(propagation = Propagation.SUPPORTS)
public void queryOrder(Long id) {
// 如果没有事务,则非事务方式执行
return orderDao.findById(id);
}
}

@Service
public class PaymentService {

@Transactional(propagation = Propagation.SUPPORTS)
public void recordPayment(Order order) {
// 如果调用方有事务,则加入;否则非事务方式执行
paymentDao.insert(order.getPayment());
}
}

特点: 有事务则加入,无事务则以非事务方式执行 适用于只读查询操作 适用场景: 记录操作日志(日志失败不应影响主业务),发送消息通知,审计操作

3.3 MANDATORY

@Service
public class AccountService {

@Transactional(propagation = Propagation.MANDATORY)
public void updateBalance(Long accountId, BigDecimal amount) {
// 必须在事务中执行
accountDao.updateBalance(accountId, amount);
}

@Transactional(propagation = Propagation.REQUIRED)
public void transfer(Long fromId, Long toId, BigDecimal amount) {
// 正确调用:有事务环境
updateBalance(fromId, amount.negate());
updateBalance(toId, amount);
}

public void illegalCall() {
// 错误调用:没有事务环境,会抛出 IllegalTransactionStateException
updateBalance(1L, BigDecimal.TEN);
}
}

特点: 必须运行在已有事务中 如果没有事务则抛出 IllegalTransactionStateException 适用于强制要求在事务中执行的操作

3.4 REQUIRES_NEW

@Service
public class AuditService {

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void audit(Action action) {
// 总是创建新事务执行
auditDao.insert(action);
// 即使外部事务回滚,这里的操作也会提交
}
}

@Service
public class BusinessService {

@Autowired
private AuditService auditService;

@Transactional(propagation = Propagation.REQUIRED)
public void doBusiness() {
try {
// 业务操作
businessDao.update();
// 审计操作在新事务中
auditService.audit(new Action("业务操作成功"));
} catch (Exception e) {
// 审计操作不会因为业务异常而回滚
auditService.audit(new Action("业务操作失败:" + e.getMessage()));
}
}
}

特点: 总是启动一个新事务 如果当前存在事务,则挂起当前事务 内外事务独立:外部事务回滚不影响内部事务提交 适用于日志记录、审计等独立操作

3.5 NOT_SUPPORTED

@Service
public class ReportService {

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void generateLargeReport() {
// 非事务方式执行,避免长时间持有数据库连接
// 适合大数据量查询操作
List<Data> data = reportDao.queryLargeDataSet();
generateReport(data);
}
}

@Service
public class DataService {

@Transactional(propagation = Propagation.REQUIRED)
public void updateData() {
// 更新操作
dataDao.update();

// 调用非事务方法
reportService.generateLargeReport();

// 继续其他操作
dataDao.updateStatus();
}
}

特点: 以非事务方式执行 如果当前存在事务,则挂起事务 适用于不需要事务的操作(如报表生成、数据导出)

3.6 NEVER

@Service
public class BatchService {

@Transactional(propagation = Propagation.NEVER)
public void batchProcess(List<Item> items) {
// 绝对不能在事务中执行
for (Item item : items) {
batchDao.process(item);
}
}
}

@Service
public class MainService {

@Transactional(propagation = Propagation.REQUIRED)
public void doWork() {
// 错误:会在事务中调用 batchProcess,抛出异常
batchService.batchProcess(items);
}

public void doWorkCorrect() {
// 正确:非事务环境调用
batchService.batchProcess(items);
}
}

特点: 以非事务方式执行 如果当前存在事务则抛出 IllegalTransactionStateException 适用于绝对不能运行在事务中的操作

3.7 NESTED

@Service
public class StockService {

@Transactional(propagation = Propagation.NESTED)
public void deductStock(Long productId, Integer quantity) {
// 嵌套事务
Stock stock = stockDao.selectForUpdate(productId);
if (stock.getQuantity() < quantity) {
throw new InsufficientStockException();
}
stockDao.updateQuantity(productId, stock.getQuantity() quantity);
}
}

@Service
public class OrderService {

@Autowired
private StockService stockService;

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void createOrder(Order order) {
try {
// 保存订单
orderDao.insert(order);

// 扣减库存(嵌套事务)
stockService.deductStock(order.getProductId(), order.getQuantity());

// 其他操作…

} catch (InsufficientStockException e) {
// 库存不足,订单创建失败,但订单信息已经保存?
// 等等,由于 NESTED 特性,库存异常不会影响订单保存
// 需要手动处理
order.setStatus("FAILED");
orderDao.update(order);
}
}
}

特点: 在存在事务时,作为嵌套事务执行 嵌套事务的独立回滚不影响外部事务 外部事务回滚会导致嵌套事务一起回滚 支持保存点(Savepoint)机制 要求底层支持 JDBC 3.0 保存点

四、复杂场景详解

4.1 多个传播行为组合

@Service
public class ComplexService {

@Autowired
private ServiceA serviceA;
@Autowired
private ServiceB serviceB;
@Autowired
private ServiceC serviceC;

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void complexOperation() {
// 事务 T1 开始

try {
serviceA.methodRequired(); // 加入 T1
serviceB.methodRequiresNew(); // 挂起 T1,创建 T2
serviceC.methodNested(); // 在 T1 中创建保存点

// 如果这里抛出异常,T1 回滚,T2 已提交,嵌套事务根据配置
} catch (Exception e) {
// 处理逻辑
}
}
}

4.2 传播行为与异常回滚

@Service
public class PropagationAndRollbackDemo {

@Autowired
private UserService userService;

/**
* REQUIRED + REQUIRES_NEW 组合
*/

@Transactional(rollbackFor = Exception.class)
public void testRequiredAndRequiresNew() {
// 事务 A 开始
userService.createUserWithRequired("user1"); // 加入事务 A

try {
userService.createUserWithRequiresNew("user2"); // 新事务 B
} catch (Exception e) {
// 事务 B 已回滚,事务 A 不受影响
}

// 事务 A 继续
if (someCondition) {
throw new RuntimeException("事务 A 回滚"); // 事务 A 回滚
// 但 user2 已提交,不会回滚
}
}

/**
* REQUIRED + NESTED 组合
*/

@Transactional
public void testRequiredAndNested() {
// 事务 A 开始

try {
userService.createUserWithNested("user1"); // 嵌套事务,保存点
} catch (Exception e) {
// 嵌套事务回滚到保存点,事务 A 可以继续
}

userService.createUserWithRequired("user2"); // 加入事务 A

if (someError) {
throw new RuntimeException("事务 A 回滚"); // user1 和 user2 都回滚
}
}
}

4.3 自调用问题及解决方案

@Service
public class SelfInvocationService {

@Autowired
private SelfInvocationService self;

/**
* 问题:自调用不会触发事务代理
*/

@Transactional
public void methodA() {
// 这里的调用不会应用 methodB 的事务配置
this.methodB(); // ❌ 事务传播行为失效
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
// 期望新事务,实际在同一个事务中
}

/**
* 解决方案1:注入自身代理
*/

@Transactional
public void methodACorrect() {
self.methodB(); // ✅ 通过代理调用,传播行为生效
}

/**
* 解决方案2:使用 AopContext
*/

@Transactional
public void methodAWithAop() {
((SelfInvocationService) AopContext.currentProxy()).methodB();
}
}

五、传播行为选择指南

5.1 选择决策树

5.2 典型使用场景

传播行为典型场景示例
REQUIRED 大部分业务操作 创建订单、更新用户信息
SUPPORTS 只读查询操作 数据查询、报表查看
MANDATORY 强制事务内的操作 资金转账、敏感操作
REQUIRES_NEW 独立日志/审计 操作日志、审计记录
NOT_SUPPORTED 大数据量查询 报表生成、数据导出
NEVER 特定批处理 数据同步、批量导入
NESTED 部分回滚场景 库存扣减、积分累积

六、注意事项与最佳实践

6.1 性能考虑

@Service
public class PerformanceConsiderations {

// ❌ 不推荐:不需要事务的方法使用 REQUIRED
@Transactional(propagation = Propagation.REQUIRED)
public List<User> findAllUsers() {
return userDao.findAll(); // 只读操作,不必要的事务开销
}

// ✅ 推荐:只读事务或 SUPPORTS
@Transactional(readOnly = true)
public List<User> findAllUsersOptimized() {
return userDao.findAll(); // 只读事务优化
}

// ✅ 推荐:大数据量操作不使用事务
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void exportLargeData() {
// 避免长事务导致连接池耗尽
dataStream.export();
}
}

6.2 常见陷阱

@Service
public class CommonPitfalls {

/**
* 陷阱1:检查型异常默认不回滚
*/

@Transactional
public void checkedExceptionExample() throws IOException {
// 抛出 IOException(检查型异常)
// 默认不回滚,除非配置 rollbackFor
throw new IOException();
}

@Transactional(rollbackFor = Exception.class)
public void checkedExceptionFixed() throws IOException {
// 正确:配置了 rollbackFor
throw new IOException(); // 会回滚
}

/**
* 陷阱2:传播行为与数据库隔离级别混淆
*/

// 传播行为控制事务边界
@Transactional(propagation = Propagation.REQUIRES_NEW)
// 隔离级别控制并发访问
@Transactional(isolation = Isolation.READ_COMMITTED)
public void correctSeparation() {
// 传播行为和隔离级别是不同概念
}

/**
* 陷阱3:REQUIRES_NEW 与数据库连接池
*/

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void consumesMoreConnections() {
// 每个 REQUIRES_NEW 会占用一个独立的数据库连接
// 可能导致连接池耗尽
}
}

6.3 最佳实践总结

@Service
public class BestPractices {

// 1. 明确指定回滚策略
@Transactional(rollbackFor = {BusinessException.class, RuntimeException.class})
public void explicitRollback() { }

// 2. 只读事务优化
@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public void readOptimized() { }

// 3. 合理使用 REQUIRES_NEW
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void independentOperation() {
// 只用于真正需要独立提交的场景
}

// 4. 避免过长事务
@Transactional
public void avoidLongTransaction() {
// 不要在事务中执行耗时操作
// 如:外部API调用、文件处理等
}

// 5. 嵌套事务使用保存点
@Transactional(propagation = Propagation.NESTED)
public void useNestedWithSavepoint() {
// 注意:需要支持保存点的数据库
}
}

七. 总结

Spring 事务传播行为提供了灵活的事务边界控制能力,不同传播行为适用于不同场景:

  • REQUIRED:最常用,适合大多数业务场景
  • REQUIRES_NEW:用于独立操作,不受外部事务影响
  • NESTED:支持部分回滚的复杂场景
  • MANDATORY / NEVER:用于强制约束的场景
  • SUPPORTS / NOT_SUPPORTED:优化性能的可选事务场景

正确选择传播行为可以提高应用的可靠性、性能和可维护性。

END

赞(0)
未经允许不得转载:171主机测评 » Spring 传播行为(Propagation)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址