You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

已标注@Transactional的方法仍抛出TransactionRequiredException问题咨询

为什么调用postPessoa()时merge()抛出TransactionRequiredException,但直接调用savePessoa()正常?

嘿,这个问题我太熟了,本质是Spring事务代理的内部调用限制导致的,我给你拆解清楚:

首先看你的核心代码:

public void postPessoa() { 
    savePessoa(); 
    initializePessoa(); 
} 

@Transactional(rollbackOn = {Exception.class}) 
public void savePessoa() { 
    pessoa = getEntityManager().merge(pessoa); 
} 

还有对应的JSF按钮:

<p:commandButton id="btnSalvar" value="Salvar" action="#{pessoasController.savePessoa}"/> 
<p:commandButton id="btnSalvar" value="Salvar" action="#{pessoasController.postPessoa}"/>

问题根源:Spring事务代理的“内部调用盲区”

@Transactional注解的生效依赖于Spring的动态代理机制:

  • 当你从类外部调用被@Transactional标记的方法时,Spring会先通过代理对象拦截请求,自动开启事务,执行目标方法后再处理事务的提交/回滚;
  • 但如果是同一个类内部的方法互相调用(比如这里postPessoa()调用savePessoa()),此时的调用是直接指向当前对象的原生方法,完全绕开了Spring的代理类!也就是说,savePessoa()上的@Transactional注解根本没被触发,自然不会开启任何事务,EntityManager.merge()因为没有活跃事务支持,就抛出了TransactionRequiredException。

而你直接用按钮调用savePessoa()时,是从JSF这个外部上下文发起的调用,Spring的代理会正常拦截并生效事务,所以merge()能顺利执行。

几个可行的解决办法

  • 抽离服务类:把savePessoa()方法单独放到一个专门的服务类(比如PessoaService)中,然后在pessoasController里注入这个服务类,通过服务类调用savePessoa(),这样就能走代理触发事务;
  • 注入自身代理对象:在pessoasController中注入自己的代理实例(比如@Autowired private PessoasController selfProxy;),然后在postPessoa()里调用selfProxy.savePessoa();
  • 使用AopContext获取代理:在postPessoa()中通过((PessoasController) AopContext.currentProxy()).savePessoa()来调用,不过这种方式需要开启exposeProxy = true。

内容的提问来源于stack exchange,提问作者Guilherme Bernardi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:21:13