EJB 3.1 TransactionAttributeType.REQUIRES_NEW和setRollbackOnly

时间:2012-02-15 09:15:22

标签: transactions glassfish ejb

请帮助我理解EJB 3.1中的事务。我正在使用GlassFish v3,并且遇到以下情况:

@Stateless
@LocalBean
public class BeanA {

    @Inject BeanB bean; /* which has no TransactionAttribute set */
    @Resource SessionContext context;

    public void run() {
        ...
        for (...) {
            process(someValue);
        }
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void process(String someValue) {

        try {

            SomeEntity entity = bean.getEntity(someValue);
            entity.setSomeProperty("anotherValue");

            ...

        } catch(CustomException e)  {
            this.context.setRollbackOnly();
        }
    }

}

从servlet调用BeanA.run。我想将每次迭代视为一个单独的事务。我以为使用TransactionAttributeType.REQUIRES_NEW会意识到这一点,但是在调用setRollbackOnly之后,我在beanB上的后续迭代中得到了javax.ejb.EJBTransactionRolledbackException。奇怪的是,当我将除了run()之外的所有内容移动到新的BeanC并调用beanC.process时,它确实有效。我错过了什么?任何人都可以解释为什么它的运作方式如何?

编辑:想一想:是不是因为容器不拦截对同一EJB内方法的调用? (这似乎是合理的)

编辑2:是的,在这里找到答案:EJB Transactions in local method-calls(我必须知道答案才能找到它:))

2 个答案:

答案 0 :(得分:4)

在这里找到答案:EJB Transactions in local method-calls

简而言之:容器不拦截本地方法调用,因此setRollbackOnly标记了回滚的唯一事务,解释了异常。

答案 1 :(得分:0)

老问题,但为了完整起见......

实际上,当在类中直接调用时,不会处理像@TransactionAttribute这样的注释。发生这种情况是因为,您正在直接调用它,就像在过程函数中一样。它不会经历EJB生命周期(包括拦截器)。

有一种方法可以做到这一点。您需要注入self类并使用该引用:

// Bean A

@Resource SessionContext context;

public void run() {
    ...
    for (...) {
       context.getBusinessObject(BeanA.class).process(someValue);
    }
}

通过这种方式,它将创建流程@TransactionAttribute 虽然它有用,但我不确定这是不是一个好的设计。