Spring事务传播行为详解
引言
事务传播行为是Spring框架用于解决方法间事务调用问题的核心机制。当一个方法调用另一个方法时,如何处理事务的加入、创建、挂起等行为,直接影响数据一致性和系统性能。本文将深入解析Spring的7种事务传播行为、其底层实现原理以及在Spring Boot中的最佳实践。
一、七种传播行为
1.1 传播行为概览
Spring定义了7种事务传播行为:REQUIRED(默认):支持当前事务,如果不存在则创建新事务;REQUIRES_NEW:挂起当前事务,创建新事务;SUPPORTS:支持当前事务,如果不存在则以非事务执行;NOT_SUPPORTED:以非事务执行,挂起当前事务;MANDATORY:必须在事务中执行,否则抛异常;NEVER:必须在非事务中执行,否则抛异常;NESTED:如果当前存在事务,则在嵌套事务中执行。
1.2 行为详解
public enum Propagation {
REQUIRED, // 0 - 默认
REQUIRES_NEW, // 1 - 创建新事务
SUPPORTS, // 2 - 跟随当前事务
NOT_SUPPORTED, // 3 - 非事务执行
MANDATORY, // 4 - 强制事务
NEVER, // 5 - 强制非事务
NESTED // 6 - 嵌套事务
}
二、REQUIRED行为
2.1 基本原理
REQUIRED是默认的传播行为。如果当前存在事务,则加入该事务;如果不存在事务,则创建新事务。
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private UserService userService;
@Transactional(propagation = Propagation.REQUIRED)
public void createOrder(Order order) {
// 使用当前事务
orderRepository.save(order);
// 调用用户服务,也在同一事务中
userService.updateUserBalance(order.getUserId(),
order.getAmount());
}
}
@Service
public class UserService {
@Transactional(propagation = Propagation.REQUIRED) // 默认
public void updateUserBalance(Long userId, BigDecimal amount) {
// 与调用方在同一事务中
}
}
2.2 事务合并
@Transactional(propagation = Propagation.REQUIRED)
public void outerMethod() {
// 事务T1开始
doSomethingA();
innerMethod(); // 加入事务T1
doSomethingB();
// 提交事务T1
}
@Transactional(propagation = Propagation.REQUIRED)
public void innerMethod() {
// 加入外层事务T1
doSomethingC();
}
三、REQUIRES_NEW行为
3.1 基本原理
REQUIRES_NEW总是创建新事务,挂起当前事务(如果存在)。
@Service
public class OrderService {
@Transactional(propagation = Propagation.REQUIRED)
public void createOrder(Order order) {
orderRepository.save(order);
// 创建独立新事务T2
notificationService.sendNotification(order);
// 即使T2失败,T1中的订单仍会提交
}
}
@Service
public class NotificationService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void sendNotification(Order order) {
// 在独立事务中执行
// 失败不会影响调用方事务
}
}
3.2 使用场景
REQUIRES_NEW适用于日志记录、审计追踪、发送通知等辅助功能,即使失败也不应影响主业务。
四、NESTED嵌套事务
4.1 基本原理
NESTED使用JDBC的保存点机制,在嵌套事务中执行。
@Service
public class OrderService {
@Transactional(propagation = Propagation.REQUIRED)
public void createOrder(Order order) {
orderRepository.save(order);
// 创建保存点
nestedMethod();
// 嵌套方法失败只会回滚到保存点
}
@Transactional(propagation = Propagation.NESTED)
public void nestedMethod() {
// 在嵌套事务中执行
}
}
4.2 与REQUIRES_NEW的区别
// REQUIRED_NEW:完全独立的事务
// - 外层事务挂起
// - 内层事务独立提交/回滚
// - 内层回滚不影响外层
// NESTED:同一事务的嵌套
// - 不挂起外层事务
// - 使用保存点机制
// - 嵌套回滚只回滚到保存点
五、SUPPORTS行为
5.1 基本原理
SUPPORTS如果有事务则加入,否则以非事务执行。
@Service
public class CacheService {
@Transactional(propagation = Propagation.SUPPORTS)
public Object getData(String key) {
// 如果在事务中,可以利用事务一致性
// 不在事务中也可以正常工作
Cache cache = cacheManager.getCache("data");
return cache.get(key);
}
}
六、NOT_SUPPORTED行为
6.1 基本原理
NOT_SUPPORTED挂起当前事务,以非事务方式执行。
@Service
public class ReportService {
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void generateReport(ReportRequest request) {
// 长时间运行,不应在事务中
// 挂起调用方的事务
}
}
七、其他传播行为
7.1 MANDATORY
@Transactional(propagation = Propagation.MANDATORY)
public void executeInTransaction() {
// 必须在有事务的情况下调用
// 如果没有事务,抛出IllegalTransactionStateException
}
7.2 NEVER
@Transactional(propagation = Propagation.NEVER)
public void executeWithoutTransaction() {
// 必须在没有事务的情况下调用
// 如果有事务,抛出IllegalTransactionStateException
}
八、事务隔离级别
8.1 隔离级别定义
public enum Isolation {
DEFAULT, // 使用数据库默认
READ_UNCOMMITTED, // 读未提交
READ_COMMITTED, // 读已提交
REPEATABLE_READ, // 可重复读
SERIALIZABLE // 串行化
}
@Transactional(isolation = Isolation.READ_COMMITTED)
public void readData() {
// 使用读已提交隔离级别
}
8.2 隔离级别问题
脏读:一个事务读取另一个事务未提交的数据;不可重复读:一个事务两次读取同一数据,结果不同;幻读:一个事务按条件查询,结果包含另一个事务插入的数据。
九、Spring Boot配置
9.1 全局事务配置
spring:
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
jpa:
hibernate:
ddl-auto: validate
properties:
hibernate:
connection:
isolation: 2 # READ_COMMITTED
9.2 自定义事务管理器
@Configuration
public class TransactionConfig {
@Bean
public PlatformTransactionManager transactionManager(
DataSource dataSource) {
DataSourceTransactionManager manager =
new DataSourceTransactionManager(dataSource);
manager.setDefaultTimeout(30);
return manager;
}
@Bean
public TransactionTemplate transactionTemplate(
PlatformTransactionManager transactionManager) {
TransactionTemplate template = new TransactionTemplate(
transactionManager);
template.setIsolationLevel(
TransactionDefinition.ISOLATION_READ_COMMITTED);
template.setPropagationBehavior(
TransactionDefinition.PROPAGATION_REQUIRED);
return template;
}
}
十、最佳实践
10.1 传播行为选择
@Service
public class TransactionStrategy {
// 核心业务逻辑 - REQUIRED
@Transactional(propagation = Propagation.REQUIRED)
public void coreBusiness() {
// 需要事务保证一致性
}
// 日志记录 - REQUIRES_NEW
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logOperation() {
// 独立事务,失败不影响主业务
}
// 只读操作 - SUPPORTS
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public List<?> query() {
// 可以利用事务优化,但不强制
}
}
10.2 避免长事务
@Service
public class OptimizedService {
@Transactional
public void optimizedOrderProcess(Order order) {
// 验证
validateOrder(order);
// 执行业务
orderRepository.save(order);
// 不要在事务中做这些耗时操作
// 提交事务后,再发送通知
sendNotificationAsync(order);
}
}
总结
理解Spring事务传播行为对于构建正确的数据访问层至关重要。REQUIRED适合大多数场景;REQUIRES_NEW适合需要独立提交的操作;NESTED适合需要部分回滚的场景。合理选择传播行为和隔离级别,可以平衡数据一致性和系统性能。

7372

被折叠的 条评论
为什么被折叠?



