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

JTA的REQUIRES_NEW在同一CDI注入类中不生效问题

为什么同一类中@Transactional(REQUIRES_NEW)不生效?怎么解决?

这个问题其实是事务代理机制的典型坑——不管是CDI还是Java EE的事务实现,都是基于动态代理工作的。当你在同一个类内部调用方法时,是直接调用原始对象的方法,完全没经过事务代理类,所以@Transactional(value = Transactional.TxType.REQUIRES_NEW)这个注解根本不会被触发,自然和start()方法共享同一个事务。

下面给你几个可行的解决方案:

方案1:把forceSave()移到单独的类中(最推荐)

这是你已经发现的有效方法,也是最符合单一职责原则的做法。把独立的事务操作抽成单独的服务类,注入到原类中调用,此时调用会经过代理,触发新事务:

@Named
@RequestScoped
public class SaveService {
    @Transactional(value = Transactional.TxType.REQUIRES_NEW)
    public void forceSave() {
        // 你的保存逻辑
    }
}

然后修改你的MyClass:

@Named
@RequestScoped
public class MyClass{
    @Inject
    private SaveService saveService;

    @Transactional
    public void start(){
        for(int i = 0; i < 100;i++){
            //执行一些逻辑
            saveService.forceSave();//现在会开启新事务立即保存
        }
    }
}

方案2:自注入代理对象(不拆类的快速方案)

如果你不想拆分类,可以通过CDI自注入自己的代理实例,然后通过这个代理调用forceSave()方法,就能触发事务拦截器:

@Named
@RequestScoped
public class MyClass{
    @Inject
    private MyClass self; // 注入的是代理对象,不是原始类实例

    @Transactional
    public void start(){
        for(int i = 0; i < 100;i++){
            //执行一些逻辑
            self.forceSave();//通过代理调用,触发REQUIRES_NEW
        }
    }

    @Transactional(value = Transactional.TxType.REQUIRES_NEW)
    public void forceSave(){
        //保存逻辑
    }
}

这里要注意:CDI允许自注入,注入的是代理实例而非原始对象,所以调用self的方法会经过事务代理,从而开启新事务。

方案3:通过BeanManager获取代理实例(更底层的方式)

如果自注入遇到循环依赖问题(虽然CDI通常能处理),还可以通过BeanManager手动获取代理对象:

@Named
@RequestScoped
public class MyClass{
    @Inject
    private BeanManager beanManager;

    @Transactional
    public void start(){
        MyClass proxy = beanManager.getReference(
            beanManager.resolve(beanManager.getBeans(MyClass.class)),
            MyClass.class,
            beanManager.createCreationalContext(null)
        );

        for(int i = 0; i < 100;i++){
            //执行一些逻辑
            proxy.forceSave();//通过代理调用
        }
    }

    @Transactional(value = Transactional.TxType.REQUIRES_NEW)
    public void forceSave(){
        //保存逻辑
    }
}

这个方法比较繁琐,一般优先推荐前两种方案,除非你有特殊场景需求。

总结一下:核心就是要让带有@Transactional(REQUIRES_NEW)的方法调用经过事务代理,而同一类内部直接调用会绕开代理,所以必须通过外部代理实例来触发新事务。

内容的提问来源于stack exchange,提问作者Ronaldo Lanhellas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:42:53