当已经在 ejb 中时打开新事务

Open new transaction when already inside ejb

考虑以下情况:

@Stateless
@Clustered
public class FacadeBean implements Facade {

    @EJB
    private Facade facade;

    @Override
    public void foo(List<Integer> ids) {
        // read specific id's from the db
        for (Integer id : ids) {
            facade.bar(id);
        }
    }

    @Override
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void bar(Integer id) {
        // save something to the db
    }

}

从 ejb 外部调用方法 foo。我希望每个 id 在其自己的事务中执行,以便数据直接保存到数据库中。在这个 class 之外不可能有 foreach。我想知道最好的方法是什么?目前我正在再次注入接口,以跨过 ejb 边界(以便对 TransactionAttribute 进行评估)。

您真的必须将两种方法合而为一吗class?您可以将 bar() 移动到自己的 bean 并使其成为事务性的。那就不用这种自注入了。

您也可以尝试使用SessionContext#getBusinessObject()方法。

@Resource
SessionContext sessionContext;

@Override
public void foo(List<Integer> ids) {
    Facade facade = sessionContext.getBusinessObject(Facade.class);
    // read specific id's from the db
    for (Integer id : ids) {
        facade.bar(id);
    }
}

您对循环引用的处理方式非常好。 EJB 中的循环引用是允许的。为了开始一个新事务或一个 @Asynchronous 线程,这甚至是强制性的(否则当前线程仍会阻塞)。

@Stateless
public class SomeService {

    @EJB
    private SomeService self; // Self-reference is perfectly fine.


    // Below example starts a new transaction for each item.

    public void foo(Iterable<Long> ids) {
        for (Long id : ids) {
            self.fooInNewTransaction(id);
        }
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void fooInNewTransaction(Long id) {
        // ...
    }


    // Below example fires an async thread in new transaction.

    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    public void bar(Iterable<Long> ids) {
        for (Long id : ids) {
            self.fooAsynchronously(id);
        }
    }

    @Asynchronous
    public void fooAsynchronously(Long id) {
        // ...
    }

}

仅在较旧的容器中,这不起作用,最显着的是 JBoss AS 5 与古老的 EJB 3.0 API。这就是为什么人们发明了 SessionContext#getBusinessObject() 甚至通过 JNDI 手动抓取的变通方法。

现在这些都没有必要了。这些是解决方法而不是解决方案。

就交易而言,我个人只会反过来做。 foo() 方法显然从未打算成为事务性的。

@Stateless
public class SomeService {

    @EJB
    private SomeService self;

    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    public void foo(Iterable<Long> ids) {
        for (Long id : ids) {
            self.foo(id);
        }
    }

    public void foo(Long id) {
        // ...
    }

}

根据具体的功能需求,您甚至可以制作foo(Long id)@Asynchronous,从而加快任务速度。