简体   繁体   English

已经在ejb内时打开新交易

[英]Open new transaction when already inside ejb

Consider the following situation: 请考虑以下情况:

@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
    }

}

Method foo gets called from outside the ejb. 从ejb外部调用方法foo。 I want each id to be executed in its own transaction, so that data get's persisted directly to the database. 我希望每个id在自己的事务中执行,以便数据直接保存到数据库中。 It is not possible to have the foreach outside of this class. 此类之外的foreach是不可能的。 I am wondering what is the best way to do this? 我想知道什么是最好的方法吗? At the moment I am injecting the interface again, to step over the ejb boundaries (so that the TransactionAttribute get's evaluated). 此刻,我再次注入接口,以跨过ejb边界(以便对TransactionAttribute进行评估)。

Your approach as to circular reference is perfectly fine. 您关于循环参考的方法非常好。 Circular reference in EJBs is allowed. 允许在EJB中进行循环引用。 This is even mandatory in order to start out a new transaction, or an @Asynchronous thread (otherwise the current thread would still block). 为了开始新的事务或@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) {
        // ...
    }

}

Only in older containers, this did not work, most notably JBoss AS 5 with the ancient EJB 3.0 API. 仅在较旧的容器中,此方法不起作用,最显着的是带有古老EJB 3.0 API的JBoss AS 5。 That's why people invented workarounds like SessionContext#getBusinessObject() or even manually grabbing via JNDI. 这就是为什么人们发明了诸如SessionContext#getBusinessObject()类的变通方法,甚至是通过JNDI手动抓取的原因。

Those are unnecessary these days. 这些天这些都是不必要的。 Those are workarounds not solutions. 这些是解决方法,而不是解决方案。

I'd personally only do it the other way round as to transactions. 我个人只会以相反的方式进行交易。 The foo() method is clearly never intented to be transactional. 显然, 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) {
        // ...
    }

}

Depending on the concrete functional requirement, you could even make the foo(Long id) @Asynchronous , hereby speeding up the task. 根据具体的功能要求,您甚至可以使foo(Long id) @Asynchronous ,从而加快任务的执行速度。

Do you really have to have both methods in one class? 您真的必须在一个类中同时使用这两种方法吗? You can move bar() to an own bean and make it transactional. 您可以将bar()移到自己的bean上并使其具有事务性。 Then you don't have to use this kind of self-injection. 然后,您不必使用这种自我注入。

You can also try to use SessionContext#getBusinessObject() method. 您也可以尝试使用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);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM