当前位置: 代码迷 >> 综合 >> 02 事务伴生源-Propagation
  详细解决方案

02 事务伴生源-Propagation

热度:56   发布时间:2024-01-04 13:33:16.0

文章目录

  • 一、Propagation(传播行为)
  • 二 通过同类+异类方法调用玩转事务传播性
    • 2.1 PROPAGATION_REQUIRED
    • 2.2 PROPAGATION_SUPPORTS
    • 2.3 PROPAGATION_MANDATORY
    • 2.4 PROPAGATION_REQUIRES_NEW
    • 2.5 PROPAGATION_NOT_SUPPORTED
    • 2.6 PROPAGATION_NEVER
    • 2.7 PROPAGATION_NESTED

01 Spring ‘拌‘ 事务 中提到事务需要注意以下情况:

恰如其分的使用’propagation’, 下面进入传播行为的视界.

一、Propagation(传播行为)

key属性确定代理应该给哪个方法增加事务行为。这样的属性最重要的部份是传播行为。有以下选项可供使用:

PROPAGATION_REQUIRED --支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS --支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY --支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW --新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED --以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER --以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED --如果当前事务存在,则嵌套事务执行。

解惑 spring 嵌套事务

TransactionDefinition 接口定义***********

int PROPAGATION_REQUIRED = 0;
int PROPAGATION_SUPPORTS = 1;
int PROPAGATION_MANDATORY = 2;
int PROPAGATION_REQUIRES_NEW = 3;
int PROPAGATION_NOT_SUPPORTED = 4;
int PROPAGATION_NEVER = 5;
int PROPAGATION_NESTED = 6;


二 通过同类+异类方法调用玩转事务传播性

如下,经典的事务嵌套(涉及到同类方法调用,异类方法调用,不同的事务传播性)
sample********************************

class ServiceA {
       void methodA() {
       // methodB()ServiceB.methodB();   }   void methodB(){
    }
}   class ServiceB {
    void methodB() {
       }   }      

我们这里一个个分析吧

2.1 PROPAGATION_REQUIRED

(1) ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED;
(2) 那么由于执行ServiceA.methodA的时候,ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA的事务内部,就不再起新的事务。
(3) 而假如ServiceB.methodB运行的时候发现自己没有在事务中,他就会为自己分配一个事务。

第一种:

if( ServiceA.methodA有事务){
    result1:ServiceA.methodA调用ServiceB.methodB时,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会被回滚。即使ServiceB.methodB的事务已经被提交,但是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚.result2:ServiceA.methodA调用ServiceA.methodB时,在ServiceA.methodA或者在ServiceA.methodB内的任何地方出现异常,事务都会被回滚。
}

第二种:

eif(  ServiceA.methodA无事务 &&(ServiceA.methodB 有事务 || ServiceB.methodB 有事务)){
    result1:ServiceA.methodA调用ServiceB.methodB时, ServiceB.methodB 有事务,并且不影响ServiceA.methodA。result2:(类调用内部方法事务不起作用)ServiceA.methodA调用ServiceA.methodB时,两者均没有事务。
}

如下代码:

public class ServiceA {
    //@Transactional(propagation = Propagation.REQUIRED)void methodA() {
    //methodB();new ServiceB().methodB();}void methodB() {
    }
}class ServiceB {
    @Transactional(propagation = Propagation.REQUIRED)void methodB() {
    }
}

**第二种情况,result2: 的原因:

??通过 Spring 官方文档可以知道:在默认的代理模式下,只有目标方法由外部调用,才能被 Spring 的事务拦截器拦截。在同一个类中的两个方法直接调用,是不会被 Spring 的事务拦截器拦截,就像上面的 methodA 方法直接调用了同一个类中 methodB 方法,methodB 方法不会被 Spring 的事务拦截器拦截。可以使用 AspectJ 取代 Spring AOP 代理来解决这个问题。

2.2 PROPAGATION_SUPPORTS

??如果当前在事务中,即以事务的形式运行; 如果当前不再一个事务中,那么全部以非事务的形式运行。这就跟平常用的普通非事务的代码只有一点点区别了。和PROPAGATION_REQURIED的唯一区别就是:

有事务调用,即事务有效; 无事务调用,即无事务.

if(ServiceA.methodA 有事务){ServiceA.methodA调用ServiceB.methodB时,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会回滚。
}
if(ServiceA.methodA 无事务){ServiceA.methodA调用ServiceB.methodB时,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都不会被回滚。
}

2.3 PROPAGATION_MANDATORY

??强制必须在一个事务中运行。也就是说,他只能被一个父事务调用。否则,他就要抛出异常:IllegalTransactionStateException

异常如下:

org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:358)at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:417)

2.4 PROPAGATION_REQUIRES_NEW

??新建一个事务

??我们设计 ServiceA.methodA 的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB 的事务级别为PROPAGATION_REQUIRES_NEW,那么当执行到ServiceB.methodB 的时候,ServiceA.methodA 所在的事务就会挂起,ServiceB.methodB 会起一个新的事务,等待ServiceB.methodB的事务完成以后,methodA 才继续执行。

? 与PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为ServiceB.methodB是新起一个事务,那么就是存在两个不同的事务。如果ServiceB.methodB已经提交,那么之后ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。如果ServiceB.methodB失败回滚,如果他抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。

2.5 PROPAGATION_NOT_SUPPORTED

??当前不支持事务

??比如ServiceA.methodA的事务级别是PROPAGATION_REQUIRED ,而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED
那么当执行到ServiceB.methodB时,ServiceA.methodA的事务挂起,而他以非事务的状态运行完,再继续ServiceA.methodA的事务。

??但是如果ServiceB.methodB失败回滚,如果他抛出的异常被 ServiceA.methodA 捕获,ServiceA.methodA 事务仍然可能提交,如果没有被捕获,且异常是运行时异常或者rollbackFor制定的异常,那么ServiceA.methodA会回滚。

2.6 PROPAGATION_NEVER

??不能在事务中运行

??假设ServiceA.methodA的事务级别是PROPAGATION_REQUIRED, 而ServiceB.methodB的事务级别是PROPAGATION_NEVER ,那么ServiceB.methodB就要抛出异常了。

异常在这里: IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'.

2.7 PROPAGATION_NESTED

??理解Nested的关键是savepoint 。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的; 他回滚,父类也需要回滚。

??而Nested事务的好处是他有一个savepoint

ServiceA {   void methodA() {   try {//savepoint   ServiceB.methodB();    //PROPAGATION_NESTED 级别} catch (SomeException) {   // 执行其他业务, 如 ServiceC.methodC();   }   }   
}   

??也就是说ServiceB.methodB失败回滚,那么ServiceA.methodA也会回滚到savepoint点上,ServiceA.methodA可以选择另外一个分支,比如 ServiceC.methodC,继续执行,来尝试完成自己的事务。