文章目录
- [Ⅰ. Spring中事务的实现](#Ⅰ. Spring中事务的实现)
-
- 一、编程式事务(了解)
- 二、声明式事务:`@Transactional`
-
- [@Transactional 的作用](#@Transactional 的作用)
- [Ⅱ. @Transactional 详解](#Ⅱ. @Transactional 详解)
-
- [一、异常回滚属性 `rollbackFor`](#一、异常回滚属性
rollbackFor) - [二、Spring事务隔离级别 `Isolation`](#二、Spring事务隔离级别
Isolation) - [三、Spring事务传播机制 `Propagation`](#三、Spring事务传播机制
Propagation) -
- [1. 什么是事务传播机制](#1. 什么是事务传播机制)
- [2. 事务的传播机制有哪些](#2. 事务的传播机制有哪些)
- [💥`nested` 和 `required` 的区别](#💥
nested和required的区别)
- [一、异常回滚属性 `rollbackFor`](#一、异常回滚属性
- [Ⅲ. Spring 事务的失效问题](#Ⅲ. Spring 事务的失效问题)
-
- 一、调用方式不对(自调用)💥
- [二、方法不是 `public`💥](#二、方法不是
public💥) - 三、异常没抛出💥
- 四、数据库引擎不支持事务
- 五、多线程调用💥
- 六、配置问题
- 七、手动设置了传播属性
- [✅ 快速排查清单](#✅ 快速排查清单)

Ⅰ. Spring中事务的实现
Spring中的事务操作分为两类:
- 编程式事务(手动实现事务)
- 声明式事务(利用注解自动开启事务)
一、编程式事务(了解)
Spring 手动操作事务和 MySQL 操作事务类似,有三个重要操作步骤:
- 开启事务(获取事务)
- 提交事务
- 回滚事务
SpringBoot 内置了两个对象:
DataSourceTransactionManager:事务管理器,用来获取事务(开启事务),提交或回滚事务的TransactionDefinition:事务的属性,在获取事务的时候需要将TransactionDefinition传递进去从而获得一个事务TransactionStatus
代码实现:
java
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/user")
@RestController
public class UserController {
// 注入事务管理器
@Autowired
private DataSourceTransactionManager dataSourceTransactionManager;
// 注入事务属性
@Autowired
private TransactionDefinition transactionDefinition;
@Autowired
private UserService userService;
@RequestMapping("/registry")
public String registry(String name,String password){
// 开启事务
TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
// 用户注册
userService.registryUser(name,password);
// 提交事务
dataSourceTransactionManager.commit(transactionStatus);
// 回滚事务
//dataSourceTransactionManager.rollback(transactionStatus);
return "注册成功";
}
}
二、声明式事务:@Transactional
-
添加依赖
xml<dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> </dependency> -
在需要事务的方法上添加
@Transactional注解就可以实现了。无需手动开启事务和提交事务,进入方法时自动开启事务,方法执行完会自动提交事务,如果中途发生了没有处理的异常会自动回滚事务。java@RequestMapping("/trans") @RestController public class TransactionalController { @Autowired private UserService userService; @Transactional @RequestMapping("/registry") public String registry(String name,String password){ // 用户注册 userService.registryUser(name,password); return "注册成功"; } }
@Transactional 的作用
- 修饰 方法 时:只有修饰
public方法时才生效 (修饰其他方法时不会报错,也不生效)[推荐] - 修饰 类 时:对
@Transactional修饰的类中所有的public方法都生效。
💥注意事项:
- 如果在方法执行过程中,出现异常,且异常未被捕获,就进行事务回滚操作。
- 如果异常被程序捕获,方法就被认为是成功执行,依然会提交事务。
下面对异常进行捕获:
java
@Transactional
@RequestMapping("/registry")
public String registry(String name,String password){
userService.registryUser(name,password); // 模拟用户注册功能
log.info("用户数据插入成功");
// 对异常进行捕获
try {
// 强制程序抛出异常
int a = 10/0;
}catch (Exception e){
e.printStackTrace();
}
return "注册成功";
}
运行程序,发现虽然程序出错了,但是由于异常被捕获了 ,所以事务依然得到了提交。
如果需要事务进行回滚,有以下两种方式:
-
重新抛出异常
java@Transactional @RequestMapping("/registry") public String registry(String name,String password){ userService.registryUser(name,password); log.info("用户数据插入成功"); // 对异常进行捕获 try { int a = 10/0; }catch (Exception e){ // 将异常重新抛出去 throw e; } return "注册成功"; } -
手动回滚事务 :使用
TransactionAspectSupport.currentTransactionStatus()得到当前的事务,并使用setRollbackOnly设置回滚。java@Transactional @RequestMapping("/registry") public String registry(String name,String password){ userService.registryUser(name,password); log.info("用户数据插入成功"); // 对异常进行捕获 try { int a = 10/0; }catch (Exception e){ // 手动回滚事务 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } return "注册成功"; }
Ⅱ. @Transactional 详解
这里主要介绍注解中三个常见属性:
rollbackFor:异常回滚属性。指定能够触发事务回滚的异常类型。可以指定多个异常类型Isolation:事务的隔离级别。默认值为Isolation.DEFAULTPropagation:事务的传播机制。默认值为Propagation.REQUIRED
一、异常回滚属性 rollbackFor
@Transactional 默认只在遇到 RuntimeException 和 Error 时才会回滚 ,而非运行时异常不回滚。

如果我们需要所有异常都回滚,需要来配置 @Transactional 注解当中的 rollbackFor 属性,通过这个属性指定出现何种异常类型时事务进行回滚。
java
// Controller 层
@RequestMapping("/r2")
public String r2(String name, String password) throws IOException {
userService.registryUserWithTx(name, password);
return "r2";
}
// Service 层
@Transactional(rollbackFor = Exception.class)
public void registryUserWithTx(String name, String password) throws IOException {
userRepository.save(new User(name, password));
log.info("用户数据插入成功");
if (true) {
throw new IOException(); // 模拟异常
}
}
发现虽然程序抛出了异常,但是事务依然进行了回滚!
二、Spring事务隔离级别 Isolation
| 隔离级别 | 含义 |
|---|---|
| Isolation.DEFAULT | 以连接的数据库的事务隔离级别为主 |
| Isolation.READ_UNCOMMITTED | 读未提交 |
| Isolation.READ_COMMITTED | 读已提交 |
| Isolation.REPEATABLE_READ | 可重复读 |
| Isolation.SERIALIZABLE | 串行化 |
它的实现如下所示:
java
public enum Isolation {
DEFAULT(-1),
READ_UNCOMMITTED(1),
READ_COMMITTED(2),
REPEATABLE_READ(4),
SERIALIZABLE(8);
private final int value;
private Isolation(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
Spring 中事务隔离级别可以通过 @Transactional 中的 isolation 属性进行设置:
java
@Transactional(isolation = Isolation.READ_COMMITTED)
@RequestMapping("/r3")
public String r3(String name,String password) throws IOException {
//... 代码省略
return "r3";
}
三、Spring事务传播机制 Propagation
1. 什么是事务传播机制
事务传播机制就是: 多个事务方法存在调用关系时,事务是如何在这些方法间进行传播的。
比如有两个方法 A,B 都被 @Transactional 修饰,A 方法调用 B 方法
A 方法运行时,会开启一个事务。当 A 调用 B 时,B 方法本身也有事务,此时 B 方法运行时,是加入 A 的事务呢,还是创建一个新的事务呢?
这就涉及到了事务的传播机制。
事务隔离级别 解决的是 多个事务同时调用一个数据库的问题 。
事务传播机制 解决的是 一个事务在多个方法之间传递的问题。
2. 事务的传播机制有哪些
@Transactional 注解支持事务传播机制的设置,通过 propagation 属性来指定传播行为。
| 传播机制 | 作用 |
|---|---|
Propagation.REQUIRED |
默认的事务传播级别 。如果当前存在事务,则加入该事务 。如果当前没有事务,则创建一个新的事务。 |
Propagation.SUPPORTS |
如果当前存在事务,则加入该事务 。如果当前没有事务,则以非事务的方式继续运行。 |
Propagation.MANDATORY |
如果当前存在事务,则加入该事务 。如果当前没有事务,则抛出异常。 |
Propagation.REQUIRES_NEW |
如果当前存在事务,则把当前事务挂起 。也就是说不管外部方法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部方法都会新开启自己的事务 , 且开启的事务相互独立,互不干扰。 |
Propagation.NOT_SUPPORTED |
以非事务方式运行 ,如果当前存在事务,则把当前事务挂起,不使用它。 |
Propagation.NEVER |
以非事务方式运行 ,如果当前存在事务,则抛出异常。 |
Propagation.NESTED |
如果当前存在事务,则创建一个事务作为当前事务的子事务来运行 。如果当前没有事务,则创建一个新的事务。 |
实现类如下所示:
java
public enum Propagation {
REQUIRED(0),
SUPPORTS(1),
MANDATORY(2),
REQUIRES_NEW(3),
NOT_SUPPORTED(4),
NEVER(5),
NESTED(6);
private final int value;
private Propagation(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
💥nested 和 required 的区别
- 整个事务如果全部执行成功,二者的结果是一样的。
- 如果事务部分执行成功
REQUIRED加入事务会导致整个事务全部回滚。NESTED嵌套事务可以实现局部回滚,不会影响上一个方法中执行的结果。
嵌套事务之所以能够实现部分事务的回滚,是因为事务中有一个保存点(savepoint)的概念,嵌套事务进入之后相当于新建了一个保存点,而滚回时只回滚到当前保存点。
REQUIRED是加入到当前事务中,并没有创建事务的保存点,因此出现了回滚就是整个事务回滚,这就是嵌套事务和加入事务的区别!
Ⅲ. Spring 事务的失效问题
一、调用方式不对(自调用)💥
- 场景:在同一个类里,一个方法调用另一个加了
@Transactional的方法。 - 原因:Spring 事务是通过代理实现的,内部方法调用不会走代理,事务不会生效。
- 解决:把需要事务的方法放到另一个类里,或通过 AopContext 获取代理调用。
- 具体可以参考《黑马点评》中的优惠券一人一单问题。
二、方法不是 public💥
- Spring 默认只对
public方法生效。 - 如果
@Transactional标注在private、protected或default方法上,事务不会生效。
三、异常没抛出💥
-
默认回滚规则:Spring 只会对 运行时异常(RuntimeException 或其子类) 回滚。
-
如果你捕获异常后没抛出,事务不会回滚。
-
如果抛的是受检异常(如
IOException),默认也不会回滚。 -
解决:
-
重新抛出运行时异常。
-
或在注解里指定
rollbackFor:
java@Transactional(rollbackFor = Exception.class) -
四、数据库引擎不支持事务
- MySQL 如果表引擎是 MyISAM,不支持事务,事务自然无效。
- 必须用 InnoDB 才能支持。
五、多线程调用💥
- 如果事务方法里开了新线程,线程里的操作不受当前事务控制。
- 解决:避免在事务方法里直接用多线程,或者手动管理事务。
六、配置问题
@EnableTransactionManagement没配置。- 事务管理器没有正确注入。
七、手动设置了传播属性
- 如果事务传播属性设置不当(如
PROPAGATION_NEVER),也会导致失效。
✅ 快速排查清单
- 方法必须是
public - 确保是代理调用(不是同类内调用)
- 确保异常能抛出,没被吞掉
- 数据库表必须支持事务
- 确认 Spring 的事务配置启用


