前言
这是我在 spring 事务中遇到关于事务嵌套问题的一些记录总结。
一、@Transactional注解的使用禁忌
Spring 的 AOP 是基于动态代理机制实现的。关于 AOP Spring 官方网站有这样一段描述:
Spring AOP defaults to using standard JDK dynamic proxies for AOP proxies. This enables any interface (or set of interfaces) to be proxied.
Spring AOP can also use CGLIB proxies. This is necessary to proxy classes rather than interfaces. CGLIB is used by default if a business object does not implement an interface. As it is good practice to program to interfaces rather than classes; business classes normally will implement one or more business interfaces. It is possible to force the use of CGLIB, in those (hopefully rare) cases where you need to advise a method that is not declared on an interface, or where you need to pass a proxied object to a method as a concrete type.
大致意思是:Spring AOP 机制默认基于 JDK 动态代理来实现也可以设置基于 CGLib 动态代理来实现。
我们知道 JDK 动态代理类,通过继承和被代理类相同的 Interface 来实现对原有方法的调用和增强。而 CGLib 动态代理类,则通过继承被代理类的方式实现对原有方法的调用和增强。 Spring 注解类(包含Transactional)都是基于 Spring AOP 机制实现的。这里引出 Transactional 使用的禁忌(也是其它注解的使用禁忌):
1、不建议将 Transactional 标注在 Interface 上
Transactional 注解即可以注在在Interface的方法上。又可以写在具体的 Class 上。而如果标注在 Interface 上且配置 Spring AOP 使用CGLib 动态代理。会导致 Transactional 失效。原因看上面分析。
2、不能将Transactional标注在private、protected方法上
Transactional 标签必须标注在public修饰的方法上,否则 Transactional 同样不生效。
3、不能将Transactional标注在内部调用的方法上
Transactional 必须标注在给外部调用的接口上。如:不带 Transactional 注解的方法a,调用带有 Transactional 注解的方法b,事务同样不生效。
// 以下情况事务不生效:
public class Demo {
public void a(){
b();
}
@Transactional
public void b(){
}
}
以上几条禁忌同样适用于Spring的其它注解。
二、事务嵌套回滚总结
测试数据库:sql server,A 类的 service A 方法调用 B 类的 service B 方法;
ServiceA {
void methodA() {
ServiceB.methodB();
}
}
ServiceB {
void methodB() {
}
}
1、场景一
A,B 都使用事务注解 @Transactional(rollbackFor = Exception.class);
测试条件 | 结果 |
---|---|
B 发生异常,A 未 catch | A 回滚,B 回滚 |
B 发生异常,A catch 了异常,记录日志,未抛出异常 | A 回滚,B 回滚,但出现了异常(Transaction rolled back because it has been marked as rollback-only) |
B 发生异常,A catch 了异常,记录日志,抛出异常 | A 回滚,B 回滚 |
B 执行成功后,A 发生异常 | A 回滚,B 回滚 |
结论:A,B 只要有一个发生异常,A 和 B 都会回滚。
2、场景二
A 使用事务注解 @Transactional(rollbackFor = Exception.class),B 不使用事务;
测试条件 | 结果 |
---|---|
B 发生异常,A 未 catch | A 回滚,B 回滚 |
B 发生异常,A catch了异常,记录日志,未抛出异常 | A,B 都不回滚 |
B 发生异常,A catch了异常,记录日志,抛出异常 | A 回滚,B 回滚 |
B 执行成功后,A 发生了异常 | A 回滚,B 回滚 |
结论:只要 A 发生异常,A,B 都回滚;B 发生异常,若 A catch 了异常,但没有将异常抛出,则 A,B 都不回滚;除此之外,A,B 都回滚。
3、场景三(一般不用)
A 不使用事务,B 使用事务注解:@Transactional(rollbackFor = Exception.class);
测试条件 | 结果 |
---|---|
B 发生异常,A 未 catch | A 不回滚,B 回滚 |
B 发生异常,A catch 了异常,记录日志,未抛出异常 | A 不回滚,B 回滚 |
B 发生异常,A catch 了异常,记录日志,抛出异常 | A 不回滚,B 回滚 |
B 执行成功后,A 发生了异常 | A,B 都不回滚 |
结论:无论 AB 是否发生异常,A 都不回滚;只有 B 发生异常,B 才会回滚。
4、场景四:
A 使用默认事务注解:@Transactional(rollbackFor = Exception.class);
B 使用新事务注解:@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class);
测试条件 | 结果 |
---|---|
B 发生异常,A 未 catch | A 不回滚,B 回滚 |
B 发生异常,A catch 了异常,记录日志,未抛出异常 | A 不回滚,B 回滚 |
B 发生异常,A catch 了异常,记录日志,抛出异常 | A 回滚,B 回滚 |
B 执行成功后,A 发生了异常 | A 回滚,B 不回滚 |
A 发生异常,只有 A 回滚,B 不回滚;B 发生异常,只有 B 回滚,A 不回滚。
原文链接:关于Spring事务嵌套回滚的一些测试总结(精简)
转载仅为方便学习查看,一切权利属于原作者,本人只是做了整理和排版,如果带来不便请联系我删除。

发表评论