@Transactional 不建议放在 Controller 层方法上,原因如下:
- 职责不清:Controller 负责接收请求和返回响应,事务应由 Service 层控制,保证业务原子性。
- AOP失效风险:Spring 事务是通过 AOP 实现的,Controller 层通常不是代理对象,事务可能不会生效。
- 异常处理冲突:Controller 层常有全局异常处理,容易导致事务感知不到异常,无法回滚。
- 正确做法:应将 @Transactional 放在 Service 层的业务方法上。
正确使用 @Transactional 的方法如下:
- 加在 Service 层方法上
事务应由 Service 层控制,不要加在 Controller 层或 DAO 层。
- 避免同类内部 this.方法调用
事务是通过 Spring AOP 代理实现的,类内部用 this.xxx() 调用不会生效,需通过代理对象调用。
- 异常抛出才能回滚
默认只对运行时异常(RuntimeException)回滚。如果需要对所有异常回滚,使用
@Transactional(rollbackFor = Exception.class)。
- 不要加在 private、static、final 方法上
这些方法 Spring 无法代理,事务不会生效。
- 保证数据库操作在事务方法内
事务方法内的所有数据库操作才会被统一提交或回滚。
- 避免在 Controller 层 try-catch 吞掉异常
异常被捕获后不抛出,事务感知不到异常,不会回滚。
如何正确地调用本类的事务方法
在 Spring 中,如果你想在本类内部调用其他带有 @Transactional
注解的方法,并让事务生效,不能直接用 this.方法()
,而要通过 Spring 的代理对象调用。常见做法如下:
实现方式:
- 注入自身的代理对象(推荐)在类中加上如下代码:
@Autowired
private ChargeApi chargeApi; // 这里用自己类的类型
- 通过代理对象调用方法例如:
public void someMethod() {
// 通过代理对象调用带有@Transactional的方法
chargeApi.transactionalMethod();
}
注意事项:
- 需要在 Spring 容器中有该类的 Bean(如
@Component
、@Service
、@RestController
等注解)。 - 被调用的方法必须是
public
,且有@Transactional
注解。
原理说明:
Spring 的事务是基于 AOP 代理实现的,只有通过代理对象调用方法时,事务才会生效。直接用 this
调用不会经过代理,事务失效。
总结:
- 注入自身代理对象,通过它调用带事务的方法,事务才能生效。