文章目录
- 前言
- 1. 动态代理无法实现
- 2. Spring管理不当
- 3. 事务遇到异常未回滚
前言
理解
@Transactional
注解失效的情况,先要谈Spring中注解的实现原理,是以动态代理的形式出现的,如果无法实现动态代理,注解自然就无法生效。另外,如果类没有被Spring管理,其上的@Transactional
注解也不会生效。此外,如果执行事务的过程中遇到异常却没有回滚处理,不同事务之间的传播机制的问题,或者Spring管理不正确也会导致失效。
因此,注解@Transactional
失效的情况可以分以下几类:
- 动态代理无法实现
- Spring管理不当
- 事务遇到异常未回滚
接下来将分别进行介绍。
1. 动态代理无法实现
首先,搞清楚动态代理的实现方式有两种:
- 要代理的对象实现了接口:使用JDK Proxy类来创建代理对象。
- 要代理的对象没有实现接口:使用Cglib生成被代理对象的子类来代理。
而动态代理无法实现的,进一步导致@Transactional
注解失效的情况有以下几种:
-
方法自调用(也就是递归):通过this引用完成,绕过了代理。
- 示例:
java">@Service public class MyService { @Transactional public void methodA() { // 业务逻辑 methodA(); // 自调用,不会应用事务 } }
- 解决方法:methodA() 是被标记为 @Transactional 的方法,事务在调用 recursiveMethod() 时保持生效。
java">@Service public class MyService { @Transactional public void methodA() { // 业务逻辑 recursiveMethod(0); // 开始递归 } private void recursiveMethod(int count) { if (count < 5) { // 递归逻辑 System.out.println("Count: " + count); recursiveMethod(count + 1); // 继续递归 } } }
- 示例:
-
同一个类中方法之间的调用:
- 示例:
java">@Service public class MyService { @Transactional public void outerMethod() { innerMethod(); // 事务不会生效 } public void innerMethod() { // 业务逻辑 } }
- 解决方法:将 innerMethod() 提取到另一个 Spring 管理的 Bean 中,然后通过依赖注入的方式调用它。
java">@Service public class MyService { @Autowired private MyService myService; // 注入同一个服务 @Transactional public void outerMethod() { myService.innerMethod(); // 通过代理调用 } public void innerMethod() { // 业务逻辑 } }
- 示例:
-
方法的修饰符不是public:由于只有公共的方法才会被代理,如果方法是私有的(private)或保护的(protected),它们不会被 Spring 的代理机制拦截,因此 @Transactional 注解不会生效。
- 示例:
java">@Service public class MyService { // 私有方法,@Transactional 注解将失效 @Transactional private void myPrivateMethod() { // 业务逻辑 System.out.println("Executing private method"); } public void execute() { myPrivateMethod(); // 自调用,且私有方法不会被代理 } }
- 解决办法:
java">@Service public class MyService { // 将方法改为 public,确保事务生效 @Transactional public void myPublicMethod() { // 业务逻辑 System.out.println("Executing public method"); } public void execute() { myPublicMethod(); // 通过代理调用,事务将生效 } }
- 示例:
2. Spring管理不当
- 调用对象不是Spring管理的bean,比如new
- 示例:
java">public class MyClass { public void myMethod() { MyService service = new MyService(); // 直接创建,不是 Spring 管理的 service.methodA(); // 不会应用事务 } }
- 解决办法:
java">@Component public class MyClass { @Autowired private MyService myService; // 通过 Spring 注入 public void myMethod() { myService.methodA(); // 通过 Spring 管理的对象调用,事务将生效 } }
- 示例:
3. 事务遇到异常未回滚
-
@Transactional 注解默认情况下只会在遇到未检查的异常(如 RuntimeException)时回滚事务。如果抛出的是检查异常(如 IOException),则事务不会回滚。
- 示例:
java">@Transactional public void myMethod() throws IOException { throw new IOException(); // 不会导致事务回滚 }
- 解决方法:
java">@Transactional(rollbackFor = IOException.class) // 指定检查异常 public void myMethod() throws IOException { throw new IOException(); // 将导致事务回滚 }
- 示例:
-
required修饰的方法
outerMethod()
调用required_new修饰的方法innerMethod()
:在这种情况下,方法innerMethod()
的异常不会导致方法outerMethod()
的事务回滚,从而导致数据不一致。- 示例:
java">@Service public class OuterService { @Transactional public void outerMethod() { // 调用内部方法 innerMethod(); // 使用 REQUIRES_NEW } @Transactional(propagation = Propagation.REQUIRES_NEW) public void innerMethod() { // 业务逻辑 throw new RuntimeException("Exception in innerMethod"); // 仅会回滚 innerMethod 的事务 } }
- 解决办法:
java">@Transactional public void outerMethod() { try { innerMethod(); // 处理结果 } catch (RuntimeException e) { // 处理 innerMethod 的异常 System.out.println("Handled inner exception: " + e.getMessage()); } }
- 示例:
-
required修饰的方法
outerMethod()
调用nested修饰的方法innerMethod()
:如果innerMethod()
抛出异常,innerMethod()
的子事务将回滚,但outerMethod()
的外部事务仍然可以继续执行。- 示例:
java">@Service public class OuterService { @Transactional public void outerMethod() { // 调用内部方法 innerMethod(); // 使用 NESTED } @Transactional(propagation = Propagation.NESTED) public void innerMethod() { // 业务逻辑 throw new RuntimeException("Exception in innerMethod"); // 事务将回滚 } }
- 解决办法:
java">@Transactional public void outerMethod() { try { innerMethod(); // 可能会抛出异常 } catch (RuntimeException e) { // 处理异常,可能继续执行其他逻辑 // 或者决定是否回滚 outerMethod 的事务 System.out.println("Handled inner exception: " + e.getMessage()); } }
- 示例:
以上为个人学习分享,如有问题,欢迎指出:)