1. 概述
Spring 事务管理是框架中确保数据库操作 原子性、一致性、隔离性和持久性 (ACID) 的核心机制。它支持声明式和编程式两种管理方式,其中声明式事务通过 @Transactional 注解实现,无需手动编写事务代码,大大简化了开发流程。
相较于编程式事务,声明式事务更易于维护和理解。关于事务的基本概念(如 ACID 的具体含义),这里不再赘述,我们直接聚焦于 Spring 中的实际配置与应用。
2. @Transactional 注解
作用与级别
@Transactional 提供了声明式事务管理能力。开发者只需在方法或类上添加此注解,Spring 容器就会自动处理事务的开启、提交和回滚。
- 作为类注解:为类中所有 public 方法生效。
- 作为方法注解:默认仅对 public 方法生效,非 public 方法通常会被忽略。
@RestController
@RequestMapping("/test")
@Slf4j
public class TestController {
private final UserService userService;
@Autowired
public TestController(UserService userService) {
this.userService = userService;
}
@Transactional
@RequestMapping("/test1")
public String test1(String userName, String password) {
UserInfo userInfo = new UserInfo();
userInfo.setUserName(userName);
userInfo.setPassword(password);
Integer result = userService.register(userInfo);
if (result == 1) {
log.info("test1 注册成功,userName:{},password:{}", userName, password);
}
return "注册成功";
}
}
在实际调用时,如果业务逻辑正常,数据会写入数据库;若抛出异常且符合回滚条件,事务会自动回滚。
2.1 rollbackFor
默认情况下,Spring 仅在抛出 非受查异常 (RuntimeException) 或 错误 (Error) 时触发回滚。对于受查异常(Checked Exception),默认不会回滚。
指定回滚类型
如果需要受查异常也能触发回滚,可以通过 rollbackFor 属性显式指定。
@Transactional(rollbackFor = Exception.class)
@RequestMapping("/test4")
public String test4(String userName, String password) throws IOException {
UserInfo userInfo = new UserInfo();
userInfo.setUserName(userName);
userInfo.setPassword(password);
Integer result = userService.register(userInfo);
if (result == 1) {
log.info("test4 注册成功,userName:{},password:{}", userName, password);
throw new IOException(); // 受查异常
}
return "注册成功";
}
此时即使抛出 IOException,事务也会回滚。
抛出非受查异常时
如果不配置 rollbackFor,抛出 RuntimeException 会触发回滚。
@Transactional
@RequestMapping("/test3")
public String test3(String userName, String password) {
UserInfo userInfo = new UserInfo();
userInfo.setUserName(userName);
userInfo.setPassword(password);
Integer result = userService.register(userInfo);
if (result == 1) {
log.info("test3 注册成功,userName:{},password:{}", userName, password);
throw new RuntimeException(); // 非受查异常
}
return "注册成功";
}
抛出受查异常时
若未配置 rollbackFor,抛出受查异常则不会回滚。
@Transactional
@RequestMapping("/test2")
public String test2(String userName, String password) throws IOException {
UserInfo userInfo = new UserInfo();
userInfo.setUserName(userName);
userInfo.setPassword(password);
Integer result = userService.register(userInfo);
if (result == 1) {
log.info("test2 注册成功,userName:{},password:{}", userName, password);
throw new IOException(); // 受查异常,默认不回滚
}
return "注册成功";
}
2.2 isolation
用于指定事务的隔离级别,避免脏读、不可重复读和幻读等问题。
- Isolation.DEFAULT:使用底层数据库默认的隔离级别。
- Isolation.READ_UNCOMMITTED:读未提交(最低级别,允许脏读)。
- Isolation.READ_COMMITTED:读已提交(大多数数据库默认)。
- Isolation.REPEATABLE_READ:可重复读(MySQL 默认)。
- Isolation.SERIALIZABLE:串行化(最高级别,性能较差)。
具体每种隔离级别的效果差异较大,建议根据业务场景选择,通常保持默认即可。
2.3 propagation
事务传播行为定义了当前事务方法被另一个事务方法调用时,事务应如何传播。Spring 提供了 7 种传播行为,均基于 Propagation 枚举类实现。
Propagation.REQUIRED
默认传播行为。如果当前存在事务,则加入该事务;如果不存在事务,则新建一个事务。这是最常用的模式。
Propagation.SUPPORTS
如果当前存在事务,则加入该事务;如果不存在事务,则以非事务方式执行。
Propagation.MANDATORY
强制要求当前必须存在事务并加入,否则抛出异常。适用于必须在事务上下文中运行的方法。
Propagation.REQUIRES_NEW
无论当前是否存在事务,都新建一个事务。新事务与当前事务独立,互不干扰。常用于日志记录等需要独立提交的操作。
Propagation.NOT_SUPPORTED
以非事务方式执行操作,如果当前存在事务,则挂起该事务。适用于不需要事务支持但处于事务环境中的方法。
Propagation.NEVER
强制要求当前不能存在事务,否则抛出异常。这与 MANDATORY 相反。
Propagation.NESTED
如果当前存在事务,则在嵌套事务中执行;如果不存在事务,则行为与 Propagation.REQUIRED 相同。嵌套事务的回滚不影响外部事务,但外部事务回滚会导致嵌套事务回滚。这适用于需要部分回滚的场景,例如主流程失败需保留部分子流程数据的情况。


