Spring中,@Transactional 注解的失效测试以及传播行为测试

分别测试四种情况:

  1. 同一个service中,A方法调用B方法,B方法抛异常
  2. 同一个service中,A方法调用B方法,A方法抛异常
  3. service1中A方法,调用service2中B方法,B方法抛异常
  4. service1中A方法,调用service2中B方法,A方法抛异常

分别测试A、B方法加上@Transactional注解

Controller层:

@RestController
@RequestMapping(value = "/transaction")
public class TransactionController {

	@Autowired
	TransactionService transactionService;
	
	@GetMapping(value="/test")
	public String test() {
		return transactionService.test();
	}
}

Dao层:

@Mapper
public interface TransactionDao {

	@Insert(value = "insert into test(name) values ('小明')")
	public void insertUser();

	@Insert(value = "insert into test(name) values ('小李')")
	public void insertAnotherUser();
}

Service1:

@Service
public class TransactionService {

	@Autowired
	TransactionDao transactionDao;
	
	@Autowired
	TransactionServiceB transactionServiceB;
	
	public String test() {
		transactionDao.insertUser();
		return "success";
	}
	
	public void testException() {
		transactionDao.insertAnotherUser();
		throw new RuntimeException();
	}

}

Service2:

@Service
public class TransactionServiceB {

	public void testException() {
		transactionDao.insertAnotherUser();
		throw new RuntimeException();
	}
}

测试地址:http://localhost:8080/transaction/test

一、同一个service中,A方法调用B方法,B方法抛异常

1.1 方法A加@Transactional注解,方法B不加@Transactional注解

Service:

	@Transactional
	public String test() {
		transactionDao.insertUser();
		testException();
		return "success";
	}
	
	public void testException() {
		transactionDao.insertAnotherUser();
		throw new RuntimeException();
	}

结果:
在这里插入图片描述
数据库:
在这里插入图片描述
事务成功回滚

1.2 方法A加@Transactional注解,方法B加@Transactional注解

Service:

	@Transactional
	public String test() {
		transactionDao.insertUser();
		testException();
		return "success";
	}
	
	@Transactional
	public void testException() {
		transactionDao.insertAnotherUser();
		throw new RuntimeException();
	}

结果:
在这里插入图片描述
数据库:
在这里插入图片描述
事务成功回滚

1.3 方法A不加@Transactional注解,方法B加@Transactional注解

Service:

	public String test() {
		transactionDao.insertUser();
		testException();
		return "success";
	}
	
	@Transactional
	public void testException() {
		transactionDao.insertAnotherUser();
		throw new RuntimeException();
	}

结果:
在这里插入图片描述
数据库:
在这里插入图片描述
事务没有回滚,B方法上的@Transactional注解失效。由于中间重置了一下自增的序列,因此id是从3开始,下面可以看到正常的主键的id的跳跃

二、 同一个service中,A方法调用B方法,A方法抛异常

2.1 方法A加@Transactional注解,方法B不加@Transactional注解

Service:

	@Transactional
	public String test() {
		transactionDao.insertUser();
		testException();
		throw new RuntimeException();
	}
	
	public void testException() {
		transactionDao.insertAnotherUser();
	}

结果:
在这里插入图片描述
数据库:
在这里插入图片描述
事务成功回滚

2.2 方法A加@Transactional注解,方法B加@Transactional注解

Serice:

	@Transactional
	public String test() {
		transactionDao.insertUser();
		testException();
		throw new RuntimeException();
	}
	
	@Transactional
	public void testException() {
		transactionDao.insertAnotherUser();
	}

结果:
在这里插入图片描述
数据库:
在这里插入图片描述
事务成功回滚

2.3 方法A不加@Transactional注解,方法B加@Transactional注解

Service:

	public String test() {
		transactionDao.insertUser();
		testException();
		throw new RuntimeException();
	}
	
	@Transactional
	public void testException() {
		transactionDao.insertAnotherUser();
	}

结果:
在这里插入图片描述
数据库:
在这里插入图片描述
事务没有回滚,B方法上的@Transactional注解失效,同时自增也间隔了4,说明之前的4次插入数据的回滚,会影响自增的递增

三、 service1中A方法,调用service2中B方法,B方法抛异常

3.1 方法A加@Transactional注解,方法B不加@Transactional注解

Service1:

	@Transactional
	public String test() {
		transactionDao.insertUser();
		transactionServiceB.testException();
		return "success";
	}

Service2:

	public void testException() {
		transactionDao.insertAnotherUser();
		throw new RuntimeException();
	}

结果:
在这里插入图片描述
数据库:
在这里插入图片描述
事务回滚成功

3.2 方法A加@Transactional注解,方法B加@Transactional注解

Service1:

	@Transactional
	public String test() {
		transactionDao.insertUser();
		transactionServiceB.testException();
		return "success";
	}

Service2:

	@Transactional
	public void testException() {
		transactionDao.insertAnotherUser();
		throw new RuntimeException();
	}

结果:
在这里插入图片描述
数据库:
在这里插入图片描述
事务回滚成功

3.3 方法A不加@Transactional注解,方法B加@Transactional注解

Service1:

	public String test() {
		transactionDao.insertUser();
		transactionServiceB.testException();
		return "success";
	}

Service2:

	@Transactional
	public void testException() {
		transactionDao.insertAnotherUser();
		throw new RuntimeException();
	}

结果:
在这里插入图片描述
数据库:
在这里插入图片描述
Service1中没有事务,也没有回滚,Service2中的事务成功回滚

四、 service1中A方法,调用service2中B方法,A方法抛异常

4.1 方法A加@Transactional注解,方法B不加@Transactional注解

Service1:

	@Transactional
	public String test() {
		transactionDao.insertUser();
		transactionServiceB.testException();
		throw new RuntimeException();
	}

Service2:

	public void testException() {
		transactionDao.insertAnotherUser();
	}

结果:
在这里插入图片描述
数据库:
在这里插入图片描述
事务回滚成功

4.2 方法A加@Transactional注解,方法B加@Transactional注解

Service1:

	@Transactional
	public String test() {
		transactionDao.insertUser();
		transactionServiceB.testException();
		throw new RuntimeException();
	}

Service2:

	@Transactional
	public void testException() {
		transactionDao.insertAnotherUser();
	}

结果:
在这里插入图片描述
数据库:
在这里插入图片描述
事务回滚成功,同时可以说明,默认的传播行为REQUIRED,一会可以测试一下其他的传播机制

4.3 方法A不加@Transactional注解,方法B加@Transactional注解

Service1:

	public String test() {
		transactionDao.insertUser();
		transactionServiceB.testException();
		throw new RuntimeException();
	}

Service2:

	@Transactional
	public void testException() {
		transactionDao.insertAnotherUser();
	}

结果:
在这里插入图片描述
数据库:
在这里插入图片描述
Service1中没有事务,也没有进行回滚

五、测试其他传播机制

5.1 采用4.2 方法A加@Transactional注解,方法B加@Transactional注解进行测试,A方法抛出异常,修改方法B的传播机制

Service1:

	@Transactional
	public String test() {
		transactionDao.insertUser();
		transactionServiceB.testException();
		throw new RuntimeException();
	}

Service2:

	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void testException() {
		transactionDao.insertAnotherUser();
	}

结果:
在这里插入图片描述
数据库:
在这里插入图片描述
可以看到,Service1中的事务成功回滚,Service2中由于新开启了一个事务,所以Service1中的回滚,并不会影响Service2中的数据的提交

5.2 采用3.2 方法A加@Transactional注解,方法B加@Transactional注解进行测试,B方法抛出异常,修改方法B的传播机制

Service1:

	@Transactional
	public String test() {
		transactionDao.insertUser();
		transactionServiceB.testException();
		return "success";
	}

Service2:

	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void testException() {
		transactionDao.insertAnotherUser();
		throw new RuntimeException();
	}

结果:
在这里插入图片描述
数据库:
在这里插入图片描述
Service1和Sevice2中的事务都成功回滚

再进行一个尝试,A方法中捕获一下异常
Service1:

	@Transactional
	public String test() {
		transactionDao.insertUser();
		try {
			transactionServiceB.testException();
		} catch (Exception e) {
			return e.getMessage();
		}
		return "success";
	}

Service2:

	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void testException() {
		transactionDao.insertAnotherUser();
		throw new RuntimeException();
	}

结果:
返回空

数据库:
在这里插入图片描述
Service1中捕获了异常,因此Service1中的事务可以正常提交,Service2中由于出现异常,事务进行了回滚

六、 总结

同一个类中,A方法调用B方法,此时B方法的@Transactional注解是无效的,因为没有经过Spring的代理,直接对方法进行了调用,所以注解没有办法被AOP扫描到,就不能实现事务的功能。

不同类中,A方法调用B方法,此时B方法的@Transactional注解是生效的,因为经过了Spring的代理。此时A方法或者B方法中出现异常,是否回滚需要看事务的传播行为,以及异常是否进行了捕获。

<think>好的,我需要回答用户关于Spring框架中@Transactional注解的用法和功能的问题。首先,我得整理一下现有的参考资料,确保涵盖用户提到的各个要点。用户提到了使用说明、功能介绍、官方文档和最佳实践,所以需要从这几个方面展开。 首先,@Transactional注解的基本功能是声明式事务管理。根据引用[1],Spring的事务管理分为编程式和声明式,这里重点在声明式。需要说明注解的作用范围,比如类或方法级别,并提到默认的回滚行为是仅运行时异常。同时,用户可能想知道如何扩展回滚到其他异常,比如检查型异常,这时候可以用rollbackFor属性。 接下来是事务传播机制,引用[2]提到了传播机制,比如REQUIRED、REQUIRES_NEW等,需要简要解释这些传播行为的含义,以及如何通过propagation属性设置。同时,事务失效的场景也是一个重点,比如自调用问题、非public方法等,引用[3]指出非public方法会导致事务不生效,这点必须强调。 然后要整理官方文档的要点,引用[2]的链接是Spring官方文档,可以提到推荐用户查阅该链接获取详细信息。此外,最佳实践部分需要结合资料中的内容,比如正确设置rollbackFor、避免自调用、使用public方法等。 还需要注意格式要求,用户提到行内数学表达式用$...$,不过这个问题可能不涉及数学公式,但需要确保其他格式正确,如代码块用```包裹,引用标识自然添加在段落末尾。同时生成相关问题,参考示例中的结构,最后列出相关问题。 现在检查是否有遗漏的信息。比如,引用[1]中的异常继承体系图虽然没有展示,但可以提到运行时异常和检查型异常的区别。事务传播机制的不同类型可能需要更详细的例子,但用户可能只需要基本说明。另外,事务失效的其他场景,如异常被捕获未抛出,或者数据库引擎不支持事务,这些可能需要补充。 最后,确保回答结构清晰,分点说明,符合用户要求的层次结构,并引用正确的来源。同时,生成的三个相关问题需要相关且有价值,帮助用户深入理解。</think>### Spring @Transactional 注解使用说明与功能介绍 #### 1. 核心功能 - **声明式事务管理**:通过注解简化事务配置,替代编程式事务管理[^1]。 - **事务范围控制**:支持类或方法级别的事务声明,方法级注解会覆盖类级配置。 - **默认回滚规则**:仅对运行时异常(`RuntimeException`)及其子类触发回滚,检查型异常(如`IOException`)默认不触发回滚。 - **自定义回滚**:通过`@Transactional(rollbackFor = Exception.class)`扩展回滚范围。 #### 2. 事务传播机制 通过`propagation`属性配置事务边界行为,常见选项: - `REQUIRED`(默认):存在事务则加入,否则新建 - `REQUIRES_NEW`:始终新建事务,挂起当前事务 - `NESTED`:嵌套事务执行 - `SUPPORTS`:存在事务则加入,否则无事务运行 [完整传播行为参考官方文档](https://docs.spring.io/spring-framework/reference/data-access/transaction/declarative/tx-propagation.html)[^2] #### 3. 使用规范(官方建议) ```java @Transactional public void updateOrder(Order order) { // 业务逻辑 } ``` - **可见性要求**:必须作用于`public`方法,私有/保护方法注解无效[^3] - **代理机制**:基于AOP代理实现,非Spring管理对象无效 - **自调用失效**:类内部方法互相调用会导致事务失效 #### 4. 最佳实践 1. **明确回滚异常** ```java @Transactional(rollbackFor = {SQLException.class, IOException.class}) ``` 2. **超时设置** ```java @Transactional(timeout = 30) // 单位:秒 ``` 3. **只读事务优化** ```java @Transactional(readOnly = true) ``` 4. **避免嵌套事务复杂度** 谨慎使用`REQUIRES_NEW`,可能引发死锁 #### 5. 典型失效场景 | 场景 | 解决方案 | |---------------------|----------------------------| | 非public方法 | 改用public修饰 | | 自调用 | 通过AopContext代理调用 | | 异常被捕获未抛出 | 在catch块抛出`throw new RuntimeException(e)` | | 数据库引擎不支持事务| 确认使用InnoDB等支持事务的引擎 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值