Спящий режим Выберите верхний и нижний ряды с критериями - PullRequest
21 голосов
/ 31 июля 2011

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

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

Если API-интерфейс Критерии не поддерживаетМне нужны другие предложения, такие как HQL, SQL и т. Д.

Ответы [ 7 ]

19 голосов
/ 19 апреля 2012

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

ex:

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

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

14 голосов
/ 08 августа 2011

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

  // 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();
5 голосов
/ 09 августа 2011

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

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

Итак ... В итоге я решил проблему. Вместо того, чтобы вычислять верхние и нижние Обзоры всякий раз, когда я запрашиваю Книги (что является очень частым запросом - для справки. Справочные книги и обзоры приведены в качестве примера - фактический домен отличается), я вычисляю верхнее / нижнее при каждой отправке нового Обзора. Это изменяет отправку рецензии из одного «запроса» в три запроса, но отправка рецензии происходит довольно редко по сравнению с запросами для книг.

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

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

1 голос
/ 02 августа 2011

Обычно это делается с помощью чего-то вроде объединения, чего нельзя сделать в 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();
0 голосов
/ 05 августа 2016

Два запроса 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);
}
0 голосов
/ 04 августа 2011

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

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

0 голосов
/ 31 июля 2011

Я не думаю, что вы можете делать то, что вы хотите, используя один запрос. Однако вы можете сделать это двумя способами (используя 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;

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

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