Spring中的事务隔离级别和传播机制
一、什么是事务?
在这之前,我们先来回顾一下什么是事务。
设想一个场景,有一个表叫table,里面有三个字段分别为字段1、字段2。我们现在做了一个程序——当字段1的值减少1,那么字段2的值就增加1。这时候,我们就需要两句sql语句去执行这个操作:sql1=“让字段1减少1”,sql2=“让字段2增加1”。这时候,先执行sql1,再执行sql2就完成了我们的需求。但是,有时候事情总不会那么顺利,如果sql1执行之后,程序突然出错了嗝屁了,那么问题就很尴尬了,字段1的值减少了1,但是字段2却没有增加。那么,如何应对这种情况呢?这时候,就需要用到事务了。
事务的主要作用可以理解为控制sql语句的执行,一个事务中的更新操作,要么都执行,要么都不执行,这个特征也叫做事务的原子性。一般情况下,只有在事务执行完毕,调用commit的时候,才会对数据库中的数据进行修改(如果表中的主键是自增长的话,就算不执行commit,也会发生主键值增加的情况)。
事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。
二、什么是事务的隔离?
现在,我们继续设想一个场景,一个程序有两个事务分别为事务1,事务2,前面提到了,只有当事务commit的时候,表中的数据才会改变。如果现在,事务1已经对一个表的某一行进行了操作,但是由于事务1没有commit,所以这一列的数据并没有改变,但是!尽管事务1没有提交,但是它也没有回滚,所以程序会认为此时该行的数据是已经修改过的数据了。这时候,如果事务2再去读这行数据,读到的是没有被修改过的数据,这时候,问题就出现了,事务1提供给程序的数据是新数据,然而事务2读到的信息却是旧数据。这非常致命,稍有不慎就会引起所有操作的回滚。 而这种情况,叫脏读。
除了脏读之外,使用事务的时候还有其他的问题,比如:
-
更新丢失
-
不可重复读
一个事务对同一行数据重复读取两次,但是却得到了不同的结果。
包括以下情况
-
-
- 虚读:事务1查询了某数据之后,事务2对该数据进行了修改,事务1再去读取该数据时,就会发现两次数据的数据结果不一样
- 幻读:事务1查询了某些数据之后,事务2对这些数据进行了插入或者删除操作,事务1再去查询这些数据时,就会发现第二次读取时的结果集和第一次查询的不一样。
-
其实上面的情况和Java中的多线程操作一个资源时会发生的情况一样。为了避免上面的情况发生,我们需要把每个事务隔离起来,人为的规定好两个事务的相互影响程度。甚至让两个事务完全隔离,不让他们互相影响,这就叫事务的隔离。规定事务之间的相互影响程度,就是规定事务的隔离级别,不同的隔离级别对事务的处理不同。
在标准SQL规范中,定义了4个事务隔离级别。
-
未授权读取(Read Uncommitted)
-
- 也称为读未提交:允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,就不让别的事务数据同时进行写操作,但是允许其他事务读此行程序
-
授权读取(Read Committed)
- 也成为读提交:允许不可重复读取,但不允许脏读取。这可以通过“瞬间共享读锁”
-
可重复读取(Repeatable Read)
- 禁止不可重复读取和脏读取,但是有时可能出现幻读数据。读取数据的事务会禁止写事务(但允许读事务),写事务则禁止任何其他事务。
-
序列化(Serializable)
- 提供严格的事务隔离。它要求事务序列化执行,事务只能一个接一个的执行,不能并发执行。
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
三、什么是事务的传播级别?
继续设想一个场景,一个程序中有两个method,分别为method1和method2。当我们为方法method1配置了事务,当method1执行的时候,就会开启一个事务,只有等到method1执行完毕,这个事务才会被提交。这时候,如果method1调用了method2,method2就会有几种选择:1、加入method1创建的事务、2、自己新开一个事务嵌套着method1的事务运行、2、不以事务的形式执行,并且还要把method1的事务暂停,等到method2执行完毕再继续method1的事务等等。。 这个method2做出的选择,就是事务的传播级别(行为),我们可以为method2配置传播级别,让他在遇到这种情况时,知道怎么选择。
在TransactionDefinition接口中定义了七个事务传播行为。

PROPAGATION_REQUIRED 如果存在一个事务,则加入当前事务。如果没有事务则开启一个新的事务。
PROPAGATION_SUPPORTS 如果存在一个事务,加入当前事务。如果没有事务,则非事务的执行。但是对于事务同步的事务管理器,PROPAGATION_SUPPORTS与不使用事务有少许不同。
PROPAGATION_MANDATORY 如果已经存在一个事务,加入当前事务。如果没有一个活动的事务,则抛出异常。
PROPAGATION_REQUIRES_NEW 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
PROPAGATION_NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务。
PROPAGATION_NEVER 总是非事务地执行,如果存在一个活动事务,则抛出异常
PROPAGATION_NESTED如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行
四、如何在spring中配置事务?
spring中对事务的控制主要依赖AOP
1、使用xml的方式配置事务
①导入依赖
1 <dependency> 2 <groupId>org.springframework</groupId> 3 <artifactId>spring-tx</artifactId> 4 <version>5.0.2.RELEASE</version> 5 </dependency>
②将主配置文件中的头目录增加支持事务的声明
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
③配置事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
④配置事务的通知以及需要用到事务的方法
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--
isolation="" 指定事务的隔离级别,默认是数据库中设定的隔离级别。如果该属性的设定值和数据库的设定值不一样,则以该属性值为准
propagation="" 指定事务的传播行为,默认是REQUIRED
read-only="" 用于指定事务是否只读 默认值是false
rollback-for="" 用于指定一个异常,当产生该异常时,事务回滚。产生其他事务时,不回滚。 没有默认值
no-rollback-for="" 用于指定一个异常,当产生该异常时,事务不回滚
timeout="" 用于指定事务的超时时间,单位是秒
-->
<tx:attributes>
<tx:method name="*" isolation="DEFAULT"/> <-- 这里就是配置要用到事务的方法和事务的隔离级别,传播行为等。name属性可以使用*通配符-->
</tx:attributes>
</tx:advice>
⑤配置切入点表达式以及建立切入点表达式和事务通知的对应关系
<aop:config>
<aop:pointcut id="pt1" expression="execution(* com.sunlin.service.impl.*.*(..))"/> <--这里配置切入点表达式-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor> <--这里让切入点表达式和事务通知建立关系-->
</aop:config>

浙公网安备 33010602011771号