Spring 框架@Transactional注解,事务的各个传播行为的逻辑以及使用场景。
Spring框架的@Transactional
注解提供了丰富的事务传播行为配置。以下是各种传播行为的详细逻辑说明:
1. 传播行为概述
@Transactional(propagation = Propagation.REQUIRED)
public void someMethod() {// 方法逻辑
}
2. 7种传播行为详解
2.1 REQUIRED(默认)
逻辑:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
@Service
public class UserService {@Transactional(propagation = Propagation.REQUIRED)public void methodA() {// 如果调用methodA时没有事务,则创建新事务// 如果调用methodA时已有事务,则加入该事务methodB(); // methodB会加入methodA的事务}@Transactional(propagation = Propagation.REQUIRED)public void methodB() {// 加入methodA的事务}
}
2.2 REQUIRES_NEW
逻辑:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
@Service
public class OrderService {@Transactional(propagation = Propagation.REQUIRED)public void processOrder(Order order) {// 主事务orderRepository.save(order);try {auditService.logAction("ORDER_CREATED"); // 独立事务,不受主事务回滚影响} catch (Exception e) {// 审计日志失败不影响主事务}// 如果这里抛出异常,order会被回滚,但审计日志已提交}
}@Service
public class AuditService {@Transactional(propagation = Propagation.REQUIRES_NEW)public void logAction(String action) {// 总是创建新事务,挂起调用方的事务auditRepository.save(new AuditLog(action));// 即使调用方事务回滚,这个日志记录也会被提交}
}
2.3 SUPPORTS
逻辑:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。
@Service
public class ProductService {@Transactional(propagation = Propagation.SUPPORTS)public Product getProduct(Long id) {// 如果调用方有事务,则加入事务// 如果调用方无事务,则非事务执行return productRepository.findById(id);}@Transactional(propagation = Propagation.REQUIRED)public void updateProduct(Product product) {// 有事务getProduct(product.getId()); // 加入当前事务productRepository.update(product);}
}
2.4 MANDATORY
逻辑:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
@Service
public class AccountService {@Transactional(propagation = Propagation.MANDATORY)public void validateAccount(Account account) {// 必须在事务中调用,否则抛出IllegalTransactionStateExceptionif (account.getBalance() < 0) {throw new InsufficientBalanceException();}}@Transactional(propagation = Propagation.REQUIRED)public void transferMoney(Account from, Account to, BigDecimal amount) {// 有事务环境validateAccount(from); // 正常执行// 转账逻辑...}public void checkAccount(Account account) {validateAccount(account); // 抛出异常:没有事务}
}
2.5 NOT_SUPPORTED
逻辑:以非事务方式执行操作,如果当前存在事务,则把当前事务挂起。
@Service
public class ReportService {@Transactional(propagation = Propagation.NOT_SUPPORTED)public Report generateLargeReport() {// 总是非事务执行,即使调用方有事务也会被挂起// 适合耗时操作,避免长时间占用事务连接return reportGenerator.generate();}@Transactional(propagation = Propagation.REQUIRED)public void processData() {// 事务操作dataRepository.process();// 生成报表(非事务,不占用事务资源)Report report = generateLargeReport();// 继续事务操作dataRepository.finalizeProcess();}
}
2.6 NEVER
逻辑:以非事务方式执行,如果当前存在事务,则抛出异常。
@Service
public class CacheService {@Transactional(propagation = Propagation.NEVER)public void refreshCache() {// 必须在非事务环境下调用// 如果有事务存在,抛出IllegalTransactionStateExceptioncacheManager.refreshAll();}public void scheduledRefresh() {refreshCache(); // 正常执行(无事务环境)}@Transactionalpublic void updateAndRefresh() {updateData();refreshCache(); // 抛出异常:存在事务}
}
2.7 NESTED
逻辑:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则创建新事务。
@Service
public class BatchService {@Transactional(propagation = Propagation.REQUIRED)public void processBatch(List<Item> items) {for (Item item : items) {try {processItem(item); // 嵌套事务} catch (Exception e) {// 单个item处理失败不影响其他itemlog.error("Item processing failed: " + item.getId(), e);}}}@Transactional(propagation = Propagation.NESTED)public void processItem(Item item) {// 嵌套事务:可以独立回滚而不影响外部事务itemRepository.save(item);inventoryService.updateStock(item);if (item.getQuantity() < 0) {throw new InvalidQuantityException(); // 只回滚这个item的处理}}
}
3. 实际应用场景
3.1 订单处理系统
@Service
public class OrderService {@Autowiredprivate InventoryService inventoryService;@Autowiredprivate AuditService auditService;@Transactional(propagation = Propagation.REQUIRED)public Order createOrder(Order order) {// 主事务开始// 1. 保存订单(在主事务中)orderRepository.save(order);// 2. 扣减库存(独立事务,即使订单失败库存扣减也生效)inventoryService.deductStock(order.getItems());// 3. 记录审计日志(独立事务,必须成功)auditService.logOrderCreation(order);// 4. 发送消息(非事务,不阻塞主事务)notificationService.sendConfirmation(order);return order;// 主事务提交}
}@Service
public class InventoryService {@Transactional(propagation = Propagation.REQUIRES_NEW)public void deductStock(List<OrderItem> items) {// 独立事务,不受订单事务回滚影响items.forEach(item -> {inventoryRepository.reduceStock(item.getProductId(), item.getQuantity());});}
}@Service
public class AuditService {@Transactional(propagation = Propagation.MANDATORY)public void logOrderCreation(Order order) {// 必须在事务中调用auditRepository.save(new AuditLog("ORDER_CREATED", order.getId()));}
}@Service
public class NotificationService {@Transactional(propagation = Propagation.NOT_SUPPORTED)public void sendConfirmation(Order order) {// 非事务执行,避免长时间占用数据库连接emailService.sendOrderConfirmation(order);}
}
3.2 异常处理示例
@Service
public class ComplexService {@Transactional(propagation = Propagation.REQUIRED)public void complexOperation() {try {// 主业务逻辑mainBusinessLogic();// 独立事务的辅助操作independentOperation();} catch (BusinessException e) {// 业务异常,回滚主事务throw e;} catch (AuxiliaryException e) {// 辅助操作异常,不影响主事务log.error("Auxiliary operation failed", e);}}@Transactional(propagation = Propagation.REQUIRES_NEW)public void independentOperation() {// 这个操作失败不会影响complexOperation的主事务auxiliaryService.process();}
}
4. 传播行为选择指南
传播行为 | 适用场景 | 注意事项 |
---|---|---|
REQUIRED | 大多数业务方法 | 默认选择,简单可靠 |
REQUIRES_NEW | 需要独立提交的操作(如日志、消息) | 注意数据库连接资源 |
NESTED | 批量处理中的单个项目 | 需要数据库支持保存点 |
SUPPORTS | 查询方法 | 可根据调用方决定是否事务 |
MANDATORY | 必须在事务中调用的方法 | 强制事务环境 |
NOT_SUPPORTED | 耗时操作、外部系统调用 | 避免长时间占用事务 |
NEVER | 不应该在事务中调用的方法 | 强制非事务环境 |
5. 重要注意事项
- 自调用问题:同一个类中的方法调用不会经过AOP代理
- 异常处理:默认只对RuntimeException回滚,可通过
rollbackFor
配置 - 超时设置:长时间事务需要设置超时时间
- 只读事务:查询操作可设置
readOnly = true
优化性能
理解这些传播行为的逻辑对于设计正确的事务边界和保证数据一致性至关重要。