2

有时,我的有状态 EJB (3.0) 中的操作会引发 SQLException(超时已过期)。它发生的方法:

@PersistenceContext(unitName = "MYPU")
EntityManager entityManager;

List<Message> list;

public List<Message> newSearch() {
    // do some unsignificant things
    loadFirstPage();
}

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public List<Message> loadFirstPage() {
    CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
    cq.select(cq.from(entityClass));
    list = getEntityManager().createQuery(cq).getResultList();
}

此 SFSB 已注入另一个类:

private MyBean myBean;
@EJB(name = "messageloaderbean")
public void setMyBean(MyBean myBean) {
    this.myBean = myBean;
}

然后它的引用在调用类中作为参数传递:

public class Controller{
    private MyBean myBean;

    public Controller(MyBean myBean){
        this.myBean = myBean;
    }
    public void methodThatCallsMyBean(){
        this.myBean.newSearch();
    }
}

如果抛出运行时异常(如 SQLException)现在会发生什么,因为我正在使用 CMT 并根据 EJB 规范,容器首先回滚事务,然后丢弃 EJB。然后,如果我想在丢弃这个 EJB 后再次使用它,我会得到一个javax.ejb.NoSuchEJBException: Bean has been deleted.

这是有道理的,因为 bean 已被丢弃,但我怎样才能获得对新的有状态 bean 的引用?

我是否应该在我的 SFSB 中捕获异常并避免这种丢弃?如果我捕捉到异常,事务的状态会怎样?我必须做一些手动回滚吗?

谢谢。

4

1 回答 1

1

但是我怎样才能得到一个新的有状态bean的引用呢?

容器在 bean 生命周期的开始注入代理,因此在这种情况下,每个新的 staless bean 实例将只注入一次 statefull 引用。另一种方法是通过 JNDI 查找来获取参考,但这会更麻烦。

我是否应该在我的 SFSB 中捕获异常并避免这种丢弃?

是的,我认为这是最好的解决方案。

我必须做一些手动回滚吗?

是的。当 Container 拦截到 RuntimeException 时,它会将 Transaction 标记为回滚。我需要保持这种行为当前事务必须手动标记。有几种选择:

a) 抛出应用程序异常并使用 @AnnotationException(rollback=true) 对其进行注释。在这种情况下,容器会将事务标记为回滚,而不丢弃 bean 实例。

b) 通过 SessionContext.setRollbackOnly() 方法标记事务。

//inject a SessionConetext instance
@Resource
SessionContext session;

try {
    //your code goes here
} catch (RuntimeException e) 
//log the error
    session.setRollbackOnly();
{
于 2014-01-29T16:54:44.383 回答