Spring事务的事务属性
Posted 骑着乌龟去看海
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring事务的事务属性相关的知识,希望对你有一定的参考价值。
大家都知道,Spring的声明式事务是通过事务属性来定义的,而spring的事务属性包含了5个方面:传播行为,隔离级别,是否只读,事务超时,回滚规则;
传播行为
传播行为,是属于事务边界相关的属性,定义了何时要创建一个事务或何时使用已有的事务。
Spring提供了7种不同的传播行为:
PROPAGATION_REQUIRED 该方法必须在事务中运行,如果当前事务不存在,则启动一个新的事务,如果当前事务存在,就在这个事务中执行,是最常见的;
PROPAGATION_MANDATORY 该方法必须在事务中进行,如果没有事务,则抛出一个异常;
PROPAGATION_NESTED 如果当前存在事务,那么该方法将会在嵌套事务内运行,嵌套事务可以独立于当前事务进行单独的提交或回滚。如果当前没有事务,则进行与PROPAGATION_REQUIRED相同的操作;
PROPAGATION_SUPPORTS 表示该方法不需要必须在事务中运行,如果当前存在事务,则运行在该事务中,如果不存在,则按非事务方式运行;
PROPAGATION_REQUIRES_NEW 表示方法必须运行在它自己的事务中。如果当前存在事务,则该事务会被挂起,会启动一个新的事务来执行方法;
PROPAGATION_NOT_SUPPORTED 表示该方法不应该运行在事务中,如果当前存在事务,则在方法运行期间,该事务会被挂起;
PROPAGATION_NEVER 表示该方法不应该运行在事务中,如果当前存在事务,则会抛出一个异常;
如果声明了方法的传播行为为PROPAGATION_REQUIRES_NEW ,这就意味着事务边界与方法自己的边界是一样的:方法在开始执行的时候启动一个新事务并在方法返回或抛出异常时结束该事务。
隔离级别
隔离级别决定了一个事务会被其他并行的事务所能影响的程度
1. 为什么要有事务隔离级别以及能解决的问题 (why)?
在一个多用户,用并发的系统中,为了保证数据的一致性和完整性,避免出现脏读、不可重复读、幻读等情况,引入了事务隔离机制的概念;
2. 脏读、不可重复读、幻读概念?
脏读:一个事务读取到另一事务未提交的更新新据。当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据, 那么另外一个事务读到的这个数据是脏数据,依据脏数据所做的操作也可能是不正确的;
不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事务已提交的更新数据。相反,“可重复读”在同一事务中多次读取数据时,能够保证所读数据一样,也就是,后续读取不能读到另一事务已提交的更新数据。
幻读:和不可重复读类似,但不同的是不可重复读的是update,幻读的是insert。事务T1执行一次查询,然后事务T2新插入一行记录,这行记录恰好可以满足T1所使用的查询的条件。然后T1又使用相同的查询再次对表进行检索,但是此时却看到了事务T2刚才插入的新行。这个新行就称为“幻像”,因为对T1来说这一行就像突然出现的一样。
3. 事务隔离级别?
一般来说,分为四个隔离级别:
a. 未提交读(Read uncommitted):读未提交数据,这是事务最低的隔离级别,在并发的事务中,它充许一个事务可以读到另一个事务未提交的更新数据(会出现脏读,不可重复读和幻读);
b. 已提交读(Read committed):读已提交数据,保证在并发的事务中,一个事务修改的数据提交后才能被另外一个事务读取到(会出现不可重复读和幻读);
c. 可重复读(Repeatable read):可重复读,这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻读。一般是采用“快照”的方式来实现的;
d. 可串行化(Serializable ):事务被处理为顺序执行或者说是以串行化方式执行。这是花费最高,但也是最可靠的事务隔离级别。能有效的避免脏读、不可重复读、幻读。
4. 数据库默认的隔离级别?
每种数据库的默认隔离级别是不同的,例如SQL Server、Oracle默认Read Commited,mysql默认Repeatable Read。
参考自:https://gist.github.com/JagoWang/4555317#file-mysql-L27
https://tech.meituan.com/innodb-lock.html
是否只读
如果事务只对后端的数据库进行读操作,数据库可以利用事务的只读特性来进行一些优化;通过将事务设置为只读,你就可以给数据库一个机会,让它应用它认为何时的优化措施,例如Oracle对于只读事务,不启动回滚段,不记录回滚log;
如果你一次执行多条查询语句,例如统计查询,报表查询,在这种场景下,多条查询SQL必须保证整体的读一致性,否则,在第一条SQL查询之后,第二条SQL查询之前,数据被其他用户改变,则该次整体的统计查询将会出现读数据不一致的状态,此时,应该启用事务支持。
【注意是一次执行多次查询来统计某些信息,这时为了保证数据整体的一致性,要用只读事务】
没有事务的时候,在执行一条sql语句看到执行前点的数据状态,保证数据一致性;
只读事务,在执行多条sql语句看到执行前点的数据状态,保证数据一致性;
事务超时
为了使应用程序更好的运行,事务不能运行太长的时间。因为事务可能涉及对后端数据库的锁定,所以长时间的事务会不必要的占用数据库资源。
回滚规则
默认情况下,事务只有在遇到运行期异常时才会回滚(java.lang.RuntimeException及其子类),而在遇到检查型异常时不会回滚;不过,我们可以手动设置回滚时的异常类型;
我们可以使用@Transaction注解中的noRollbackFor和RollbackFor指定某种异常是否回滚。
@Transaction(noRollbackFor=RuntimeException.class)
@Transaction(RollbackFor=Exception.class)
这样就改变了默认的事务处理方式。
这就要求我们在自定义异常的时候,让自定义的异常继承自RuntimeException,这样抛出的时候才会被Spring默认的事务处理准确处理。
参考自《Spring实战》
以上是关于Spring事务的事务属性的主要内容,如果未能解决你的问题,请参考以下文章