分析和总结spring事务REQUIRES_NEW,REQUIRED的区别

本文深入探讨了Spring框架中REQUIRES_NEW与REQUIRED事务传播特性的使用区别,通过源码解析和实例演示,清晰地阐述了这两种事务类型如何在不同场景下影响外部事务及其自身的行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分析和总结spring事务REQUIRES_NEW,REQUIRED的区别

问题描述

我们在开发中经常用到的事务传播属性有REQUIRES_NEW和REQUIRED,但是具体它们怎么使用,有什么区别,不管官方说明还是其他地方的描述总是不能让我们直观明白。下文将通过源码说明和具体实例来总结一下它们的区别和使用场景。

REQUIRES_NEW的使用特性

1.源码说明的理解
事务传播行为的枚举Propagation定义如下:
即,创建一个新事务,如果当前事务存在,则挂起当前事务在这里插入图片描述
即创建一个新事务,如果当前事务存在,则挂起当前事务。改如何理解呢,我们用一个例子显示一下。
insertUserAndUserBank作为外层方法,实现两个业务操作,新增用户并新增用户的银行卡信息。int i=1/0模拟了外层出现了异常。

/**
	 * 新增用户和用户银行卡信息
	 * 使用注解式事务
	 */
	@Transactional(propagation=Propagation.REQUIRED,rollbackForClassName="Exception")
	public void insertUserAndUserBank() throws SQLException {
		User user=new User();
		user.setCode("user01");
		user.setName("testName");
		//insertUser方法用Transactional修饰,可以看做它是处于insertUserAndUserBank的事务中的
		userDao.insertUser(user);
		
		UserBank userBank=new UserBank();
		userBank.setBank_code("bank01");
		userBank.setUser_id(user.getCode());
		userBankDao.insertUserBank(userBank);
		//模拟出现了异常情况
		int i=1/0;
	}

使用REQUIRES_NEW修饰的insertUserBank方法,实现新增银行卡操作,它被外层insertUserAndUserBank方法调用。

/**
	 *增加一条用户银行卡信息
	 * @throws SQLException 
	 */
	@Transactional(propagation=Propagation.REQUIRES_NEW,rollbackForClassName="Exception")
	public void insertUserBank(UserBank userBank) throws SQLException {
		System.out.println("userBankDao的jdbcTemplate:"+jdbcTemplate);
		System.out.println("userBankDao的jdbcTemplate的connction为:"+jdbcTemplate.getDataSource().getConnection());
		String sql = "insert into user_bank_tb (user_id,bank_code) values (?,?)";
		jdbcTemplate.update(sql, new Object[]{userBank.getUser_id(),userBank.getBank_code()});
		
	}

执行结果我们发现,外层事务出现异常,用户表更新操作被回滚,但REQUIRES_NEW修饰的insertUserBank方法执行成功。
在这里插入图片描述在这里插入图片描述
总结,REQUIRES_NEW修饰方法的特性:外面的事务对其不影响,不管外层是否提交回滚,它里面的内容会根据自己的执行情况,该提交就提交,该回滚就回滚。
现在我们模拟REQUIRES_NEW修饰方法出现异常情况,看看它是否对外层造成影响。
这里将int i=1/0 从insertUserAndUserBank方法中移到insertUserBank中。

/**
	 * 新增用户和用户银行卡信息
	 * 使用注解式事务:默认发生运行时异常或Erro异常时才会回滚,
	 */
	@Transactional(propagation=Propagation.REQUIRED,rollbackForClassName="Exception")
	public void insertUserAndUserBank() throws SQLException {
		User user=new User();
		user.setCode("user01");
		user.setName("testName");
		userDao.insertUser(user);
		
		UserBank userBank=new UserBank();
		userBank.setBank_code("bank01");
		userBank.setUser_id(user.getCode());
		userBankDao.insertUserBank(userBank);

	}
/**
	 *增加一条用户银行卡信息
	 * @throws SQLException 
	 */
	@Transactional(propagation=Propagation.REQUIRES_NEW,rollbackForClassName="Exception")
	public void insertUserBank(UserBank userBank) throws SQLException {
		System.out.println("userBankDao的jdbcTemplate:"+jdbcTemplate);
		System.out.println("userBankDao的jdbcTemplate的connction为:"+jdbcTemplate.getDataSource().getConnection());
		String sql = "insert into user_bank_tb (user_id,bank_code) values (?,?)";
		jdbcTemplate.update(sql, new Object[]{userBank.getUser_id(),userBank.getBank_code()});
		int i=1/0;
	}

执行结果我们发现,用户表和银行卡表都未更新,REQUIRES_NEW修饰的insertUserBank方法执行异常,它也引起外层其他业务的回滚。

在这里插入图片描述
在这里插入图片描述
总结,REQUIRES_NEW修饰方法的特性:它会对外层的事务有影响,如果它里面执行时出现异常不仅自己的内容回滚,也会导致外层事务也回滚。

REQUIRED的使用特性

1.源码说明的理解
事务传播行为的枚举Propagation定义如下:
在这里插入图片描述
即支持当前事务,如果不存在,则创建一个新事务。该如何理解呢,我们在用一个类似的例子演示一下。
insertUserAndUserBank作为外层方法,和上述一样。int i=1/0模拟了外层出现了异常。我们把insertUserBank改为REQUIRED修饰

/**
	 *增加一条用户银行卡信息
	 * @throws SQLException 
	 */
	@Transactional(propagation=Propagation.REQUIRED,rollbackForClassName="Exception")
	public void insertUserBank(UserBank userBank) throws SQLException {
		System.out.println("userBankDao的jdbcTemplate:"+jdbcTemplate);
		System.out.println("userBankDao的jdbcTemplate的connction为:"+jdbcTemplate.getDataSource().getConnection());
		String sql = "insert into user_bank_tb (user_id,bank_code) values (?,?)";
		jdbcTemplate.update(sql, new Object[]{userBank.getUser_id(),userBank.getBank_code()});
		
	}

执行结果我们发现,用户表和银行卡表都未更新,外层其他业务执行异常,REQUIRED修饰的insertUserBank方法也被回滚。
在这里插入图片描述
总结,REQUIRED修饰方法的特性:外面的事务对其有影响,外层事务出现异常,它里面的内容也会回滚。
现在我们模拟REQUIRED修饰方法出现异常情况,看看它是否对外层造成影响。
这里将int i=1/0 从insertUserAndUserBank方法中移到insertUserBank中。

/**
	 *增加一条用户银行卡信息
	 * @throws SQLException 
	 */
	@Transactional(propagation=Propagation.REQUIRED,rollbackForClassName="Exception")
	public void insertUserBank(UserBank userBank) throws SQLException {
		System.out.println("userBankDao的jdbcTemplate:"+jdbcTemplate);
		System.out.println("userBankDao的jdbcTemplate的connction为:"+jdbcTemplate.getDataSource().getConnection());
		String sql = "insert into user_bank_tb (user_id,bank_code) values (?,?)";
		jdbcTemplate.update(sql, new Object[]{userBank.getUser_id(),userBank.getBank_code()});
		int i=1/0 ;
	}

执行结构我们发现,用户表和银行卡表都未更新,REQUIRED修饰方法insertUserBank方法执行异常,它也引起外层其他业务的回滚。
在这里插入图片描述

问题总结

REQUIRES_NEW的事务,不受外层调用者影响,但会影响外层的事务。
REQUIRED的事务,即受外层调用者影响,也会影响外层的事务。

实际业务如何使用: 在同一个方法中,因为大多数情况是一系列业务要保证要么都成功要么都失败的,所以各个业务方法使用默认的REQUIRED方式即可。
如果中间有一个特殊的业务方法,和其他业务不关联,我们可以给它的方法设置REQUIRES_NEW,这样就能保证其他业务有异常时,它也不会被回滚。

<think>好的,用户现在想了解Spring事务传播行为中Propagation.REQUIRES_NEWPropagation.REQUIRED的具体区别。之前我已经给出过相关的回答,现在需要结合用户提供的引用资料来进一步详细说明。 首先,我需要回顾之前的回答内容。之前的回答中,我解释了REQUIRED是默认的传播行为,支持当前事务或新建事务,而REQUIRES_NEW则是无论当前是否存在事务,都创建新事务,并挂起当前事务。用户现在可能希望更深入地了解两者的区别,特别是在实际应用中的表现需要注意的问题。 接下来,用户提供的引用资料里,引用[2][3]详细说明了REQUIRES_NEW的使用场景问题,特别是内部方法调用时事务不生效的情况,以及解决方法。引用[3]提到REQUIRES_NEW会挂起当前事务,创建独立的新事务,且内部事务的异常会影响外部事务。引用[4]则比较了NESTED、REQUIREDREQUIRES_NEW的不同,指出NESTED是嵌套事务,允许部分回滚,而REQUIRES_NEW是完全独立的事务。 用户的问题集中在REQUIRES_NEWREQUIRED区别,需要结合这些引用资料来详细说明。需要注意的是,用户可能在实际开发中遇到事务不生效的情况,比如同类内部调用导致REQUIRES_NEW失效,这时候需要解释原因解决办法,如使用代理对象调用方法。 另外,用户可能想了解这两种传播行为在事务管理上的不同,比如事务的提交回滚是否相互影响,异常处理机制有何不同。例如,REQUIRED的情况下,内部方法加入外部事务,任何一个方法回滚都会导致整个事务回滚;而REQUIRES_NEW的内部事务回滚不会影响外部事务,除非异常传播到外部。 还需要提到事务的挂起恢复机制,REQUIRES_NEW会挂起当前事务,启动新事务,完成后恢复原事务。而REQUIRED则复用现有事务,不会挂起。这部分在引用[3]中有详细说明,应该整合到回答中。 此外,用户提供的引用[2]中提到,同类内部调用方法时,由于Spring AOP代理的问题,REQUIRES_NEW可能不会生效,必须通过代理对象调用才能触发新事务。这也是一个关键点,需要在区别中强调,并提供解决方案,比如使用ApplicationContext获取代理Bean或自我注入。 最后,总结应用场景,REQUIRED适用于需要事务统一管理的场景,而REQUIRES_NEW适用于需要独立事务,不受外部影响的子操作,比如日志记录或审计,这些操作即使失败也不应影响主业务事务。 需要确保回答结构清晰,分点列出核心区别事务行为对比、代码示例、注意事项应用场景,并引用相关资料中的内容作为支持。同时,要避免使用Markdown格式,保持自然的中文口语化表达,但思考过程需要详细且逻辑连贯。</think>以下是针对`Propagation.REQUIRES_NEW``Propagation.REQUIRED`的详细对比分析: --- ### 一、核心区别对比 | **特性** | **REQUIRED** | **REQUIRES_NEW** | |------------------------|-----------------------------------------------|-----------------------------------------------| | **事务依赖关系** | 与当前事务合并或新建 | 始终新建独立事务,挂起当前事务(若存在)[^3] | | **异常影响范围** | 内部异常导致整个事务回滚 | 内部异常仅回滚自身事务,不影响外部事务 | | **资源连接管理** | 共享同一数据库连接 | 使用独立数据库连接 | | **应用场景** | 业务逻辑需统一提交/回滚 | 需独立提交的子操作(如日志记录、异步任务) | --- ### 二、事务行为对比 #### 1. **存在外部事务时** - **REQUIRED** - 直接加入外部事务,共用同一事务上下文 - **示例**: ```java @Transactional(propagation = Propagation.REQUIRED) public void methodA() { methodB(); // 加入methodA的事务 } ``` - **REQUIRES_NEW** - 挂起外部事务,创建新事务独立运行 - **示例**: ```java @Transactional(propagation = Propagation.REQUIRES_NEW) public void methodB() { // 独立事务执行,与methodA的事务隔离[^3] } ``` #### 2. **无外部事务时** - **REQUIRED** - 新建事务执行 - **REQUIRES_NEW** - 同样新建事务执行(此时两者行为相同) --- ### 三、关键代码示例 ```java @Service public class OrderService { @Autowired private AuditService auditService; // 通过代理对象调用 @Transactional(propagation = Propagation.REQUIRED) public void placeOrder(Order order) { try { // 主业务操作 orderRepository.save(order); // 调用REQUIRES_NEW方法(必须通过代理对象) auditService.logOperation(); // 独立事务执行[^2] } catch (Exception e) { // 即使auditService抛出异常,主事务仍可能提交(取决于配置) } } } @Service public class AuditService { @Transactional(propagation = Propagation.REQUIRES_NEW) public void logOperation() { // 独立事务记录日志,失败不影响主订单事务[^4] } } ``` --- ### 四、注意事项 1. **同类内部调用失效问题** - 若在同一个类中直接调用`REQUIRES_NEW`方法(如`this.methodB()`),事务会失效[^2] - **解决方案**: - 通过注入代理对象调用(如`@Autowired private OrderService self;`) - 使用`AopContext.currentProxy()`获取代理对象 2. **异常处理差异** - **REQUIRED**:若内部方法抛出未捕获异常,整个事务回滚 - **REQUIRES_NEW**:仅内部事务回滚,外部事务需显式处理异常 ```java try { auditService.logOperation(); // REQUIRES_NEW事务独立回滚 } catch (Exception e) { // 捕获后外部事务可继续提交 } ``` 3. **性能影响** - `REQUIRES_NEW`因频繁挂起/恢复事务创建新连接,性能开销较大,需谨慎使用 --- ### 五、典型应用场景 | **传播行为** | **适用场景** | |--------------------|-----------------------------------------------------------------------------| | **REQUIRED** | 订单创建(包含库存扣减、支付操作需原子性提交)[^1] | | **REQUIRES_NEW** | 操作日志记录(即使主业务失败,仍需保留日志)<br>异步任务触发(需立即提交)[^4] | --- ### 六、引用总结 - `REQUIRES_NEW`通过挂起当前事务实现独立性,但需注意代理调用问题[^2][^3] - `REQUIRED`通过事务合并保证原子性,适用于紧密关联的业务操作[^4] --- **相关问题** 1. 如何在同类方法中正确触发`REQUIRES_NEW`事务? 2. `Propagation.NESTED`与`REQUIRES_NEW`在部分回滚场景下的区别是什么? 3. 如何通过事务管理器手动控制事务边界?
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值