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
