Рекомендации по кодированию Java для повторного использования части запроса для подсчета

StackOverflow https://stackoverflow.com/questions/1600440

Вопрос

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

Теперь вы знаете, что вам нужно повторно использовать часть запроса 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)
  • манипулирование Строкой запрос (но все согласны с тем, что это кажется сложным и не очень безопасным)
  • обертывание запроса, полагаясь на оптимизацию базы данных (но есть ощущение, что это небезопасно)

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

Это было полезно?

Решение

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

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

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

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

Обновить (ручная сборка HQL)

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

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

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

Другие советы

Вы пробовали прояснить свои намерения для Hibernate, установив проекцию на ваши критерии (SQL?)?В основном я использовал Критерии, поэтому я не уверен, насколько это применимо к вашему случаю, но я использовал

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

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

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

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

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.

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

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

В ситуации с 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().

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top