1. 主要的三种传播机制

https://www.cnblogs.com/xzwbl...

REQUIRED、REQUIRES_NEW、NESTED

注意:以下所有的testA()方法与testB()方法是在两个类之中且都讲bean交给spring管理,如非如此会造成事务失效问题请看第二部分

1.1 REQUIRED(默认)

当前没有事务,则新建一个事务,如存在事务,则加入此事务

// 例子
// A.class
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void testA() {
    insertDataToTableA(dataList);
    testB();
}

// B.class
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void testB(){
    insertDataToTableB(dataList);
    int i = 1/0;
    insertDataToTableC(dataList);
}

// 结果 testB()加入testA()的事务,testB()发生异常,两个方法均回滚
// 如果 testA()不加事务,只有testB()加,则testA()成功,testB()回滚

1.2 REQUIRES_NEW

创建一个新事务,如果存在当前事务,则挂起该事务。即无论是否已存在事务都新建事务

// 例子
// A.class
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void testA() {
    insertDataToTableA(dataList);
    testB();
      int i = 1/0;
}

// B.class
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
public void testB(){
    insertDataToTableB(dataList);
    insertDataToTableC(dataList);
}

// 结果 testA() 回滚 testB() 成功,说明不是一个事物里 

1.3 NESTED

如果当前事务存在,则在嵌套事务中执行(子事务),否则REQUIRED的操作一样(开启一个事务)

这里需要注意两点:

  • 和REQUIRES_NEW的区别
REQUIRES_NEW是新建一个事务并且新开启的这个事务与原有事务无关,而NESTED则是当前存在事务时(我们把当前事务称之为父事务)会开启一个嵌套事务(称之为一个子事务)。
在NESTED情况下父事务回滚时,子事务也会回滚,而在REQUIRES_NEW情况下,原有事务回滚,不会影响新开启的事务。
  • 和REQUIRED的区别
REQUIRED情况下,调用方存在事务时,则被调用方和调用方使用同一事务,那么被调用方出现异常时,由于共用一个事务,所以无论调用方是否catch其异常,事务都会回滚
而在NESTED情况下,被调用方发生异常时,调用方可以catch其异常,这样只有子事务回滚,父事务不受影响
// 例子1
// A.class
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void testA() {
    insertDataToTableA(dataList);
    testB();
      int i = 1/0;
}

// B.class
@Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)
public void testB(){
    insertDataToTableB(dataList);
    insertDataToTableC(dataList);
}

// 结果 二者皆回滚,因为是嵌套的父子级事务,所以有一个有问题就都回滚
// 例子2
// A.class
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void testA() {
    insertDataToTableA(dataList);
  
      try{
      testB();
    }catch(Exception e){
      log.error(e)
    }
   
}

// B.class
@Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)
public void testB(){
    insertDataToTableB(dataList);
    int i = 1/0;
    insertDataToTableC(dataList);
}

// 结果 testA成功、testB回滚,说明相对 REQUIRED 异常可以被捕获处理

2. 事务失效

2.1 内部调用问题

1. A带事务调B不带事务

// 例0 无异常 -> 两个都能顺利插入表

// 例1 B方法异常
// 结果 事务生效(A,B公用一个事务),两个方法皆回滚
@Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.REQUIRED)
public Integer insertUserInfo() {
    jdbcTemplate.execute("INSERT INTO user (id, username) VALUES\n" +
            "(5, 'Jack5')");
  
    // 内部调用问题
    insertUserInfo2();
    return null;
}


//@Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.NESTED)
public Integer insertUserInfo2() {
    jdbcTemplate.execute("INSERT INTO user (id, username) VALUES\n" +
            "(9, 'tom9')");
    int i = 1 / 0;
    return null;
}

// 例2 A方法调用完B后再异常 -> 结果相同事务生效

2. A带事务调B带事务(REQUIRES_NEW)

// 例1 B异常 且 B的传播机制为 REQUIRES_NEW(新建事务)
// 结果 B事务失败(追加到A事务里),A,B皆回滚,说明A,B还是在一个事务里,A方法里异常时同样情况
@Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.REQUIRED)
public Integer insertUserInfo() {
    jdbcTemplate.execute("INSERT INTO user (id, username) VALUES\n" +
            "(5, 'Jack5')");

    // 内部调用问题
    insertUserInfo2();
    return null;
}


@Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.REQUIRES_NEW)
public Integer insertUserInfo2() {
    jdbcTemplate.execute("INSERT INTO user (id, username) VALUES\n" +
            "(9, 'tom9')");
    int i = 1 / 0;
    return null;
}

3. A带事务调B带事务(NESTED)

// 例1 B异常 且 B的传播机制为 NESTED,在A中捕获异常
// 结果 A事务成功,B事务失败,异常后仍能插入数据
@Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.REQUIRED)
public Integer insertUserInfo() {
    jdbcTemplate.execute("INSERT INTO user (id, username) VALUES\n" +
            "(5, 'Jack5')");
  
    // 内部调用问题
        try {
            insertUserInfo2();
        }catch (Exception e){
            e.printStackTrace();
        }
    return null;
}


@Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.NESTED)
public Integer insertUserInfo2() {
    jdbcTemplate.execute("INSERT INTO user (id, username) VALUES\n" +
            "(9, 'tom9')");
    int i = 1 / 0;
    return null;
}

// 例2 B异常 且 B的传播机制为 NESTED,在A中不捕获异常
// 结果 A,B皆回滚

// 例3 A异常 且 B的传播机制为 NESTED,在A中捕获异常与否都一样
// 结果 A,B皆回滚

4. A不带事务调B带事务

// 例0 无异常 -> 两个都能顺利插入表

// 例1 B方法异常 -> 事务失效,A,B都不会回滚,都会插入数据
// 第一部分中的三种传播机制都会是这种情况
//@Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.REQUIRED)
public Integer insertUserInfo() {
    jdbcTemplate.execute("INSERT INTO user (id, username) VALUES\n" +
            "(5, 'Jack5')");

    // 内部调用问题
    insertUserInfo2();
    return null;
}


@Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.REQUIRED)
public Integer insertUserInfo2() {
    jdbcTemplate.execute("INSERT INTO user (id, username) VALUES\n" +
            "(9, 'tom9')");
    int i = 1 / 0;
    return null;
}

// 例2 A方法调用完B后异常 -> 与例1相同,事务失效,A,B都不会回滚,都会插入数据

5. 综述

尽量避免内部调用,最多用A带事务调B不带事务,尤其是不能用A不带事务调B带

2.2 内部调用问题事务失败原因

  1. A带事务调B带事务:放在一个代理类里,追加到A的事务,只会代理A,然后this调用B
  2. A不带事务调B带事务:内部调用没走Spring的AOP增强代理机制,也就没做事务增强操作,本质是this实例调用

深蓝
1 声望0 粉丝

下一篇 »
Spring