Spring 事务
关于理论性的内容,我在之前的一篇文章中介绍过,这里不再过多阐述,这里给出之前文章的链接:Spring 事务管理
什么是事务
是一组逻辑操作,要么执行,要么不执行。
事务的特性
ACID
(原子性、一致性、隔离性、持久性)
并发事务带来的问题
- 脏读
- 丢失修改
- 不可重复读
- 幻读
配置事务管理器
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 数据源 -->
<property name="dataSource" ref="dataSource" />
</bean>
Spring 事务接口
PlatformTransactionManager
:(平台)事务管理器TransactionDefinition
: 事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)TransactionStatus
: 事务运行状态
1. PlatfromTransactionManager 接口介绍:
该接口主要有三个方法:
Public interface PlatformTransactionManager()...{
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
Void commit(TransactionStatus status) throws TransactionException;
Void rollback(TransactionStatus status) throws TransactionException;
}
getTransaction
:根据指定的传播行为,返回当前活动的事务或创建一个新事务commit
:使用事务目前的状态提交事务rollback
:对执行的事务进行回滚
一般情况下用的比较多的就是 commit
提交事务和 rollback
回滚事操作了。
2. TransactiDefinition 接口介绍:
该接口主要定义了一些表示事务属性(如隔离级别,传播行为等)的常量。当然还有一些方法,仅有篇幅,不对方法做介绍。
(1)事务隔离级别(定义了一个事务可能受其他并发事务影响的程度):
TransactionDefinition 接口中定义了五个表示隔离级别的常量:
TransactionDefinition.ISOLATION_DEFAULT
: 使用后端数据库默认的隔离级别。Mysql 默认采用REPEATALBE-READ
隔离级别;Oracle 默认采用READ_COMMITTED
隔离级别。TransactionDefinition.ISOLATION_READ_UNCOMMITTED
:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。TransactionDefinition.ISOLATION_READ_COMMITTED
:允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。TransactionDefinition.ISOLATION_REPEATABLE_READ
:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。TransactionDefinition.ISOLATION_SERIALIZABLE
:最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
(2)事务传播行为(为了解决业务层方法之间互相调用的事务问题):
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
支持当前事务的情况:
TransactionDefinition.PROPAGATION_REQUIRED
:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。TransactionDefinition.PROPAGATION_SUPPORTS
:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。TransactionDefinition.PROPAGATION_MANDATORY
: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)
不支持当前事务的情况:
TransactionDefinition.PROPAGATION_REQUIRES_NEW
:创建一个新的事务,如果当前存在事务,则把当前事务挂起。TransactionDefinition.PROPAGATION_NOT_SUPPORTED
: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。TransactionDefinition.PROPAGATION_NEVER
:以非事务方式运行,如果当前存在事务,则抛出异常。
其他情况:
TransactionDefinition.PROPAGATION_NESTED
:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED
。
(3)TransactionStatus 接口介绍
TransactionStatus接口用来记录事务的状态 该接口定义了一组方法,用来获取或判断事务的相应状态信息。
TransactionStatus 接口方法如下:
public interface TransactionStatus{
boolean isNewTransaction(); // 是否是新的事物
boolean hasSavepoint(); // 是否有恢复点
void setRollbackOnly(); // 设置为只回滚
boolean isRollbackOnly(); // 是否为只回滚
boolean isCompleted; // 是否已完成
}
举例
事务操作一般都是在 service 层编写,结合增删改查方法。我举个栗子:
class Test {
private PlatformTransactionManager transactionManager;
public void saveFromDB() {
DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus transactionStatus = transactionManager.getTransaction(defaultTransactionDefinition);
try {
// 增删改查操作(使用 dao 中的方法)
//...
transactionManager.commit(transactionStatus);
// 删除本地缓存操作
//...
} catch (Exception e) {
transactionManager.rollback(transactionStatus);
}
}
}
这里需要注意的是:当我们执行增删查改操作时,应该在 service 层添加事务,并且删除之前的缓存(本地缓存和 redis 缓存),然后再执行增删查改操作,这三方面都兼顾到了才是一个比较好的增删查改操作。