Spring事务的七种传播行为

1.事务的传播行为是什么?

事务的传播行为就是指多个声明了事务的方法相互调用的时候存在事务嵌套的问题,事务在方法中如何传播。
比如说:methodA()调用methodB(),两个方法都开启了事务,那么methodB()是开启了一个新事物,还是继续在methodA()这个事务中执行呢?就取决与事务的传播行为。
在这里插入图片描述
所以,Spring 为了解决这个问题,定义了 7 种事务传播行为。

传播行为含义
REQUIRED默认的Sring事务传播级别,如果当前事务存在,则加入该事务,如果不存在 就新建一个事务。
REQUIRES_NEW不管是否存在事务,都会新开启一个事务,新老事务相互独立;外部事务抛出异常回滚不会影响内部事务的正常提交。
NESTED如果当前存在事务,则嵌套在当前事务中执行,如果当前没有事务,则新建一个事务,类似与REQUIRE_NEW。
SUPPORTS表示支持当前事务,如果当前不存在事务,则以非事务的方式执行。
NOT_SUPPORTED表示以非事务的方式运行,如果当前存在事务,则把当前事务挂起。
MANDATORY表示强制事务执行,若当前不存在事务,则抛出异常。
NEVER表示以非事务的方式来运行,如果当前存在事务,则抛出异常。

2.具体传播行为

2.1 REQUIRED ,默认,存在事务则加入该事务,不存在则新建一个事务

当前存在事务则加入该事务,不存在事务则新建一个事务

示例代码:

	@Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
		userMapper.updateById(new TbUser(2, "李开", 33, 1));  // 更新用户
        methodB();		//调用methodB()
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void methodB() {
        userMapper.updateById(new TbUser(3, "李福", 22, 1));  //更新用户
        int a = 1/0;	// 发生异常
        System.out.println("=====执行 methodB 完成=====");
    }

结论:用户数据不会发生变化。当调用methodA()上下文没有事务就会开启一个事务,当执行methodB(),因为已经存在一个事务,methodB()不会开启新事务,而是加入到methodA()事务中执行。当‘int a = 1/0;’执行报错就会回滚methodA事务,而methodB也在methodA事务中,所有都会回滚。

2.2 REQUIRES_NEW,每次新开启事务,新老事务相互独立

不管是否存在事务,都会新开启一个事务,新老事务相互独立;外部事务抛出异常回滚不会影响内部事务的正常提交。

2.3 NESTED, 存在事务则嵌套当中执行,不存在事务则新建一个事务

如果一个活动的事务存在,则运行在一个嵌套事务中,如果没有活动事务,则按照PROPAGATION_REQUIRED属性执行。

2.4 SUPPORTS,存在则支持当前事务,不存在事务则以非事务执行。

如果存在事务则支持当前事务,如果不存在事务则无事务运行

2.5 NOT_SUPPORTED, 以非事务的方式运行,存在事务则把当前事务挂起

表示以非事务的方式运行,如果当前存在事务,则把当前事务挂起。

2.6 MANDATORY,强制事务执行,不存在事务则抛出异常。

如果存在一个事务,则事务的运行,没有事务则抛出异常。

2.7 NEVER,以非事务来运行,存在事务则抛出异常。

总是非事务的运行,如果存在一个活动的事务,则抛出异常。

3.事务失效的场景

在方法上添加@Transactional 注解后,Spring 会基于这个类生成一个代理对象,会将这个代理对象作为 bean,当使用这个代理对象的方法时,那么代理逻辑会先把事务的自动提交设置为 false,然后再去执行具体的业务逻辑,如果执行逻辑没有出现异常,那么代理逻辑就会将事务进行提交,如果执行逻辑出现了异常,那么则会将事务进行回滚。默认情况下会对 RuntimeException 和 Error 进行回滚。可以利用 @Transactional 注解中的 rollbackFor 属性进行配置异常信息。

3.1 非public修饰的方法

@Transactional 只能用于 public 的方法上,否则事务不会生效,如果要用在非 public 方法上,可以开启 AspectJ 代理模式。

3.2 自身调用

事务的管理是通过代理执行的方式生效的,如果是方法内部调用,没有经过 Spring 的代理类,就调用不到了

3.3 被 final、static 关键字修饰的类或方法

3.4 没有被 Spring 管理

没有注入到IOC容器的的类。

3.5 数据库不支持事务

比如 Mysql 的 Myisam 存储引擎是不支持事务的,只有 innodb存储引擎才支持。

3.6 异常被捕获

当异常被捕获后,而且没有再抛出,那么事务是不会回滚的。

3.7 异常类型错误

@Service
public class OrderServiceImpl implements OrderService 
    @Transactional
    public void updateOrder(Order order) {
        try {
            // update order
        } catch {
            throw new Exception("更新错误");
        }
    }
}

这样事务也是不生效的,因为默认回滚的是:RuntimeException 和 Error,如果想触发其他异常的回滚,需要在注解上配置一下,如通过指定@Transactional(rollbackFor = Exception.class)的方式进行全异常捕获。

3.8 多线程调用

Spring 的事务是通过数据库连接来实现,而数据库连接是放在 ThreadLocal 里面。同一个事务,只能用同一个数据库连接。而多线程场景下,拿到的数据库连接是不一样的。

<think>好的,用户现在想了解Spring Boot中事务传播方式的使用方法以及解决saveBatch方法事务失效的问题。之前已经回答过类似的问题,但需要结合之前的对话来提供更详细的解答。 首先,回顾用户之前的提问,他提到调用其他服务的saveBatch方法时事务失效,之前的回答已经分析了原因,比如代理机制失效、跨服务调用方式错误、事务传播配置不当等,并给出了解决方案。现在用户进一步询问事务传播行为的详解以及saveBatch事务失效的具体解决方案,需要更详细地展开。 用户可能的需求是深入理解事务传播机制,特别是不同的传播行为如REQUIRED、REQUIRES_NEW等的应用场景,以及如何正确配置这些行为来避免事务失效。此外,用户可能在实践中遇到了saveBatch方法的事务不生效的问题,需要具体的解决步骤。 需要确认用户是否了解Spring事务的基本原理,比如AOP代理、事务管理器等。因为事务传播行为是建立在Spring事务管理之上的,如果用户对这些基础概念不熟悉,可能需要先简要说明。但根据用户之前的提问,可能已经有一定了解,所以可以深入一些。 在解释事务传播行为时,要列举七种类型,但重点放在常用的几种,比如REQUIRED、REQUIRES_NEW、NESTED等,给出具体的应用场景和代码示例。例如,当方法A调用方法B时,不同传播行为事务如何创建或加入现有事务。 对于saveBatch方法的事务失效,需要再次强调可能的原因,比如自调用导致代理失效、未正确注入服务、批量操作未启用事务等。同时,结合MyBatis Plus的saveBatch方法,说明需要配置事务管理器和批量操作的优化,比如设置合适的batchSize,启用事务支持。 还要注意用户可能遇到的特殊情况,比如跨服务调用时的事务传播,这时候可能需要分布式事务解决方案如Seata,但用户当前的问题可能集中在单服务内的事务处理,所以先解决单服务的问题,再提及分布式的情况作为扩展。 另外,用户可能需要验证事务是否生效的方法,比如抛出异常看是否回滚,或者查看事务日志,这些在之前的回答中已经提到过,可以再次强调。 最后,确保回答结构清晰,分点讲解事务传播行为,再针对saveBatch的问题给出解决方案,并附上相关问题,帮助用户进一步学习。</think>以下是Spring Boot事务传播行为的详解及`saveBatch`方法事务失效解决方案的技术解析: --- ### 一、Spring事务传播行为详解(7种模式) #### 1. 核心传播行为(标*为常用) | 传播类型 | 描述 | 代码示例 | |-------------------|---------------------------------------------------------------------|-------------------------------------------------------------------------| | **REQUIRED*** | 默认值,存在事务则加入,无事务则新建(原子操作场景) | `@Transactional(propagation = Propagation.REQUIRED)` | | **REQUIRES_NEW*** | 总是新建独立事务(日志记录/审计等独立操作) | `@Transactional(propagation = Propagation.REQUIRES_NEW)` | | **NESTED** | 嵌套事务,使用保存点实现部分回滚(电商订单多步骤处理) | `@Transactional(propagation = Propagation.NESTED)` | | SUPPORTS | 跟随当前事务,无事务则以非事务执行(查询优化) | `@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)` | | NOT_SUPPORTED | 非事务执行,挂起当前事务(耗时统计等与事务无关操作) | `@Transactional(propagation = Propagation.NOT_SUPPORTED)` | | MANDATORY | 强制要求存在事务,否则抛异常(严格事务依赖场景) | `@Transactional(propagation = Propagation.MANDATORY)` | | NEVER | 强制非事务环境,存在事务则抛异常(非核心业务校验) | `@Transactional(propagation = Propagation.NEVER)` | #### 2. 典型应用场景对比 - **REQUIRED vs REQUIRES_NEW** ```java // 主事务 @Transactional public void mainMethod() { // 操作1(参与主事务) subMethod1(); // 操作2(独立事务) subMethod2(); } @Transactional(propagation = REQUIRED) public void subMethod1() { /*...*/ } @Transactional(propagation = REQUIRES_NEW) public void subMethod2() { /*...*/ } ``` 若`subMethod2`发生异常,仅回滚其独立事务,`subMethod1`的操作仍随主事务回滚[^3] #### 3. 嵌套事务实现原理 ```mermaid graph TD A[主事务BEGIN] --> B[设置保存点] B --> C[执行嵌套操作] C -->|异常| D[回滚到保存点] C -->|成功| E[释放保存点] ``` --- ### 二、`saveBatch`事务失效解决方案 #### 1. 失效原因深度分析 - **批量操作未启用事务** ```sql -- MyBatis Plus默认执行流程 INSERT INTO table VALUES (...); -- 逐条执行 INSERT INTO table VALUES (...); ``` - **自调用绕过代理** ```java public void process() { this.saveBatch(list); // 直接调用导致@Transactional失效 } ``` #### 2. 完整解决方案 **步骤1:启用批量事务模式** ```java @Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 关键配置:批量操作拦截器 interceptor.addInnerInterceptor(new BatchInsertInnerInterceptor()); return interceptor; } } ``` **步骤2:正确的事务配置** ```java @Service public class DataServiceImpl implements DataService { @Autowired private DataMapper dataMapper; @Override @Transactional(rollbackFor = Exception.class) public boolean saveBatch(Collection<Data> list) { // 必须开启事务才能生效 return dataMapper.saveBatch(list, 1000); // 设置合理batchSize } } ``` **步骤3:调用方正确使用** ```java @Service public class BusinessService { @Autowired // 必须通过代理对象调用 private DataService dataService; @Transactional public void batchProcess(List<Data> data) { dataService.saveBatch(data); // 跨服务调用 // 其他业务操作... } } ``` #### 3. 性能优化技巧 1. **合理设置batchSize** ```java // 根据数据库配置调整 dataMapper.saveBatch(list, 500); ``` 2. **关闭自动提交** ```properties spring.datasource.hikari.auto-commit=false ``` 3. **启用JDBC批处理** ```properties spring.jpa.properties.hibernate.jdbc.batch_size=1000 spring.jpa.properties.hibernate.order_inserts=true ``` --- ### 三、事务验证方案 #### 1. 基础验证方法 ```java @Test @Transactional public void testSaveBatch() { // 准备测试数据 List<Data> testData = generateTestData(10000); try { dataService.saveBatch(testData); throw new RuntimeException("强制异常"); // 验证回滚 } catch (Exception e) { Assert.assertEquals(0, dataMapper.count()); // 断言数据已回滚 } } ``` #### 2. 高级调试技巧 ```properties # 开启事务日志 logging.level.org.springframework.orm.jpa=DEBUG logging.level.org.hibernate.SQL=DEBUG logging.level.org.hibernate.engine.transaction.internal.TransactionImpl=TRACE ``` --- ### 四、扩展应用场景 **分布式事务整合方案** ```java @GlobalTransactional // Seata分布式事务注解 public void distributedOperation() { // 本地事务 dataService.saveBatch(localData); // 远程服务调用 feignClient.remoteOperation(remoteData); } ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值