【问题标题】:How to impose LIMIT on sub-query of JPA query?如何对 JPA 查询的子查询施加 LIMIT?
【发布时间】:2012-01-23 05:43:01
【问题描述】:

是否可以对 JPA 查询中的子查询施加 LIMIT 限制?

我在纯 SQL 中有以下查询

select * from ipinfo 
where RangeEnd < (select RangeStart from ipinfo where RangeStart >= 1537022421 order by RangeStart asc limit 1) and (1537022421 <= RangeEnd)
ORDER BY RangeEnd desc
limit 1

将其直接转换为 JPQL 我会有类似的东西

select obj from IpInfo obj
where obj.rangeEnd < (select obj2.rangeStart from IpInfo obj2 where obj2.rangeStart >= ?1 order by obj2.rangeStart asc limit 1) and (?1 <= obj.rangeEnd)
ORDER BY obj.rangeEnd desc
limit 1

因为我不能在 JPQL 中使用 LIMIT,所以我必须在上面使用 setMaxResults(1)。但是子查询呢?

更新:

我现在决定使用@NamedNativeQuery,但它是特定于数据库的代码。如果你们能提出纯 JPA 解决方案,我将不胜感激。

【问题讨论】:

    标签: jpa subquery jpql


    【解决方案1】:

    我不知道如何使用 JPQL 来做到这一点,但您可能可以使用 Criteria API 来处理它,至少我很确定我们可以使用 Hibernate 标准子查询来做到这一点,所以我想 JPA 也可以,即使JPA 标准 api 对我来说似乎有点混乱。

    检查这个: JPA 2.0, Criteria API, Subqueries, In Expressions

    无论如何,您甚至不需要对子查询进行限制。

    您的原始查询: 从 ipinfo 中选择 RangeStart,其中 RangeStart >= 1537022421 order by RangeStart asc limit 1 您似乎想要 ipinfo 列表的最小 RangeStart,它刚好高于给定值。为此制作了 min 函数。

    您可以简单地使用这样的子查询:

    select min(RangeStart) from ipinfo where RangeStart >= 1537022421
    

    即使您需要在子查询中返回其他 ipinfo,也可以这样做:

    select RangeEnd, anything,blabla from ipinfo where RangeStart = (
        select min(RangeStart) from ipinfo where RangeStart >= 1537022421
    )
    

    【讨论】:

    • 见鬼!我没有想到min。非常感谢!
    【解决方案2】:

    目前无法通过 JPQL 添加LIMIT (maxResults) 或OFFSET (startIndex),也无法在 JPA 子查询中添加。有一个开放的 JPA 票可以添加此功能:

    https://github.com/eclipse-ee4j/jpa-api/issues/88

    【讨论】:

      【解决方案3】:

      对此没有 JPA 解决方案。您可以使用在 SQL 查询生成期间运行的自定义 SQL 函数。所有 JPA 提供程序都以一种或另一种方式支持类似的东西。

      如果您不想自己实现,甚至不想使用适当的 API 来构建此类查询,我只能向您推荐我实现的名为 Blaze-Persistence 的库。

      这里是展示带有子查询的限制/偏移用例的文档:https://persistence.blazebit.com/documentation/core/manual/en_US/index.html#pagination

      使用查询构建器 API,您的查询可能如下所示:

      criteriaBuilderFactory.create(entityManager, IpInfo.class)
        .where("rangeEnd").lt()
          .from(IpInfo.class, "subInfo")
          .select("subInfo.rangeStart")
          .where("subInfo.rangeStart").geExpression("1537022421")
          .orderByAsc("subInfo.rangeStart")
          .setMaxResults(1)
        .end()
        .where("1537022421").leExpression("rangeEnd")
        .orderByDesc("rangeEnd")
        .setMaxResults(1)
      

      本质上归结为使用 Blaze-Persistence 注册的LIMIT SQL 函数。因此,当您使用 EntityManagerFactory 引导 Blaze-Persistence 时,您甚至应该能够像这样使用它

      entityManager.createQuery(
          "select * from ipinfo where RangeEnd < LIMIT((" +
          "  select RangeStart " +
          "  from ipinfo " +
          "  where RangeStart >= 1537022421 " +
          "  order by RangeStart asc" +
          "),1) " +
          "and (1537022421 <= RangeEnd)" +
          "ORDER BY RangeEnd desc"
      ).setMaxResults(1)
      

      如果您使用的是 EclipseLink,则此类函数的调用约定类似于 OPERATOR('LIMIT', ...)

      【讨论】:

        猜你喜欢
        • 2014-09-04
        • 2015-08-05
        • 2011-01-23
        • 1970-01-01
        • 2014-01-17
        • 2015-06-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多