spring事务传播机制

spring事务传播机制

1、7种传播机制

我们在@Transactional注解的源码中可以看到,事务默认的机制是REQUIRED。进入Propagation中可以看到,spring定义了7种传播机制。如下所示,我这里贴上了网上的参考解释,不一定全面,大家也可以自己百度一下。这里可以先浏览一下,后续案例中进行验证,大家可以对照验证结果来看这里的解释,会更容易理解。重点理解REQUIRED、REQUIRES_NEW、NESTED这三种机制,也是工作中最常用的三种机制。

/**
 * The transaction propagation type.
 * <p>Defaults to {@link Propagation#REQUIRED}.
 * @see org.springframework.transaction.interceptor.TransactionAttribute#getPropagationBehavior()
 */
Propagation propagation() default Propagation.REQUIRED;
public enum Propagation {
   /**
   	* spring默认传播行为,支持当前事务,如果当前没有事务,则新建一个事务。
   	* 外层方法、内层方法不管哪个抛出异常,都会回滚,即使在外层方法中捕获内层方法抛出的异常也会回滚
   	*/
   REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
   /**
   	* 支持当前事务(和REQUIRED行为一样),如果当前没有事务,就以非事务方式运行
   	*/
   SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
   /**
   	* 支持当前事务(和REQUIRED行为一样),如果当前没有事务,则抛出异常,属于强制事务
   	*/
   MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
   /**
   	* 新建事务,如果当前存在事务,则事务挂起,新增一个事务,新建的事务和当前事务没有任何关系,是两个独立的事务。
   	* 外层的方法失败回滚后,不能回滚内层方法的运行结果,内层方法失败抛出异常,外层方法如果进行捕获,也能不回滚外层方法运行结果。
   	*/
   REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
   /**
   	* 不执行当前事务,而总是以非事务的方式执行
   	*/
   NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
   /**
   	* 以非事务的方式执行,如果当前存在事务,则抛出异常,强制非事务
   	*/
   NEVER(TransactionDefinition.PROPAGATION_NEVER),
   /**
   	* 如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则进行与REQUIRED类似的操作
   	* 内层事务受外层事务的影响,外层事务不受内层事务的影响(前提 外层事务捕获了内层事务的异常)
   	*/
   NESTED(TransactionDefinition.PROPAGATION_NESTED);
   private final int value;
   Propagation(int value) {this.value = value;}
   public int value() { return this.value;}
}

2、案例准备

1、准备2张表,tablea、tableb

CREATE TABLE `tablea` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1;
CREATE TABLE `tableb` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1;

2、新建一个springboot项目,在pom文件中引入以下jar包,在yml文件中添加数据库和mybatis配置,需要修改为你的数据库账号、密码、库名、实体所在的包名

	<properties>
        <java.version>1.8</java.version>
        <spring.boot.version>2.5.1</spring.boot.version>
        <junit.versin>4.13</junit.versin>
        <lombok.version>1.18.8</lombok.version>
        <mysql.version>8.0.15</mysql.version>
        <mybatis.version>2.0.1</mybatis.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis.version}</version>
        </dependency>
    </dependencies>
server:
  port: 8081
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
mybatis:
  type-aliases-package: org.west.sky.scripture.transaction.model
  configuration:
    map-underscore-to-camel-case: true

3、准备业务代码和单元测试的代码

//mapper
@Mapper
public interface ITableMapper {
    @Insert("INSERT INTO tablea(id, name) VALUES(#{id}, #{name})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    void insertTableA(TableEntity tableEntity);
    @Insert("INSERT INTO tableb(id, name) VALUES(#{id}, #{name})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    void insertTableB(TableEntity tableEntity);
}
//entity
@Data
public class TableEntity {
    private static final long serialVersionUID = 1L;
    private Long id;
    private String name;
}
//service
public interface ITableService {
    void insertTableA(TableEntity tableEntity);
    void insertTableB(TableEntity tableEntity);
}
//serviceimpl
@Service
public class TableServiceImpl implements ITableService {
    @Autowired
    ITableMapper tableMapper;
    @Override
    public void insertTableA(TableEntity tableEntity) {tableMapper.insertTableA(tableEntity);}
    @Override
    public void insertTableB(TableEntity tableEntity) {tableMapper.insertTableB(tableEntity);}
}
//controller
@RestController
@RequestMapping("/transaction")
public class TransactionTestController {
    @Autowired
    private TransactionServiceA transactionServiceA;
    @RequestMapping("/test")
    public String testTransaction() {
        transactionServiceA.methodA();
        return "SUCCESS";
    }
}
//junit
@SpringBootTest
@AutoConfigureMockMvc
class TransactionTestControllerTest {
    @Autowired
    private MockMvc mockMvc;
    @Test
    public void testTransaction() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/transaction/test")).andExpect(MockMvcResultMatchers.status().isOk());
    }
}

4、事务操作代码和spring工具类

@Service
public class TransactionServiceA {
    @Autowired
    private ITableService tableService;
    @Autowired
    private TransactionServiceB transactionServiceB;
    /**
     * 这里我们为了方便后续的调试,定义了一个methodA,就不需要每次都去修改controller中调用的方法了
     */
    public void methodA() {
        //可以先忽略这里的调用方法,后续会解释
        TransactionServiceA bean = SpringUtil.getBean(TransactionServiceA.class);
        bean.methodA__4();
    }
    
    public void methodA_1() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_1();
    }
}
@Service
public class TransactionServiceB {
	@Autowired
    private ITableService tableService;
    
    public void methodB_1() {
        System.out.println("methodB");
        tableService.insertTableB(new TableEntity());
        throw new RuntimeException();
    }
}

5、运行junit中的测试方法,看看数据库中能不能正常插入数据。如果可以插入,进行下一步,没有插入,可以检查控制台有没有报错,根据报错解决问题后进行下一步操作。

3、测试案例

这里我就不展示数据库的执行结果了,直接说结果和原因,建议大家自己是执行代码验证一下结果。每次执行案例时,记得修改methodA中调用的方法。
具体代码详见
spring事务的7中传播机制
注意:后续的测试中,如果大家不清楚是没有执行插入操作,还是执行了插入操作,但是由于报错进行了回滚。可以在每个案例执行前,先执行TRUNCATE命令,把数据库还原,不了解TRUNCATE原理的可以百度一下。因为我们设置了表主键自增,所以正常插入操作,表的主键应该是1、2、3…这样递增。如果你发现执行了案例后,表中没有数据,可以收到插入1条数据,如果主键自动生成的是2,而不是1,表示进行了数据回滚操作,但是如果是1,表示根本没有执行插入操作。通过控制台中是否打印了“methodA”、“methodB”也可以判断方法有没有执行。

TRUNCATE tablea;
TRUNCATE tableb;
1、有事务的方法调用无事务的方法
//加入到TransactionServiceA类中
	/**
     * 条件: A有事务、B没有事务、B报错
     * 结果:A、B均未插入数据(都回滚)
     * 原因:A、B在同一个事务中,可以理解为A、B在一个方法中
     */
    @Transactional
    public void methodA_2_1() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_1();
    }
    /**
     * 条件: A有事务、B没有事务、A报错
     * 结果:A、B均未插入数据(都回滚)
     * 原因:A、B在同一个事务中,可以理解为A、B在一个方法中
     */
    @Transactional
    public void methodA_2_2() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_2();
        throw new RuntimeException();
    }
    /**
     * 条件: A有事务、B没有事务、捕获B抛出的异常
     * 结果:A、B均插入数据(都未回滚)
     * 原因:A、B在同一个事务中,捕获异常后,并没抛出异常,可以理解为A、B在一个方法中,B这段代码加了异常捕获
     */
    @Transactional
    public void methodA_2_3() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        try {
            transactionServiceB.methodB_1();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
//加入到TransactionServiceB类中
	public void methodB_2() {
        System.out.println("methodB");
        tableService.insertTableB(new TableEntity());
    }
2、没有事务的方法调用了有事务的方法
//加入到TransactionServiceA类中
	/**
     * 条件: A没有事务、B有事务、B抛出异常
     * 结果:A插入数据,B未插入数据(A未回滚,B回滚)
     * 原因:A以无事务方式运行,B以有事务方式运行,A、B独立,互不影响
     */
    public void methodA_3_1() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_3();
    }
    /**
     * 条件: A没有事务、B有事务、捕获B抛出的异常
     * 结果:A插入数据,B未插入数据(A未回滚,B回滚)
     * 原因:A以无事务方式运行,B以有事务方式运行,A、B独立,互不影响
     */
    public void methodA_3_2() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        try {
            transactionServiceB.methodB_3();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 条件: A没有事务、B有事务、A抛出异常
     * 结果:A、B均插入数据(都未回滚)
     * 原因:A以无事务方式运行,B以有事务方式运行,A报错不影响B的事务,A、B独立,互不影响
     */
    public void methodA_3_3() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_4();
        throw new RuntimeException();
    }
//加入到TransactionServiceB类中
    @Transactional
    public void methodB_3() {
        System.out.println("methodB");
        tableService.insertTableB(new TableEntity());
        throw new RuntimeException();
    }
    @Transactional
    public void methodB_4() {
        System.out.println("methodB");
        tableService.insertTableB(new TableEntity());
    }
3、内层方法以REQUIRED执行事务
//加入到TransactionServiceA类中
	/**
     * 条件: A有事务、B有事务(REQUIRED)、B抛出异常
     * 结果:A、B均未插入数据(都回滚)
     * 原因:B加入A的事务中,A、B在同一个事务中
     */
    @Transactional
    public void methodA_4_1() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_5();
    }
    /**
     * 条件: A有事务、B有事务(REQUIRED)、A抛出异常
     * 结果:A、B均未插入数据(都回滚)
     * 原因:B加入A的事务中,A、B在同一个事务中
     */
    @Transactional
    public void methodA_4_2() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_6();
        throw new RuntimeException();
    }
    /**
     * 条件: A有事务、B有事务(REQUIRED)、捕获B抛出的异常
     * 结果:A、B均未插入数据(都回滚)
     * 原因:B加入A的事务中,A、B在同一个事务中。A、B互相影响,有一个报错就全部回滚,即是在外层方法捕获异常,也会回滚。
     * 区别于{@link #methodA_2_3}
     */
    @Transactional
    public void methodA_4_3() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        try {
            transactionServiceB.methodB_5();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
//加入到TransactionServiceB类中
    @Transactional(propagation = Propagation.REQUIRED)
    public void methodB_5() {
        System.out.println("methodB");
        tableService.insertTableB(new TableEntity());
        throw new RuntimeException();
    }
    @Transactional(propagation = Propagation.REQUIRED)
    public void methodB_6() {
        System.out.println("methodB");
        tableService.insertTableB(new TableEntity());
    }
4、内层方法以REQUIRED_NEW执行事务
//加入到TransactionServiceA类中
	/**
     * 条件: A有事务、B有事务(REQUIRED_NEW)、B抛出异常
     * 结果:A、B均未插入数据(都回滚)
     * 原因:A事务挂起,B新增一个事务,B报错,B回滚,A未进行捕获,导致A也产生报错,A也回滚
     */
    @Transactional
    public void methodA_5_1() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_7();
    }
    /**
     * 条件: A有事务、B有事务(REQUIRED_NEW)、A抛出异常
     * 结果:A未插入数据,B插入数据(A回滚,B不回滚)
     * 原因:A事务挂起,B新增一个事务,A报错,但是此时B的事务已经提交,B不回滚,A回滚
     */
    @Transactional
    public void methodA_5_2() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_8();
        throw new RuntimeException();
    }
    /**
     * 条件: A有事务、B有事务(REQUIRED_NEW)、捕获B抛出的异常
     * 结果:A插入数据,B未插入数据(A未回滚,B回滚)
     * 原因:A事务挂起,B新增一个事务,B报错,B回滚,但A捕获的B抛出的异常,所以A不回回滚
     * 区别于{@link #methodA_4_3}
     */
    @Transactional
    public void methodA_5_3() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        try {
            transactionServiceB.methodB_7();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
//加入到TransactionServiceB类中
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB_7() {
        System.out.println("methodB");
        tableService.insertTableB(new TableEntity());
        throw new RuntimeException();
    }
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB_8() {
        System.out.println("methodB");
        tableService.insertTableB(new TableEntity());
    }
5、内层方法以SUPPORTS执行事务
//加入到TransactionServiceA类中
    /**
     * 条件: A有事务、B有事务(SUPPORTS)、A抛出异常
     * 结果:A、B均未插入数据(都回滚)
     * 原因:A有事务,B加入A的事务,相当于(REQUIRED)
     * 等于{@link #methodA_4_2}
     */
    @Transactional
    public void methodA_6_2() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_10();
        throw new RuntimeException();
    }
    /**
     * 条件: A有事务、B有事务(SUPPORTS)、捕获B抛出的异常
     * 结果:A、B均未插入数据(都回滚)
     * 原因:A有事务,B加入A的事务,相当于(REQUIRED)
     * 等于{@link #methodA_4_3}
     */
    @Transactional
    public void methodA_6_3() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        try {
            transactionServiceB.methodB_9();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 条件: A没有事务、B有事务(SUPPORTS)、B抛出异常
     * 结果:A、B均插入数据(都不回滚)
     * 原因:A无事务,B也以非事务方式执行。相当于A、B都没有事务
     * 等于{@link #methodA_1}
     */
    public void methodA_6_4() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_9();
    }
//加入到TransactionServiceB类中
    @Transactional(propagation = Propagation.SUPPORTS)
    public void methodB_9() {
        System.out.println("methodB");
        tableService.insertTableB(new TableEntity());
        throw new RuntimeException();
    }
    @Transactional(propagation = Propagation.SUPPORTS)
    public void methodB_10() {
        System.out.println("methodB");
        tableService.insertTableB(new TableEntity());
    }
6、内层方法以NOT_SUPPORTS执行事务
//加入到TransactionServiceA类中
	/**
     * 条件: A有事务、B有事务(NOT_SUPPORTS)、B抛出异常
     * 结果:A未插入数据、B插入数据(A回滚、B不回滚)
     * 原因:A有事务,B总是以非事务的方式执行
     * 区别于{@link #methodA_2_1}
     */
    @Transactional
    public void methodA_7_1() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_11();
    }
    /**
     * 条件: A有事务、B有事务(NOT_SUPPORTS)、A抛出异常
     * 结果:A未插入数据、B插入数据(A回滚、B不回滚)
     * 原因:A有事务,B总是以非事务的方式执行
     * 区别于{@link #methodA_2_2}
     */
    @Transactional
    public void methodA_7_2() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_12();
        throw new RuntimeException();
    }
    /**
     * 条件: A有事务、B有事务(NOT_SUPPORTS)、捕获B抛出的异常
     * 结果:A、B均插入数据(都不回滚)
     * 原因:A有事务,B总是以非事务的方式执行,但是B抛出的异常被捕获,A不会进行回滚操作
     */
    @Transactional
    public void methodA_7_3() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        try {
            transactionServiceB.methodB_11();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
//加入到TransactionServiceB类中
	@Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void methodB_11() {
        System.out.println("methodB");
        tableService.insertTableB(new TableEntity());
        throw new RuntimeException();
    }
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void methodB_12() {
        System.out.println("methodB");
        tableService.insertTableB(new TableEntity());
    }
7、内层方法以MANDATORY执行事务
//加入到TransactionServiceA类中
    /**
     * 条件: A有事务、B有事务(MANDATORY)、B抛出异常
     * 结果:A、B均未插入数据(都回滚)
     * 原因:A有事务,B支持A的事务,和REQUIRED一样
     * 等于{@link #methodA_4_1}
     */
    @Transactional
    public void methodA_8_1() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_13();
    }
    /**
     * 条件: A有事务、B有事务(MANDATORY)、A抛出异常
     * 结果:A、B均未插入数据(都回滚)
     * 原因:A有事务,B支持A的事务,和REQUIRED一样
     * 等于{@link #methodA_4_2}
     */
    @Transactional
    public void methodA_8_2() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_13();
    }
    /**
     * 条件: A有事务、B有事务(MANDATORY)、捕获B抛出的异常
     * 结果:A、B均未插入数据(都回滚)
     * 原因:A有事务,B支持A的事务,和REQUIRED一样
     * 等于{@link #methodA_4_3}
     */
    @Transactional
    public void methodA_8_3() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        try {
            transactionServiceB.methodB_13();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 条件: A没有事务、B有事务(MANDATORY)
     * 结果:A插入数据、B未插入数据(A未回滚,B未执行插入操作)
     * 原因:MANDATORY如果当前无事务,会报错,因为A先执行且无事物,所以A正常插入。
     * 在进入B方法前就报错 No existing transaction found for transaction marked with propagation 'mandatory'
     * 所以B方法未执行
     */
    public void methodA_8_4() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_14();
    }
//加入到TransactionServiceB类中
	@Transactional(propagation = Propagation.MANDATORY)
    public void methodB_13() {
        System.out.println("methodB");
        tableService.insertTableB(new TableEntity());
        throw new RuntimeException();
    }
    @Transactional(propagation = Propagation.MANDATORY)
    public void methodB_14() {
        System.out.println("methodB");
        tableService.insertTableB(new TableEntity());
    }
8、内层方法以NEVER执行事务
//加入到TransactionServiceA类中
    /**
     * 条件: A有事务、B有事务(NEVER)
     * 结果:A、B均未插入数据(A回滚,B未执行插入操作)
     * 原因:A有事务,B判断当前有事务,会报错,所以A会回滚
     * 在进入B方法前就报错 existing transaction found for transaction marked with propagation 'never'
     * 所以B方法未执行
     */
    @Transactional
    public void methodA_9_1() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_16();
    }
    /**
     * 条件: A没有事务、B有事务(NEVER),B抛出异常
     * 结果:A、B均插入数据(都不回滚)
     * 原因:A无有事务,B以非事务的方式运行,A、B都没有事务,所以都不会回滚
     */
    public void methodA_9_2() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_15();
    }
//加入到TransactionServiceB类中
    @Transactional(propagation = Propagation.NEVER)
    public void methodB_15() {
        System.out.println("methodB");
        tableService.insertTableB(new TableEntity());
        throw new RuntimeException();
    }
    @Transactional(propagation = Propagation.NEVER)
    public void methodB_16() {
        System.out.println("methodB");
        tableService.insertTableB(new TableEntity());
    }
9、内层方法以NESTED执行事务
//加入到TransactionServiceA类中
	/**
     * 条件: A有事务、B有事务(NESTED),B抛出异常
     * 结果:A、B均未插入数据(都回滚)
     * 原因:A有事务,B嵌套在A事务内,当B抛出异常时,B进行正常回滚操作。由于A并未捕获B的异常,所以A也会回滚
     */
    @Transactional
    public void methodA_10_1() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_17();
    }
    /**
     * 条件: A有事务、B有事务(NESTED),A抛出异常
     * 结果:A、B均未插入数据(都回滚)
     * 原因:A有事务,B嵌套在A事务内,B事务受A事务的影响,即外层事务可以音响内层事务
     */
    @Transactional
    public void methodA_10_2() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_18();
        throw new RuntimeException();
    }
    /**
     * 条件: A有事务、B有事务(NESTED),捕获B抛出的异常
     * 结果:A、B均未插入数据(都回滚)
     * 原因:A有事务,B嵌套在A事务内,B抛出的异常被A捕获后,A的事务不受影响
     * 区别于{@link #methodA_4_3}
     */
    @Transactional
    public void methodA_10_3() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        try {
            transactionServiceB.methodB_17();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 条件: A没有事务、B有事务(NESTED),B抛出异常
     * 结果:A插入数据、B未插入数据(A未回滚、B回滚)
     * 原因:A无事务不回滚,B会像REQUIRED方式一样新建一个事务,当B抛出异常时,B会进行正常回滚。
     */
    public void methodA_10_4() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_17();
    }
//加入到TransactionServiceB类中
    @Transactional(propagation = Propagation.NESTED)
    public void methodB_17() {
        System.out.println("methodB");
        tableService.insertTableB(new TableEntity());
        throw new RuntimeException();
    }
    @Transactional(propagation = Propagation.NESTED)
    public void methodB_18() {
        System.out.println("methodB");
        tableService.insertTableB(new TableEntity());
    }

4、事务不生效的几种特殊情况

1、MySQL存储引擎必须是InnoDB,Myisam引擎不支持事务。

2、事务注解@Transactional必须放在public方法上才能生效。

3、如果methodA上没有@Transactional注解,但是调用了一个本类的有@Transactional注解的methodB,事务也不会生效,这时候需要借助我们上文提供的SpringUtil从spring上下文中获取实例才可以,因为注解的本质是aop和动态代理。

4、@Transactional 注解的方法所在的类必须被 Spring 管理。这个很好理解,因为我们所使用的声明式注解,本身就是spring,如果不被spring容易托管,是肯定不会生效的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值