likes
comments
collection
share

Spring 事务传播机制

作者站长头像
站长
· 阅读数 30

一、demo目录介绍

​ 所有demo代码位置:gitee.com/old_yogurt/…

Spring 事务传播机制

​ 所有的demo测试从 测试类出发,命名规则都是传播行为的名字。

​ 每一个传播行为,都定义了A method()方法,和B method()方法;如:上图中绿色框住的 mandatory 传播行为。

建议:查看文章的同时下载项目代码,方便测试;同时:通过观察,事务ID,事务状态,事务名,数据库操作结果,会有比较深刻的理解。

二、Spring 事务对于异常的捕获

ExceptionTransactionalTest 测试出发!!!

  • 事务只会捕获RuntimeException。对于Exception不进行事务回滚。
    • 1、抛出RuntimeException
    • 2、如果想要抛出 Exception,或者自定义异常,需要同时在事务声明中加上 @Transactional(rollbackFor = Exception.class)

2.1 抛出 RuntimeException运行时异常

/**
 * 1、抛出 RuntimeException运行时异常
 */
@Test
public void runtimeExceptionTest(){
    Integer bId = 3;
    try {
        bnTransactionalService.runtimeExceptionTest(bId);
    } catch (RuntimeException e) {
        System.out.println("############ 捕获B method 抛出的异常RuntimeException ############");
        e.printStackTrace();
    }
}

@Transactional
public void runtimeExceptionTest(Integer id) throws RuntimeException{
    bnMapper.deleteB(id);
    // 或者不用主动抛出异常,直接
    // int a = 2/0,抛出的异常就是运行时异常
    throw new RuntimeException();
}

测试结果:事务可以处理RuntimeException()异常,再抛出异常的同时,方法中对于 数据库的delete操作会回滚。

2.2 抛出Exception,并声明异常

@Test
public void exceptionTest(){
    Integer bId = 3;
    try {
        bnTransactionalService.exceptionTest(bId);
    } catch (Exception e) {
        System.out.println("############ 捕获B method 抛出的Exception异常 ############");
        e.printStackTrace();
    }
}

@Transactional(rollbackFor = Exception.class)
public void exceptionTest(Integer id) throws Exception {
    bnMapper.deleteB(id);
    throw new Exception();
}

测试结果:事务可以处理Exception()异常,再抛出异常的同时,方法中对于 数据库的delete操作会回滚。

2.3 抛出Exception,但不声明异常

@Test
public void exceptionTest_3(){
    Integer bId = 3;
    try {
        bnTransactionalService.exceptionTest_2(bId);
    } catch (Exception e) {
        System.out.println("############ 捕获B method 抛出的Exception异常 ############");
        e.printStackTrace();
    }
}

@Transactional
public void exceptionTest_2(Integer id) throws Exception {
    bnMapper.deleteB(id);
    throw new Exception();
}

测试结果:事务不可以处理Exception()异常,会抛出异常,但是数据库的操作却不会回滚。

三、REQUIRED

RequiredTransactionalTest 测试出发!!!

  • REQUIRED:
    • 表示当前方法必须在一个具有事务的上下文中运行,如果有加入当前事务,如果没有开启一个新的事务。
    • 1、A method() 不开启事务, B method() 事务传播特性:REQUIRED
      • 那么 B method 开启事务。
    • 2、A method() 开启事务, B method() 事务传播行为:REQUIRED
      • 那么B method()会加入到 A的事务中!
    • 3、A method() 开启事务, B method() 事务传播行为:REQUIRED
      • 3.1 如果 B method() 抛出异常,B method()和A method()都会回滚。
      • 3.2 如果 A method 捕获了 B method()抛出的异常,则会出现异常Transaction rolled back because it has been marked as rollback-only。
        • 抛出的一次也说明了 rollback-only 只能回滚,所以 A,B都回滚。

3.1 A 不开启事务,B 事务传播特性:REQUIRED

@Test
public void requiredTest_1(){
    A a = new A();
    a.setName("娜娜");
    a.setSex("女");
    Integer bId = 4;

    anRequiredTransactionalService.anRequiredTest_1(a,bId);
}

public void anRequiredTest_1(A a,Integer bId){
    // 可以查看当前方法所在线程是否有事务开启。
    boolean anTransactionActive = TransactionSynchronizationManager.isActualTransactionActive();
    String currentTransactionName = TransactionSynchronizationManager.getCurrentTransactionName();

    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ anTransactionActive);
    System.out.println("A method 当前事务name : "+ currentTransactionName);

    anMapper.insertSelective(a);
    bnRequiredTransactionalService.bnRequiredTest_1(bId);
}

@Transactional(propagation = Propagation.REQUIRED)
public void bnRequiredTest_1(Integer id){
    boolean bnTransactionActive = TransactionSynchronizationManager.isActualTransactionActive();
    String currentTransactionName = TransactionSynchronizationManager.getCurrentTransactionName();

    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " +bnTransactionActive);
    System.out.println("B method 当前事务name : "+ currentTransactionName);

    bnMapper.deleteB(id);
}

测试结果:

可以看到下面打印的信息:
     A method 返回当前是否有实际事务处于活动状态 : false
     A method 当前事务name : null
     B method 返回当前是否有实际事务处于活动状态 : true
     B method 当前事务name : com.transactional.service.impl.required.BnRequiredTransactionalServiceImpl.bnRequiredTest_1

从打印结果中我们可以看到,B开启了事务。

3.2 A 开启事务,B 事务传播特性:REQUIRED

@Test
public void requiredTest_2(){
    A a = new A();
    a.setName("Tom");
    a.setSex("man");
    Integer bId = 4;

    anRequiredTransactionalService.anRequiredTest_2(a,bId);
}

@Transactional
public void anRequiredTest_2(A a,Integer bId){
    // 可以查看当前方法所在线程是否有事务开启。
    boolean anTransactionActive = TransactionSynchronizationManager.isActualTransactionActive();
    String currentTransactionName = TransactionSynchronizationManager.getCurrentTransactionName();

    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ anTransactionActive);
    System.out.println("A method 当前事务name : "+ currentTransactionName);

    anMapper.insertSelective(a);
    bnRequiredTransactionalService.bnRequiredTest_2(bId);
}

@Transactional(propagation = Propagation.REQUIRED)
public void bnRequiredTest_2(Integer id){
    boolean bnTransactionActive = TransactionSynchronizationManager.isActualTransactionActive();
    String currentTransactionName = TransactionSynchronizationManager.getCurrentTransactionName();

    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " +bnTransactionActive);
    System.out.println("B method 当前事务name : "+ currentTransactionName);

    bnMapper.deleteB(id);
}

测试结果:

从打印信息 可以看到,A,B在同一事务中:
   A method 返回当前是否有实际事务处于活动状态 : true
   A method 当前事务name : com.transactional.service.impl.required.AnRequiredTransactionalServiceImpl.anRequiredTest_2
   B method 返回当前是否有实际事务处于活动状态 : true
   B method 当前事务name : com.transactional.service.impl.required.AnRequiredTransactionalServiceImpl.anRequiredTest_2

从打印结果可以看到:由于A开启了事务,说明有当前事务存在,那么,B 事务传播特性:REQUIRED会使得 B加入到当前事务中,所示,A,B两个事务的名称是一样的。

3.3 A 开启事务,B 事务传播特性:REQUIRED且抛出异常

@Test
public void anRequiredTest_4(){
    A a = new A();
    a.setName("Tom");
    a.setSex("man");
    Integer bId = 5;
    anRequiredTransactionalService.anRequiredTest_4(a,bId);
}

@Transactional
public void anRequiredTest_4(A a,Integer bId){
    // 可以查看当前方法所在线程是否有事务开启。
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    anMapper.insertSelective(a);
    bnRequiredTransactionalService.bnRequiredTest_4(bId);
}

@Transactional(propagation = Propagation.REQUIRED)
public void bnRequiredTest_4(Integer id){
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " +TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    bnMapper.deleteB(id);
    //抛出异常
    throw new RuntimeException();
}

测试结果:A,B在同一个事务中,异常由当前事务捕获,两个都会回滚。查看数据库:a表没有插入数据,b表没有删除数据。

3.4 A 开启事务,B 事务传播特性:REQUIRED且抛出异常,由A捕获B的异常

@Test
public void requiredTest_5(){
    A a = new A();
    a.setName("Tom");
    a.setSex("man");
    Integer bId = 4;
    anRequiredTransactionalService.anRequiredTest_5(a,bId);
}

@Transactional
@Override
public void anRequiredTest_5(A a, Integer bId) {
    // 可以查看当前方法所在线程是否有事务开启。
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : "+  TransactionSynchronizationManager.getCurrentTransactionName());

    anMapper.insertSelective(a);
    // 这里A method()只要开启事务,B method()没有事务或者事务传播行为是REQUIRED,B method()都会在事务中
    // 这里bnRequiredTest_5() 抛出异常
    try {
        bnRequiredTransactionalService.bnRequiredTest_5(bId);
    }catch (RuntimeException e){
        e.printStackTrace();
    }

}

@Transactional(propagation = Propagation.REQUIRED)
public void bnRequiredTest_5(Integer id){
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " +TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    bnMapper.deleteB(id);

    throw new RuntimeException();
}

运行结果:A,B都会回滚,并且抛出 Transaction rolled back because it has been marked as rollback-only。

四、SUPPORTS

SupportsTransactionTest 测试出发!!!

  • SUPPORTS:
    • 1、 A 不开启事务,B 事务传播行为:SUPPORTS
      • 不会以事务方式运行
    • 2、A 开启事务,B 事务传播行为:SUPPORTS
      • B 会在A开启的事务中运行
    • 3、A 开启事务;A抛出异常,B 事务传播行为:SUPPORTS
      • A method,B method 都会回滚
    • 4、A 开启事务;B 事务传播行为SUPPORTS,抛出异常,A捕获B的异常
      • A method()和B method()都会被回滚,并抛出 UnexpectedRollbackException: Transaction silently rolled back because it has been marked as rollback-only
    • 5、A 开启事务;B 事务传播行为SUPPORTS,抛出异常,A不捕获B的异常
      • A method()和B method()都会被回滚

4.1 A 不开启事务,B 事务传播行为:SUPPORTS

@Test
public void supportTest_1(){
    A a = new A();
    a.setName("小明");
    a.setSex("男");
    Integer bId = 5;

    anSupportsTransactionalService.anSupportsTest_1(a,bId);
}

public void anSupportsTest_1(A a, Integer bId){
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    anMapper.insertSelective(a);
    bnSupportsTransactionalService.bnSupportsTest_1(bId);
}

@Transactional(propagation = Propagation.SUPPORTS)
public void bnSupportsTest_1(Integer id){
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " +TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    bnMapper.deleteB(id);
}

运行结果:

打印结果:
   A method 返回当前是否有实际事务处于活动状态 : false
   A method 当前事务name : null
   B method 返回当前是否有实际事务处于活动状态 : false
   B method 当前事务name : com.transactional.service.impl.supports.BnSupportsTransactionalServiceImpl.bnSupportsTest_1

从打印结果可以看出:B method 不会以事务方式运行!

4.2 A 开启事务,B 事务传播行为:SUPPORTS

@Test
public void supportTest_2(){
    A a = new A();
    a.setName("小明");
    a.setSex("男");
    Integer bId = 5;

    anSupportsTransactionalService.anSupportsTest_2(a,bId);
}

@Transactional
public void anSupportsTest_2(A a, Integer bId){
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    anMapper.insertSelective(a);
    bnSupportsTransactionalService.bnSupportsTest_1(bId);
}

@Transactional(propagation = Propagation.SUPPORTS)
public void bnSupportsTest_1(Integer id){
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " +TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    bnMapper.deleteB(id);
}

测试结果:

打印结果:
    A method 返回当前是否有实际事务处于活动状态 : true
    A method 当前事务name : com.transactional.service.impl.supports.AnSupportsTransactionalServiceImpl.anSupportsTest_2
    B method 返回当前是否有实际事务处于活动状态 : true
    B method 当前事务name : com.transactional.service.impl.supports.AnSupportsTransactionalServiceImpl.anSupportsTest_2

从打印结果看到: B,A在同一事务中运行

4.3 A 开启事务;A 抛出异常,B 事务传播行为:SUPPORTS

@Test
public void supportTest_3(){
    A a = new A();
    a.setName("小明");
    a.setSex("男");
    Integer bId = 5;

    anSupportsTransactionalService.anSupportsTest_3(a,bId);
}

@Transactional
public void anSupportsTest_3(A a, Integer bId){
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    anMapper.insertSelective(a);
    bnSupportsTransactionalService.bnSupportsTest_1(bId);

    // 抛出运行时异常
    int f = 2/0;
}

@Transactional(propagation = Propagation.SUPPORTS)
public void bnSupportsTest_1(Integer id){
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " +TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    bnMapper.deleteB(id);
}

运行结果:

首先 B 是在A 的事务中运行的;然后A抛出异常,那么 A method,B method 都会回滚。 可以查看数据库, A表没有插入对用数据,B表没有删除对应数据。

4.4 A 开启事务;B 事务传播行为SUPPORTS,抛出异常,A捕获B的异常

@Test
public void supportTest_4(){
    A a = new A();
    a.setName("小明");
    a.setSex("男");
    Integer bId = 5;

    anSupportsTransactionalService.anSupportsTest_4(a,bId);
}

@Transactional
public void anSupportsTest_4(A a, Integer bId){
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    anMapper.insertSelective(a);
    try {
        bnSupportsTransactionalService.bnSupportsTest_2(bId);
    }catch (RuntimeException e){
        // 注意,这里异常是RuntimeException时的
        // 如果想要自定义异常去捕获处理,那么就要继承RuntimeException类
        e.printStackTrace();
    }
}

@Transactional(propagation = Propagation.SUPPORTS)
public void bnSupportsTest_2(Integer id){
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " +TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    bnMapper.deleteB(id);
    // method抛出运行时异常
    int f = 2/0;
}

运行结果:A method()和B method()都会被回滚,并抛出 UnexpectedRollbackException: Transaction silently rolled back because it has been marked as rollback-only

4.5 A 开启事务;B 事务传播行为SUPPORTS,抛出异常,A不捕获B的异常

@Test
public void supportTest_5(){
    A a = new A();
    a.setName("小明");
    a.setSex("男");
    Integer bId = 5;

    anSupportsTransactionalService.anSupportsTest_5(a,bId);
}

@Transactional
@Override
public void anSupportsTest_5(A a, Integer bId) {
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    anMapper.insertSelective(a);
    bnSupportsTransactionalService.bnSupportsTest_2(bId);
}

@Transactional(propagation = Propagation.SUPPORTS)
public void bnSupportsTest_2(Integer id){
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " +TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    bnMapper.deleteB(id);

    // method抛出运行时异常
    int f = 2/0;
}

运行结果:A method()和B method()都会被回滚

五、MANDATORY

MandatoryTransactionalTest 测试出发!!!

  • MANDATORY
    • 1、A 没有事务,而B 事务传播特性定义为:MANDATORY
    • 那么会抛出No existing transaction found for transaction marked with propagation 'mandatory';
      • 而且需要注意的是:A Method()有数据库操作是正常执行的,不会回滚(因为本身就没有事务),B Method()也不会进行回滚;其实应该是B都没有执行,因为在调用B的时候,spring事务会检查到B的注解上面的的参数是 MANDATORY就会抛出相应异常)。
    • 2、A 开启事务,B 事务传播行为:MANDATORY
      • 由于A开启事务,B会加入到A开始的事务中运行
    • 3、A 开启事务并抛出异常,B 事务传播行为:MANDATORY
      • A method 和 B method()执行的操作被回滚。
    • 4、A 开启事务,B 事务传播行为:MANDATORY,抛出异常,A不捕获B的异常
      • A method 和 B method()执行的操作被回滚。
    • 5、A 开启事务,B 事务传播行为:MANDATORY,抛出异常,A捕获B的异常
      • A method 和 B method()执行的操作被回滚,并且抛出异常 Transaction rolled back because it has been marked as rollback-only

5.1 A 没有事务,而B 事务传播特性定义为:MANDATORY

@Test
public void mandatoryTest_1(){
    A a = new A();
    a.setName("赵武");
    a.setSex("男");

    Integer bId = 3;
    anTransactionalService.mandatoryTest_1(a,bId);
}

public void mandatoryTest_1(A a,Integer bId){
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    anMapper.insertSelective(a);
    System.out.println("A method 当前事务id : "+ TransactionalId.getTransactionalId());

    bnTransactionalService.mandatoryTest_1(bId);
}

@Transactional(propagation = Propagation.MANDATORY)
public void mandatoryTest_1(Integer id){
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " + TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    bnMapper.deleteB(id);
    System.out.println("B method 当前事务id : "+ TransactionalId.getTransactionalId());
}

运行结果:

​ 抛出 No existing transaction found for transaction marked with propagation 'mandatory';

​ 而且需要注意的是:A Method()有数据库操作是正常执行的,不会回滚(因为本身就没有事务),B Method()也不会进行回滚,其实应该是B都没有执行,因为在调用B的时候,spring事务会检查到B的注解上面的的参数是 MANDATORY就会抛出相应异常)。

数据库结果:a表正常插入,b表没有删除数据。

5.2 A 开启事务,B 事务传播行为:MANDATORY

@Test
public void mandatoryTest_2(){
    A a = new A();
    a.setName("小红");
    a.setSex("女");

    Integer bId = 3;
    anTransactionalService.mandatoryTest_2(a,bId);
}

@Transactional
@Override
public void mandatoryTest_2(A a, Integer bId) {
    // A method()执行了,B method()也执行了
    anMapper.insertSelective(a);
    bnTransactionalService.mandatoryTest_1(bId);

    // 在A中抛出异常
    throw new RuntimeException();
}

@Transactional(propagation = Propagation.MANDATORY)
public void mandatoryTest_1(Integer id){
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " + TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    bnMapper.deleteB(id);

    System.out.println("B method 当前事务id : "+ TransactionalId.getTransactionalId());
}

运行结果:

A method 返回当前是否有实际事务处于活动状态 : true
A method 当前事务name : com.transactional.service.impl.mandatory.AnMandatoryTransactionalServiceImpl.mandatoryTest_2
A method 当前事务id : 30630
B method 返回当前是否有实际事务处于活动状态 : true
B method 当前事务name : com.transactional.service.impl.mandatory.AnMandatoryTransactionalServiceImpl.mandatoryTest_2
B method 当前事务id : 30630

可以看到, 事务id一致,A,B处于同一事务中。

5.3 A 开启事务并抛出异常,B 事务传播行为:MANDATORY

@Test
public void mandatoryTest_3(){
    A a = new A();
    a.setName("小红");
    a.setSex("女");

    Integer bId = 3;
    anTransactionalService.mandatoryTest_3(a,bId);
}

@Transactional
public void mandatoryTest_3(A a,Integer bId) {
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    anMapper.insertSelective(a);
    System.out.println("A method 当前事务id : "+ TransactionalId.getTransactionalId());

    bnTransactionalService.mandatoryTest_1(bId);

    // 在A中抛出异常
    throw new RuntimeException();
}

@Transactional(propagation = Propagation.MANDATORY)
public void mandatoryTest_1(Integer id){
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " + TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    bnMapper.deleteB(id);

    System.out.println("B method 当前事务id : "+ TransactionalId.getTransactionalId());
}

运行结果:

​ A method 和 B method()执行的操作被回滚。

​ 数据库结果:a表没有插入数据,b表没有删除数据

5.4 A 开启事务,B 事务传播行为:MANDATORY,抛出异常,A不捕获B的异常

@Test
public void mandatoryTest_4(){
    A a = new A();
    a.setName("李四");
    a.setSex("男");

    Integer bId = 3;
    anTransactionalService.mandatoryTest_4(a,bId);
}

@Transactional
@Override
public void mandatoryTest_4(A a, Integer bId) {
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    anMapper.insertSelective(a);
    System.out.println("A method 当前事务id : "+ TransactionalId.getTransactionalId());

    bnTransactionalService.mandatoryTest_2(bId);

}

@Transactional(propagation = Propagation.MANDATORY)
public void mandatoryTest_2(Integer id){
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " + TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());
    bnMapper.deleteB(id);
    System.out.println("B method 当前事务id : "+ TransactionalId.getTransactionalId());

    // 抛出异常
    int f = 2/0;
}

运行结果:

​ A method 和 B method()执行的操作被回滚。

​ 数据库结果:a表没有insert数据,b表没有delete数据。

5.5 A 开启事务,B 事务传播行为:MANDATORY,抛出异常,A捕获B的异常

@Test
public void mandatoryTest_5(){
    A a = new A();
    a.setName("李四");
    a.setSex("男");

    Integer bId = 3;
    anTransactionalService.mandatoryTest_5(a,bId);
}

@Transactional
@Override
public void mandatoryTest_5(A a, Integer bId) {
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    anMapper.insertSelective(a);
    System.out.println("A method 当前事务id : "+ TransactionalId.getTransactionalId());

    try {
        bnTransactionalService.mandatoryTest_2(bId);
    }catch (RuntimeException e){
        System.out.println("########## A method 捕获 B method抛出的异常");
    }
}

@Transactional(propagation = Propagation.MANDATORY)
@Override
public void mandatoryTest_2(Integer id){
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " + TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());
    bnMapper.deleteB(id);
    System.out.println("B method 当前事务id : "+ TransactionalId.getTransactionalId());

    // 抛出异常
    int f = 2/0;
}

运行结果:

​ A,B都会回滚。

​ 并且抛出异常:Transaction rolled back because it has been marked as rollback-only

六、REQUIRES_NEW

RequiresNewTransactionTest 测试出发!!!

  • REQUIRES_NEW

    • 表示当前方法需要运行在一个新的事务中,并且 上一个事务会被挂起。(点到 Propagation.REQUIRES_NEW中 看源码注释说明)

    • 1、A 开启一个事务,B 事务传播行为:REQUIRES_NEW

      • 结果: A 虽然开启事务,但是B 也开启了一个新的事务执行数据库操作。并且 上一个事务会被挂起,先执行当前事务操作。
    • 2、A 开启一个事务,B 事务传播行为:REQUIRES_NEW,A 抛出异常

      A.method()的事务被回滚,但B.method()事务不受影响
         -> A method() insert操作被回滚,B method delete操作正常执行
         ( 因为 AB是在不同的事务中!!! )
      
    • 3、A 开启一个事务,B 事务传播行为:REQUIRES_NEW并抛出异常

      A B method()的事务都会被回滚
         -> A method() insert操作被回滚,B method delete操作也会被回滚
      
    • 4、A 开启一个事务,B 事务传播行为:REQUIRES_NEW并抛出异常,同时,A捕获B的异常

      • A method() insert操作会正常执行,B method() delete操作会被回滚

6.1 A 开启一个事务,B 事务传播行为:REQUIRES_NEW

@Test
public void requiresNewTest_1(){
    A a = new A();
    a.setName("娜美");
    a.setSex("女");
    Integer bId = 16;

    anRequiresNewTransactionalService.anRequiresNewTest_1(a,bId);
}

@Transactional
@Override
public void anRequiresNewTest_1(A a, Integer bId) {
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());
    anMapper.insertSelective(a);
    System.out.println("A method 当前事务id : "+ TransactionalId.getTransactionalId());

    bnRequiresNewTransactionalService.bnRequiresNewTest_1(bId);
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void bnRequiresNewTest_1(Integer id) {
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " + TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());
    bnMapper.deleteB(id);
    //在这里打断点
    System.out.println("B method 当前事务id : "+ TransactionalId.getTransactionalId());
}

看图打印结果:

测试的时候需要注意:

为了测试 B method开启新事务效果,需要在bnRequiresNewTest_1()打印 事务id列表的时候打个断点,走到断点在释放断点即可看到两个事务id返回。

Spring 事务传播机制

运行结果:

从上面的打印结果可以看出, A method()虽然开启事务,但是B method()也开启了一个新的事务执行数据库操作;同时A 事务会挂起,先执行B 事务,B事务执行完之后,恢复A事务,A事务再执行

6.2 A 开启一个事务,B 事务传播行为:REQUIRES_NEW,A 抛出异常

@Test
public void requiresNewTest_2(){
    A a = new A();
    a.setName("娜美");
    a.setSex("女");
    Integer bId = 16;

    anRequiresNewTransactionalService.anRequiresNewTest_2(a,bId);
}

@Transactional
@Override
public void anRequiresNewTest_2(A a, Integer bId) {
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());
    anMapper.insertSelective(a);
    System.out.println("A method 当前事务id : "+ TransactionalId.getTransactionalId());

    bnRequiresNewTransactionalService.bnRequiresNewTest_1(bId);

    //抛出运行时异常
    int f = 2/0;
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void bnRequiresNewTest_1(Integer id) {
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " + TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());
    bnMapper.deleteB(id);
    System.out.println("B method 当前事务id : "+ TransactionalId.getTransactionalId());
}

运行结果:

​ A method 回滚,B method 正常执行 (这个其实也可以说明处在不同的事务中)

6.3 A 开启一个事务,B 事务传播行为:REQUIRES_NEW并抛出异常

@Test
public void requiresNewTest_3(){
    A a = new A();
    a.setName("娜美");
    a.setSex("女");
    Integer bId = 16;

    anRequiresNewTransactionalService.anRequiresNewTest_3(a,bId);
}

@Transactional
@Override
public void anRequiresNewTest_3(A a, Integer bId) {
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());
    anMapper.insertSelective(a);
    System.out.println("A method 当前事务id : "+ TransactionalId.getTransactionalId());

    bnRequiresNewTransactionalService.bnRequiresNewTest_2(bId);
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void bnRequiresNewTest_2(Integer id) {
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " + TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());
    bnMapper.deleteB(id);
    System.out.println("B method 当前事务id : "+ TransactionalId.getTransactionalId());

    // 抛出异常
    int f = 2/0;
}

运行结果:

A B method()的事务都会被回滚
   -> A method() insert操作被回滚,B method delete操作也会被回滚

6.4 A 开启一个事务,B 事务传播行为:REQUIRES_NEW并抛出异常,同时,A捕获B的异常

@Test
public void requiresNewTest_4(){
    A a = new A();
    a.setName("娜美");
    a.setSex("女");
    Integer bId = 16;

    anRequiresNewTransactionalService.anRequiresNewTest_4(a,bId);
}

@Transactional
@Override
public void anRequiresNewTest_4(A a, Integer bId) {
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());
    anMapper.insertSelective(a);
    System.out.println("A method 当前事务id : "+ TransactionalId.getTransactionalId());

    // A 捕获 B的异常
    try {
        bnRequiresNewTransactionalService.bnRequiresNewTest_2(bId);
    }catch (RuntimeException e){
        e.printStackTrace();
    }
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void bnRequiresNewTest_2(Integer id) {
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " + TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());
    bnMapper.deleteB(id);
    System.out.println("B method 当前事务id : "+ TransactionalId.getTransactionalId());

    // 抛出异常
    int f = 2/0;
}

运行结果:A method() insert操作会正常执行,B method() delete操作会被回滚

七、NOT_SUPPORTED

从 `NotSupportedTransactionTest 测试出发!!!

  • NOT_SUPPORTED

    • 表示该方法不应该在一个事务中运行。如果有一个事务正在运行,他将在运行期被挂起,直到这个事务提交或者回滚才恢复执行。

    • 1、 A 不开启事务,B 事务传播行为:NOT_SUPPORTED

    • 结果:B 没有开启事务

    • 2、 A method开启事务,B,事务传播行为:NOT_SUPPORTED

      • 结果:A method 是在事务中运行的,并且事务是活动状态。

        ​ B method 则是等 A事务提交结束,以非事务的状态执行。

    • 3、 A 开启事务并且A 抛出异常;B 事务传播行为:NOT_SUPPORTED

      • 结果:这种属于,A method是在事务中,A 异常会回滚,B由于不在事务中执行,所以会被执行。 验证:表a没有插入数据库,表b中对应的数据被删除。
    • 4、A 开启事务;B 事务传播行为:NOT_SUPPORTED;B 抛出异常,A不捕获

      • 结果:A 的insert操作回滚,B 的delete的操作正常执行
    • 5、A 开启事务;B 事务传播行为:NOT_SUPPORTED;B 抛出异常,A捕获

      • A的insert操作正常执行,B的delete的操作也正常执行

7.1 A 不开启事务,B 事务传播行为:NOT_SUPPORTED

@Test
public void notSupportsTest_1(){
    A a = new A();
    a.setName("诺诺罗亚");
    a.setSex("男");
    Integer bId = 12;

    anNotSupportedTransactionalService.anNoSupportedTest_1(a,bId);
}

@Override
public void anNoSupportedTest_1(A a, Integer bId) {
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());
    anMapper.insertSelective(a);
    System.out.println("A method 当前事务id : "+ TransactionalId.getTransactionalId());

    bnNotSupportedTransactionalService.bnNoSupportedTest_1(bId);
}

@Transactional(propagation = Propagation.NOT_SUPPORTED)
@Override
public void bnNoSupportedTest_1(Integer bId) {
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " + TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());
    bnMapper.deleteB(bId);
    System.out.println("B method 当前事务id : "+ TransactionalId.getTransactionalId());
}

运行结果:

打印记录:
   A method 返回当前是否有实际事务处于活动状态 : false
   A method 当前事务name : null
   B method 返回当前是否有实际事务处于活动状态 : false
   B method 当前事务name : com.transactional.service.impl.notsupported.BnNotSupportedTransactionalServiceImpl.bnNoSupportedTest_1

从打印记录可以看出 B method没有开启事务。

7.2 A method开启事务,B,事务传播行为:NOT_SUPPORTED

@Test
public void notSupportsTest_2(){
    A a = new A();
    a.setName("诺诺罗亚");
    a.setSex("男");
    Integer bId = 12;

    anNotSupportedTransactionalService.anNoSupportedTest_2(a,bId);
}

@Transactional
@Override
public void anNoSupportedTest_2(A a, Integer bId) {
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : " + TransactionSynchronizationManager.getCurrentTransactionName());
    anMapper.insertSelective(a);
    System.out.println("A method 当前事务id : "+ TransactionalId.getTransactionalId());

    bnNotSupportedTransactionalService.bnNoSupportedTest_1(bId);
}

@Transactional(propagation = Propagation.NOT_SUPPORTED)
@Override
public void bnNoSupportedTest_1(Integer bId) {
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " + TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());
    bnMapper.deleteB(bId);
    System.out.println("B method 当前事务id : "+ TransactionalId.getTransactionalId());
}

运行结果:

打印记录:
 A method 返回当前是否有实际事务处于活动状态 : true
A method 当前事务name : com.transactional.service.impl.notsupported.AnNotSupportedTransactionalServiceImpl.anNoSupportedTest_2
A method 当前事务id : 30746
B method 返回当前是否有实际事务处于活动状态 : false
B method 当前事务name : com.transactional.service.impl.notsupported.BnNotSupportedTransactionalServiceImpl.bnNoSupportedTest_1
B method 当前事务id : null

A method 是在事务中运行的,并且事务是活动状态。

B method 则是 A事务提交结束,以非事务的状态执行。

7.3 A 开启事务并且A 抛出异常;B 事务传播行为:NOT_SUPPORTED

@Test
public void notSupportsTest_3(){
    A a = new A();
    a.setName("诺诺罗亚");
    a.setSex("男");
    Integer bId = 12;

    anNotSupportedTransactionalService.anNoSupportedTest_3(a,bId);
}

@Transactional
@Override
public void anNoSupportedTest_3(A a, Integer bId) {
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : " + TransactionSynchronizationManager.getCurrentTransactionName());
    anMapper.insertSelective(a);
    System.out.println("A method 当前事务id : "+ TransactionalId.getTransactionalId());

    bnNotSupportedTransactionalService.bnNoSupportedTest_1(bId);

    int f = 2/0;
}

@Transactional(propagation = Propagation.NOT_SUPPORTED)
@Override
public void bnNoSupportedTest_1(Integer bId) {
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " + TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());
    bnMapper.deleteB(bId);
    System.out.println("B method 当前事务id : "+ TransactionalId.getTransactionalId());
}

运行结果:这种属于,A method是在事务中,A 异常会回滚,B由于不在事务中执行,所以会被执行。

验证:表a没有插入数据库,表b中对应的数据被删除。

7.4 A 开启事务;B 事务传播行为:NOT_SUPPORTED;B 抛出异常,A不捕获

@Test
public void notSupportsTest_4(){
    A a = new A();
    a.setName("诺诺罗亚");
    a.setSex("男");
    Integer bId = 12;

    anNotSupportedTransactionalService.anNoSupportedTest_4(a,bId);
}

@Transactional
@Override
public void anNoSupportedTest_4(A a, Integer bId) {
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : " + TransactionSynchronizationManager.getCurrentTransactionName());
    anMapper.insertSelective(a);
    System.out.println("A method 当前事务id : "+ TransactionalId.getTransactionalId());

    bnNotSupportedTransactionalService.bnNoSupportedTest_2(bId);
}

@Transactional(propagation = Propagation.NOT_SUPPORTED)
@Override
public void bnNoSupportedTest_2(Integer bId) {
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " + TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());
    bnMapper.deleteB(bId);
    System.out.println("B method 当前事务id : "+ TransactionalId.getTransactionalId());

    // 异常在数据库操作的后面
    int f = 2/0;
}

运行结果:(注意:B 抛出异常,抛出的异常在数据库操作后面)

A的insert操作回滚,B的delete的操作正常执行。

7.5 A 开启事务;B 事务传播行为:NOT_SUPPORTED;B 抛出异常,A捕获

@Test
public void notSupportsTest_5(){
    A a = new A();
    a.setName("诺诺罗亚");
    a.setSex("男");
    Integer bId = 12;

    anNotSupportedTransactionalService.anNoSupportedTest_5(a,bId);
}

@Transactional
@Override
public void anNoSupportedTest_5(A a, Integer bId) {
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : " + TransactionSynchronizationManager.getCurrentTransactionName());
    anMapper.insertSelective(a);
    System.out.println("A method 当前事务id : "+ TransactionalId.getTransactionalId());

    // 捕获异常
    try {
        bnNotSupportedTransactionalService.bnNoSupportedTest_2(bId);
    }catch (RuntimeException e){
        e.printStackTrace();
    }
}

@Transactional(propagation = Propagation.NOT_SUPPORTED)
@Override
public void bnNoSupportedTest_2(Integer bId) {
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " + TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());
    bnMapper.deleteB(bId);
    System.out.println("B method 当前事务id : "+ TransactionalId.getTransactionalId());

    // 异常在数据库操作的后面
    int f = 2/0;	
}

运行结果:

​ A,B都没有回滚。

​ A的insert操作正常执行,B的delete的操作也正常执行。

八、NEVER

  • NEVER
    • 表示当方法务不应该在一个事务中运行,如果存在一个事务,则抛出异常;
    • 1、A 不开启事务,B 传播行为为:NEVER
      • 结果:A,B方法都没有在事务中运行
    • 2、A 开启事务,B 传播行为为:NEVER
      • 结果:抛出异常 IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'

8.1 A 不开启事务,B 传播行为:NEVER

@Test
public void neverTest_1(){
    A a = new A();
    a.setName("尼卡");
    a.setSex("男");
    Integer bId = 12;

    anNeverTransactionalService.anRequiredTest_1(a,bId);
}

@Override
public void anRequiredTest_1(A a, Integer bId) {
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    anMapper.insertSelective(a);
    bnNeverTransactionalService.bnRequiredTest_1(bId);
}

@Transactional(propagation = Propagation.NEVER)
@Override
public void bnRequiredTest_1(Integer id) {
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " + TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());
    bnMapper.deleteB(id);
}

运行结果:

打印日志:
   A method 返回当前是否有实际事务处于活动状态 : false
   A method 当前事务name : null
   B method 返回当前是否有实际事务处于活动状态 : false
   B method 当前事务name : com.transactional.service.impl.never.BnNeverTransactionalServiceImpl.bnRequiredTest_1

通过上面打印日志可以看到,A,B方法都没有在事务中运行

8.2 A 开启事务,B 传播行为为:NEVER

@Test
public void neverTest_2(){
    A a = new A();
    a.setName("尼卡");
    a.setSex("男");
    Integer bId = 12;

    anNeverTransactionalService.anRequiredTest_2(a,bId);
}

@Transactional
@Override
public void anRequiredTest_2(A a, Integer bId) {
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    anMapper.insertSelective(a);
    bnNeverTransactionalService.bnRequiredTest_1(bId);
}

@Transactional(propagation = Propagation.NEVER)
@Override
public void bnRequiredTest_1(Integer id) {
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " + TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());
    bnMapper.deleteB(id);
}

运行结果:A,B都会回滚,并抛出异常 IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'

九、NESTED

补充

  • 1、一个数据库事务的知识:数据库的嵌套事务,嵌套事务其实就是一个savepoint,一个保存点,内层事务的异常回滚只是恢复到了上一个savepoint!!! 下面我们会证实,A,B处于同一事务中。
  • 2、因为 spring 默认的数据源 不支持嵌套事务:NestedTransactionNotSupportedException: JpaDialect does not support savepoints - check your JPA provider's capabilities 所以在测试这个嵌套事务的时候利用 JdbcTemplate测试。
  • NESTED
    • 如果存在当前事务,则在嵌套事务中执行,如果不存在当前事务,那么就新开启一个事务。
    • 1、 A method 不存在事务,B事务传播行为NESTED
      • 结果:则B method()运行在一个新的事务中。
    • 2、 A method()存在事务,B事务传播行为NESTED
      • 结果:B method()运行在嵌套事务中,(这就是上面补充说道的,在同一个事务,只是有保存点。)
    • 3、 A 作为外层事务,B 作为内层事务;A 发生异常
      • 结果:A,B都回滚;
    • 4、A 作为外层事务,B 作为内层事务;B 发生异常,A不捕获
      • 结果:A,B都回滚;
    • 5、A 作为外层事务,B 作为内层事务;B 发生异常,A捕获
      • 结果:B method发生异常,B回滚不影响A事务的提交。(也就是B的回滚,只是回滚到了上一个保存点)

9.1 A 不存在事务,B 传播行为 NESTED

@Test
public void nestedTest_1(){
    A a = new A();
    a.setName("王麻子");
    a.setSex("男");

    Integer bId = 11;
    anNestedTransactionalService.anNestedTest_1(a,bId);
}

@Override
public void anNestedTest_1(A a, Integer bId) {
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());
    jdbcTemplate.execute("insert into a(name,sex) values('"+a.getName()+"','"+a.getSex()+"')");
    System.out.println("A method 当前事务 Id : "+ TransactionalId.getTransactionalId());

    bnNestedTransactionalService.bnNestedTest_1(bId);
}

@Transactional(propagation = Propagation.NESTED)
@Override
public void bnNestedTest_1(Integer bId) {
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " + TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    jdbcTemplate.execute("delete from b where id = '"+bId+"'");
    System.out.println("B method 当前事务 Id : "+ TransactionalId.getTransactionalId());
}

运行结果:

打印结果:
   A method 返回当前是否有实际事务处于活动状态 : false
   A method 当前事务name : null
   A method 当前事务 Id : null
   B method 返回当前是否有实际事务处于活动状态 : true
   B method 当前事务name : com.transactional.service.impl.nested.BnNestedTransactionalServiceImpl.bnNestedTest_1
   B method 当前事务 Id : null

从打印结果可以看到:B事务传播行为NESTED,B method()事务状态处于活动状态,运行在一个新的事务中 但是,为啥事务id为 null呀,我擦!!!

9.2 A 存在事务,B传播行为 NESTED

@Test
public void nestedTest_2(){
    A a = new A();
    a.setName("王麻子");
    a.setSex("男");

    Integer bId = 11;
    anNestedTransactionalService.anNestedTest_2(a,bId);
}

@Transactional
@Override
public void anNestedTest_2(A a, Integer bId) {
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    jdbcTemplate.execute("insert into a(name,sex) values('"+a.getName()+"','"+a.getSex()+"')");
    // 注意这里为啥写在 执行操作的后面!!!
    System.out.println("A method 当前事务 Id : "+ TransactionalId.getTransactionalId());

    bnNestedTransactionalService.bnNestedTest_1(bId);
}

@Transactional(propagation = Propagation.NESTED)
@Override
public void bnNestedTest_1(Integer bId) {
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " + TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    jdbcTemplate.execute("delete from b where id = '"+bId+"'");
    System.out.println("B method 当前事务 Id : "+ TransactionalId.getTransactionalId());
}

运行结果:

打印信息:
     A method 返回当前是否有实际事务处于活动状态 : true
     A method 当前事务name : com.transactional.service.impl.nested.AnNestedTransactionalServiceImpl.anNestedTest_2
     A method 当前事务 Id : 155926
     B method 返回当前是否有实际事务处于活动状态 : true
     B method 当前事务name : com.transactional.service.impl.nested.AnNestedTransactionalServiceImpl.anNestedTest_2
     B method 当前事务 Id : 155926

可以看到确实是同一事务中。

9.3 A 作为外层事务,B 作为内层事务;A 发生异常

@Test
public void nestedTest_3(){
    A a = new A();
    a.setName("王麻子");
    a.setSex("男");

    Integer bId = 11;
    anNestedTransactionalService.anNestedTest_3(a,bId);
}

@Transactional
@Override
public void anNestedTest_3(A a, Integer bId) {
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    jdbcTemplate.execute("insert into a(name,sex) values('"+a.getName()+"','"+a.getSex()+"')");
    bnNestedTransactionalService.bnNestedTest_1(bId);

    // 抛出运行时异常,注意把异常写在 两个数据操作完成之后的用意。
    // 这样就可以保证了 两个内外事务都执行了,在A method发生了异常使得 A,B的数据库操作都回滚。
    int f = 2/0;
}

@Transactional(propagation = Propagation.NESTED)
@Override
public void bnNestedTest_1(Integer bId) {
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " + TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    jdbcTemplate.execute("delete from b where id = '"+bId+"'");
    System.out.println("B method 当前事务 Id : "+ TransactionalId.getTransactionalId());
}

运行结果:A,B都回滚;可以查看数据库,a表没有插入数据,b表没有删除数据

9.4 A 作为外层事务,B 作为内层事务;B 发生异常,A不捕获

@Test
public void nestedTest_4(){
    A a = new A();
    a.setName("王麻子");
    a.setSex("男");

    Integer bId = 11;
    anNestedTransactionalService.anNestedTest_4(a,bId);
}

@Transactional
@Override
public void anNestedTest_4(A a, Integer bId) {
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    jdbcTemplate.execute("insert into a(name,sex) values('"+a.getName()+"','"+a.getSex()+"')");
    bnNestedTransactionalService.bnNestedTest_2(bId);
}

@Transactional(propagation = Propagation.NESTED)
@Override
public void bnNestedTest_2(Integer bId) {
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " + TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());
    jdbcTemplate.execute("delete from b where id = '"+bId+"'");
    System.out.println("B method 当前事务 Id : "+ TransactionalId.getTransactionalId());

    int f = 2/0;
}

运行结果:A,B都回滚;

因为 A,B还是在同一个事务中,B异常,A如果不做处理,那就整个事务回滚。

9.5 A 作为外层事务,B 作为内层事务;B 发生异常,A捕获

@Test
public void nestedTest_5(){
    A a = new A();
    a.setName("王麻子");
    a.setSex("男");

    Integer bId = 11;
    anNestedTransactionalService.anNestedTest_5(a,bId);
}

@Transactional
@Override
public void anNestedTest_5(A a, Integer bId) {
    System.out.println("A method 返回当前是否有实际事务处于活动状态 : "+ TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("A method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());

    jdbcTemplate.execute("insert into a(name,sex) values('"+a.getName()+"','"+a.getSex()+"')");
    try {
        bnNestedTransactionalService.bnNestedTest_2(bId);
    }catch (RuntimeException e){
        e.printStackTrace();
    }
}

@Transactional(propagation = Propagation.NESTED)
@Override
public void bnNestedTest_2(Integer bId) {
    System.out.println("B method 返回当前是否有实际事务处于活动状态 : " + TransactionSynchronizationManager.isActualTransactionActive());
    System.out.println("B method 当前事务name : "+ TransactionSynchronizationManager.getCurrentTransactionName());
    jdbcTemplate.execute("delete from b where id = '"+bId+"'");
    System.out.println("B method 当前事务 Id : "+ TransactionalId.getTransactionalId());

    int f = 2/0;
}

运行结果:B method发生异常,B回滚不影响A事务的提交。(也就是B的回滚,只是回滚到了上一个保存点)

数据库结果:A insert操作正常插入,B delete操作回滚。

转载自:https://juejin.cn/post/7392071148751994932
评论
请登录