前言:
在开发中,我们使用 Spring 的 @Transactional 注解管理事务,而 Propagation(传播机制)正是 @Transactional 注解中的一个关键配置。Propagation 决定了当前方法的事务如何与已有事务交互,是控制事务嵌套行为的重要参数。但因为涉及多个选项和一些细微的差异,传播机制总给人“雾里看花”的感觉。
今天,我用简单通俗的方式来带你一探究竟,顺便配上代码案例,让你对 Propagation 的每种传播方式都理解得一清二楚。让我们开始吧!
一、REQUIRED(默认)
REQUIRED 是 @Transactional 的默认传播机制。它的规则是:如果当前存在事务,则加入该事务;否则创建一个新的事务。
代码示例
@Service
public class UserService {
@Transactional(propagation = Propagation.REQUIRED)
public void registerUser() {
// 主方法
addUser();
sendWelcomeEmail();
}
@Transactional(propagation = Propagation.REQUIRED)
public void addUser() {
// 向数据库添加用户
}
@Transactional(propagation = Propagation.REQUIRED)
public void sendWelcomeEmail() {
// 发送欢迎邮件
}
}
效果分析: 在上面的代码中,registerUser() 调用了 addUser() 和 sendWelcomeEmail() 方法。因为 REQUIRED 传播机制的规则是加入当前事务,所以 registerUser() 方法中的三个事务会共享同一个事务。也就是说,如果 sendWelcomeEmail() 发送失败,整个 registerUser() 事务会回滚。
二、REQUIRES_NEW
REQUIRES_NEW 的规则是:不管当前是否有事务,都创建一个新的事务,并将当前事务挂起。适合在方法中调用一些独立事务的操作。
代码示例
@Service
public class UserService {
@Transactional(propagation = Propagation.REQUIRED)
public void registerUser() {
addUser();
sendWelcomeEmail();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addUser() {
// 向数据库添加用户
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void sendWelcomeEmail() {
// 发送欢迎邮件
}
}
效果分析: 在这个案例中,registerUser() 方法会调用 addUser() 和 sendWelcomeEmail(),并且它们各自都有独立的事务。即使 sendWelcomeEmail() 抛出异常,也不会影响 addUser() 的事务提交。适合场景:某些方法需要独立事务的情形,比如记录用户日志。
三、NESTED
NESTED 的规则是:如果当前存在事务,则在当前事务中创建一个嵌套事务;否则创建一个新的事务。这种传播机制类似于“保存点”机制,即使嵌套事务失败,外部事务也可以选择回滚到嵌套事务之前的状态。
代码示例
@Service
public class UserService {
@Transactional(propagation = Propagation.REQUIRED)
public void registerUser() {
addUser();
updateUserDetails();
}
@Transactional(propagation = Propagation.NESTED)
public void addUser() {
// 向数据库添加用户
}
@Transactional(propagation = Propagation.REQUIRED)
public void updateUserDetails() {
// 更新用户详细信息
}
}
效果分析: 在这个例子中,如果 addUser() 的嵌套事务失败,主事务可以选择回滚到 addUser() 之前的状态,而无需回滚整个 registerUser() 事务。它适合场景如部分业务逻辑出错但希望主事务继续的情况。
四、SUPPORTS
SUPPORTS 的规则是:如果当前存在事务,则加入当前事务;否则以非事务方式执行。适合一些对事务要求不严格的操作。
代码示例
@Service
public class UserService {
@Transactional(propagation = Propagation.REQUIRED)
public void registerUser() {
addUser();
optionalLog();
}
@Transactional(propagation = Propagation.SUPPORTS)
public void optionalLog() {
// 记录非关键日志信息
}
}
效果分析: 这里,optionalLog() 方法会在 registerUser() 事务范围内执行;若没有事务,则会以非事务方式执行。这种机制非常适合一些非关键的操作,或对于事务一致性要求不高的场景。
五、NOT_SUPPORTED
NOT_SUPPORTED 的规则是:不支持事务,如果当前存在事务,则挂起该事务,转而以非事务方式执行。
代码示例
@Service
public class UserService {
@Transactional(propagation = Propagation.REQUIRED)
public void registerUser() {
addUser();
nonTransactionalOperation();
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void nonTransactionalOperation() {
// 执行非事务操作
}
}
效果分析: 在此例中,nonTransactionalOperation() 将以非事务方式执行,即使 registerUser() 的事务出现错误,该操作也不会回滚。通常用于一些不会影响数据一致性的操作,比如缓存清理。
六、MANDATORY
MANDATORY 的规则是:必须在一个已有事务中运行,否则抛出异常。适合某些关键的操作,确保不会意外在非事务环境中运行。
代码示例
@Service
public class UserService {
@Transactional(propagation = Propagation.REQUIRED)
public void registerUser() {
criticalOperation();
}
@Transactional(propagation = Propagation.MANDATORY)
public void criticalOperation() {
// 关键的事务操作
}
}
效果分析: 在这里,criticalOperation() 必须在事务中调用,否则会抛出异常。它适合在需要高度数据一致性的场景下使用,确保不会意外在非事务环境下执行。
七、NEVER
NEVER 的规则是:永远不在事务环境中运行,如果当前存在事务,则抛出异常。
代码示例
@Service
public class UserService {
public void performOperation() {
nonTransactionalOperation();
}
@Transactional(propagation = Propagation.NEVER)
public void nonTransactionalOperation() {
// 永远不在事务中执行的操作
}
}
效果分析: nonTransactionalOperation() 会确保自己不运行在事务中。如果调用时有事务存在,将抛出异常。适合用于不需要事务且影响较小的操作,如日志系统。
总结
Spring 的 Propagation 传播机制给了我们极大的灵活性,使得事务的控制不再“一刀切”。每种传播方式都有其适用场景,因此了解它们的区别和使用方式,能够让我们的代码更加可靠和高效。
希望这篇文章能让大家对 @Transactional 的传播机制有更深刻的认识!