已标注@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
相关产品推荐
相关产品推荐

