# Spring事务
作者:Ethan.Yang
博客:https://blog.ethanyang.cn (opens new window)
相关源码参考: Spring源码中文注释仓库5.3.x分支 (opens new window)
Spring 事务的本质其实就是数据库对事务的支持, 没有数据库的事务支持, Spring 是无法提供事务功能的。
# 1. 事务基本概念
事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。
特点
原子性
一个事务是一个不可分割的工作单位, 事务中包括的诸操作要么都做, 要么都不做。
一致性
事务必须是使数据库从一个一致性状态变到另一个一致性状态。
隔离性
一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性
一个事务一旦提交, 它对数据库中数据的改变就应该是永久性的。
# 2. 事务的基本原理
对于JDBC操作数据库可以按如下步骤操作
获取连接
Connection con = DriverManager.get Connection()
开启事务
con.setAutoCommit(true/false);
执行业务代码CRUD
提交事务/回滚事务
con.commit() / con.rollback()
关闭连接
conn.close()
在使用 Spring 事务管理后, 2和4步骤由spring完成, 以注解使用方式为例, 使用 @Transactional 标记对应的类或者方法。那么怎么实现2 4操作得了? 结合上节 AOP 的思想, 很容易理解, 在 Spring 启动时会扫描这些被标记的类或者方法为其生成代理类, 其中开启事务以及事务回滚都是由代理类完成的, 代理类也只是基于数据库的事务进行操作, 本质上是由数据库通过binlog或者redo logo实现的。
# 3. Spring事务的传播特性
事务的传播特性就是在有多个数据库事务时, Spring如何处理这些事务, 其中前三个较为常用
| 属性名(Propagation) | 作用说明 |
|---|---|
REQUIRED(默认) | 如果当前存在事务,则加入;如果没有,则新建一个事务。 |
REQUIRES_NEW | 总是新建一个事务,如果存在事务,暂停当前事务。 |
NESTED | 如果当前存在事务,则创建嵌套事务撤销点, 在回滚时可以局部回滚,不影响外层事务整体。否则行为类似 REQUIRED。 |
SUPPORTS | 如果存在事务则加入事务;如果没有事务,则以非事务方式执行。 |
NOT_SUPPORTED | 总是以非事务方式执行,如果当前有事务,则挂起当前事务。 |
NEVER | 必须在没有事务的情况下执行;如果当前有事务则抛出异常。 |
MANDATORY | 必须存在事务,否则抛出异常。 |
NESTED需要注意的事, 可以部分回滚, 有嵌套事务时可以只回滚子事务, 而主事务依旧执行
以ServiceA.methodA 调用 ServiceB.methodB 为例
如果A B都是REQUIRED, 当执行到mb时, mB会使用和mA的事务
如果A是REQUIRED, B是REQUIRES_NEW, mB会新建一个事务, 并暂停mA的事务
如果A是REQUIRED, B C都是NESTED, 如下代码
@Transactional(propagation = Propagation.REQUIRED)
public void A() {
try {
B(); // B 是 NESTED
} catch (Exception e) {
C(); // C 是 NESTED
}
}
// B异常会执行C, 而不会使外层事务A回滚, BC都是在A的事务中加了各自的撤销点用于局部回滚
2
3
4
5
6
7
8
9
# 4. 数据库的隔离级别
数据库事务的隔离级别(Isolation Level)用于控制多个事务并发执行时对数据库数据可见性的控制程度。
READ UNCOMMITTED(读未提交)
定义:一个事务可以读取另一个事务尚未提交的数据。
问题:
- 可能发生脏读:读取到另一个事务尚未提交的数据,如果那个事务回滚了,就读取到了无效数据。
并发性:最高(因为几乎不加锁)
一致性:最弱
READ COMMITTED(读已提交)
定义:一个事务只能读取另一个事务已经提交的数据。
能防止的问题:防止脏读。
可能的问题:
- 可能发生不可重复读:同一个事务中多次读取同一条数据,结果可能不一致(因为其他事务可能已经修改并提交)。
数据库支持:这是大多数数据库(如 Oracle、SQL Server)的默认隔离级别。
REPEATABLE READ(可重复读)
定义:在同一个事务中,多次读取同一条记录,读取结果始终一致(除非自己修改)。
能防止的问题:
- 防止脏读、不可重复读。
可能的问题:
- 可能发生幻读:事务中两次相同条件的查询,结果集不一样,因为其他事务可能插入了新数据。
数据库支持:MySQL InnoDB 的默认隔离级别就是 REPEATABLE READ。
SERIALIZABLE(可串行化)
定义:强制事务串行执行,相当于在每条数据上加锁,保证最高的一致性。
能防止的问题:
- 防止脏读、不可重复读、幻读。
缺点:
- 并发性能最差,容易产生大量锁等待和死锁。
** 使用场景**:对一致性要求极高的关键场景,如银行转账、库存扣减等高价值数据操作。
# 5. Spring 中的隔离级别
常量(Isolation) | 对应数据库隔离级别 | 说明描述 | 可防止的问题 |
|---|---|---|---|
Isolation.DEFAULT | 使用数据库默认隔离级别 | Spring 不做额外控制,采用底层数据库的默认设置(如 MySQL 是 REPEATABLE READ) | 取决于底层数据库 |
Isolation.READ_UNCOMMITTED | 读未提交 | 可以读取未提交数据,存在脏读 | 无 |
Isolation.READ_COMMITTED | 读已提交 | 只能读取已提交数据,防止脏读 | ✔ 脏读 |
Isolation.REPEATABLE_READ | 可重复读 | 防止不可重复读,同一事务多次读相同数据结果一致 | ✔ 脏读、✔ 不可重复读 |
Isolation.SERIALIZABLE | 串行化 | 最严格,事务串行执行,完全避免并发问题 | ✔ 脏读、✔ 不可重复读、✔ 幻读 |
# 6. Spring 事务 API总览

JdbcTemplate - 这是经典的也是最常用的 Spring 对于JDBC 访问的方案。这也是最低级别的封装,其他的工作模式事实上在底层使用了 JdbcTemplate 作为其底层的实现基础。
← MVC 详解