2012-02-23 4 views
5

Это, кажется, хорошо известная проблема в течение многих лет, как можно прочитать здесь: http://blog.xebia.com/2008/12/11/sorting-and-pagination-with-hibernate-criteria-how-it-can-go-wrong-with-joins/Hibernate - отличные результаты с пагинацией

И даже находит упоминание в Hibernate часто задаваемые вопросы:

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)?

Проблема в том, что даже после прохождения всех этих ресурсов я не смог решить свою проблему, которая, похоже, немного отличается от этой стандартной проблемы, хотя я не уверен.

Стандартное решение, предлагаемое здесь, включает в себя создание двух запросов, сначала для получения отдельных идентификаторов, а затем с использованием запросов более высокого уровня для получения желаемой разбивки на страницы. Классы зимуют в моем случае это что-то вроде

A 
- aId 
- Set<B> 

B 
- bId 

Мне кажется, что подзапрос, кажется, работает хорошо для меня, и быть в состоянии получить различный СПИД, но внешний запрос, который, как предполагается сделать пагинацию снова извлекает дубликаты, и, следовательно, отдельный в подзапросе не оказывает никакого эффекта.

Предполагая, что у меня есть один Объект, который имеет набор из четырех B объектов, Мой анализом является то, что из-за введение набора, в то время выборки данных для

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

Гибернации заселения четыре ссылки в список, указывающий на всего один объект. Из-за этого стандартное решение терпит неудачу для меня.

Может ли кто-нибудь помочь в решении проблемы для этого случая?

Редактирование: Я решил пойти для того, чтобы делать разбивку на страницы из отдельных результатов. Другой столь же плохой путь мог быть ленивой нагрузкой объектов В, но это потребовало бы отдельные запросов для всех объектов А для извлечения соответствующего 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

Я использовал это также в подзапросе. Мой подзапрос работает нормально и возвращает уникальные идентификаторы. Проблема заключается в том, что внешний запрос извлекает дубликаты. Например, если подзапрос возвращает A.ids = 1,2,3, тогда основной запрос (который используется для разбивки на страницы) извлекает дубликаты Aids, такие как 1,1,1,2,2,3,3,3 и, следовательно, полный запрос формы Выберите A. * из A, где A.id в (подзапросе) предел 10; не удается получить уникальные записи A. – Ashish

+0

. Окончательный запрос должен выглядеть так: выберите a. * из где a.id в (: ids), а запрос для получения идентификаторов A должен выглядеть так: выберите a.id из A предел 10. Так что применяйте Limit только к идентификаторам, а не к окончательному запросу. – szhem

+0

мой окончательный запрос имеет вид select a. * Из левого внешнего соединения b на a.id = b.aid, и для этого требуется полный объект A. – Ashish

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

Спасибо Арноут. Но я думаю, у этого не будет дубликатов. Моя проблема в том, что даже для небольшого запроса, такого как session.createCriteria (A.class) .list(), он возвращает мне дубликаты из-за нетерпеливой выборки. Как ваше решение предотвращает нетерпимую выборку? – Ashish

+0

А, да: сначала вы действительно оцениваете, действительно ли вам нужно желание. По моему опыту, вряд ли всегда есть хорошая идея всегда получать определенные коллекции с нетерпением - может быть полезно получить их с нетерпением в определенных конкретных запросах, конечно, и API-интерфейс Criteria позволяет вам указать это. Если вы уверены, что хотите всегда получать эту коллекцию с нетерпением, используйте «Criteria.DISTINCT_ROOT_ENTITY'« ResultTransformer »на внешних критериях (' paginate'). –

0

Я ответил на этот вопрос здесь: Pagination with Hibernate Criteria and DISTINCT_ROOT_ENTITY

Вам нужно сделать 3 вещи, 1) получить общее количество, 2) получить идентификаторы строк, которые вы хотите, а затем 3) получить свои данные для идентификаторов, найденных на шаге 2. Это действительно не все это плохо, как только вы получите правильный порядок, и вы даже можете создать общий метод и отправить ему отдельный объект критерия, чтобы сделать его более абстрактным.

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(); 
Смежные вопросы