全面解读 @Transactional 的传播机制:一次搞懂 Spring 事务的各种“传播方式”!
文章目录
- **一、REQUIRED(默认)**
- **代码示例**
- **二、REQUIRES_NEW**
- **代码示例**
- **三、NESTED**
- **代码示例**
- **四、SUPPORTS**
- **代码示例**
- **五、NOT_SUPPORTED**
- **代码示例**
- **六、MANDATORY**
- **代码示例**
- **七、NEVER**
- **代码示例**
- **总结**
- 推荐阅读文章
正文:
在开发中,我们使用 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
的传播机制有更深刻的认识!
推荐阅读文章
- 由 Spring 静态注入引发的一个线上T0级别事故(真的以后得避坑)
- 如何理解 HTTP 是无状态的,以及它与 Cookie 和 Session 之间的联系
- HTTP、HTTPS、Cookie 和 Session 之间的关系
- 使用 Spring 框架构建 MVC 应用程序:初学者教程
- 有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误
- Java Spring 中常用的 @PostConstruct 注解使用总结
- 线程 vs 虚拟线程:深入理解及区别
- 深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别
- 10大程序员提升代码优雅度的必杀技,瞬间让你成为团队宠儿!
- 探索 Lombok 的 @Builder 和 @SuperBuilder:避坑指南(一)
- 为什么用了 @Builder 反而报错?深入理解 Lombok 的“暗坑”与解决方案(二)