Hibernate Выберите верхнюю и нижнюю строки n с критериями

Скажем, у меня есть две таблицы, книги и обзоры. В отзывах есть столбец, звезды, которые могут иметь значение от 1 до 5. Книга может иметь множество отзывов.

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

Если API-интерфейс Criteria не способен, я готов к другим предложениям, таким как HQL, SQL и т.д.

Ответы

Ответ 1

Отвечая на мой собственный вопрос...

Я действительно удивлен, насколько это сложно. Все решения, связанные с запросом книг и каждой книги, имеющей список верхних и нижних обзоров, дают N запросов для каждой книги в некоторых базах данных, а для моей, в частности (MySql), требуется 3N. Конечно, кто-то может понять, насколько это возможно, но моя игра с кодом, сырым SQL, исследованием того, что сделали другие, ограничениями базы данных и т.д., Похоже, всегда возвращается к этому.

Итак... В итоге я столкнулся с проблемой. Вместо того, чтобы вычислять верхние и нижние обзоры всякий раз, когда я запрашиваю книги (это очень частый запрос - например, FYI Books/Reviews - фактический домен отличается), я вычисляю верхний/нижний при каждом представлении нового обзора. Это изменяет отправку обзора из одного запроса на три запроса, но представление обзора довольно редко по сравнению с запросом на книги.

Чтобы сделать это, я добавил два новых свойства в Book, topReviews и bottomReviews и сопоставил их как списки "один ко многим" в обзоре. Затем я обновил код, который сохраняет новые отзывы в запросе верхнего и нижнего обзоров для рассматриваемой книги (2 запроса), устанавливает верхние/нижние свойства Обзоров в Книге (перезаписывает предыдущие) и сохраняет книгу. Любые запросы на книги теперь возвращают эти "кешированные" верхние и нижние обзоры. У меня все еще есть ленивое свойство "рецензий", которое будет иметь все отзывы (а не только верхний/нижний) при желании - другое отображение "один ко многим".

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

Ответ 2

Вы всегда можете установить порядок asc/desc, а затем ограничить результаты.

Пример:

criteria.addOrder(Order.desc("id"));
criteria.setMaxResults(1);

Не уверен, где он стоит в отношении эффективности. Я предполагаю, что он выполняет фильтрацию после того, как был отключен Select *?

Ответ 3

Попробуйте это и сообщите мне, если это сработает для вас:

  // you should have the DAO class containing this queries 
  // extend HibernateDaoSupport

  List<Tag> topList = getSession().createQuery("FROM Reviews r WHERE r.book = " 
          + book + " ORDER BY r.stars asc").setMaxResults(3).list();

  List<Tag> bottomList = getSession().createQuery("FROM Reviews r WHERE r.book = " 
          + book + " ORDER BY r.stars desc").setMaxResults(3).list();

Ответ 4

Это обычно делается с помощью чего-то вроде объединения, чего вы не можете сделать в HQL.

Вы можете это сделать, однако, в HQL

WHERE id in ([SELECT top 3]) or id in ([SELECT bottom 3])

У меня нет возможности проверить это, но это тоже может работать.

DetachedCriteria topN = [ your criteria ] 
DetachedCriteria bottomN = [ your criteria ] 

session.createCriteria(..._
    .add( Property.or(Property.forName("id").in(topN),Property.forName("id").in(bottomN) )
    .list();

Ответ 5

Я не думаю, что вы можете делать то, что хотите, используя один запрос. Конечно, вы можете сделать это в два раза (используя EJBQL с именованными параметрами):

SELECT r FROM Reviews r WHERE r.book = :book ORDER BY r.stars ASC LIMIT 3;
SELECT r FROM Reviews r WHERE r.book = :book ORDER BY r.stars DESC LIMIT 3;

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

Ответ 6

Как сделать сопоставление для просмотра и книги как много-ко-многим, а затем в коллекции отзывов книг укажите: order-by = "rating desc" и lazy = "true"

Предположительно с помощью метода ленивой выборки данные будут извлекаться только тогда, когда объект будет извлечен из коллекции, но я не уверен в этом. Чтобы подтвердить, вы можете включить ведение журнала и мониторинга Hibernate SQL, какие запросы были выполнены во время выполнения.

Ответ 7

Два запроса HQL с использованием org.springframework.data.domain.Pageable, например:

раньше в службе среднего уровня вы создаете страницу и вызываете репо:

Pageable pageableTop = new PageRequest(0, 3, Direction.ASC, "yourFieldToSortBy");
yourRepository.findTopOrderByYourEntity(pageableTop);

Pageable pageableBottom = new PageRequest(0, 3, Direction.DESC, "yourFieldToSortBy");
yourRepository.findTopOrderByYourEntity(pageableBottom);

репо:

public interface YourRepository extends CrudRepository<YourEntity, String> {

    @Query("FROM YourEntity")
    List<YourEntity> findTopOrderByYourEntity(Pageable pageable);
}