EJB 拦截器和事务生命周期或如何拦截 commit/failure 事件?

EJB Interceptors and transaction lifecycle OR how to intercept a commit/failure event?

我有一个 EJB 拦截器,我遵循 Adam Bien 建议的 BCE 模式,也就是说,边界上的所有 EJB 调用都开始并完成一个事务,这意味着没有嵌套的 EJB 调用(可能有嵌套的 CDI 注入虽然 Bean 调用,但它们应该在同一事务内,该事务在 ejb 边界开始)。

所以在那些 ejb 边界中我有一个拦截器,我想拦截或知道在 EJB 的方法调用之后事务是否已经提交? (也就是说,如果涉及 EntityManager,则 COMMIT sql 调用已发送到数据库并成功返回)

  1. 我会从拦截器中获取该信息吗?
  2. 如果没有,我如何才能收到成功提交或失败的事务的通知?

注意:当然,如果我是 EJB 的客户端并且我正在调用方法,在方法调用之后我知道事务发生了什么,但我是有兴趣在客户端收到来自 EJB 的响应之前拦截它。

@AroundInvoke
public Object logMethodEntry(InvocationContext ctx) throws Exception {
    Object proceed = null;
    try {
        proceed = ctx.proceed();
        // is the transacction finished/commited already?
        // is it still open ?
        return proceed;
    } catch (Exception e) {
        throw e;
    }
}

[更新]: 我接受了一个很好的答案,但问题是 Java EE 没有办法接收事件已提交的事务。因此,无论答案如何,遗憾的是无法在 Java EE 中通知已完成的事务,在服务器内部,当然,如果您是客户端调用者,那么您肯定知道事务已提交或已滚动返回...

除非在抛出的异常上另有说明,否则如果ejb 方法调用抛出异常,则应回滚。此外,如果对 DB 的所有调用都在同一个事务中,则它们应在事务周期结束时被视为已提交。

回想起来,所有拦截器都是在调用它拦截的 ejb 方法的同一事务中调用的(这就是拦截器可能会在异常事件中决定回滚或仍然提交的原因交易)。

因此,您可以肯定地知道,如果在您的拦截器调用中,事务已成功完成,在调用并返回之后,没有抛出异常,并且有事务回滚的可能性。

所以在你的场景中:

@AroundInvoke
public Object logMethodEntry(InvocationContext ctx) throws Exception {
    Object proceed = null;
    try {
        proceed = ctx.proceed();
        // is the transacction finished/commited already?
        // The transaction is successful, but afaik, it is not yet committed, until this method returns successfully
        // is it still open ? More or less. You can still grab the Ejbtransaction and commit it manually or rollback if some other conditions have not been met yet
        return proceed;
    } catch (Exception e) {
        //If this happens, and you propagate it, then for sure the transaction will be rolledback, and never get committed. Since all db calls were being done within this transaction, then no DB commit will be done.
        throw e;
    }
}

编辑: 要在拦截器中实际提交事务,您需要 运行 应用程序管理的事务,否则,EJB 规范禁止在容器管理的事务上调用提交,您当然可以调用 setOnrollback EJBContext 的方法。 编辑 如果你真的想做一些数据库更改,我建议:

  1. 用户 ApplicationManaged 事务,您从中手动启动 并在拦截器中提交事务
  2. 使用观察者的概念,监听@Observes(AFTER_SUCCESS)事件,当 事务已成功提交并完成,因此您 可以保证进行数据库调用,并且新的更新将是 可用。
  3. 如果能忽略BCE模式,分拆一个新的事务来做update,这样returns成功后,保证commit,然后正常继续

```

@Stateless
public class TransactionService {

   @TransactionAttribute(REQUIRES_NEW)
   public Object executeTransaction(final Callable<Object> task) {
       return task.call();
   }
}

@Interceptor
public class MyInterceptor {

  @EJB
  private TransactionService service;

   @AroundInvoke
    public Object logMethodEntry(InvocationContext ctx) throws Exception {
        Object proceed = null;
        try {
            proceed = service.executeTransactional(()->ctx.proceed());
            //If you reach here, you will be guaranteed of commit and then you can do the elastic search update
            return proceed;
        } catch (Exception e) {
            //If this happens, and you propagate it, then for sure the transaction will be rolledback, and never get committed. Since all db calls were being done within this transaction, then no DB commit will be done.
            throw e;
        }
    }
}

好的 - 这个问题已经 4 年了,但我认为给出答案仍然有意义。 您肯定可以注册回调以了解交易结果。您只需使用 javax.transaction.Transaction.

的 registerSynchronization() API
Transaction tx = ((TransactionManager) (new InitialContext()).lookup("java:/TransactionManager")).getTransaction();
tx.registerSynchronization(new Synchronization() {
            public void beforeCompletion() {
               // do stuff before completion
            }

            public void afterCompletion(int status) {
                if (status == Status.STATUS_COMMITTED) {
                    // do something after successful commit                    }
            }
        });