文章目录
- 一、什么是事务
- 二、Spring的两种事务实现
-
- [1. 编程式事务](#1. 编程式事务)
- [2. 声明式事务@Transactional](#2. 声明式事务@Transactional)
- 三、@Transactional核心属性详解
-
- [1. rollbackFor:指定回滚异常](#1. rollbackFor:指定回滚异常)
- [2. isolation:事务隔离级别](#2. isolation:事务隔离级别)
- [3. propagation:事务传播机制](#3. propagation:事务传播机制)
一、什么是事务
事务是一组数据库操作的集合,不可分割。这一组操作要么全部成功提交,要么全部失败回滚。
数据库原生事务三条指令:
sql
--开启事务
start transaction;
--提交事务,全部操作生效
commit;
--回滚事务,撤销所有操作
rollback;
二、Spring的两种事务实现
Spring把数据库事务做封装,分为编程式事务 、声明式事务。
1. 编程式事务
手动写代码控制事务开启、提交、回滚。
核心对象:
DataSourceTransactionManager:事务管理器,用来获取、提交、回滚事务TransactionDefinition:事务属性TransactionStatus:事务状态
示例代码:
java
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserInfoService userInfoService;
@Autowired
private DataSourceTransactionManager transactionManager;
@Autowired
private TransactionDefinition definition;
@RequestMapping("/registry")
public String registry(String name, String password) {
//1. 开启事务
// 创建副本
TransactionStatus status = transactionManager.getTransaction(definition);
//2. 用户注册
userInfoService.registryUser(name, password);
//3. 事务提交
// transactionManager.commit(status);
// log.info("用户注册成功");
// 回滚
transactionManager.rollback(status);
log.info("rollback");
return "registry success";
}
}
缺点:代码侵入性强,业务代码和事务代码耦合,实际开发很少用。
2. 声明式事务@Transactional
基于AOP实现,注解驱动 ,不用手动写开启/提交/回滚代码。
进入方法自动开启事务;方法正常执行完毕自动提交;抛出未捕获异常自动回滚。
使用步骤:
- 引入spring‑tx依赖(mybatis自动添加)
xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
- 在public方法/类 上添加
@Transactional
注意:
- 注解加在类上:类中所有public方法都生效
- 注解加在方法上:仅public方法生效;private/protected不会报错,但事务无效
- 建议加在Service业务层,一个业务会包含多次数据库操作。
示例:
java
@Transactional
public String registry(String name,String password){
userService.registryUser(name,password);
int a = 10/0; //抛出运行时异常,事务自动回滚
return "注册成功";
}
注意: 异常被try‑catch捕获,事务不会回滚
java
@Transactional
public String registry(String name,String password){
userService.registryUser(name,password);
try {
int a = 10/0;
}catch (Exception e){
e.printStackTrace();
}
//异常被捕获,Spring认为业务执行成功,事务直接提交,不会回滚
return "注册成功";
}
两种解决方案
- catch之后重新抛出异常,接口返回500
java
catch (Exception e){
e.printStackTrace();
throw e;
}
- 手动标记回滚,接口返回200
java
catch (Exception e){
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
三、@Transactional核心属性详解
1. rollbackFor:指定回滚异常
Spring事务默认只对RuntimeException运行时异常和Error执行回滚 ,普通受检异常不会触发回滚。如果想要对于某个指定的异常进行回滚就要使用rollbackFor.eoe
案例:抛出IOException,程序报错,但是数据依旧入库,事务没有回滚。
想要所有异常都触发回滚,配置rollbackFor = Exception.class
java
@Transactional(rollbackFor = Exception.class)
public void test() throws IOException{
//业务逻辑
}
2. isolation:事务隔离级别
隔离级别解决多个并发事务之间数据读取的问题,解决脏读、不可重复读、幻读。
MySQL原生4种隔离级别:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ UNCOMMITTED读未提交 | 存在 | 存在 | 存在 |
| READ COMMITTED读已提交 | 解决 | 存在 | 存在 |
| REPEATABLE READ可重复读(MySQL默认) | 解决 | 解决 | 存在 |
| SERIALIZABLE串行化 | 解决 | 解决 | 解决 |
Spring提供5种隔离级别枚举:
Isolation.DEFAULT:默认,跟随数据库本身隔离级别Isolation.READ_UNCOMMITTED:读未提交Isolation.READ_COMMITTED:读已提交Isolation.REPEATABLE_READ:可重复读Isolation.SERIALIZABLE:串行化
使用示例:
java
@Transactional(isolation = Isolation.READ_COMMITTED)
3. propagation:事务传播机制
事务隔离级别解决的是多个事务并发访问数据库;事务传播机制解决的是一个方法调用另一个带事务的方法时,事务该如何传递。
场景:A方法带@Transactional,调用同样带@Transactional的B方法。B是加入A的事务?还是新建独立事务?这就是传播机制控制。
Spring有7种传播行为:
| 传播枚举 | 说明 |
|---|---|
| REQUIRED(默认) | 如果当前有事务,就加入当前事务;没有事务新建事务 |
| SUPPORTS | 有事务就加入;没有事务就非事务运行 |
| MANDATORY | 必须存在外部事务,没有直接抛异常 |
| REQUIRES_NEW | 总是新建独立事务;外部存在事务就挂起外部事务 |
| NOT_SUPPORTED | 非事务运行;有外部事务就挂起外部事务 |
| NEVER | 非事务运行,如果存在外部事务直接抛异常 |
| NESTED | 存在外部事务,就创建嵌套子事务(保存点savepoint);无外部事务时等价REQUIRED |
REQUIRED
controller层调用两个方法,这三个方法同属于一个事务


如果内部抛出异常,整个事务全部回滚。
REQUIRES_NEW

内外都是独立事务,互不干扰。
外层调用内层,内层新开事务。如果内层异常回滚,不影响外层事务提交。
场景:希望日志记录失败,不要影响主业务执行,就可以使用REQUIRES_NEW。
NESTED嵌套事务
基于数据库savepoint保存点实现,可以实现局部回滚 。

如果子事务2回滚,父事务和子事务1不会受到影响,可以正常提交;如果父事务回滚,所有子事务都不能正常提交。
NESTED 和 REQUIRED区别
- REQUIRED:多个方法合并为同一个事务,任意位置回滚,整体全部回滚,没有保存点。
- NESTED:父事务下创建子事务保存点;子事务回滚只回滚自己,父事务可以继续提交。