大家所了解的事务Transaction,它是一些列严密操作动作,要么都操作完成,要么都回滚撤销。Spring事务管理基于底层数据库本身的事务处理机制。数据库事务的基础,是掌握Spring事务管理的基础。这篇总结下Spring事务。
事务具备ACID四种特性,ACID是Atomic(原子性)、Consistency(一致性)、Isolation(隔离性)和Durability(持久性)的英文缩写‘’- 原子性(Atomicity)事务最基本的操作单元,要么全部成功,要么全部失败,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。
- 一致性(Consistency)事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。如果事务成功地完成,那么系统中所有变化将正确地应用,系统处于有效状态。如果在事务中出现错误,那么系统中的所有变化将自动地回滚,系统返回到原始状态。
- 隔离性(Isolation)指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。
- 持久性(Durability)指的是只要事务成功结束,它对数据库所做的更新就必须永久保存下来。即使发生系统崩溃,重新启动数据库系统后,数据库还能恢复到事务成功结束时的状态。
spring事务管理机制
spring事务依赖的jar包是 Spring framework
spring事务管理高层抽象主要包括3个接口,spring的事务主要是由他们共同完成的:
1.PlatformTranscationManager:事务管理器-主要用于平台相关事务的管理
2.TranscationDefinition:事务定义信息(隔离,传播,超时,只读)-通过配置如何进行事务管理
3.TranscationStatus:事务具体运行状态-事务管理过程中,每个时间点事务的状态信息
三个接口之间的关系:
1.如果用户需要通过spring管理实务,就需要先配置不同的TranscationManager(不同的持久层框架,有不同的事务管理器实现类)
2.然后根据TranscationDefinition(实务定义信息),通过TranscationManager进行事务管理。
3.最后事务在运行过程中,每个时刻都通过TranscationStatus(事务状态)来了解事务运行状态。
1.1 PlatformTranscationManager事务管理器
PlatformTranscationManager是一个顶层接口,它定义了三个方法:
void commit(); 提交事务
void rollback(); 回滚事务
TranscationStatus getTranscationStatus(); 获取事务状态
spring为不同的持久化层框架提供了不同的PlatformTranscationManager接口实现类:
DataSourceTranscationManager针对JdbcTemplate,MyBatis事务控制,使用connection(连接)进行事务控制:
开启事务:connection.setAutoCommite(false); // 关闭自动提交
提交事务:connection.commit();
回滚事务:connection.rollback();
1.2 TranscationDefinition事务的定义信息
TranscationDefinition也是一个接口,它拥有的方法:
int getIsolationLevel(); // 获取事务隔离等级
String getName(); // 返回当前事务名称
int getPropagationBehavior(); // 返回传播行为
int getTimeout(); // 获取超时时间,默认是-1 不超时
boolean isReadOnly(); // 是否只读
这些事务的定义信息,可以通过配置文件来定制
1.2.1 常用事务的隔离级别 isolationLevel
隔离级别 意义
DEFAULT 使用选用的数据库的默认的隔离级别(spring中的选择项)
注意:不同厂商的数据库,默认的事务隔离等级也是不一样的,比如oracle默认的是READ_COMMITED,mysql默认的 隔离级别是:REPEATABLE_READ
READ_UNCOMMITED 允许读取还未提交的数据,可能导致脏读,幻读、不可重复读
READ_COMMITED 允许在并发事务已经提交后读取,可防止脏读,但是可能会幻读、不可重复读
REPEATABLE_READ 对相同数据的多次读取结果是一致的,除非数据被当前事务改变。可防止脏读、不可重复读,但是幻读仍会发生。
SERIALIZABLE 完全服从ACID的隔离级别,确保不发生脏读,幻读,不可重复读。这在所有事务级别中是最慢的,它是通过完全锁定事 务中数据所涉及的表来实现的
这里,我们解释一下脏读,不可重复读,幻读:
脏读:一个事务读取了另一个事务还未提交的数据,如果这些数据被回滚了,那读取到的数据是无效的。
不可重读:在同一个事务中,多次读取同一数据,返回的结果不同。意味着后面读取到的数据,是另一个事务提交后的数据。
可重复读:在同一个事务中,多次读取同一数据,能够保证读取到的结果是一样的,也就是后续读取数据不会读取到其他事务提交后的数据。
幻读:一个事物读取了几行记录后,另一个事务插入了一些新纪录,再后来的查询中,第一个事物读取到了之前没有的记录,这就发生了幻读。
1.2.2 PropagationBehavior事务的传播行为
什么是事务的传播行为?有什么作用?
事物的传播行为,用于解决两个被事务管理的方法之间互相调用的问题。
业务层中有两个方法,有些时候需要两个方法处于同一个事务中,有些时候又需要两个方法不能在同一个事务中。
事务传播行为的7种类型
事务传播行为类型 说明
PROPAGATION_REQUIRED 支持当前事务,如果不存在,就新建一个
PROPAGATION_SUPPORTS 支持当前事务,如果不存在,就不使用事务
PROPAGATION_MANDATORY 支持当前事务,如果不存在,抛出异常
PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事务,创建一个新事物
PROPAGATION_NOT_SUPPORTS 以非事务方式运行,如果有当前事务,挂起当前事务
PROPAGATION_NEVER 以非事务方式运行,如果有当前事务,抛出异常
PROPAGATION_NESTED 如果当前事务存在,则嵌套事务执行,只对DataSourcePlatformTranscationManager起效
主要分为三大类:
PROPAGATION_REQUIRED(默认),PROPAGATION_SUPPORTS,PROPAGATION_MANDATORY
支持当前事务,A调用B,如果A有事务存在,B和A处于同一个事务,一起提交或一起回滚。
事务的默认传播行为,PROPAGATION_REQUIRED,最常用
PROPAGATION_REQUIRES_NEW,PROPAGATION_NOT_SUPPORTS,PORPAGATION_NEVER
不支持当前事务,A调用B,如果A有事务存在,B肯定不会和A处在同一事务
常用的事务传播行为,PORPAGATION_REQUIRES_NEW
PORPAGATION_NESTED
嵌套事务,只对DataSourcePlatformTranscationManager有效,底层使用JDBC的Savepoint机制,允许在同一个事务设置保存点,回滚到保存点。
REQUIRED,REQUIRES_NEW,NESTED的区别:
REQUIRED:支持当前事务,不存在事务,创建新事务
REQUIRES_NEW:不支持当前事务,如果存在事务,挂起当前事务,创建新事物
NESTED:嵌套事务,事务可以设置保存点,回滚到保存点
1.3 TranscationStatus事务状态
TranscationStatus也是一个接口,它含有一下方法:
void flush(); // 发送,执行SQL语句
boolean hasSavepoint(); // 是否有保留点
boolean isCompleted(); // 执行完了没有
boolean isNewTranscation(); // 是否是新建的事务
boolean siRollbackOnly(); // 判断是否为只能回滚的事务,通常用于进行测试,不希望测试操作真的操作到数据库
void setRollbackOnly(); // 设置事务是否回滚
事务的结束:必须通过commit确认事务的提交,rollback作用是标记为回滚,比如回滚到未操作的时候。
try{
操作数据库
}catch(){
// 如果发生异常了,我们回滚到未操作的时候
rollback();
}finally{
// 执行完毕,提交
commit;
}
1.4spring事务管理方式
两种方式:
1.编程式的事务管理
通过TranscationTemplate手动管理事务。
在实际开发中很少用到,原因是需要修改原有代码,加入事务控制代码(侵入性)
2.通过xml配置文件或者在类中添加注解的方式
spring的声明式事务是通过AOP实现的(环绕通知)
方法执行前,开启事务,方法执行后,关闭事务。
Spring如何配置事务
2.1 xml配置方式添加事务管理(tx事务,aop切面)
操作思路:AOP三步走
1.确定目标:需要对service层的方法,配置切入点
2.需要配置Adivce(环绕通知),方法执行前开启事务,方法执行后关闭事务
3.配置切面和切入点
在spring的配置文件中,我们首先引入数据源的配置文件,然后通过数据源驱动类,创建dataSource,指定数据源的驱动类名称,url,用户名,密码。
然后呢,我们通过dataSource,来配置事务管理器,
配置事务通知
transfer是service层中一个业务方法的名称。
rollback-for就是指定一个地方,当事务要回滚的时候,可以指定事务只回滚到rollback-for这里。
配置切入点和切面
其中expression是表达式,bean(*service)表达所有配置文件中,id或者name为service结尾的bean
配置的dao层和service层
2.2 基于注解的方式实现事务管理
annotations
spring的配置文件中,可以不用再配置service,dao的bean和事务通知,AOP的bean了。因为它们会在项目中基于注解实现。
反之,需要配置 注解扫描,并制定扫描的包,确保可以扫描到注解
基于注解的方式配置事务,还需要配置事务相关的注解驱动
再给需要事务的方法,加上事务的注解
虽然执行会报错,但是因为被事务管理了,报错后发生了回滚,数据库的数据不会改变。
@Transactional注解,可以针对方法去加,只对方法进行管理,也可以加到类上,对整个类进行事务管理
那事务的隔离级别,传播行为怎么控制?
ReadyOnly属性,设置为false,说明该事务会对数据库进行增删改操作,设置为true,说明该事务只对数据库进行查的操作。
timeout默认设置为-1,意思是不超时。
就近原则,当方法上的注解和类上的注解冲突后,优先以方法上的注解为准。
杂记
- 事务属性这个概念,事务属性通常由事务的传播行为,事务的隔离级别,事务的超时值和事务只读标志组成。这些属性都是在使用@Transactional可以指定的。
- 底层原理:Spring事务其实就是Spring AOP,底层创建动态代理对象,在代码的开头结尾封装了开启事务和事务回滚操作。用过JDBC原生代码的更应该清楚了,都是显示在代理里commit和rollback的。然后一大堆try catch。
- 在service类前加上,声明这个service所有方法需要事务管理。每一个业务方法开始时都会打开一个事务。或者加在service层方法上,@Transactional只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能..