【问题标题】:How do you do a limit query in JPQL or HQL?如何在 JPQL 或 HQL 中进行限制查询?
【发布时间】:2010-11-17 09:43:15
【问题描述】:

在 Hibernate 3 中,有没有办法在 HQL 中执行以下 MySQL 限制的等效操作?

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

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

【问题讨论】:

  • 我正在使用Hibernate-5.0.12。这仍然不可用吗?正如@Rachel 在@skaffman 的回答中所注意到的那样,获得一百万条左右的记录然后对其应用过滤器-setMaxResults 将非常繁重。

标签: java hibernate hql hibernate3


【解决方案1】:

如果您不想在 Query 对象上使用 setMaxResults(),那么您可以随时恢复使用普通 SQL。

【讨论】:

  • 这并不是那么令人兴奋。
  • 我也不觉得 HQL 令人兴奋。为什么不在您的数据库服务器上编写一个应用限制的视图,然后让 HQL 查看该视图:P
  • 这只是其中之一,虽然 SQL 对于每个查询都比 HQL 容易得多,但创建视图和编写原生 SQL 往往不太适合重构。我尽量避免它。真正的问题是我无论如何都写错了我的 MySQL 查询,并认为 setMaxResults 很奇怪。不是。
  • 如果您尝试在不同的 DBMS 供应商之间切换,痛苦正在等着您。
  • 我没有使用限制,而是使用可分页来避免额外的数据
【解决方案2】:

This was posted 几年前在 Hibernate 论坛上被问及为什么这在 H​​ibernate 2 中有效但在 Hibernate 3 中无效:

限制是从不受支持的子句 在 HQL 中。你打算使用 setMaxResults().

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

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

【讨论】:

  • 我认为 Hibernate 3 的方法更正确。您对 Hibernate 的使用意味着与数据库无关,因此您应该以抽象的方式来做这些事情。
  • 我同意,但是当这样删除功能时,迁移会让人头疼。
  • 但使用 setMaxResults,首先运行查询,然后在结果集上调用 setMaxResults,这将从结果集中获取有限数量的结果行并将其显示给用户,在我的情况下,我有 300 万被查询的记录,然后调用 setMaxResults 来设置 50 条记录,但我不想这样做,而查询本身我想查询 50 条记录,有没有办法做到这一点?
  • 我知道的旧帖子。我完全同意瑞秋的观点。使用 NHibernate(Hibernate 的 .Net 端口),我最近从 2 升级到 3,同样的事情,top X 现在抛出解析器错误。但是,当我在查询中添加 setMaxResults 时,它确实在生成的 SQL 中生成了一个 TOP X(使用 MsSql2008Dialect)。这很好。
  • @Rachel 使用setMaxResults hibernate 会将limit 部分附加到查询中。它不会得到所有的结果。您可以通过启用来检查它产生的查询:<property name="show_sql">true</property>
【解决方案3】:

如果您不想使用 setMaxResults,您也可以使用 Query.scroll 代替 list,并获取您想要的行。例如对分页很有用。

【讨论】:

  • 谢谢,接受的答案并没有解决我的问题,因为setMaxResults() 首先加载内存中的每条记录,然后创建一个子列表,当有数十万或更多记录时,服务器崩溃,因为内存不足。但是,我可以通过 QueryImpl hibernateQuery = query.unwrap(QueryImpl.class) 从 JPA 类型查询转到 Hibernate 查询,然后我可以按照您的建议使用 scroll() 方法。
  • 至少对于 Oracle 方言,这是不正确的(Hibernate 使用 ROWNUM 虚拟列)。也许这取决于驱动程序。其他 DB 有 TOP 功能。
  • 我的查询正在使用连接提取。这会导致 Hibernate 警告“firstResult/maxResults specified with collection fetch; apply in memory”。因此,通过连接提取,Hibernate 将完整的集合加载到内存中。由于性能原因,放弃连接是没有选择的。当我使用 ScrollableResults 时,我可以更好地控制哪些记录被加载到内存中。我无法使用单个 ScrollableResults 加载所有记录,因为这也会导致内存不足。我正在尝试使用不同的 ScrollableResults 加载多个页面。如果这不起作用,我将使用 SQL。
  • 这很奇怪,我从来没有遇到过。是的,有时直接使用 JDBC 是可行的方法,特别是对于大规模/批处理过程。
  • @OneToMany 关系导致了我的问题。如果我可以在 Hibernate 中执行 Oracle 聚合函数 LISTAGG 以将多个值连接到单个值,那么我可以删除连接并用子查询替换它们。
【解决方案4】:

我的观察是,即使您在 HQL(hibernate 3.x)中有限制,它也会导致解析错误或被忽略。 (如果limit前有order by + desc/asc,会被忽略,如果limit前没有desc/asc,会导致解析错误)

【讨论】:

    【解决方案5】:

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

    这对我有用,但不使用 setmaxResults();

    只需在最后一个(在本例中为 5)提供最大值,而不使用关键字 limit。 :P

    【讨论】:

    • 嗯...不太确定,[需要引用]这可能是个意外,可能会突然停止在新版本的休眠中工作。
    • 请参考官方文档
    • 这在 Hibernate Version 5.2.12 Final 中对我不起作用
    • 它不工作,也不支持hibernate 4.x。
    【解决方案6】:
     // SQL: SELECT * FROM table LIMIT start, maxRows;
    
    Query q = session.createQuery("FROM table");
    q.setFirstResult(start);
    q.setMaxResults(maxRows);
    

    【讨论】:

    • 我最喜欢这个,因为在这个答案中实际上提到了setFirstResult,而在这里和其他地方他们只是说setMaxResults这个和setMaxResults那个没有提到如何设置偏移量。
    【解决方案7】:

    如果可以在此模式下管理限制

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

    这是处理限制或列表的非常简单的代码。

    【讨论】:

      【解决方案8】:
      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());
                  }
      

      【讨论】:

        【解决方案9】:

        你需要写一个原生查询,参考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);
        

        【讨论】:

        • 感谢您添加纯界面解决方案
        【解决方案10】:
        @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);
        

        【讨论】:

          【解决方案11】:

          您可以轻松地为此使用分页。

              @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)来获取记录并从列表中获取第一条记录。

          【讨论】:

          • 根据问题“如果可能,我不想使用 setMaxResults”,您提供了更好的解决方案,只需将Pageable 作为方法中的参数并调用它发送“new PageRequest(0, 20)”我得到了前 20 条记录。谢谢。
          【解决方案12】:

          setFirstResultsetMaxResults Query 方法

          对于 JPA 和 Hibernate QuerysetFirstResult 方法等效于 OFFSETsetMaxResults 方法等效于 LIMIT:

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

          LimitHandler 抽象

          Hibernate LimitHandler 定义了特定于数据库的分页逻辑,如下图所示,Hibernate 支持许多特定于数据库的分页选项:

          现在,根据您使用的底层关系数据库系统,上述 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_ > ?
          

          使用setFirstResultsetMaxResults 的优势在于Hibernate 可以为任何受支持的关系数据库生成特定于数据库的分页语法。

          而且,您不仅限于 JPQL 查询。原生 SQL 查询可以使用setFirstResultsetMaxResults 方法七。

          原生 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)是一种更好的方法。

          【讨论】:

            【解决方案13】:

            您可以使用以下查询

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

            【讨论】:

              【解决方案14】:

              sn-p下面是使用HQL进行limit查询的。

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

              您可以通过link获取演示应用程序。

              【讨论】:

                猜你喜欢
                • 2015-10-21
                • 1970-01-01
                • 2011-04-07
                • 2020-12-05
                • 2018-09-03
                • 2014-01-07
                • 1970-01-01
                相关资源
                最近更新 更多