本节学习在 Spring 框架中使用事务。
数据库事务( Transaction):是指操作数据库的一组操作序列,它可能包含查询或者更新操作,这组操作在执行过程中是一个逻辑单位,要么一起成功,要么一起失败。
事务包含了一个序列的对数据库的读/写操作,具有以下特性::
数据库事务拥有以下四个特性,习惯上被称之为ACID特性。
原子性(Atomicity)
:事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。一致性(Consistency)
:事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。隔离性(Isolation)
:多个事务并发执行时,一个事务的执行不应影响其他事务的执行。持久性(Durability)
:已被提交的事务对数据库的修改应该永久保存在数据库中。SQL国际标准 使用三个过程来描述事务:
Spring 使用 PlatformTransactionManager 类来管理事务,根据不同的数据访问框架提供不同的实现,主要有下面这些API:
类(Class) | 说明 |
---|---|
PlatformTransactionManager | 事务管理器 |
TransactionDefinition | 事务的定义信息,包括隔离级别,传播,超时时间设置等。 |
TransactionStatus | 事务的运行时状态。 |
这三个类构成了 Spring 事务管理的主要内容,下面分别说明。
PlatformTransactionManager 负责管理事务的开始,提交和回滚。
看下接口定义:
public interface PlatformTransactionManager extends TransactionManager {
// 开始一个事务
TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
// 提交事务
void commit(TransactionStatus var1) throws TransactionException;
// 回滚事务
void rollback(TransactionStatus var1) throws TransactionException;
}
Spring 可根据不同的数据访问框架选择不同的具体 事务管理器实现。
TransactionDefinition 包含了事务的定义信息,包括隔离级别,传播,超时时间设置等
事务在执行过程中要与其他事务隔离,不得影响其他事务的执行。由并发的的访问可能引发一系列的数据访问问题,Spring 的事务隔离级别定义了一些事务的隔离策略:
隔离级别 | 说明 | 解释 |
---|---|---|
DEFAULT (默认) | 默认级别 | 使用数据库自身默认的事务隔离级别 |
READ_UNCOMMITTED (读未提交) | 允许读取到未提交的数据 | 允许其他事务读取到这个事务已修改而未提交的数据 |
READ_COMMITTED (读已提交) | 允许读取已提交的数据 | 这个事务修改且提交后,才可用被其他事务读取到。 |
REPEATABLE_READ (可重复读) | 允许多次读取相同字段时数据一致,期间字段不可被其他事务修改。 | 读取指定字段数据期间,其他事务不能修改这个数据。 |
SERIALIZABLE(串行化) | 事务被处理成按顺序执行 | 按序列执行事务,即便新增记录的影响也是排队执行后发生。 |
脏读 当一个事务允许读取另外一个事务修改但未提交的数据时,就可能发生脏读(dirty reads)。
不可重复读 在一次事务中,当一行数据获取两遍得到不同的结果表示发生了不可重复读(non-repeatable reads).
幻读 在事务执行过程中,当两个完全相同的查询语句执行得到不同的结果集。这种现象称为“幻读(phantom read)”。幻读 强调的是结果集的数量不同。
从下表可以看到,隔离级别越向下,可能发生的问题越少。不过性能也越来越低。
隔离级别 | 脏读 | 不可重复读 | 幻影读 |
---|---|---|---|
READ_UNCOMMITTED | 可能发生 | 可能发生 | 可能发生 |
READ_COMMITTED | - | 可能发生 | 可能发生 |
REPEATABLE_READ | - | - | 可能发生 |
SERIALIZABLE | - | - | - |
在基于锁的并发控制中,隔离级别决定了锁的持有时间。如果锁在语句执行完毕就释放则另外一个事务就可以在这个事务提交前修改锁定的数据,从而造成混乱。
隔离级别 | 写操作 | 读操作 |
---|---|---|
READ_UNCOMMITTED | 持有锁到该语句完毕 | 持有锁到该语句完毕 |
READ_COMMITTED | 持有锁到提交时 | 持有锁到该语句完毕 |
REPEATABLE_READ | 持有锁到提交时 | 持有锁到提交时 |
SERIALIZABLE | 持有锁到提交时 | 持有锁到提交时 |
Spring事务传播机制规定了事务方法和事务方法发生嵌套调用时事务如何进行传递。即:面对在已有事务中被调用后怎么处理自身的事务。
REQUIRED | 支持当前事务,如果没有事务会创建一个新的事务 |
SUPPORTS | 支持当前事务,如果没有事务的话以非事务方式执行 |
MANDATORY | 支持当前事务,如果没有事务抛出异常 |
REQUIRES_NEW | 创建一个新的事务并挂起当前事务 |
NOT_SUPPORTED | 以非事务方式执行,如果当前存在事务则将当前事务挂起 |
NEVER | 以非事务方式进行,如果存在事务则抛出异常 |
NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。 |
TransactionStatus 类里描述了一些运行时事务的状态和方法。
它包含这些方法:
boolean hasSavepoint();
// 刷新
void flush();
boolean isNewTransaction();
void setRollbackOnly();
boolean isRollbackOnly();
//是否结束
boolean isCompleted();
// 检查点 相关的操作:创建,回滚,释放
Object createSavepoint() throws TransactionException;
void rollbackToSavepoint(Object savepoint) throws TransactionException;
void releaseSavepoint(Object savepoint) throws TransactionException;
获得注入的 TransactionTemplate 实例,执行 execute 方法。
public class DemoDao {
@Autowired
TransactionTemplate transactionTemplate;
public void method1() {
transactionTemplate.execute(new TransactionCallback<Void>() {
@Override
public Void doInTransaction(TransactionStatus status) {
// 在这里写 具体的数据库操作调用
return null;
}
});
}
}
使用 @Transactional 注解 作用在方法上即可。
@Transactional
public void method2() {
// 在这里写 具体的数据库操作调用
}
前面说过,Srping 可根据不同的持久层框架可选择不同的的 事务管理器的实现,比如:
DataSourceTransactionManager | JDBC 和 mybatis 时使用 |
JpaTransactionManager | 使用 JPA 时使用 |
HibernateTransactionManager | 使用 Hibernate 时使用 |
JtaTransactionManager | 使用分布式事务 JTA 时使用 |
https://zh.wikipedia.org/wiki/%E6%95%B0%E6%8D%AE%E5%BA%93%E4%BA%8B%E5%8A%A1 https://www.imooc.com/video/9323 https://zh.wikipedia.org/wiki/%E4%BA%8B%E5%8B%99%E9%9A%94%E9%9B%A2