先上错误示例:
@Service
public class MyService {
public void mainMethod() {
// 主方法逻辑
doMainBusiness();
try {
doSubBusiness();
return true;
} catch (Exception e) {
log.error("子事务失败", e);
return false;
}
// 主方法继续执行
}
private void doMainBusiness() {
// 主方法的数据库操作
}
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void doSubBusiness() {
// 子方法的数据库操作
}
}
代码中,子方法 doSubBusiness 的数据库操作不会回滚。这是因为 Spring AOP 的代理机制无法拦截同一个 Bean 内的私有方法调用,导致 @Transactional 注解失效。
详细原因分析
-
Spring AOP 代理机制
Spring 的事务管理基于 AOP 代理,而 同一个 Bean 内部的方法调用不会触发代理。当mainMethod()调用doSubBusiness()时,实际上是通过this引用调用,绕过了 Spring 的事务代理。因此:@Transactional注解虽然在public方法上有效,但在同一 Bean 内部调用时不会生效。- 子方法的数据库操作会在主事务(如果存在)中执行,或默认不使用事务。
-
事务传播行为失效
由于代理未生效,Propagation.REQUIRES_NEW无法创建独立事务。若主方法没有事务,子方法的操作会直接提交到数据库;若主方法有事务,子方法会加入该事务
解决方案
方案 1:将子方法提取到独立 Service
@Service
public class MainService {
@Autowired
private SubService subService;
public void mainMethod() {
try {
subService.doSubBusiness(); // 通过代理调用,事务生效
} catch (Exception e) {
log.error("子方法失败", e);
}
}
}
@Service
public class SubService {
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void doSubBusiness() {
// 数据库操作
}
}
方案 2:通过 ApplicationContext 获取代理对象
@Service
public class MyService {
@Autowired
private ApplicationContext context;
private MyService selfProxy;
@PostConstruct
public void init() {
selfProxy = context.getBean(MyService.class);
}
public void mainMethod() {
try {
selfProxy.doSubBusiness(); // 通过代理调用,事务生效
} catch (Exception e) {
log.error("子方法失败", e);
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void doSubBusiness() {
// 数据库操作
}
}
方案 3:使用 TransactionTemplate 手动管理事务
@Service
public class MyService {
@Autowired
private TransactionTemplate transactionTemplate;
public void mainMethod() {
Boolean result = transactionTemplate.execute(status -> {
try {
doSubBusiness();
return true;
} catch (Exception e) {
status.setRollbackOnly(); // 手动回滚
log.error("子事务失败", e);
return false;
}
});
}
private void doSubBusiness() {
// 数据库操作
}
}
总结
代码中,由于 Spring AOP 无法拦截同一 Bean 内的方法调用,@Transactional 注解失效,子方法的数据库操作不会回滚。建议采用方案 1 或方案 3 解决此问题。
2169

被折叠的 条评论
为什么被折叠?



