谈到事务,我们就绕不开事务的ACID四大特性,我们先来简单介绍一下何为事务
一. 概念
事务是数据库操作的最小工作单元,作为单个逻辑工作单元执行的一系列操作。这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行。事务是一组不可再分割的操作集合,具有四个关键特性,即ACID特性:
- 原子性(Atomicity):事务不可分割,事务中包括的动作要么都做,要么都不做。事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。
- 一致性(Consistency):事务执行后,数据库的状态必须满足所有的约束和规则,保证数据的一致性。事务必须保证数据库从一个一致性状态变到另一个一致性状态。
- 隔离性(Isolation):一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相打扰。
- 持久性(Durability):一旦事务成功完成并提交,它对数据库中数据的改变就是永久性的,接下来的其他操作或故障不应该对其执行结果有任何影响。
二. @Transactional
Spring的事务为我们提供了非常便携的事务功能,只需要你添加@Transactional注解即可.
但需要注意的是,Spring的事务是需要数据库本身具有事务功能的支撑,才能完成事务的功能,也就是他是基于数据库的事务功能进行的.没有数据库事务的支持,Spring是无法提供事务功能的.
我们就从这个注解入手,来看看Spring是怎么完成事务的功能的设计的.
这是注解内的三个属性,简单解释一下他们的含义
2.1 value 与 transactionManager
value与transactionManager表示的是同一个意思,@AliasFor这个注解的含义就是别名,他两的别名都分别对应着彼此,所以这两个属性是一样的.那作用是什么呢? 这两个其实是用来设置你用哪个事务管理器来处理你的事务.
其实对应着就是需要去实现Spring的一个接口TransactionManager
在这个接口下有许多的父接口,其中我们需要知道的就是这个PlatformTransationManger接口
这里用三个方法
- 第一个方法(getTransaction): 获取事务的状态
- 第二个方法(commit): 提交事务
- 第三个方法(rollback): 事务的回滚
也就是简单来讲,这个事务管理器就是用来控制事务的获取、提交、回滚。
那Spring底层是使用哪个具体的实现类呢,我们测试来看看
测试结果为
说明Spring底层的事务管理器是JdbcTransactionManager.
2.2 label
就是一个标签,用来表明这个事务是为了处理什么样的业务场景. 例如现在这个方法是用来处理转账流程的一个业务.那么就可以设置这个label属性为转账.这个作用并不大.平常基本也不使用.
我们接着查看其他属性
2.3 timeout与timeoutString
这两个其实是一样的含义,都表示超时时间,只不过一个是用int类型输入,一个是以字符串的形式输入.默认单位为秒
2.4 readOnly
如果当前事务只与读有关,那么可以开启这个属性,默认情况是false,是不开启的.Spring会对只读事务进行一些性能上的优化.
剩下还有几个属性,如下图所示,几个控制回滚条件.
2.5 rollbackFor 与 rollbackForClassName
rollbackFor以及rollbackForClassName都是用来指定回滚触发的异常是什么,一个是填写类的class,一个是填写类的全限类名的字符串形式.Spring默认的回滚触发的异常是RuntimeException(运行时异常)或者Error异常.
回顾一下异常体系
另外两个noRollbackFor与noRollbackForClassName是类似的,就是指定发生什么异常时不需要回滚,这里就不再赘述了.
重点是接下来两个属性
2.6 propagation(传播级别)
传播级别的含义为,一个大事务里面会有很多的小事务,而传播级别就是用来控制各个事务之间的关系,是包含还是独立,本身的事务出现状况是否会影响其他的事务.
我们大概能把REQUIRED与REQUIRES_NEW分清楚就行,其他的很少使用.
REQUIRED简单理解就是上别人的车.
REQUIRES_NEW就是我自己开车.
举三个例子
A{
B(){ //REQUIRED
F();//REQUIRES_NEW
G();//REQUIRED
H();//REQUIRES_NEW
}
C(){ //REQUIRES_NEW
I();//REQUIRES_NEW
J();//REQUIRED
}
D(){ //REQUIRES_NEW
K();//REQUIRES_NEW
L();//REQUIRES_NEW //点位2
}
E(){ //REQUIRED
M();//REQUIRED
//点位3
N();//REQUIRES_NEW
}
// 点位1
}
- 假设在点位1炸了,出现了异常,会导致什么现象呢?
-
- A事务肯定得炸,那么绑定A事务的会收到牵连
- B事务绑定A,B也炸,G绑定B,所以G事务也会炸.
- E事务绑定A,E也炸,M绑定E,所以M事务也会炸
- 假设在点位2炸了,出现了异常,会导致什么现象呢?
-
- L炸了,由于异常机制将会向上抛,所以会导致D事务也炸了
- D事务炸,A事务也会跟着炸
- 那么后续也就跟点位1的情况一样了.但是E还会炸吗?答案是不会的,因为点位2炸了过后都不会在向下执行
- 假设在点位3炸了,出现了异常,会导致什么现象呢
-
- 那么会导致E炸了,绑定在E事务的M肯定也炸,异常向上抛,所以A事务也得炸
- A炸还是跟点位1炸了的后续一样,只不过N不受影响,因为不会执行到N处
2.7 Isolation (隔离级别)
Spring提供了四种隔离级别,其实跟我们学习MySQL的事务隔离级别是一样的.
- DEFAULT: 默认隔离级别,以连接的数据库的默认级别为准.例如连接的是MySQL数据库,那就以MySQL的隔离级别为准,MySQL默认为REPEATABLE_READ,那么此时就是REPEATABLE_READ.
- READ_UNCOMMITTED: 读未提交.读到未提交的数据.产生脏读
- READ_COMMITTED: 读已提交.读到已提交事务的数据.产生不可重复读
- REPEATABLE_READ: 可重复读. 在同一个事务下可以重复读取到相同的数据.产生幻读
- SERIALIZABLE: 串行化. 所有事务串行执行.