2011-08-24 27 views
5

問題是我在我的實體中有兩個包,我想顯示在我的jsf前端(後面的Spring中沒有延遲加載)。所以,我必須預先抓取它們顯示在這樣的列表中的信息:休眠獲取加入 - >無法獲取多個包

  • 1點(標籤1,標籤2)(Tag1中...標籤N)
  • 點2(標籤3,標籤4)(Tag1 ... Tag n)

把這兩個列表都渴望沒有工作。所以我嘗試了一個fetch連接。它允許我獲取一個列表,但是當我添加第二個列表時,我得到已知的「無法取多個行李」錯誤。

Hibernate可以處理查詢中的兩個提取連接嗎?

public class PointOfInterest 
@OneToMany(mappedBy="poi") 
private List<PointOfInterestLabel> labels = new ArrayList<PointOfInterestLabel>(); 

@ManyToMany 
private List<Tag> tags = new ArrayList<Tag>(); 

我的抓取連接:

SELECT DISTINCT p from PointOfInterest p 
     left join fetch p.labels 
     left join fetch p.tags WHERE p.figure = :figure 

在啓動時創建我的休眠廠的失敗:

Caused by: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags 
    at org.hibernate.loader.BasicLoader.postInstantiate(BasicLoader.java:94) 
    at org.hibernate.loader.hql.QueryLoader.<init>(QueryLoader.java:123) 
    at org.hibernate.hql.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:206) 
    at org.hibernate.hql.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:136) 
    at org.hibernate.engine.query.HQLQueryPlan.<init>(HQLQueryPlan.java:101) 
    at org.hibernate.engine.query.HQLQueryPlan.<init>(HQLQueryPlan.java:80) 
    at org.hibernate.engine.query.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:98) 
    at org.hibernate.impl.SessionFactoryImpl.checkNamedQueries(SessionFactoryImpl.java:557) 
    at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:422) 
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1385) 
    at org.hibernate.cfg.AnnotationConfiguration.buildSessionFactory(AnnotationConfiguration.java:954) 
    at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:883) 
    ... 55 more 

回答

5

答案是:沒有。它無法處理它。這就是它說的。

對於值類型(複合元素)它甚至不起作用,因爲你沒有得到實際屬於同一個包項目的信息。

通常它甚至沒有意義。如果您查詢表格並在起始表格中獲得10條記錄,則第一個袋子中有10條記錄,第二個袋子中有另外10條記錄,您將檢索1000條記錄,以便在內存中創建這30個對象。想象一下當每個表中有100條記錄(提示:1,000,000而不是300)時的記錄數,以及當您取得加入另一個包時(提示:100,000,000而不是400)...

順便說一句:join fetch可能會導致奇怪的效果和問題,應該避免,除非你完全知道你在做什麼。

+0

謝謝!目前我用Set取代它,但我想我會重構明天的界面並顯示更少的信息。感謝您的反饋!當你的前端沒有持久化範圍時,你會如何解決它。使用DAO中構建的數據傳輸對象而不是獲取的實體?我繼承了這個項目,並且我不允許進行巨大的設計更改,也不允許將其切換到j2ee。 : -/ – mkuff

+0

您可以關閉延遲加載,這對性能不利。這通過單獨的查詢立即提取行李。你也可以訪問袋子(如尺寸)來強制裝載。完美的解決方案是在dao之外創建會話並保留整個業務事務。 (在每次調用dao時創建一個會話稱爲session-per-call,並且是一種反模式。) –

+0

所以真的這是一個catch 22問題:如果你不使用join提取,你會得到n + 1問題或者延遲初始化異常。在Dao之外的會話是一種破解,首先因爲它遠不及JPA合同,其次是因爲它幾乎不可控,因爲在創建實體時不應該對實體的生命週期做出任何假設。它可能跨越多個請求,不一定是http。 – Deroude

1

除了使用Set之外,還可以拆分查詢並在不同查詢中加載實體。

From PointOfInterest p left join fetch p.labels WHERE p.figure = :figure 
    From PointOfInterest p left join fetch p.tags WHERE p.figure = :figure    

請參閱the link