Як зробити запит обмеження в JPQL або HQL?


239

Чи існує спосіб hibernate 3, як зробити еквівалент наступного ліміту MySQL в HQL?

select * from a_table order by a_table_column desc limit 0, 20;

Я не хочу використовувати setMaxResults, якщо можливо. Це, безумовно, було можливим у більш старій версії Hibernate / HQL, але вона, схоже, зникла.


2
Я використовую Hibernate-5.0.12. Це все ще недоступно? Було б дуже важко отримати мільйон або близько того записів, а потім застосувати до нього фільтр, setMaxResultsяк помітив @Rachel у відповіді @skaffman.
Радєєв Ранджан

Відповіді:


313

Це було розміщено на форумі сплячки кілька років тому, коли його запитали про те, чому це спрацювало в сплячому режимі 2, а не в сплячому режимі 3:

Ліміт ніколи не підтримувався в HQL. Вам призначено використовувати setMaxResults ().

Тож якщо це працювало в режимі зимування 2, то, здається, це було випадково, а не за дизайном. Я думаю, це було тому, що аналізатор HQL Hibernate 2 замінить біти запиту, який він розпізнав як HQL, і залишить решта таким, яким він був, щоб ви могли прокрастися в якомусь рідному SQL. Однак у режимі hibernate 3 є належний AST HQL-аналізатор, і це набагато менше прощає.

Я думаю, що це Query.setMaxResults()справді ваш єдиний варіант.


3
Я б заперечував, що підхід Hibernate 3 є більш правильним. Використання Hibernate призначене для агностування баз даних, тому вам доведеться робити такі речі абстрактно.
мат б

6
Я погоджуюся, але це робить міграцію королівським болем у попці, коли такі функції відпадають.
skaffman

52
але з setMaxResults спочатку виконується запит, а потім на виклик набору результатів, setMaxResultsякий забирає обмежену кількість рядків результатів із набору результатів і відображає його користувачеві, у моєму випадку у мене є 3 мільйони записів, які запитуються, а потім я закликаю setMaxResults встановити 50 записів, але я не хочу цього робити, хоча сам запит хочу запитувати 50 записів, чи є спосіб це зробити?
Рейчел

2
Старий пост я знаю. Я повністю погоджуюся з Рейчел. Використовуючи NHibernate (.Net порт Hibernate), я нещодавно оновив з 2 до 3 і те саме, top X тепер кидає помилку аналізатора. Однак, коли я додав setMaxResults у запиті, він генерував TOP X у результуючому SQL (використовуючи MsSql2008Dialect). Це добре.
Thierry_S

17
@Rachel З setMaxResultsсплячим режимом додасть limitчастину запиту. Це не отримає всіх результатів. Ви можете перевірити його запит, увімкнувши:<property name="show_sql">true</property>
Андрейс

148
 // SQL: SELECT * FROM table LIMIT start, maxRows;

Query q = session.createQuery("FROM table");
q.setFirstResult(start);
q.setMaxResults(maxRows);

6
Мені це найбільше подобається, тому що setFirstResultнасправді згадується у цій відповіді, тоді як тут і в інших місцях вони просто говорять setMaxResultsце і setMaxResultsте, не згадуючи, як встановити компенсацію.
demongolem

19

Якщо ви не хочете використовувати setMaxResults()на Queryоб'єкті , то ви завжди можете повернутися до використання нормального SQL.


37
Це насправді не все так захоплююче.
stevedbrown

8
Я також не вважаю HQL захоплюючим. Чому б не написати перегляд на своєму сервері БД, який застосовує ліміт, а потім отримати HQL для перегляду цього виду: P
pjp

1
Це лише одна з цих речей, тоді як SQL набагато простіше, ніж HQL для кожного запиту, створюючи перегляди та запису нативного SQL, як правило, не так вже й чудово для рефакторингу. Я намагаюся уникати цього, коли можу. Ця реальна проблема полягала в тому, що я записував MySQL-запит неправильно і думав, що setMaxResults дивно. Це не було.
stevedbrown

і якщо ви спробуєте переключитися між різними постачальниками СУБД, на вас чекає біль.
Olgun Kaya

5

Якщо ви не хочете використовувати setMaxResults, ви можете також використовувати Query.scroll замість списку та отримувати бажані рядки. Наприклад, корисно для пейджингу.


Дякую, прийнята відповідь не вирішила питання для мене, тому що setMaxResults()завантажується спочатку кожен запис у пам'ять, а потім створює підпис, який, коли є сотні тисяч і більше записів, вибиває сервер, оскільки він не має пам'яті. Однак я міг перейти від запиту, набраного JPA, до запиту в сплячку, QueryImpl hibernateQuery = query.unwrap(QueryImpl.class)і тоді я міг би використовувати scroll()метод, як ви запропонували.
toongeorges

Принаймні, з діалектом Oracle це неправда (Hibernate використовує віртуальний стовпець ROWNUM). Можливо, це залежить від водія. Інші БД мають функцію TOP.
Луїс Мартінес

Мій запит використовує приєднання. Це призводить до попередження про сплячку "firstResult / maxResults, вказаний при виборі колекції; застосовується в пам'яті". Таким чином, за допомогою приєднання, Hibernate завантажує повну колекцію в пам'ять. Відмовлятися від з'єднання не є можливим через причини продуктивності. Коли я використовую ScrollableResulta, я більше контролюю, які записи завантажуються в пам'ять. Я не можу завантажити всі записи за допомогою одного ScrollableResulta, оскільки це також призводить до втрати пам’яті. Я експериментую з завантаженням декількох сторінок з різними результатами прокрутки. Якщо це не допоможе, я піду з SQL.
toongeorges

Це дивно, я ніколи з цим не стикався. Так, іноді використання прямого JDBC - це шлях, спеціально для масових / пакетних процесів.
Lluis Martinez

Відносини @OneToMany викликають мої проблеми. Якщо я якось міг би виконати функцію сукупності Oracle LISTAGG в Hibernate, щоб об'єднати кілька значень в одне значення, то я можу скинути приєднання та замінити їх підзапитом.
toongeorges

2

Ви можете легко використовувати для цього сторінки.

    @QueryHints({ @QueryHint(name = "org.hibernate.cacheable", value = "true") })
    @Query("select * from a_table order by a_table_column desc")
    List<String> getStringValue(Pageable pageable);

вам потрібно передати new PageRequest(0, 1)для отримання записів і зі списку отримати перший запис.


2

Оскільки це дуже поширене питання, я написав цю статтю , на якій ґрунтується ця відповідь.

Методи setFirstResultі setMaxResults Queryметоди

Для JPA і Hibernate Query, то setFirstResultметод є еквівалентом OFFSET, а setMaxResultsметод є еквівалентом LIMIT:

List<Post> posts = entityManager
.createQuery(
    "select p " +
    "from Post p " +
    "order by p.createdOn ")
.setFirstResult(10)
.setMaxResults(10)
.getResultList();

LimitHandlerабстракція

Hibernate LimitHandlerвизначає логіку розбиття на певну базу даних, і, як показано на наступній схемі, Hibernate підтримує безліч параметрів сторінки для конкретних баз даних:

<code> LimitHandler </code> реалізації

Тепер, залежно від основної системи реляційних баз даних, яку ви використовуєте, вищезазначений запит JPQL використовуватиме належний синтаксис сторінки розбиття.

MySQL

SELECT p.id AS id1_0_,
       p.created_on AS created_2_0_,
       p.title AS title3_0_
FROM post p
ORDER BY p.created_on
LIMIT ?, ?

PostgreSQL

SELECT p.id AS id1_0_,
       p.created_on AS created_2_0_,
       p.title AS title3_0_
FROM post p
ORDER BY p.created_on
LIMIT ?
OFFSET ?

SQL Server

SELECT p.id AS id1_0_,
       p.created_on AS created_on2_0_,
       p.title AS title3_0_
FROM post p
ORDER BY p.created_on
OFFSET ? ROWS 
FETCH NEXT ? ROWS ONLY

Oracle

SELECT *
FROM (
    SELECT 
        row_.*, rownum rownum_
    FROM (
        SELECT 
            p.id AS id1_0_,
            p.created_on AS created_on2_0_,
            p.title AS title3_0_
        FROM post p
        ORDER BY p.created_on
    ) row_
    WHERE rownum <= ?
)
WHERE rownum_ > ?

Перевага використання setFirstResultі в setMaxResultsтому , що Hibernate може генерувати бази даних конкретних посторінковий синтаксису для будь-яких підтримуваних реляційних баз даних.

І ви не обмежені лише запитами JPQL. Ви можете використовувати setFirstResultі setMaxResultsметод сім для нативних запитів SQL.

Рідні запити SQL

Вам не доведеться жорстко кодувати базові сторінки для використання під час використання нативних SQL-запитів. Hibernate може додати це до ваших запитів.

Отже, якщо ви виконуєте цей SQL-запит на PostgreSQL:

List<Tuple> posts = entityManager
.createNativeQuery(
    "SELECT " +
    "   p.id AS id, " +
    "   p.title AS title " +
    "from post p " +
    "ORDER BY p.created_on", Tuple.class)
.setFirstResult(10)
.setMaxResults(10)
.getResultList();

Зимова сплячка перетворить її наступним чином:

SELECT p.id AS id,
       p.title AS title
FROM post p
ORDER BY p.created_on
LIMIT ?
OFFSET ?

Класно, правда?

Поза базування на основі SQL

Пагинація добре, коли ви можете проіндексувати критерії фільтрування та сортування. Якщо ваші вимоги до сторінок передбачають динамічну фільтрацію, це набагато кращий підхід використовувати рішення з інвертованим індексом, наприклад ElasticSearch.

Перегляньте цю статтю для отримання більш детальної інформації.


1

String hql = "select userName from AccountInfo order by points desc 5";

Це працювало для мене без використання setmaxResults();

Просто введіть максимальне значення в останньому (у цьому випадку 5) без використання ключового слова limit. : P


7
Гм ... не надто впевнений у цьому, [потрібне цитування] це може бути випадковістю і може раптом перестати працювати в новій версії сплячого режиму.
Konrad 'ktoso' Малавські

4
Будь ласка, зверніться до офіційного документа.
Робіть Nhu Vy

1
Це не працює для мене у
сплячій

1
Не працює, також не підтримує сплячий режим 4.x.
Фаїз Акрам

0

Моє зауваження полягає в тому, що навіть у вас є обмеження в HQL (сплячий режим 3.x), воно буде викликати помилку розбору або просто ігнорувати. (якщо у вас замовлення на + desc / asc перед лімітом, воно буде ігноровано; якщо у вас немає desc / asc перед лімітом, це призведе до помилки розбору)


0

Якщо можна керувати лімітом у цьому режимі

public List<ExampleModel> listExampleModel() {
    return listExampleModel(null, null);
}

public List<ExampleModel> listExampleModel(Integer first, Integer count) {
    Query tmp = getSession().createQuery("from ExampleModel");

    if (first != null)
        tmp.setFirstResult(first);
    if (count != null)
        tmp.setMaxResults(count);

    return (List<ExampleModel>)tmp.list();
}

Це дійсно простий код для обробки обмеження чи списку.


0
Criteria criteria=curdSession.createCriteria(DTOCLASS.class).addOrder(Order.desc("feild_name"));
                criteria.setMaxResults(3);
                List<DTOCLASS> users = (List<DTOCLASS>) criteria.list();
for (DTOCLASS user : users) {
                System.out.println(user.getStart());
            }

0

Потрібно написати власний запит, зверніться до цього .

@Query(value =
    "SELECT * FROM user_metric UM WHERE UM.user_id = :userId AND UM.metric_id = :metricId LIMIT :limit", nativeQuery = true)
List<UserMetricValue> findTopNByUserIdAndMetricId(
    @Param("userId") String userId, @Param("metricId") Long metricId,
    @Param("limit") int limit);

0

Ви можете використовувати нижче запит

NativeQuery<Object[]> query = session.createNativeQuery(select * from employee limit ?)
query.setparameter(1,1);

0

Нижче фрагмент використовується для виконання обмежень запиту за допомогою HQL.

Query query = session.createQuery("....");
query.setFirstResult(startPosition);
query.setMaxResults(maxRows);

Ви можете отримати демо-програму за цим посиланням .


-1
@Query(nativeQuery = true,
       value = "select from otp u where u.email =:email order by u.dateTime desc limit 1")
public List<otp> findOtp(@Param("email") String email);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.