EJB拦截器和事务生命周期,或者如何拦截提交/失败事件



我有一个EJB拦截器,我遵循Adam Bien建议的BCE模式,即边界上的所有EJB调用都开始并完成一个事务,这意味着没有嵌套的EJB调用(尽管可能有嵌套的CDI注入Bean调用,但这些调用应该在从EJB边界开始的同一事务内)。

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

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

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

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

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

因此,您可以肯定地知道,如果在您的拦截器调用中,在调用并返回proceed之后,没有抛出异常,可能会导致事务回滚,那么事务就成功完成了。

所以在你的场景中:

@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规范禁止在容器管理的事务上调用commit,您当然可以调用EJBContext的setOnrollback方法。编辑如果你真的想做一些数据库更改,我建议:

  1. 用户ApplicationManaged事务,从中手动启动并在拦截器内提交事务
  2. 使用观察者的概念,并侦听@Observes(AFTER_SUCCESS)事件,当事务已成功提交并完成,因此您可以保证进行数据库调用,并且新的更新将可用
  3. 如果您可以忽略BCE模式,并衍生出一个新的事务来进行更新,以便在它成功返回后,您将保证提交,然后正常继续

```

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

相关内容

最新更新