关于Transactional注解的正确使用

@Transactional 不建议放在 Controller 层方法上,原因如下:

  1. 职责不清:Controller 负责接收请求和返回响应,事务应由 Service 层控制,保证业务原子性。
  2. AOP失效风险:Spring 事务是通过 AOP 实现的,Controller 层通常不是代理对象,事务可能不会生效。
  3. 异常处理冲突:Controller 层常有全局异常处理,容易导致事务感知不到异常,无法回滚。
  4. 正确做法:应将 @Transactional 放在 Service 层的业务方法上。

正确使用 @Transactional 的方法如下:

  1. 加在 Service 层方法上

事务应由 Service 层控制,不要加在 Controller 层或 DAO 层。

  1. 避免同类内部 this.方法调用

事务是通过 Spring AOP 代理实现的,类内部用 this.xxx() 调用不会生效,需通过代理对象调用。

  1. 异常抛出才能回滚

默认只对运行时异常(RuntimeException)回滚。如果需要对所有异常回滚,使用

@Transactional(rollbackFor = Exception.class)。

  1. 不要加在 private、static、final 方法上

这些方法 Spring 无法代理,事务不会生效。

  1. 保证数据库操作在事务方法内

事务方法内的所有数据库操作才会被统一提交或回滚。

  1. 避免在 Controller 层 try-catch 吞掉异常

异常被捕获后不抛出,事务感知不到异常,不会回滚。

如何正确地调用本类的事务方法

在 Spring 中,如果你想在本类内部调用其他带有 @Transactional 注解的方法,并让事务生效,不能直接用 this.方法(),而要通过 Spring 的代理对象调用。常见做法如下:

实现方式:

  1. 注入自身的代理对象(推荐)在类中加上如下代码:
@Autowired
private ChargeApi chargeApi; // 这里用自己类的类型
  1. 通过代理对象调用方法例如:
public void someMethod() {
    // 通过代理对象调用带有@Transactional的方法
    chargeApi.transactionalMethod();
}

注意事项:

  • 需要在 Spring 容器中有该类的 Bean(如 @Component@Service@RestController 等注解)。
  • 被调用的方法必须是 public,且有 @Transactional 注解。

原理说明:

Spring 的事务是基于 AOP 代理实现的,只有通过代理对象调用方法时,事务才会生效。直接用 this 调用不会经过代理,事务失效。

总结:

  • 注入自身代理对象,通过它调用带事务的方法,事务才能生效。