问题描述
在某个类中,A方法直接调用同类中的事务方法时,@Transactional 注解失效,导致事务不生效,可能引发数据不一致问题。
代码如下:
public void a() {
// ❌ 直接调用同类事务方法,@Transactional 失效
b();
}
@Transactional // 事务注解不生效
public void b() {
// 数据库操作...
}
原因分析:
Spring AOP 动态代理机制限制:
- Spring 通过动态代理实现事务管理(@Transactional)
- 同类内部调用(如 A.a() → A.b())会绕过代理对象
- 导致事务切面逻辑(事务开启/提交/回滚)未被执行
解决方案:
方案1:事务方法提取到独立服务类
-
新建事务服务类
@Autowired private XxxMapper xxxMapper; @Transactional // ✅ 事务注解生效 public void b() { xxxMapper.insert(); // 数据库操作 } } ```
-
修改任务类调用方式
@Configuration public class TestService{ @Autowired private TransactionService transactionService; // 注入事务服务 public void a() { // ✅ 通过代理对象调用事务方法 transactionService.b(); // 其他非事务操作 cleanTempFiles(); } private void cleanTempFiles() { // 无需事务的文件操作 } } ```
方案2:通过AopContext获取代理
原理:使用 AopContext.currentProxy() 获取当前类的代理对象,通过代理调用方法触发事务
import org.springframework.aop.framework.AopContext;
@Configuration
@EnableAspectJAutoProxy(exposeProxy = true) // 关键配置:暴露代理对象
public class TestService {
@Autowired
private XxxMapper xxxMapper;
public void a() {
// 1. 获取当前类的代理对象
TestService proxy = (TestService) AopContext.currentProxy();
// 2. 通过代理调用事务方法(触发@Transactional)
proxy.b();
}
@Transactional // ✅ 事务注解生效
public void b() {
// 数据库操作
xxxMapper.insert();
}
}
配置说明:
-
在启动类添加注解:
@SpringBootApplication @EnableScheduling @EnableAspectJAutoProxy(exposeProxy = true) // 启用代理暴露 public class Application { ... }
-
添加 Maven 依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
方案3:编程式事务管理(TransactionTemplate)
通过 TransactionTemplate 手动控制事务边界,完全绕过注解机制
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.transaction.TransactionStatus;
@Configuration
public class TestService {
@Autowired
private XxxMapper xxxMapper;
@Autowired
private TransactionTemplate transactionTemplate; // 注入事务模板
public void test() {
// 1. 使用编程式事务执行数据库操作
Boolean success = transactionTemplate.execute(status -> {
try {
// 事务内的数据库操作
xxxMapper.insert();
// 可嵌套其他事务操作
processSubTasks();
return true; // 提交事务
} catch (Exception e) {
status.setRollbackOnly(); // 标记回滚
return false;
}
});
// 2. 根据事务结果处理后续逻辑
if (Boolean.TRUE.equals(success)) {
cleanTempFiles();
}
}
// 嵌套事务示例
private void processSubTasks() {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
xxxMapper.updateStatistics();
} catch (Exception e) {
status.setRollbackOnly(); // 子事务回滚
}
}
});
}
}