2015-03-02 70 views
1

我有一個EJB攔截器,我遵循Adam Bien建議的BCE模式,也就是說,邊界上的所有EJB調用都會啓動並完成一個事務,這意味着沒有嵌套的EJB調用(可能有嵌套的CDI注入Bean調用,但這些應該在ejb邊界開始的同一事務中)。EJB攔截器和事務生命週期或如何攔截提交/失敗事件?

因此,在那些ejb邊界我有一個攔截器,我想攔截或知道在EJB的方法調用後交易已經提交了嗎? (也就是說,如果EntityManager涉及COMMIT sql調用被髮送到數據庫併成功返回)

  1. 我會從Interceptor中獲取該信息嗎?
  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沒有辦法接收已經COMMITED交易的事件。因此,不管好的答案是什麼,遺憾的是,在服務器內部沒有辦法在Java EE中通知已完成的事務,當然,如果您是客戶端調用者,那麼您確定知道事務已提交或回退...

+1

我想你可能會誤解Adam Bien。你寫道:「邊界上的所有EJB調用都會啓動並完成一個事務,這意味着沒有嵌套的EJB調用」,但事實並非如此。您可以嵌套EJB調用,除非您特別重寫它,否則調用將發生在同一個事務中。無論如何,我不認爲BCE意味着您不能在單個邊界呼叫中使用多個交易。最後一個提示:'AroundInvoke'調用發生在與方法相同的事務中,因此您的問題中的代碼註釋無法實現。 – DavidS 2017-04-12 16:56:11

回答

2

除非另有說明拋出的異常,如果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規範調用一個容器管理事務的提交,你當然可以調用EJBContext的setOnrollback方法。 編輯 如果你真的想要做一些數據庫的變化,我建議:

  1. 用戶ApplicationManaged交易,從中你手動啓動 和攔截
  2. 內提交事務,使用的概念觀察者,並監聽@Observes(AFTER_SUCCESS)事件,當事務成功提交併完成時將調用該事件,因此您可以保證執行db調用,並且新更新將爲 可用。
  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; 
     } 
    } 
} 
+0

感謝您的回答,但是我需要知道我是否可以抓住'commited'事件,而不是'在幾毫秒內肯定會提交'事件。那可能嗎 ?因爲基於commited事件,我需要對數據庫進行讀取以更新ElasticSearch文檔,而且我不能依賴'將被提交',因爲如果讀取數據庫的代碼發生在'actual commit'事件之前,那麼ElasticSearch將不會獲取更新的文檔。也許我搞砸了一些東西,這可以做得更容易... – 2015-03-02 07:52:40