【问题标题】:Nested method calls in EJBs attempting to start/create a new transaction in a nested method callEJB 中的嵌套方法调用尝试在嵌套方法调用中启动/创建新事务
【发布时间】:2015-08-28 05:54:01
【问题描述】:

让我们考虑在无状态 EJB 中使用 @TransactionAttribute(TransactionAttributeType.REQUIRED)(默认)注释的两种方法 methodA() 和使用 @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 注释的 methodB()

通过methodA()methodB() 进行嵌套调用不会启动/创建新事务,因为它看起来是(无论目标方法使用什么事务属性类型),因为对methodB() 的嵌套调用来自methodA() 使用this 指针/引用(即实际的EJB 实例)来调用methodB(),因此不会被要在运行时由容器注入并需要设置环境的代理拦截在调用方法之前。

基本演示:

@Stateless
public class TestBean implements TestBeanService {

    @PersistenceContext
    private EntityManager entityManager;

    // At a glance, this method should cause an exception but it does not.
    @Override
    @TransactionAttribute(TransactionAttributeType.NEVER)
    public Long getRowCount() {
        return entityManager.createQuery("SELECT count(e) AS cnt FROM Employee e", Long.class).getSingleResult();
    }

    // This method is invoked by the application client.
    // making a nested call to getRowCount() using the "this" pointer.
    @Override
    public void test() {
        Long rowCount = getRowCount();
        System.out.println("rowCount : " + rowCount);
    }
}

虽然getRowCount()方法被@TransactionAttribute(TransactionAttributeType.NEVER)修饰了一眼应该会导致异常,但它成功返回了查询返回的行数。

► 这是因为test() 方法启动的事务被传播(扩展)到getRowCount(),即所有事情都发生在同一个事务中。


但是,如果使用通过javax.ejb.SessionContext 获得的代理实例来调用getRowCount() 方法,则会引发异常。下面的 sn-p 演示了这种修改。

@Stateless
public class TestBean implements TestBeanService {

    @PersistenceContext
    private EntityManager entityManager;

    @Resource
    private SessionContext sessionContext;

    @Override
    @TransactionAttribute(TransactionAttributeType.NEVER)
    public Long getRowCount() {
        return entityManager.createQuery("SELECT count(e) AS cnt FROM Employee e", Long.class).getSingleResult();
    }

    @Override
    public void test() {
        // Invocation to getRowCount() is done by a proxy instance. Hence, it causes an exception,
        // since the transaction started by this method is now not propagated to a subsequent call to getRowCount().
        Long rowCount = sessionContext.getBusinessObject(TestBeanService.class).getRowCount();
        System.out.println("rowCount : " + rowCount);
    }
}

由于getRowCount() 方法使用TransactionAttributeType.NEVER,因此上述对getRowCount() 的方法调用会引发以下与第一种情况相矛盾的异常。

javax.ejb.EJBException: EJB cannot be invoked in global transaction

► 这是因为由test() 方法启动的事务现在传播(扩展)到getRowCount(),就像第一种情况一样,因为现在通过代理实例调用此方法(因此,不像往常那样直接通过 this 指针 - 实际的 EJB 实例)。

通过javax.ejb.SessionContext 获取代理实例并在该代理实例上调用方法是一种 hack。我认为它不应该在实际应用中使用。

是否有任何其他方法可以在需要时在嵌套方法调用中启动/创建新事务?

【问题讨论】:

    标签: jakarta-ee transactions ejb


    【解决方案1】:

    只有当您通过接口/代理访问 bean 时才会使用事务属性,而不是像您观察到的内部方法。因此,除了通过 SessionContext 访问它之外,您还可以在您的班级中有一个参考:@EJB TestBeanService serviceBean 并通过 serviceBean.getRowCount() 访问它(也是一种 hack)。

    【讨论】:

    • 通过 CDI @Inject 注释的自注入以异常 org.jboss.weld.exceptions.DeploymentException: WELD-001443: Pseudo scoped bean has circular dependencies. 结束。也许,在当前的 CDI 实现中是a missing feature or a bug。在这种情况下,@EJB 必须用作解决方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-03
    • 2015-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多