如何在JPQL或HQL中进行限制查询?


239

在Hibernate 3中,有没有办法等效于以下HQL中的MySQL限制?

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

如果可能的话,我不想使用setMaxResults。在旧版本的Hibernate / HQL中,这肯定是可能的,但似乎已经消失了。


2
我正在使用Hibernate-5.0.12。这还不可用吗?要获得一百万个左右的记录,然后对其应用过滤器,setMaxResults就像@Rachel在@skaffman的答案中所注意到的那样,这真的很沉重。
拉杰夫·兰扬

Answers:


313

几年前,当有人问到为什么它在Hibernate 2中有效但在Hibernate 3中无效时,此消息发布在Hibernate论坛上:

在HQL中,从不支持Limit 子句。您应该使用setMaxResults()。

因此,如果它在Hibernate 2中工作,那似乎是巧合,而不是设计。我认为这是因为Hibernate 2 HQL解析器将替换它识别为HQL的查询的位,而其余部分保持不变,因此您可以使用一些本机SQL。但是,Hibernate 3具有适当的AST HQL解析器,并且它的宽容度要低得多。

我认为Query.setMaxResults()确实是您唯一的选择。


3
我认为Hibernate 3的方法更正确。您对Hibernate的使用是与数据库无关的,因此您必须以抽象的方式来做这些事情。
马特b

6
我同意,但是当像这样删除功能时,这使迁移成为繁琐的事情。
skaffman

52
但是使用setMaxResults时,首先运行查询,然后在您调用的结果集上执行,setMaxResults这将从结果集中获取有限数量的结果行并将其显示给用户,在我的情况下,我要查询300万条记录,然后调用setMaxResults进行设置50条记录,但我不想这样做,而查询本身我想查询50条记录,有没有办法做到这一点?
雷切尔

2
我知道的旧帖子。我完全同意雷切尔的看法。使用NHibernate(Hibernate的.Net端口),我最近从2升级到3,同样的事情,top X现在抛出了解析器错误。但是,当我在查询上添加setMaxResults时,它的确在生成的SQL中生成了TOP X(使用MsSql2008Dialect)。很好
Thierry_S 2013年

17
@Rachel与setMaxResults冬眠会将limit部分追加到查询。它不会获得所有结果。您可以通过以下方式检查它产生的查询:<property name="show_sql">true</property>
Andrejs 2014年

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
那并不是真的那么令人兴奋。
09年

8
我也不认为HQL令人兴奋。为什么不在您的数据库服务器上写一个应用限制的视图,然后让HQL来查看该视图:P
pjp

1
这只是其中之一,尽管对于每个查询而言,SQL都比HQL容易得多,但是创建视图和编写本机SQL对于重构而言并不是那么好。我会尽量避免这种情况。实际的实际问题是我无论如何都写错了我的MySQL查询,并认为setMaxResults很奇怪。不是。
09年

如果您尝试在不同的DBMS供应商之间进行切换,那么痛苦就在等待着您。
奥尔贡·卡亚

5

如果不想使用setMaxResults,也可以使用Query.scroll而不是list,并获取所需的行。例如,对于分页很有用。


谢谢,接受的答案对我来说没有解决问题,因为setMaxResults()首先将每个记录加载到内存中,然后创建一个子列表,当有数十万个或更多记录时,该子列表会使服务器崩溃,因为它内存不足。但是,我可以从JPA类型查询转到Hibernate查询QueryImpl hibernateQuery = query.unwrap(QueryImpl.class),然后可以scroll()按照您的建议使用该方法。
toongeorges

至少对于Oracle方言,这不是正确的(Hibernate使用ROWNUM虚拟列)。也许取决于驱动程序。其他DB具有TOP功能。
路易斯·马丁内斯

我的查询正在使用联接提取。这将导致休眠警告“使用集合获取指定的firstResult / maxResults;在内存中应用”。因此,通过联接获取,Hibernate会将完整的集合加载到内存中。由于性能原因,删除联接是不可行的。使用ScrollableResults时,我可以更好地控制将哪些记录加载到内存中。我无法使用单个ScrollableResults加载所有记录,因为这也会导致内存不足。我正在尝试加载具有不同ScrollableResults的多个页面。如果这不起作用,我将使用SQL。
toongeorges

太奇怪了,我从未遇到过。是的,有时使用直接JDBC是一种方法,特别是对于大规模/批处理过程。
Lluis Martinez'6

@OneToMany关系导致了我的问题。如果可以通过某种方式在Hibernate中执行Oracle聚合函数LISTAGG以将多个值连接为单个值,则可以删除联接并用子查询替换它们。
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

由于这是一个非常常见的问题,因此我写了 这篇文章,此答案基于该文章

setFirstResultsetMaxResults 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服务器

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

甲骨文

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查询。您可以将setFirstResultand setMaxResults方法7用于本机SQL查询。

本机SQL查询

使用本机SQL查询时,不必对特定于数据库的分页进行硬编码。Hibernate可以将其添加到您的查询中。

因此,如果您要在PostgreSQL上执行此SQL查询:

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();

Hibernate将对其进行如下转换:

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
嗯...对此不太确定,[需要引用]这可能是个意外,可能会突然停止在新版本的休眠模式下工作。
2011年

4
请参考正式文件。
做Nhu Vy

1
在Hibernate版本5.2.12最终版中,这不适用于我
jDub9

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

您需要编写本机查询,请参考this

@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

以下代码段用于使用HQL执行限制查询。

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

您可以在此链接上获得演示应用程序。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.