在讲事务的7种传播行为前可以分享一个个人亲身的小经历,我大三拿到学位证以及成绩合格后和学校申请大四离校外出实习;有屎以来第一次面试就是一家大厂,当时初生牛犊不怕虎,也是不知天高地厚,面试官因为某种原因来的是架构师和项目经理,他们的第一问题就是问我事务的传播行为以及事务的隔离级别,很可惜那家大厂我很向往,但与我无缘;搞技术这行,菜就是原罪,我菜的真实,所以每次谈到事务就让我有所回忆,所以请你打死也要记住这玩意,不要管别人说什么工作中用处大不大,别只局限于眼前的苟且。
进入正题:
一、对传播的初步认识
事务的传播行为,既然是传播行为,那么就要进行传播这个动作,传播就是至少需要两个的,单个个体是不存在传播的。
例如:
ServiceA {
void methodA() {
ServiceB.methodB();
}
}
methodA事务方法调用methodB事务方法时,methodB是继续在调用者methodA的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodB的事务传播行为决定的
二、7种传播行为
-1、PROPAGATION_REQUIRED:支持当前事务,假设当前没有事务。就新建一个事务。
-2、PROPAGATION_SUPPORTS:支持当前事务,假设当前没有事务,就以非事务方式运行。
-3、PROPAGATION_MANDATORY:支持当前事务,假设当前没有事务,就抛出异常。
-4、PROPAGATION_REQUIRES_NEW:新建事务,假设当前存在事务。把当前事务挂起。
-5、PROPAGATION_NOT_SUPPORTED:以非事务方式运行操作。假设当前存在事务,就把当前事务挂起。
-6、PROPAGATION_NEVER:以非事务方式运行,假设当前存在事务,则抛出异常。
-7、PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
三、理解
ServiceA {
void methodA() {
ServiceB.methodB();
}
}
ServiceB {
void methodB() {
}
}
结合以上代码来理解下面内容
1、PROPAGATION_REQUIRED
如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。
如果只有一个methodB那么它可以有自己的事务,但是当methodB在methodA中,类似这种嵌套的方式存在事务,
这里我们可以理解为try{}catch{}finally{},方法在try块中,出现异常在catch中都会进行回滚。
可以理解成类似的情形:
try{
conn.setAutoCommit(false);
methodA(){
methodB….
}
conn.commit();//提交
}catch(Exception e){
conn.rollback();//回滚
}finally{
//关闭各种流和关闭连接以及回收入连接池等
}
那么methodB在进行事务时发现自己是在methodA中被调用的,而methodA已经开启了事务,那么methodB就不再自己新开一个事务,当methodA出现事务回滚,尽管methodB已经提交了,还是会和methodA一起进行滚回,体现了事务的一致性和原子性,那么当methodA并没有开启事务,methodB执行了并提交了 ,当到methodA执行发现自己没有事务,它会自己开启一个新的事务,这样尽管methodB提交,methodA也会回滚,methodA与methodB只有其中一个出现了异常也就失败就会被catch到一起进行回滚。
2、PROPAGATION_SUPPORTS
如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行
如果只存在methodB那么它没有事务则以没有事务的非事务的形式执行,当methodB在其他的方法中被调用,如methodA中被调用,那么当methodA开启了事务那么methodB则以methodA中的事务为准进行事务。
3、PROPAGATION_MANDATORY
如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常
如果只存在methodB, 那么它没有事务,则会抛出异常throw new IllegalTransactionStateException(“
Transaction propagation ‘mandatory’ but no existing transaction found”); 当methodB在其他的方法中被调用,如methodA中被调用,那么当methodA开启了事务那么methodB则以methodA中的事务为准进行事务。
4、PROPAGATION_REQUIRES_NEW
新建事务,假设当前存在事务。把当前事务挂起
这里可以把methodB以及methodA理解成开启了两个不相关的事务了,如果methodA开启了PROPAGATION_REQUIRED,而methodB在methodA中,methodB开启了PROPAGATION_REQUIRES_NEW,当methodB执行时,methodA中的事务被挂起了,methodB新建了一个事务,这个事务与methodA中被挂起的事务暂时无关了,也就是说,存在两个事务,A中调用了B,B执行时,只是先把A的事务挂起了,methodB新建的事务提交成功后,再进行A的事务,都成功就可以一起提交成功,其中一个事务失败就回滚。而A只需要进行自己的事务,两个都成功才可以提交,其中一个失败也回滚。
类似这种情景:
methodA:
try{
connA.setAutoCommit(false);
methodA(){
methodB:
try{
connB.setAutoCommit(false);
methodB(){
}...
connB.commit();//提交
}catch(Exception e){
connB.rollback();//回滚
}finally{
//关闭各种流和关闭连接以及回收入连接池等
}
}...
connA.commit();//提交
}catch(Exception e){
connA.rollback();//回滚
}finally{
//关闭各种流和关闭连接以及回收入连接池等
}
5、PROPAGATION_NOT_SUPPORTED
以非事务方式运行操作。假设当前存在事务,就把当前事务挂起
如果只存在methodB,则以非事务的方式进行,当methodB在其他的方法中被调用,如methodA中被调用,那么当methodA开启了事务那么当methodB执行时,methodA中的事务就被挂起,methodB以非事务的方式进行。
这里相当于方法A存在事务,而A调用了方法B,而方法B事务特性用的是PROPAGATION_NOT_SUPPORTED,那么它就会以非事务来进行,类似于try{}catch{}里B的try中,设置了自动提交,就是以非事务进行,而A存在事务则继续以存在事务进行。
6、PROPAGATION_NEVER
以非事务方式运行,假设当前存在事务,则抛出异常
7、PROPAGATION_NESTED
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
如果只存在methodB而它的事务是PROPAGATION_NESTED, 则按REQUIRED属性执行, 当methodB在其他的方法中被调用,如在methodA中被调用,如果methodB失败了methodB会回滚,并不影响到methodA,而methodA回滚了会影响到methodB,methodB在methodA中,但它并没提交,methodB要与methodA保持一致性,一起提交,但methodB回滚不影响到methodA,而methodA回滚了会影响到methodB
类似这种情景:
methodA:
try{
conn.setAutoCommit(false);
methodA(){
methodB:
try{
methodB(){
}...
//内层这里没有提交
//内层这里没有提交
//内层这里没有提交
}catch(Exception e){
conn.rollback();//回滚
//内层这里回滚
//内层这里回滚
//内层这里回滚
}finally{
//关闭各种流和关闭连接以及回收入连接池等
}
}...
conn.commit();//提交
//外层这里提交
//外层这里提交
//外层这里提交
}catch(Exception e){
conn.rollback();//回滚
//外层这里也回滚
//外层这里也回滚
//外层这里也回滚
}finally{
//关闭各种流和关闭连接以及回收入连接池等
}
嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚
四、Spring事务的配置
请看我写的AOP__事务配置(简单代码xml配置,注解等)
https://www.cnblogs.com/zhangsonglin/p/10922828.html
借鉴于大佬:
https://blog.csdn.net/qq_38526573/article/details/87898161
https://blog.csdn.net/soonfly/article/details/70305683
加入了大量自己的理解,通俗易懂