Spring Boot中同类非事务方法调用@Transactional方法的正确方式
问题根源
Spring声明式事务依赖AOP代理机制生效。当你在同一个类的update方法内部调用updateToDB时,调用的是当前类的原生对象而非Spring生成的代理对象,导致@Transactional注解完全不生效,数据库更新自然不会被提交。
解决方案
针对你的WildFly JTA环境,以下几种方案都能解决问题:
方案1:自注入代理对象
在MyUpdateService中注入自身的代理实例,通过代理调用updateToDB方法:
@Service public class MyUpdateService { // 注入自身的代理对象 @Autowired private MyUpdateService selfProxy; public void update(MyModel myModel){ // 通过代理对象调用带事务的方法 MyEntity updatedEntity = selfProxy.updateToDB(myModel); myThirdPartyService.update(100); } @Transactional public void updateToDB(MyModel myModel) { MyEntity currentEntity = myRepository.fetchById(myModel.getId()); currentEntity.setName(myModel.getName()); return currentEntity; } }
方案2:拆分方法到独立Bean
把带事务的updateToDB方法抽离到单独的@Service类中,通过依赖注入调用:
// 新的数据库更新服务类 @Service public class DbUpdateService { @Autowired private MyRepository myRepository; @Transactional public MyEntity updateToDB(MyModel myModel) { MyEntity currentEntity = myRepository.fetchById(myModel.getId()); currentEntity.setName(myModel.getName()); return currentEntity; } } // 原服务类修改 @Service public class MyUpdateService { @Autowired private DbUpdateService dbUpdateService; @Autowired private MyThirdPartyService myThirdPartyService; public void update(MyModel myModel){ MyEntity updatedEntity = dbUpdateService.updateToDB(myModel); myThirdPartyService.update(100); } }
方案3:通过AopContext获取代理对象
开启Spring的代理暴露配置后,用AopContext获取当前代理:
- 在启动类或配置类添加
@EnableAspectJAutoProxy(exposeProxy = true) - 修改调用逻辑:
@Service public class MyUpdateService { public void update(MyModel myModel){ // 获取代理对象并调用方法 MyUpdateService proxy = (MyUpdateService) AopContext.currentProxy(); MyEntity updatedEntity = proxy.updateToDB(myModel); myThirdPartyService.update(100); } @Transactional public void updateToDB(MyModel myModel) { MyEntity currentEntity = myRepository.fetchById(myModel.getId()); currentEntity.setName(myModel.getName()); return currentEntity; } }
方案4:手动管理JTA事务(备选)
如果上述方案不适用,可以手动通过JTA的UserTransaction控制事务:
@Service public class MyUpdateService { @Resource private UserTransaction userTransaction; @Autowired private MyRepository myRepository; public void update(MyModel myModel){ try { userTransaction.begin(); MyEntity updatedEntity = updateToDB(myModel); userTransaction.commit(); myThirdPartyService.update(100); } catch (Exception e) { try { userTransaction.rollback(); } catch (Exception rollbackEx) { rollbackEx.printStackTrace(); } throw new RuntimeException("事务执行失败", e); } } public MyEntity updateToDB(MyModel myModel) { MyEntity currentEntity = myRepository.fetchById(myModel.getId()); currentEntity.setName(myModel.getName()); return currentEntity; } }
注意事项
- 你的WildFly环境使用JTA事务,
@Transactional注解无需额外指定事务管理器,Spring会自动适配WildFly提供的JTA事务管理器。 - 若使用JPA,修改实体后无需手动调用
save方法,事务提交时JPA会自动同步实体状态到数据库,你的代码这部分逻辑是正确的。
内容的提问来源于stack exchange,提问作者MFH
相关产品推荐
相关产品推荐

