Hibernate – отличные результаты с разбивкой на страницы

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

И даже находит ссылку в hibernate faqs:

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 

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

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

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

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

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

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

Подумайте об использовании трансформатора результата DistinctRootEntity

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

ОБНОВИТЬ

Образцы запросов для ассоциаций «один ко многим».

 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; } 

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

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

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

   Фильтр критериев = session.createCriteria (A.class)
     .add (... любые критерии, которые вы хотите фильтровать, включая псевдонимы и т. д.);
   filter.setProjection (Projections.id ());

   Критерии paginate = session.createCriteria (A.class)
     .add (Subqueries.in ("id", filter))
     .addOrder (Order.desc ( "Foo"))
     .setMaxResults (макс)
     .setFirstResult (первый);

   return paginate.list ();

(псевдокод, не проверял, правильно ли синтаксис, но вы получаете идею)

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

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

Для этого я использовал свойство 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();