Hibernate - отличные результаты с нумерацией страниц - PullRequest
6 голосов
/ 23 февраля 2012

Это, кажется, хорошо известная проблема в течение многих лет, о чем можно прочитать здесь: 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

Как получить отличные результаты в спящем режиме с объединениями и ограничением на основе строк (подкачкой страниц)?

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

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

A
 - aId
 - Set<B>

B
 - bId 

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

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

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

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

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

Редактировать: я решил сделать пагинацию самостоятельно из отдельного набора результатов.Другим не менее плохим способом могла быть ленивая загрузка объектов B, но для этого потребовались бы отдельные запросы для всех объектов A для получения соответствующих объектов B

Ответы [ 4 ]

6 голосов
/ 24 февраля 2012

Рассмотрите возможность использования 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;
}
1 голос
/ 27 февраля 2012

Вы упоминаете причину, по которой вы видите эту проблему, потому что Set<B> загружен с нетерпением.Если вы разбиваете на страницы, скорее всего, вам не нужны B для каждого A, поэтому может быть лучше их лениво извлекать.

Однако такая же проблема возникает при присоединенииB в запросе для выбора.

В некоторых случаях вам нужно не только разбивать на страницы, но и сортировать по другим полям, кроме идентификатора.Я думаю, что способ сделать это - сформулировать запрос следующим образом:

  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 голосов
/ 17 февраля 2017

Я использовал свойство 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();
0 голосов
/ 03 мая 2014

Я ответил здесь: Пагинация с критериями гибернации и DISTINCT_ROOT_ENTITY

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...