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