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

时间:2015-03-02 07:26:33

标签: java java-ee ejb cdi

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

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

  1. 我会从拦截器中获取该信息吗?
  2. 如果没有,我怎样才能收到成功提交或失败的交易的通知?
  3. 注意:当然,如果我是EJB的客户端并且我正在调用该方法,那么在方法调用之后我知道事务发生了什么,但是我有兴趣拦截那个BEFORE客户端从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中通知服务器内部已完成的事务,当然,如果您是客户端调用者,那么您确定知道事务已提交或回滚...

2 个答案:

答案 0 :(得分:3)

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

回想起来,所有拦截器都是在它拦截的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规范禁止在容器管理事务上调用commit,你当然可以调用EJBContext的setOnrollback方法。 的修改 如果您真的想要进行一些数据库更改,我建议:

  1. 用户ApplicationManaged事务,您可以从中手动启动 并在拦截器中提交事务
  2. 使用观察者的概念,并监听@Observes(AFTER_SUCCESS)事件,该事件将在 事务已成功提交并完成,因此您 可以保证做一个db调用,新的更新将是 可用。
  3. 如果您可以忽略BCE模式,并剥离一个新事务来进行更新,那么在它成功返回后,您将保证提交,然后正常继续
  4. ```

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

答案 1 :(得分:2)

好的-这个问题已经有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                    }
            }
        });