Spring事务

Spring 事务可以简化对常规事务的一些操作,比如获取连接、关闭连接、事务提交和回滚等,在使用 Spring 事务时,有一个非常重要的概念就是事务属性,事务属性通常由事务的传播行为、隔离级别、超时时间和只读标志组成

什么是事务

事务是指一个工作单元,它包含了一组数据操作命令,并且所有的命令作为一个整体一起向系统提交或撤消请求操作,即这组命令要么都执行,要么都不执行

事务的特性

  • 原子性(Atomicity):事务中的操作要么全部完成,要么全部不完成
  • 一致性(Consistency):事务执行的结果必须是从一个一致性状态转换到另一个一致性状态
  • 隔离性(Isolation):事务的执行不能被其他事务干扰
  • 持久性(Durability):事务一旦提交,它对数据的改变就应该是永久性的

并发事务带来的问题

  • 脏读(Dirty read):当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据,因为这个数据还没有提交,那么另外一个事务读到的这个数据是“脏数据”,根据“脏数据”所做的操作可能是不正确的
  • 丢失修改(Lost to modify):在一个事务正在读取数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据,这样第一个事务内的修改结果就被丢失,因此称为丢失修改
  • 不可重复读(Unrepeatable read):指在一个事务内多次读同一数据,在这个事务还没有结束时,另一个事务也访问该数据,那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样,这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读
  • 幻读(Phantom read):幻读与不可重复读类似,它发生在一个事务读取了几行数据,接着另一个事务插入了一些数据时,在随后的查询中,第一个事务就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读

不可重复度和幻读的区别:不可重复读的重点是修改和删除,幻读的重点在于新增

事务的分类

编程式事务

通过在代码中显示调用 commit()、rollback() 等方法来精确定义事务的边界,使用 TransactionTemplate 或者 PlatformTransactionManager,对于编程式事务,Spring 推荐使用TransactionTemplate

声明式事务

声明式事务是建立在 AOP 之上的,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务
声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需修改配置文件或者添加相应注解,便可以将事务规则应用到业务逻辑中

核心接口

PlatformTransactionManager

Spring 并不会直接管理事务,而是通过此接口为各个平台提供对应的事务管理器

1
2
3
4
5
6
7
8
9
10
11
public interface PlatformTransactionManager extends TransactionManager {

// 获取事务状态信息
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;

// 提交事务
void commit(TransactionStatus status) throws TransactionException;

// 回滚事务
void rollback(TransactionStatus status) throws TransactionException;
}
TransactionDefinition

此接口提供了获取事务属性的一些方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public interface TransactionDefinition {

// 获取事务的传播行为
default int getPropagationBehavior() {
return PROPAGATION_REQUIRED;
}

// 获取事务的隔离级别
default int getIsolationLevel() {
return ISOLATION_DEFAULT;
}

// 获取事务的超时时间
default int getTimeout() {
return TIMEOUT_DEFAULT;
}

// 获取事务是否只读
default boolean isReadOnly() {
return false;
}

// 获取事务的名称
default String getName() {
return null;
}

static TransactionDefinition withDefaults() {
return StaticTransactionDefinition.INSTANCE;
}
}
TransactionStatus

此接口提供一些查询事务状态的方法

1
2
3
4
5
6
7
8
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {

// 是否有保存点
boolean hasSavepoint();

// 刷新
void flush();
}

事务传播行为

名称 说明
PROPAGATION_REQUIRED 支持当前事务,如果不存在事务,则创建一个新的事务,这是默认的设置
PROPAGATION_SUPPORTS 支持当前事务,如果不存在事务,则以非事务方式运行
PROPAGATION_MANDATORY 支持当前事务,如果不存在事务,则抛出异常
PROPAGATION_REQUIRES_NEW 创建一个新的事务,如果当前存在事务,则将当前事务挂起
PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果当前存在事务,则将当前事务挂起
PROPAGATION_NEVER 以非事务方式运行,如果当前存在事务,则抛出异常
PROPAGATION_NESTED 如果存在当前事务,则在嵌套事务中执行,否则类似于 PROPAGATION_REQUIRED

事务隔离级别

名称 说明
ISOLATION_DEFAULT 使用底层数据存储的默认隔离级别
ISOLATION_READ_UNCOMMITTED 允许读取尚未提交的更改,可能导致脏读、幻读和不可重复读
ISOLATION_READ_COMMITTED 允许读取已提交的并发事务,防止脏读,可能出现不可重复读和幻读,这是 Oracle 的默认级别
ISOLATION_REPEATABLE_READ 多次读取相同字段的结果是一致的,防止脏读和不可重复读,可能出现幻读,这是 MySQL 的默认级别
ISOLATION_SERIALIZABLE 最高代价但是最可靠的事务隔离级别,事务被处理为顺序执行,防止脏读、不可重复读和幻读