Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。Spring只提供统一事务管理接口,具体实现都是由各数据库自己实现,数据库事务的提交和回滚是通过 redo log 和 undo log实现的。Spring会在事务开始时,根据当前环境中设置的隔离级别,调整数据库隔离级别,由此保持一致。
@Transactional 注解的关键属性大致有九个
参数 意义
isolation 事务隔离级别,默认为DEFAULT
propagation 事务传播机制,默认为REQUIRED
readOnly 事务读写性,默认为false
noRollbackFor 一组异常类,遇到时不回滚,默认为{}
noRollbackForClassName 一组异常类名,遇到时不回滚,默认为{}
rollbackFor 一组异常类,遇到时回滚,默认为{}
rollbackForClassName 一组异常类名,遇到时回滚,默认为{}
timeout 超时时间,以秒为单位
value 可选的限定描述符,指定使用的事务管理器,默认为“
参数 意义
isolation 事务隔离级别,默认为DEFAULT
propagation 事务传播机制,默认为REQUIRED
readOnly 事务读写性,默认为false
noRollbackFor 一组异常类,遇到时不回滚,默认为{}
noRollbackForClassName 一组异常类名,遇到时不回滚,默认为{}
rollbackFor 一组异常类,遇到时回滚,默认为{}
rollbackForClassName 一组异常类名,遇到时回滚,默认为{}
timeout 超时时间,以秒为单位
value 可选的限定描述符,指定使用的事务管理器,默认为“”
(1)Spring事务的种类:
spring支持编程式事务管理和声明式事务管理两种方式:
①编程式事务管理使用TransactionTemplate。
②声明式事务管理建立在AOP之上的。其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前启动一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
声明式事务最大的优点就是不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明或通过@Transactional注解的方式,便可以将事务规则应用到业务逻辑中,减少业务代码的污染。唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。
spring事务的传播机制说的是,当多个事务同时存在的时候,spring如何处理这些事务的行为。事务传播机制实际上是使用简单的ThreadLocal实现的,所以,如果调用的方法是在新线程调用的,事务传播实际上是会失效的。
**
**
隔离级别是指若干个并发的事务之间的隔离程度,与我们开发时候主要相关的场景包括:脏读取、重复读、幻读。
I. Isolation 的 Eum 类中定义了“五个”表示隔离级别的值,如下:
Isolation.DEFAULT:使用各个数据库默认的隔离级别【默认】
Isolation.READ_UNCOMMITTED:读取未提交数据(会出现脏读, 不可重复读)(基本不使用)
Isolation.READ_COMMITTED:读取已提交数据(会出现不可重复读和幻读)
Isolation.REPEATABLE_READ:可重复读(会出现幻读)
Isolation.SERIALIZABLE:串行化
II. 在这里,简单解释下什么是“脏读”,“不可重复读”,“幻读”:
脏读:一个事务读取到另一事务未提交的更新数据;
不可重复读: 在同一事务中,多次读取同一数据返回的结果有所不同。换句话说:后续读取可以读到另一事务已提交的更新数据。相反,"可重复读"在同一事务中多次读取数据时,能够保证所读数据一样,也就是后续读取不能读到另一事务已提交的更新数据;
幻读: 一个事务读到另一个事务已提交的 insert 数据;
III. 最后,有必要补充一下常用数据库的默认隔离级别:
MYSQL:默认为REPEATABLE_READ
SQLSERVER:默认为READ_COMMITTED
Oracle:默认隔离级别 READ_COMMITTED
注意:mysql数据库,当且仅当引擎是InnoDB,才支持事务(MyIsam引擎不支持事务)。
**
**
所谓事务的传播机制是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。
Propagation 的 Eum 类中定义了“七个”表示隔离级别的值,如下:
Propagation.REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是 最常见的选择,也是Spring【默认】的传播机制
Propagation.SUPPORTS:持当前事务,如果当前有事务,就以事务方式执行;如果当前没有事务,就以非事务方式执行
Propagation.MANDATORY:使用当前的事务,且必须在一个已有的事务中执行,如果当前不存在事务,否则抛出异常
Propagation.REQUIRES_NEW:不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
Propagation.NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,就把当前事务挂起
Propagation.NEVER:以非事务方式执行,且必须在一个没有的事务中执行,如果当前存在事务,则抛出异常【与Propagation.MANDATORY相反】
Propagation.NESTED:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则执行与【Propagation.REQUIRED 】类似的操作
**
**
默认情况下是 false(即:不指定只读性),设置为 true 的含义是: 告诉程序该方法下使用的是只读操作,如果进行其他非读操作,则会跑出异常。
I. 事务的只读性,概念:
从这一点设置的时间点开始(时间点a),到这个事务结束的过程中,其他事务所提交的数据,该事务将看不见!!即:查询中不会出现别人在时间点a之后提交的数据。
II. 应用场景:
如果你一次执行单条查询语句,则没有必要启用事务的只读性支持,数据库默认支持SQL执行期间的读一致性;
如果你一次执行多条查询语句,例如统计查询,报表查询。在这种场景下,多条查询SQL必须保证整体的读一致性,否则,在前条SQL查询之后,后条SQL查询之前,数据被其他用户改变,则该次整体的统计查询将会出现读数据不一致的状态。此时,就有必要启用事务的只读性支持。
【注意】:是一次执行多次查询来统计某些信息,这时为了保证数据整体的一致性,要用只读事务。
**
**
用来指明不回滚的条件是哪些异常类或者异常类名。
**
**
用来指明回滚的条件是哪些异常类或者异常类名。
Spring默认情况下会对运行期异常(RunTimeException)进行事务回滚,如果遇到checked异常就不回滚。
**
**
用于设置事务处理的时间长度,阻止可能出现的长时间的阻塞系统或者占用系统资源,单位为秒。
如果超时设置事务回滚,并抛出TransactionTimedOutException异常。
**
**
value 主要用来指定不同的事务管理器,主要用来满足在同一个系统中,存在不同的事务管理器的场景需要。
比如,在Spring中声明了两种事务管理器txManager1,txManager2。然后,用户可以根据需要,修改这个参数来指定特定的txManager。
存在多个事务管理器的情况:在一个系统中,需要访问多个数据源,则必然会配置多个事务管理器。
**
**
@Transactional 注解的看似简单易用,但如果对它的用法一知半解,还是会踩到很多坑的。
我总结了六种常见的失效场景,概要如下,具体分析,请请看我另一篇博文:@Transactional 注解的失效场景,这个问题见过太多的人栽跟头,一篇刨根问底,让面试官都闭嘴
@Transactional 应用在非 public 修饰的方法上,不支持回滚;
@Transactional 注解属性 propagation 设置错误;
@Transactional 注解属性 rollbackFor 设置错误;
在同一个类中方法调用,导致 @Transactional 失效;
异常被你的 catch 处理了,导致 @Transactional 没办法回滚而失效;
数据库配置了不支持事务的引擎,或者数据库本身就不支持事务。
文章来自:https://www.csdn.net/tags/NtzaYg0sMjYyMDgtYmxvZwO0O0OO0O0O.html