1. 事务传播机制的核心价值与应用场景
在业务系统开发中,我们经常会遇到一个业务操作需要调用另一个业务操作的场景。以用户注册送积分这个典型业务为例,注册操作需要插入用户记录,而送积分操作需要更新积分表。这两个操作要么全部成功,要么全部失败,这就是典型的事务需求。
但问题在于:如果送积分操作失败,注册操作是否应该回滚?反过来,如果注册操作失败,送积分操作是否应该执行?这些问题的答案取决于业务需求,而Spring框架的事务传播机制正是为了解决这类问题而设计的。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 事务传播的七种行为模式解析
Spring框架定义了七种事务传播行为,每种行为都有其特定的使用场景:
2.1 REQUIRED(默认行为)
这是最常用的传播行为。如果当前存在事务,就加入该事务;如果当前没有事务,就新建一个事务。在我们的注册送积分例子中,如果两个方法都使用REQUIRED传播行为,它们会共用一个事务。
java复制@Transactional(propagation = Propagation.REQUIRED)
public void register() {
// 注册逻辑
bonusService.addBonus(); // 也使用REQUIRED
}
2.2 REQUIRES_NEW
总是新建一个事务,如果当前存在事务,则挂起当前事务。这意味着内部事务的提交或回滚不会影响外部事务。
java复制@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addBonus() {
// 积分逻辑
}
2.3 NESTED
如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则新建一个事务。嵌套事务是外部事务的子事务,它的回滚不会导致外部事务回滚,但外部事务回滚会导致嵌套事务回滚。
2.4 SUPPORTS
如果当前存在事务,就加入该事务;如果当前没有事务,就以非事务方式执行。
2.5 NOT_SUPPORTED
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
2.6 MANDATORY
必须在一个已有的事务中执行,否则抛出异常。
2.7 NEVER
不能在事务中执行,如果当前存在事务,则抛出异常。
3. 注册送积分场景的深度分析
让我们回到最初的注册送积分案例,深入分析不同传播行为下的表现:
3.1 默认REQUIRED行为分析
java复制@Service
public class UserService {
@Autowired
private BonusService bonusService;
@Transactional // 默认REQUIRED
public User register(String email, String password, String name) {
// 插入用户记录
User user = userRepository.save(new User(email, password, name));
// 增加积分
bonusService.addBonus(user.getId(), 100);
return user;
}
}
@Service
public class BonusService {
@Transactional // 默认REQUIRED
public void addBonus(Long userId, int points) {
// 积分增加逻辑
bonusRepository.addPoints(userId, points);
}
}
在这种配置下:
- 当调用register()方法时,Spring会开启一个新事务
- 当调用addBonus()方法时,由于已经存在事务,Spring会让addBonus()加入register()的事务
- 如果addBonus()抛出异常,整个事务(包括用户注册)都会回滚
- 如果register()抛出异常,整个事务也会回滚
3.2 REQUIRES_NEW行为分析
如果我们修改BonusService的传播行为:
java复制@Service
public class BonusService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addBonus(Long userId, int points) {
// 积分增加逻辑
bonusRepository.addPoints(userId, points);
}
}
现在行为变为:
- r
