2012-02-23 80 views
5

不同的結果,這似乎是一個衆所周知的問題,多年如可以在這裏讀到: http://blog.xebia.com/2008/12/11/sorting-and-pagination-with-hibernate-criteria-how-it-can-go-wrong-with-joins/休眠 - 與分頁

即使發現處於休眠常見問題解答參考:

https://community.jboss.org/wiki/HibernateFAQ-AdvancedProblems#Hibernate_does_not_return_distinct_results_for_a_query_with_outer_join_fetching_enabled_for_a_collection_even_if_I_use_the_distinct_keyword

這有也已在前面討論過SO

How to get distinct results in hibernate with joins and row-based limiting (paging)?

問題在於,即使經歷了所有這些資源之後,我仍然無法解決我的問題,這似乎與此標準問題有點不同,儘管我不確定。

這裏提出的標準解決方案涉及創建兩個查詢,第一個用於獲取不同的ID,然後使用更高級查詢中的查詢來獲得所需的分頁。在我的情況Hibernate類是像

A 
- aId 
- Set<B> 

B 
- bId 

在我看來,子查詢似乎工作對我罰款,並能夠得到鮮明的艾滋病,這是應該做的分頁外部查詢再次獲取重複項,因此子查詢中的區別不起作用。

假設我有一個具有一組四個乙對象的對象,我的分析是因爲引入組的,雖然提取數據爲

session.createCriteria(A.class).list(); 

休眠被填充在列表指向四個引用中只是一個對象。正因爲如此,標準解決方案對我來說是失敗的。

有人可以幫忙想出一個解決方案,這種情況下?

編輯:我決定自己從不同的結果集中做分頁。其他同樣糟糕的方式可以一直延遲加載在B對象,但這樣做需要所有A對象單獨的查詢來獲取相應的B對象

回答

6

考慮使用DistinctRootEntity結果變壓器這樣

session.createCriteria(A.class) 
    .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list(); 

UPDATE

用於一對多關聯的查詢示例。

public Collection<Long> getIDsOfAs(int pageNumber, int pageSize) { 
    Session session = getCurrentSession(); 

    Criteria criteria = session.createCriteria(A.class) 
     .setProjection(Projections.id()) 
     .addOrder(Order.asc("id")); 

    if(pageNumber >= 0 && pageSize > 0) { 
     criteria.setMaxResults(pageSize); 
     criteria.setFirstResult(pageNumber * pageSize); 
    } 

    @SuppressWarnings("unchecked") 
    Collection<Long> ids = criteria.list(); 
    return ids; 
} 

public Collection<A> getAs(int pageNumber, int pageSize) { 
    Collection<A> as = Collections.emptyList(); 

    Collection<Long> ids = getIDsOfAs(pageNumber, pageSize); 
    if(!ids.isEmpty()) { 
     Session session = getCurrentSession(); 

     Criteria criteria = session.createCriteria(A.class) 
      .add(Restrictions.in("id", ids)) 
      .addOrder(Order.asc("id")) 
      .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); 
     @SuppressWarnings("unchecked") 
     as = criteria.list(); 
    }  

    return as; 
} 
+0

我也在子查詢中使用過。我的子查詢工作正常,並返回唯一的ID。問題是外部查詢正在獲取重複項。例如,如果子查詢返回A.ids = 1,2,3,那麼主查詢(用於分頁)將獲取重複的A.id,如1,1,1,2,2,3,3,3,因此表單完整查詢Select A. * from A where A.id in(subquery)limit 10;無法讓我唯一A記錄 – Ashish 2012-02-24 04:28:42

+0

您的最終查詢應該如下所示:select a。* from a where a.id in(:ids),並且查詢以獲取A的標識符應如下所示:select a.id from A限制爲10.因此,僅將限制僅應用於標識符而不應用於最終查詢。 – szhem 2012-02-24 07:08:01

+0

我的最後一個查詢的形式是從a.id = b.aid的左外部連接b中選擇a。*,並且需要具有完整的A對象。 – Ashish 2012-02-24 10:12:36

1

你提到你看到這個問題的原因是因爲Set<B>被預先抓取。如果你分頁,那麼你可能不需要B的每個A,所以最好是懶惰地取回它們。

但是,當您將B加入查詢以進行選擇時會發生同樣的問題。

在某些情況下,您不僅需要進行分頁,還要對ID以外的其他字段進行排序。我認爲,要做到這一點的辦法是制定這樣的查詢:

 
    Criteria filter = session.createCriteria(A.class) 
    .add(... any criteria you want to filter on, including aliases etc ...); 
    filter.setProjection(Projections.id()); 

    Criteria paginate = session.createCriteria(A.class) 
    .add(Subqueries.in("id", filter)) 
    .addOrder(Order.desc("foo")) 
    .setMaxResults(max) 
    .setFirstResult(first); 

    return paginate.list(); 

(僞代碼,並沒有檢查語法是完全正確的,但你的想法)

+0

謝謝Arnout。但你是否認爲這不會有重複。我的問題是,即使像session.createCriteria(A.class).list()這樣的小查詢,它也會因爲提前取回而返回重複項。您的解決方案如何防止提前取回? – Ashish 2012-02-28 05:19:57

+0

嗯,是的:第一次真正評估你是否真的需要提取。根據我的經驗,永遠不要總是熱切地獲取某些集合 - 當然,在某些特定的查詢中獲取它們可能是有用的,Criteria API允許您指定它。如果您確定要始終踊躍獲取此集合,請在外部Criteria('paginate')上使用'Criteria.DISTINCT_ROOT_ENTITY'' ResultTransformer'。 – 2012-02-28 20:33:46

0

我回答了這個位置: Pagination with Hibernate Criteria and DISTINCT_ROOT_ENTITY

你需要做3件事,1)獲得總數,2)獲取你想要的行的ID,然後3)獲取你在步驟2中找到的ID的數據。它實際上並非全部那麼一旦你獲得了正確的訂單就不好了,你甚至可以創建一個通用的方法,併發送一個分離的標準對象,使其更加抽象。

0

我用groupBy屬性來實現這一點。希望它有效。

Criteria filter = session.createCriteria(A.class);  
filter.setProjection(Projections.groupProperty("aId")); 
//filter.add(Restrictions.eq()); add restrictions if any 
filter.setFirstResult(pageNum*pageSize).setMaxResults(pageSize).addOrder(Order.desc("aId")); 

Criteria criteria = session.createCriteria(A.class); 
criteria.add(Restrictions.in("aId",filter.list())).addOrder(Order.desc("aId")); 
return criteria.list();