2009-10-21 2 views
5

implementing-result-paging-in-hibernate-getting-total-number-of-rows вопроса вызовет еще один вопрос для меня, о реализации некоторой озабоченности:Java кодирование передовых методов для повторного использования части запроса для подсчета

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

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

  1. выборка count(?), вместо POJO или имущества (или список)
  2. в Fetches не должно произойти, поэтому некоторые таблицы не должны быть присоединился
  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. Проверьте ВЫБРАТЬ пункт присутствует.
    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(*), если число возвращаемых результатов больше или равно limit AND offset равен нулю (во всех остальных случаях я либо запускаю count(*) раньше, либо все результаты возвращены). Это, конечно, оптимистический подход в отношении параллельных модификаций.

Update (на ручной сборки HQL)

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

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

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

+0

+1 Спасибо за эти рекомендации по обработке запроса. Спасибо также за отличную точность, что ** запрос count должен запускаться только после первого запроса **. – KLE

+0

Я обновил свой вопрос, внеся ли вы другой ответ, связанный с новой частью? Мне понравилось много других ваших сообщений, и вы эксперт по java :-) ... – KLE

+0

Спасибо :-) Я обновил свой ответ выше. – ChssPly76

2

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

Если я как-то запрос, как:

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.

Проанализируйте ваш запрос на удаление некоторых деталей, например, «заказать» очень сложно. Хорошая RDBMS оптимизирует ваш запрос для вас.

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

+0

Спасибо за вашу поддержку :-) Мой типичный запрос - это HQL, возвращающий pojos или список свойств. – KLE

+0

Как и вы, мне не хочется разбирать запрос, чтобы удалить некоторые части; но я действительно не доверяю RDBMS для оптимизации запроса во всех случаях. Я думаю, что некоторые случаи перепутались, и трудно предсказать, что именно. ** Есть ли список фактов об этих оптимизациях? ** – KLE

+0

Я не знаю, где найти эти списки фактов или эта информация является общедоступной. Я не могу быть полностью уверен, что RDBMS оптимизирует этот путь. Но как студент, я видел некоторые «старые и базовые» оптимизации, которые кажутся более сложными, чем эта. В этом случае, например, RDBMS подумает: «Они запрашивают меня за несколько строк, поэтому порядок можно избежать». – sinuhepop

2

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

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

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

+0

Hibernate не кэширует запросы самостоятельно; вы должны сделать это явно. Проблема с вышеприведенным подходом (и с использованием критериев в целом) заключается в том, что для сборки слоя критерии необходимо создать еще одну копию для подсчета. Другими словами, я не могу просто создать критерий в бизнес-слое, передать его службе (или DAO) и получить 1 страницу результатов + общий счет. Не очень много для небольших приложений, но приводит к большому количеству ненужного кода в больших. – ChssPly76

0

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