2012-01-12 19 views
3

首先,我有一個無狀態Bean上做一個簡單的retreive,看起來像這樣檢索有狀態的EJB實體throught無國籍本地EJB。(使用的session-per-談話長時間的談話)

@Stateless 
@LocalBean 
public A { 
    @PersistenceContext 
    private EntityManager em; 

    public MyEntity retrieveMethod(){ 
     em.createQuery(...).getSingleResult(); 
    } 
} 

我有用於管理與遠程客戶端長談全狀態的豆,它是這樣的:來上執行em.persist(OE)

@Statefull 
@LocalBean 
@TransactionAttribute(NOT_SUPPORTED) 
public class B implements BRemote { 
    @PersistenceContext(type = EXTENDED) 
    private EntityManager em; 

    @EJB 
    A a; 

    public void start(){ 
     OtherEntity oe = new OtherEntity(); 
     oe.setRelationMyEntitie(this.a.retrieveMethod()); 

     em.persist(oe); 
    } 

    @TransactionAttribute(REQUIRED) 
    public void end(){ 
     em.flush(); 
    } 
} 

的問題。 oe引用了由另一個EntityManager加載的MyEntity實例。所以他們不知道它抱怨持續分離的實體。

我想知道什麼是有一種方法來避免這個問題。如果沒有直接的解決方案,採用什麼樣的最佳模式?


編輯:我不想就開始使用交易(),因爲在實際應用中,有狀態Bean用於實現其必須堅持在一次複雜的實體模型。我嘗試設置名爲session-per-conversation的模式,在此處描述http://docs.jboss.org/hibernate/core/4.0/manual/en-US/html/transactions.html#transactions-basics-apptx。所以如果我理解你是正確的,解決方案是「在bean B的start()方法中使用事務」,但是如果我這樣做,在方法結束時,內容被刷新到數據庫,這不是我想要的。

我能看到的其他解決方案是在B的EntityManager中獲取MyEntity,所以做一個合併或者一個em.find()或者將retrieveMethod委託給一些DAO樣式類,使用em參數和bean A ,在DA中做一個簡單的DAO委託,直接調用DAO。

任何想法是什麼是最好的方法?

回答

0

這裏是我使用的解決方案:

import java.lang.reflect.Field; 

import javax.annotation.Resource; 
import javax.interceptor.AroundInvoke; 
import javax.interceptor.InvocationContext; 
import javax.persistence.EntityManager; 
import javax.persistence.PersistenceContext; 

public class SessionPerConversationInterceptor { 
    private final static ThreadLocal<EntityManager> s_thEntityManager = new ThreadLocal<>(); 

    @AroundInvoke 
public Object manageEntityManager(InvocationContext ctx) throws java.lang.Exception { 
    EntityManager em = s_thEntityManager.get(); 
    if (em == null) { 
     MasterPersistenceContext traversableEntityManager = ctx.getTarget().getClass().getAnnotation(MasterPersistenceContext.class); 
     if (traversableEntityManager != null) { 
      for (Field field : ctx.getTarget().getClass().getDeclaredFields()) { 
       if (field.getAnnotation(PersistenceContext.class) != null) { 
        field.setAccessible(true); 
        em = (EntityManager) field.get(ctx.getTarget()); 
        s_thEntityManager.set(em); 

        try { 
         Object oRet = ctx.proceed(); 
         return oRet; 
        } finally { 
         s_thEntityManager.set(null); 
        } 
       } 
      } 
     } 
    } else if (ctx.getTarget().getClass().getAnnotation(MasterPersistenceContext.class) == null) { 
     for (Field field : ctx.getTarget().getClass().getDeclaredFields()) { 
      if (field.getAnnotation(PersistenceContext.class) != null) { 
       field.setAccessible(true); 

       EntityManager oldEntityManager = (EntityManager) field.get(ctx.getTarget()); 

       field.set(ctx.getTarget(), em); 

       try { 
        Object oRet = ctx.proceed(); 
        return oRet; 
       } finally { 
        field.set(ctx.getTarget(), oldEntityManager); 
       } 
      } 
     } 
    } 

    return ctx.proceed(); 
} 
} 

我希望它可以幫助(我希望這是一個不那麼難看/啞溶液)。

1

這個問題似乎是正常的(非延伸)的持久性的上下文範圍限定於JTA事務。

既然你已經宣佈豆B中交易NOT_SUPPORTED,而有REQUIRED,調用方法start會給你一個不同的持久化上下文作爲一個在'retrieveMethod」。 (實際上,在第一種情況下根本就沒有持久化上下文)。

通常在EJB資源,包括實體管理器在單個事務中自動共享,所以即使看起來就像在不同的豆子不同的注射,你仍然可以得到相同的資源。

即使沒有豆A,你的代碼就不會因爲持續的工作,需要一個交易存在。顯式刷新也不會有太大的用處,因爲在這種情況下這不是必需的(當事務提交時自動發生)。

如果你想保持通話過程中附加的管理實體,豆B可使用擴展的持久化上下文@PersistenceContext(type = EXTENDED)。如果它也使用事務,那麼bean A將共享相同的上下文,即使它本身沒有擴展上下文(重要的是它將在B的事務上下文中被調用)。

+0

何,對不起,我的錯。我在bean B上使用@PersistenceContext(type = EXTENDED),忘記在示例中寫入它。 – 2012-01-13 07:37:51

+0

我不想在start()上使用事務,因爲在實際應用程序中,statefull bean用於實現需要一次保留的複雜實體模型。 – 2012-01-13 07:42:46

+0

好的,在那種情況下,實體將被「收集」,直到再次與TX交互。我認爲'em.flush'仍然可以被刪除,因爲TX的存在將觸發真正的持續。 – 2012-01-13 07:52:09

2

這是一個相當有趣的問題。設置似乎完全合理的,但難的是在交易的兩個「自動」行爲:

  • 共享資源,在調用鏈
  • 自動持續操作時,持久化上下文是不是排隊等豆類與交易相關聯

由於您要共享的資源恰好是實體管理器,因此更加困難。否則,您可能已經使用EJB中的第三個變體,稱爲「應用程序管理的」實體管理器。這可以通過程序與事務相關聯(使用em.join()),而不管事務中是否存在業務方法。

您可能需要在沒有事務的情況下共享,或者防止在事務關聯後自動刷新。這兩個都是我所知道的在EJB中缺少的功能。 也許管理擴展em的應用程序不會自動刷新,但我不會屏住呼吸。

但是更爲人工的方法呢?不要調用em.persist(),但使用一個單獨的列表來存儲對話過程中需要保留的任何實體的引用。

然後在關閉方法中迭代該列表並調用em.persist()。

P.s.

另一種選擇:如果使用em.merge()而不是em.persist()會怎麼樣?合併是一種多功能的方法,既可以更新也可以插入,並且不關心實體是否被連接。如果實體從未脫離A和B,那將會更好,但這可能是一個實際的解決方案。

+0

感謝您的回答。實際上,您描述的手動方法將不起作用。它會抱怨persist()調用由另一個EntityManager加載的對象,因爲它們不是合併或新的。 merge()解決方案是一個選項,我現在嘗試查看每個解決方案的優點和缺點。 – 2012-01-13 20:47:06