Spring中事务传播行为

1. Spring中七种事务传播行为

PROPAGATION(蔓延、传播、传输)

事务传播行为类型 说明
PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是默认的事务传播行为
PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。(一个新的事务将启动,而且如果有一个现有的事务在运行的话,则这个方法将在运行期被挂起,直到新的事务提交或者回滚才恢复执行。)
PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。(外层事务抛出异常回滚,那么内层事务必须回滚,反之内层事务并不影响外层事务)

2. Spring中七种事务定义

Spring在TransactionDefinition接口中规定了7种类型的事务传播行为。事务传播行为是Spring框架独有的事务增强特性。这是Spring为我们提供的强大的工具箱,使用事务传播行可以为我们的开发工作提供许多便利。

TransactionDefinition接口定义

public interface 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;
    int ISOLATION_DEFAULT = -1;
    int ISOLATION_READ_UNCOMMITTED = 1;
    int ISOLATION_READ_COMMITTED = 2;
    int ISOLATION_REPEATABLE_READ = 4;
    int ISOLATION_SERIALIZABLE = 8;
    int TIMEOUT_DEFAULT = -1;

    int getPropagationBehavior();
    int getIsolationLevel();
    int getTimeout();
    boolean isReadOnly();
    @Nullable
    String getName();
}

Transactional注解的定义

public @interface Transactional {
    @AliasFor("transactionManager")
    String value() default "";
?
    @AliasFor("value")
    String transactionManager() default "";
?
    Propagation propagation() default Propagation.REQUIRED;
?
    Isolation isolation() default Isolation.DEFAULT;
?
    int timeout() default -1;
?
    boolean readOnly() default false;
?
    Class<? extends Throwable>[] rollbackFor() default {};
?
    String[] rollbackForClassName() default {};
?
    Class<? extends Throwable>[] noRollbackFor() default {};
?
    String[] noRollbackForClassName() default {};
}

事务传播行为用来描述由某一个事务传播行为修饰的方法被嵌套进另一个方法的时事务如何传播。

用伪代码说明:

public void methodA(){

methodB();

//doSomething

}

@Transaction(Propagation=XXX)
public void methodB(){
//doSomething
}

代码中methodA()方法嵌套调用了methodB()方法,methodB()的事务传播行为@Transaction(Propagation=XXX)设置决定。这里需要注意的是methodA()并没有开启事务,某一个事务传播行为修饰的方法并不是必须要在开启事务的外围方法中调用。这里methodA()是外围方法,并没有开启事务。

@Transactional 的事务开启 ,或者是基于接口的 或者是基于类的代理被创建。所以在同一个类中一个方法调用另一个方法有事务的方法,事务是不会起作用的

2.1 验证Propagation.REQUIRED

@Override
@Transactional(propagation = Propagation.REQUIRED)
public void addRequired(UserEntity userEntity){
   userMapper.insertUser(userEntity);
}

@Override
@Transactional(propagation = Propagation.REQUIRED)
public void addRequiredException(UserEntity userEntity){
   userMapper.insertUser(userEntity);
   throw new RuntimeException();
}

2.1.1 场景一

此场景外围方法没有开启事务。

验证方法1:

@Override
public void noTransactionExceptionRequiredRequired(){
    UserEntity user1=new UserEntity();
    user1.setName("张三001");
    userService.addRequired(user1);

    UserEntity user2=new UserEntity();
    user2.setName("李四001");
    userService.addRequired(user2);
    throw new RuntimeException();
}

验证方法2:

@Override
public void noTransactionRequiredRequiredException(){
    UserEntity user1=new UserEntity();
    user1.setName("张三002");
    userService.addRequired(user1);

    UserEntity user2=new UserEntity();
    user2.setName("李四002");
    userService.addRequiredException(user2);
}

验证结果:

验证方法序号 数据库结果 结果分析
1 “张三”、“李四”均插入。 外围方法未开启事务,插入“张三”、“李四”方法在自己的事务中独立运行,外围方法异常不影响内部插入“张三”、“李四”方法独立的事务。
2 “张三”插入,“李四”未插入。 外围方法没有事务,插入“张三”、“李四”方法都在自己的事务中独立运行,所以插入“李四”方法抛出异常只会回滚插入“李四”方法,插入“张三”方法不受影响。

2.1.2 场景二

此场景外围方法开启事务。

验证方法1:

@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transactionExceptionRequiredRequired(){
    UserEntity user1=new UserEntity();
    user1.setName("张三003");
    userService.addRequired(user1);

    UserEntity user2=new UserEntity();
    user2.setName("李四003");
    userService.addRequired(user2);

    throw new RuntimeException();
}

验证方法2:

@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transactionRequiredRequiredException(){
    UserEntity user1=new UserEntity();
    user1.setName("张三004");
    userService.addRequired(user1);

    UserEntity user2=new UserEntity();
    user2.setName("李四004");
    userService.addRequiredException(user2);
}

验证方法3:

@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transactionRequiredRequiredExceptionTry(){
    UserEntity user1=new UserEntity();
    user1.setName("张三005");
    userService.addRequired(user1);

    UserEntity user2=new UserEntity();
    user2.setName("李四005");
    try {
        userService.addRequiredException(user2);
    } catch (Exception e) {
        System.out.println("方法回滚");
    }
}

验证结果:

验证方法序号 数据库结果 结果分析
1 “张三”、“李四”均未插入。 外围方法开启事务,内部方法加入外围方法事务,外围方法回滚,内部方法也要回滚。
2 “张三”、“李四”均未插入。 外围方法开启事务,内部方法加入外围方法事务,内部方法抛出异常回滚,外围方法感知异常致使整体事务回滚。
3 “张三”、“李四”均未插入。 外围方法开启事务,内部方法加入外围方法事务,内部方法抛出异常回滚,即使方法被catch不被外围方法感知,整个事务依然回滚。

结论:以上试验结果我们证明在外围方法开启事务的情况下Propagation.REQUIRED修饰的内部方法会加入到外围方法的事务中,所有Propagation.REQUIRED修饰的内部方法和外围方法均属于同一事务,只要一个方法回滚,整个事务均回滚。

2.2. 验证Propagation.PROPAGATION_REQUIRES_NEW

@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addRequiresNew(UserEntity userEntity){
   userMapper.insertUser(userEntity);
}

@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addRequiresNewException(UserEntity userEntity){
   userMapper.insertUser(userEntity);
   throw new RuntimeException();
}

2.2.1 场景一

此场景外围方法没有开启事务。

验证方法1:

@Override
public void noTransactionExceptionRequiresNewRequiresNew(){
    UserEntity user1=new UserEntity();
    user1.setName("张三006");
    userService.addRequiresNew(user1);

    UserEntity user2=new UserEntity();
    user2.setName("李四006");
    userService.addRequiresNew(user2);

    throw new RuntimeException();
}

验证方法2:

@Override
public void noTransactionRequiresNewRequiresNewException(){
    UserEntity user1=new UserEntity();
    user1.setName("张三007");
    userService.addRequiresNew(user1);

    UserEntity user2=new UserEntity();
    user2.setName("李四007");
    userService.addRequiresNewException(user2);
}

验证结果:

验证方法序号 数据库结果 结果分析
1 “张三”插入,“李四”插入。 外围方法没有事务,插入“张三”、“李四”方法都在自己的事务中独立运行,外围方法抛出异常回滚不会影响内部方法。
2 “张三”插入,“李四”未插入 外围方法没有开启事务,插入“张三”方法和插入“李四”方法分别开启自己的事务,插入“李四”方法抛出异常回滚,其他事务不受影响。

2.2.2 场景2

外围方法开启事务。

验证方法1:

@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transactionExceptionRequiredRequiresNewRequiresNew(){
    UserEntity user1=new UserEntity();
    user1.setName("张三008");
    userService.addRequired(user1);

    UserEntity user2=new UserEntity();
    user2.setName("李四008");
    userService.addRequiresNew(user2);

    UserEntity user3=new UserEntity();
    user3.setName("王五008");
    userService.addRequiresNew(user3);
    throw new RuntimeException();
}

验证方法2:

@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transactionRequiredRequiresNewRequiresNewException(){
    UserEntity user1=new UserEntity();
    user1.setName("张三009");
    userService.addRequired(user1);

    UserEntity user2=new UserEntity();
    user2.setName("李四009");
    userService.addRequiresNew(user2);

    UserEntity user3=new UserEntity();
    user3.setName("王五009");
    userService.addRequiresNewException(user3);
}

验证方法3:

@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transactionRequiredRequiresNewRequiresNewExceptionTry(){
    UserEntity user1=new UserEntity();
    user1.setName("张三010");
    userService.addRequired(user1);

    UserEntity user2=new UserEntity();
    user2.setName("李四010");
    userService.addRequiresNew(user2);

    UserEntity user3=new UserEntity();
    user3.setName("王五010");
    try {
        userService.addRequiresNewException(user3);
    } catch (Exception e) {
        System.out.println("回滚");
    }
}

验证结果:

验证方法序号 数据库结果 结果分析
1 “张三”未插入,“李四”插入,“王五”插入。 外围方法开启事务,插入“张三”方法和外围方法一个事务,插入“李四”方法、插入“王五”方法分别在独立的新建事务中,外围方法抛出异常只回滚和外围方法同一事务的方法,故插入“张三”的方法回滚。
2 “张三”未插入,“李四”插入,“王五”未插入。 外围方法开启事务,插入“张三”方法和外围方法一个事务,插入“李四”方法、插入“王五”方法分别在独立的新建事务中。插入“王五”方法抛出异常,首先插入 “王五”方法的事务被回滚,异常继续抛出被外围方法感知,外围方法事务亦被回滚,故插入“张三”方法也被回滚。
3 “张三”插入,“李四”插入,“王五”未插入。 外围方法开启事务,插入“张三”方法和外围方法一个事务,插入“李四”方法、插入“王五”方法分别在独立的新建事务中。插入“王五”方法抛出异常,首先插入“王五”方法的事务被回滚,异常被catch不会被外围方法感知,外围方法事务不回滚,故插入“张三”方法插入成功。

结论:在外围方法开启事务的情况下Propagation.REQUIRES_NEW修饰的内部方法依然会单独开启独立事务,且与外部方法事务也独立,内部方法之间、内部方法和外部方法事务均相互独立,互不干扰。

2.3 REQUIRED,REQUIRES_NEW,NESTED异同

NESTED和REQUIRED修饰的内部方法都属于外围方法事务,如果外围方法抛出异常,这两种方法的事务都会被回滚。但是REQUIRED是加入外围方法事务,所以和外围事务同属于一个事务,一旦REQUIRED事务抛出异常被回滚,外围方法事务也将被回滚。而NESTED是外围方法的子事务,有单独的保存点,所以NESTED方法抛出异常被回滚,不会影响到外围方法的事务。

NESTED和REQUIRES_NEW都可以做到内部方法事务回滚而不影响外围方法事务。但是因为NESTED是嵌套事务,所以外围方法回滚之后,作为外围方法事务的子事务也会被回滚。而REQUIRES_NEW是通过开启新的事务实现的,内部事务和外围事务是两个事务,外围事务回滚不会影响内部事务。

2.4 PROPAGATION_MANDATORY,PROPAGATION_NEVER

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

使用当前的事务,如果当前没有事务,就抛出异常。

org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation ‘mandatory‘

2.4.2 PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。

org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation ‘never‘

2.5 PROPAGATION_SUPPORTS

如果当前已经存在事务,那么加入该事务,否则创建一个所谓的空事务(可以认为无事务执行)。

Should roll back transaction but cannot - no transaction available

2.6 PAGATION_NOT_SUPPORTED

以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

Should roll back transaction but cannot - no transaction available

Resuming suspended transaction after completion of inner transaction

3 使用场景

3.1 如果业务需求每接受到一次请求到要记录日志,如下图:

因为insertOprLog()的操作不管扣款和创建订单成功与否都要生成日志,并且日志的操作成功与否不影响充值处理,所以insertOprLog()方法的事务传播行为可以定义为:PROPAGATION_REQUIRES_NEW。

3.2 在银行新增银行卡业务中,需要执行两个操作,一个是保存银行卡信息,一个是激活新创建的银行卡,其中激活银行卡成功与否不影响银行卡的创建。

由以上需求,我们可知对于cardActive()方法的事务传播行为,可以设置为PROPAGATION_NESTED,insertBankCardAndActive()事务的回滚,cardActive()激活的银行卡就没意义,也就需要跟着回滚,而cardActive()的回滚不影响insertBankCard()事务;insertBankCard()的事务传播行为可以设置为PROPAGATION_REQUIRED。

3.3 银行卡新增成功后,发送邮件给用户,发送邮件不涉及数据库的操作。sendCardMail()可以设置为PROPAGATION_NOT_SUPPORTED,发送邮件不属于并且不应当影响主体业务逻辑,即使发送失败也不应该对主体业务逻辑回滚。

4 代码解析

TransactionInterceptor.invoke()

TransactionAspectSupport.invokeWithinTransaction()

AbstractPlatformTransactionManager.getTransaction(TransactionDefinition definition)

DataSourceTransactionManager

事务控制是通过TransactionInterceptor.invoke()方法来实现的。下面我们来看一下这个方法的大体结构

public Object invoke(MethodInvocation invocation) throws Throwable {
    // 获取被代理类
    Class<?> targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null;
    Method var10001 = invocation.getMethod();
    invocation.getClass();
    // 执行事务方法
    return this.invokeWithinTransaction(var10001, targetClass, invocation::proceed);
}
    TransactionAspectSupport.TransactionInfo txInfo = this.createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
    result = null;
?
    try {
        result = invocation.proceedWithInvocation();
    } catch (Throwable var17) {
        this.completeTransactionAfterThrowing(txInfo, var17);
        throw var17;
    } finally {
        this.cleanupTransactionInfo(txInfo);
    }
?
    this.commitTransactionAfterReturning(txInfo);
    return result;
    }

原文地址:https://www.cnblogs.com/weixiaotao/p/10538158.html

时间: 2024-10-08 18:14:17

Spring中事务传播行为的相关文章

spring中事务传播解读:PROPAGATION_REQUIRES_NEW

第一步:获取事务状态,判断当前事务线程是否存在.第二步:如果当前事务的传播行为为PROPAGATION_REQUIRES_NEW,挂起当前线程绑定的事务,取消当前事务的sessionHolder和connectionHolder,并保存该事务的sessionHolder和connectionHolder信息以便后期恢复保存点.第三步:开启新的事务,并把该事务绑定到当前线程,生成新的sessionHolder和connectionHolder.第四步:提交新的事务,并恢复保存点,也就是之前被挂起的

Spring中事务管理

1.什么是事务? 事务是逻辑上的一组操作,这组操作要么全部成功,要么全部失败 2.事务具有四大特性ACID 1)原子性(Atomicity):即不可分割性,事务要么全部被执行,要么就全部不被执行.如果事务的所有子事务全部提交成功,则所有的数据库操作被提交,数据库状态发生转换:如果有子事务失败,则其他子事务的数据库操作被回滚,即数据库回到事务执行前的状态,不会发生状态转换. 2)一致性(Consistency):事务的执行使得数据库从一种正确状态转换成另一种正确状态.例如对于银行转账事务,不管事务

实例详解Spring的事务传播机制(二)

上面我们讨论了NEVER和MANDATORY的作用,下面我们接着讨论其他情况. 3. SUPPORTS 如果有事务则加入该事务,如果没有存在的事务则以非事务的方式运行. 我们先让insertSubTable方法在无事务的情况下运行.配置文件为: <tx:attributes>       <!--     <tx:method name="insertSuperTable" propagation="REQUIRED"/>      -

Spring之事务传播属性

在Spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量. 在使用Spring时,大部分会用到他的声明式事务,简单的在配置文件中进行一些规则配置,利用Spring的AOP功能就能轻松搞定事务问题:这里面就涉及到一个事务的传播属性问题Propagation,它在TransactionDefinition接口中定义,以供PlatfromTransactionManager使用,PlatfromTransactionManager是sprin

spring的事务传播属性

一.Propagation (事务的传播属性) Propagation : key属性确定代理应该给哪个方法增加事务行为.这样的属性最重要的部份是传播行为.有以下选项可供使用:PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务.这是最常见的选择. PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行. PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常. PROPAGATION

spring事务中隔离级别和spring的事务传播机制

Transaction 也就是所谓的事务了,通俗理解就是一件事情.从小,父母就教育我们,做事情要有始有终,不能半途而废. 事务也是这样,不能做一般就不做了,要么做完,要 么就不做.也就是说,事务必须是一个不可分割的整体,就像我们在化学课里学到的原子,原子是构成物质的最小单位.于是,人们就归纳出事务的第一个特性:原子性(Atomicity).我靠,一点都不神秘嘛. 特别是在数据库领域,事务是一个非常重要的概念,除了原子性以外,它还有一个极其重要的特性,那就是:一致性(Consistency).也就

SPRING中事务的配置

采用这种配置策略,完全可以避免增量式配置,所有的事务代理由系统自动创建.容器中的目标bean自动消失,避免需要使用嵌套bean来保证目标bean不可被访问.这 种配置方式依赖于Spring提供的bean后处理器,该后处理器用于为每个bean自动创建代理,此处的代理不仅可以是事务代理,也可以是任意的代理, 只需要有合适的拦截器即可.这些是AOP框架的概念,笔者在此处不对AOP进行深入介绍.读者只需了解这种事务代理的配置方式即可.下面是采用BeanNameAutoProxyCreator配置事务代理

实例详解Spring的事务传播机制(一)

Spring有7种事务传播机制,本文主要用实例说明各种传播机制的事务效果,和发生异常的回滚方式.7种事务传播机制网上的资料大多都是如下的描述: 事务传播行为类型 说明 PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中.这是最常见的选择. PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行. PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常. PRO

坦言spring中事务、重试、异步执行注解

一.@Transaction 我们再编码过程中,大量使用到这个注解.一般情况下,@Transaction使用默认注解可以完成90%的功能,下面会针对一些特殊场景下,@Tansaction的使用注意 1.1 事务回滚 @Transactional() public void rollback() throws SQLException { ... //do something throw new SQLException("exeception"); } 上述代码会回滚吗,答案是不会的.