事务最重要的两个特性,是事务的传播级别和数据隔离级别。传播级别定义的是事务的控制范围,事务隔离级别定义的是事务在数据库读写方面的控制范围。
1.传播机制
事务的传播性一般在事务嵌套时候使用,比如在事务A里面调用了另外一个使用事务的方法,那么这俩个事务是各自作为独立的事务执行提交,还是内层的事务合并到外层的事务一块提交呢,这就是事务传播性要确定的问题。下面一一介绍比较常用的事务传播机制。
1、PROPAGATION_REQUIRED
若当前存在事务,则加入该事务,若不存在事务,则新建一个事务。
class C1(){ @Transactional(propagation = Propagation.REQUIRED) function A(){ C2.B(); } } class C2(){ @Transactional(propagation = Propagation.REQUIRED) function B(){ do something; } }
若B方法抛出异常,A方法进行捕获,A会抛出异常,因为C2标志回滚,C1标志提交,产生冲突。
若B方法抛出异常,B方法内部捕获,A、B都不会回滚。
若A或B抛出异常,但没有捕获,则A、B都回滚。
A、B可操作同一条记录,因为处于同一个事务中。
2、PAOPAGATION_REQUIRE_NEW
若当前没有事务,则新建一个事务。若当前存在事务,则新建一个事务,新老事务相互独立。外部事务抛出异常回滚不会影响内部事务的正常提交。
class C1(){ @Transactional(propagation = Propagation.REQUIRED) function A(){ C2.B(); } } class C2(){ @Transactional(propagation = Propagation.REQUIRE_NEW) function B(){ do something; } }
若B方法抛出异常,A方法进行捕获,B方法回滚,A方法不受B异常影响。
若B方法抛出异常,B方法内部捕获,A、B都不会回滚。
若A方法抛出异常,不会影响B正常执行。
若B方法抛出异常,A、B方法都没有处理,则A、B都会回滚。
A、B不可操作同一条记录,因为处于不同事务中,会产生死锁。
3、PROPAGATION_NESTED
如果当前存在事务,则嵌套在当前事务中执行。如果当前没有事务,则新建一个事务,类似于REQUIRE_NEW。
class C1(){ @Transactional(propagation = Propagation.REQUIRED) function A(){ C2.B(); } } class C2(){ @Transactional(propagation = Propagation.NESTED) function B(){ do something; } }
若B方法抛出异常,A方法进行捕获,B方法回滚,A方法正常执行。
若A或者B抛出异常,不做任何处理的话,A、B都要回滚。
A、B可操作同一条记录,因为处于同一个事务中。
4、PROPAGATION_SUPPORTS
支持当前事务,若当前不存在事务,以非事务的方式执行。
5、PROPAGATION_NOT_SUPPORTED
以非事务的方式执行,若当前存在事务,则把当前事务挂起。
class C1(){ @Transactional(propagation = Propagation.REQUIRED) function A(){ C2.B(); } } class C2(){ @Transactional(propagation = Propagation.NOT_SUPPORTED) function B(){ do something; } }
A、B不可操作同一条记录,因为A是事务执行,B在A尚未提交前再操作同一条记录,会产生死锁。
6、PROPAGATION_MANDATORY
强制事务执行,若当前不存在事务,则抛出异常
7、PROPAGATION_NEVER
以非事务的方式执行,如果当前存在事务,则抛出异常。
2.隔离级别
隔离级别是指若干个并发的事务之间的隔离程度,与我们开发时候主要相关的场景包括:脏读取、重复读、幻读。
1、读未提交(READ_UNCOMMITED):允许读取还未提交的改变了的数据。可能导致脏读、幻读、不可重复读。
2、读已提交(READ_COMMITED):允许在并发事务已经提交后读取。可防止脏读,但幻读、不可重复读仍可能发生。
3、可重复读(REPEATABLE_READ):对相同字段的多次读取是一致的,除非数据被事务本身改变。可防止脏读、不可重复读。但幻读仍可能发生。
4、可串行化(SERIALIZABLE):事务顺序执行,可避免脏读、不可重复读、幻读,但效率最差。因为A事务执行时,要完全锁住在事务中涉及的数据表。
3.异常捕获
spring中可以指定当方法执行并抛出异常的时候,哪些异常回滚事务,哪些异常不回滚事务。默认情况下,只在方法抛出运行时异常的时候才回滚(runtime exception)。而在出现受阻异常(checked exception)时不回滚事务。当然可以采用申明的方式指定哪些受阻异常像运行时异常那样指定事务回滚。
// 所有异常都会回滚 @Transactional(rollbackFor = Exception.class)
4.配置方法
XML配置:
<tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="query*" read-only="true" propagation="SUPPORTS" /> <tx:method name="get*" read-only="true" propagation="SUPPORTS" /> <tx:method name="insert*" propagation="REQUIRED" /> <tx:method name="save*" propagation="REQUIRED" rollback-for="Exception"/> <tx:method name="update*" propagation="REQUIRED" rollback-for="Exception"/> <tx:method name="remove*" propagation="REQUIRED" /> <tx:method name="delete*" propagation="REQUIRED" /> </tx:attributes> </tx:advice>
由于没有给method配置isolation属性,所以默认是isolation=‘DEFAULT’,也就是使用后端数据库默认的隔离级别。
注解配置:
传播机制:@Transactional(propagation=Propagation.REQUIRED)
隔离级别:@Transactional(isolation = Isolation.READ_UNCOMMITTED)