Рекомендации по Java-кодированию для повторного использования части запроса для подсчета - PullRequest
5 голосов
/ 21 октября 2009

вопрос о *1001* реализации-результата-подкачки-в-спящем-получении-общем-количестве-строк-строк вызывает у меня другой вопрос, касающийся некоторой проблемы реализации :

Теперь вы знаете, что вам нужно повторно использовать часть запроса HQL для подсчета, как эффективно использовать повторно?

Различия между двумя запросами HQL:

  1. выбор count(?), вместо pojo или свойства (или списка)
  2. выборки не должны происходить, поэтому некоторые таблицы не должны объединяться
  3. order by должен исчезнуть

Есть ли другие различия?

Есть ли у вас лучшие практики кодирования для эффективного повторного использования (касается усилий, ясности, производительности)?

Пример простого запроса HQL:

    select       a     from A a join fetch a.b b where a.id=66 order by a.name
    select count(a.id) from A a                  where a.id=66

ОБНОВЛЕНО

Я получил ответы на:

  • с использованием Критерии (но мы в основном используем HQL)
  • манипулирование запросом String (но все согласны с тем, что это кажется сложным и не очень безопасным)
  • упаковка запроса , опираясь на оптимизацию базы данных (но есть ощущение, что это небезопасно)

Я надеялся, что кто-нибудь даст варианты по другому пути, более связанному с конкатенацией строк.
Можем ли мы построить оба HQL-запроса, используя общие части ?

Ответы [ 4 ]

3 голосов
/ 21 октября 2009

Пытались ли вы прояснить свои намерения в Hibernate, установив прогноз по вашим (SQL?) Критериям? Я в основном использовал критерии, поэтому не уверен, насколько это применимо к вашему делу, но я использовал

getSession().createCriteria(persistentClass).
setProjection(Projections.rowCount()).uniqueResult()

и позволить Hibernate самому разобраться в кешировании / повторном использовании / умных вещах. Хотя не совсем уверен, сколько умных вещей он на самом деле делает ... Кто-нибудь захочет это прокомментировать?

2 голосов
/ 21 октября 2009

Ну, я не уверен, что это лучшая практика, но моя практика :) 1001 *

Если у меня в качестве запроса что-то вроде:

select A.f1,A.f2,A.f3 from A, B where A.f2=B.f2 order by A.f1, B.f3

И я просто хочу знать, сколько результатов получу, я выполняю:

select count(*) from ( select A.f1, ... order by A.f1, B.f3 )

И затем получить результат в виде целого числа без сопоставления результатов в POJO.

Разобрать ваш запрос на удаление некоторых деталей, например "упорядочить по", очень сложно. Хорошая СУБД оптимизирует ваш запрос для вас.

Хороший вопрос.

1 голос
/ 21 октября 2009

Хороший вопрос. Вот что я делал в прошлом (многие вещи, о которых вы уже упоминали):

  1. Проверьте, присутствует ли предложение SELECT .
    1. Если это не так, добавьте select count(*)
    2. В противном случае проверьте, есть ли в нем DISTINCT или агрегатные функции. Если вы используете ANTLR для разбора вашего запроса, возможно обойти это, но это довольно сложно. Скорее всего, вам лучше просто обернуть все это в select count(*) from ().
  2. Удалить fetch all properties
  3. Удалите fetch из объединений, если вы анализируете HQL как строку. Если вы действительно разбираете запрос с помощью ANTLR, вы можете полностью удалить left join; это довольно грязно, чтобы проверить все возможные ссылки.
  4. Удалить order by
  5. В зависимости от того, что вы сделали в 1.2, вам нужно будет удалить / настроить group by / having.

Вышеуказанное относится, естественно, к HQL. Для запросов Criteria вы весьма ограничены тем, что вы можете сделать, потому что это не поддается легкому манипулированию. Если вы используете какой-либо слой-обертку поверх Критериев, вы получите эквивалент (ограниченный) поднабор результатов анализа ANTLR и в этом случае можете применить большинство из перечисленного выше.

Так как вы обычно держитесь за смещение вашей текущей страницы и общее количество, я обычно запускаю фактический запрос с заданным лимитом / смещением в первую очередь и запускаю запрос count(*), только если количество возвращаемых результатов больше или равно для ограничения AND смещение равно нулю (во всех других случаях я либо запускаю count(*) раньше, либо получаю все результаты обратно в любом случае). Конечно, это оптимистичный подход в отношении одновременных изменений.

Обновление (при ручной сборке HQL)

Мне не особенно нравится такой подход. При отображении в качестве именованного запроса HQL имеет преимущество проверки ошибок во время сборки (ну, технически, во время выполнения, потому что SessionFactory должен быть собран, хотя это обычно делается во время интеграционного тестирования). При генерировании во время выполнения происходит сбой во время выполнения :-) Выполнение оптимизации производительности также не совсем просто.

Конечно, те же рассуждения применимы и к Критериям, но из-за четко определенного API-интерфейса немного сложнее ошибиться, чем к конкатенации строк. Построение двух HQL-запросов параллельно (постраничный один и один «глобальный счет») также приводит к дублированию кода (и, возможно, к большему количеству ошибок) или заставляет вас написать какой-нибудь слой-обертку поверх этого, чтобы сделать это за вас. Оба пути далеки от идеальных. И если вам нужно сделать это из клиентского кода (как в over API), проблема становится еще хуже.

Я на самом деле немного размышлял по этому вопросу. API поиска из Hibernate-Generic-DAO кажется разумным компромиссом; в моем ответе на приведенный выше вопрос есть более подробная информация.

0 голосов
/ 21 октября 2009

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

Integer count = (Integer) session.createQuery("select count(*) from ....").uniqueResult();

Сделайте это один раз и корректируйте начальный номер соответственно, пока не пролистаете.

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

final Criteria criteria = session.createCriteria(clazz);  
            List<Criterion> restrictions = factory.assemble(command.getFilter());
            for (Criterion restriction : restrictions)
                criteria.add(restriction);
            criteria.add(Restrictions.conjunction());
            if(this.projections != null)
                criteria.setProjection(factory.loadProjections(this.projections));
            criteria.addOrder(command.getDir().equals("ASC")?Order.asc(command.getSort()):Order.desc(command.getSort()));
            ScrollableResults scrollable = criteria.scroll(ScrollMode.SCROLL_INSENSITIVE);
            if(scrollable.last()){//returns true if there is a resultset
                genericDTO.setTotalCount(scrollable.getRowNumber() + 1);
                criteria.setFirstResult(command.getStart())
                        .setMaxResults(command.getLimit());
                genericDTO.setLineItems(Collections.unmodifiableList(criteria.list()));
            }
            scrollable.close();
            return genericDTO;

Но каждый раз это считается, вызывая ScrollableResults:last().

...