面试之spring5种数据隔离和7种事务传播性

如果不考虑隔离性引发的安全性问题:

脏读:一个事务读到了另一个事务未提交的数据
不可重复读:一个事务督导另一个事务已经提交的update的数据导致多次查询结果不一致
虚幻读:一个事务读到了另一个事务已经提交的insert的数据导致多次查询结果不一致。

解决读问题:设置事务隔离级别(5种)

DEFAULT这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别;
未提交读(read uncommited):脏读,不可重复读,虚读都有可能发生
已提交读(read commited):避免脏读。但是不可重复读和虚读都有可能发生;
可重复读(repeatable read):避免脏读和不可重复读,但是虚读有可能发生;
串行化的(serializable):避免以上所有读问题。
MySQL默认:可重复读
Oracle默认:已提交读
 

1.1 什么是事务的传播性

事务的传播性一般在事务嵌套时候使用,比如在事务A里面调用了另外一个使用事务的方法,那么这俩个事务是各自作为独立的事务执行提交,还是内层的事务合并到外层的事务一块提交那,这就是事务传播性要确定的问题。下面一一介绍比较常用的事务传播性。

事务读传播行为

PROPAGION_XXX:事务的传播行为
*保证同一个事务中
PROPAGATION_REQUIRED支持当前事务,如果不存在,就新建一个(默认)
PROPAGATION_SUPPORTS支持当前事务,如果不存在,就不适用事务
PROPAGATION_MANDATORY 支持当前事务,如果不存在,抛出异常
*保证没有在同一个事务中
PROPAGATION_REQUIRES_NEW如果有事务存在,挂起当前事务,创建一个新的事务
PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务
PROPAGATION_NEVER 以非事务方式运行,如果有事务存在,抛出异常
PROPAGATION_NESTED 如果当前事务存在,则嵌套事务执行

1.2 PROPAGATION_REQUIRED

Spring默认的事务传播机制,如果外层有事务则当前事务加入到外层事务,一块提交一块回滚,如果外层没有事务则当前开启一个新事务。这个机制可以满足大多数业务场景。

试验:

 public class BoA {
        public void test(){
        boB.sayHello();
    }
}

BoA 和boB都是进行过事务增强后的bo,那么在执行test的时候会开启一个事务(或者test调用方已经存在事务则加入该事务),执行到sayHello()时候由于传播性是PROPAGATION_REQUIRED,所以sayHello方法加入到test的事务,那么sayHello和test就会同时提交,同时回滚。值得注意的是如果test里面调用sayHello时候加了trycatch没有把异常跑出去,而sayHello方法却抛出了异常,那么整个事务也会回滚,这时候调用test的外层会受到"Transaction rolled back because it has been marked as rollback-only的异常,而把sayHello真正的异常吃掉了。

平时我们都是在bo里面调用数据库操作,在rpc和screen调用bo,所以bo层不应该catch掉异常,而应该抛出来,在rpc和screen层catch异常。

1.3 PROPAGATION_REQUIRES_NEW

该传播机制是每次新开启一个事务,同时把外层的事务挂起,当前新事务执行完毕后在恢复上层事务的执行。

以上面代码为例,首先进入test方法前会开启一个事务,然后调用sayHello时候会把test的事务挂起,从新开启一个新事务执行sayHello,执行完毕后恢复test的事务。如果sayHello抛出来异常则sayHello的事务会回滚,那么test方法是否回滚那?这个要看情况,如果test在调用sayHello 时候使用了trycatch并且异常没有在catch中throw出来,那么test方法不会回滚,这时候sayHello是提交和回滚对test没有影响,。 如果test中没有加trycatch那么,test也会回滚。

1.4 PROPAGATION_SUPPORTS

该传播机制如果外层有事务则加入该事务,如果不存在也不会创建新事务,直接使用非事务方式执行。

以上面代码为例,由于PROPAGATION_SUPPORTS,所以test和sayHello都没有开启事务,没啥好讲的。

下面看下如果test隔离级别是PROPAGATION_REQUIRED,sayHello传播性是PROPAGATION_SUPPORTS的情况。这时候外层test会开启一个事务(或者test调用方已经存在事务则加入该事务),然后sayHello执行时候会加入到test的事务和1.2类似,同时提交同时回滚。

1.5 PROPAGATION_NOT_SUPPORTED

该传播机制不支持事务,如果外层存在事务则挂起外层事务 ,然后执行当前逻辑,执行完毕后,恢复外层事务。

同样这里看下如果test使用PROPAGATION_REQUIRED,sayHello传播性是PROPAGATION_NOT_SUPPORTED的情况,首先test会开启一个事务(或者test调用方已经存在事务则加入该事务),然后sayHello执行时候会挂起该事务然后在非事务内做自己的事情,做完后在恢复test的事务。 无论sayHello是否抛出异常,sayHello的事务都不会回滚,因为它不在事务范围内,那test? 这个就和1.3一样了,如果test catch住sayHello的异常没有throw出去,那么test就不回滚,否者回滚。

1.6 PROPAGATION_NEVER

该传播机制不支持事务,如果外层存在事务则直接抛出异常。 IllegalTransactionStateException( "Existing transaction found for transaction marked with propagation 'never'")

1.7 PROPAGATION_MANDATORY

该传播机制是说配置了该传播性的方法只能在已经存在事务的方法中被调用,如果在不存在事务的方法中被调用,会抛出异常。 IllegalTransactionStateException( "No existing transaction found for transaction marked with propagation 'mandatory'");

1.8 PROPAGATION_NESTED

该传播机制特点是可以保存状态保存点,当事务回滚后会回滚到某一个保存点上,从而避免所有嵌套事务都回滚。

上面代码test和sayHello都设置为PROPAGATION_NESTED,如果sayHello抛出异常,两者还是都回滚了,因为sayHello虽然回滚到了savePoint而savepoint里面确实包含了test的操作,但是savepoint后还是会吧异常throw给test,这导致了test的回滚。

总结,只有传播性为PROPAGATION_REQUIRED||PROPAGATION_REQUIRES_NEW||PROPAGATION_NESTED时候才可能开启一个新事务。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章