【问题标题】:Criteria API limit results in subqueryCriteria API 限制导致子查询
【发布时间】:2016-09-08 07:18:12
【问题描述】:

我正在尝试编写类似于

的查询
select * from Table a
 where a.parent_id in 
  (select b.id from Table b
   where b.state_cd = ?
   and rownum < 100)

使用Criteria API。我可以使用与https://stackoverflow.com/a/4668015/597419 类似的代码来实现对子查询没有rownum 限制的查询,但我似乎无法弄清楚如何对Subquery 施加限制

【问题讨论】:

  • 如果你不知道如何限制子查询,那么使用连接怎么样?
  • @Traubenfuchs 查询中应该有超过 100 个结果。将子查询视为父记录,而我希望将结果作为子查询返回。有些父母有多个孩子。我不知道如何使用联接为前 100 位父母找到孩子。
  • where 子句是动态的,通常多于表中的前 X 行。我应该在问题中提到这一点。
  • 可以用自己自定义的表达式接口实现docs.oracle.com/javaee/7/api/javax/persistence/criteria/…

标签: java jpa-2.0 criteria-api


【解决方案1】:

在 Hibernate 中,您可以添加实际的 SQL 限制,但值得注意的是,这将是 Oracle 特定的。如果你切换到 PostgreSQL,这会中断,你需要 LIMIT 100 代替。

DetachedCriteria criteria = DetachedCriteria.forClass(Domain.class)
   .add(Restrictions.sqlRestriction("rownum < 100"));

在 JPA API 中,简短的回答是您不能...在您提出的问题中,您建议使用 Criteria API(以及子查询)。但是,直到您真正调用EntityManager.createQuery(criteriaQuery),您才会得到一个TypedQuery,您可以在其中指定maxResult 值。

也就是说,您可以将其分成 2 个查询,第一个查询是您获得内部选择结果(最多 100 个),然后是第二个 Criteria,您可以将结果列表放入 in():

// inner query
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<YourClass> innerCriteriaQuery = cb.createQuery(YourClass.class);
Root<YourClass> yourClass = innerCriteriaQuery.from(YourClass.class);

innerCriteriaQuery.select(yourClass).where(
    cb.equal(yourClass.get(YourClass_.stateCode), someStateValue));

// list of 100 parent ids
List<YourClass> list = em.createQuery(innerCriteriaQuery).setMaxResults(100).getResultList();

// outer query
CriteriaQuery<YourClass> criteriaQuery = cb.createQuery(YourClass.class);
Root<YourClass> yourClass = criteriaQuery.from(YourClass.class);

criteriaQuery.select(yourClass).where(
    cb.in(yourClass.get(YourClass_.parentId)).value(list);

return em.createQuery(criteriaQuery).getResultList();

【讨论】:

  • 这是使用 Hibernate 的 API,而不是 Criteria API
  • 我的监督...还添加了 API 特定信息。可悲的是,没有办法用一个 Criteria 来做到这一点。
  • 这很有帮助,但对我来说仍然不是一个理想的答案。有时这个列表会超过 1000 个项目,不幸的是,Oracle 在in 子句中限制只有 1000 个项目。我仍然没有在 API 中看到任何可以让我在子查询中使用 rownum 的内容,所以我可能不得不走这条路并执行多个查询,将每个查询限制为 1000 个项目。
  • 我不清楚你的商业案例,所以也许更多细节会有所帮助。无论如何,如果您的内部选择似乎是纯 JPA 的非选项,则尝试限制大小......那么您是否有特定原因限制您的结果集?如果您可以移动第一个结果和最大结果 (query.setFirstResult(offset).setMaxResults(max)),那么分页策略可能会起作用,这样您就可以分块摄取结果?
  • 这个查询会产生一个太大的IN(...),一些数据库对IN子句有限制。
【解决方案2】:

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

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

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

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

criteriaBuilderFactory.create(entityManager, SomeEntity.class)
  .where("id").in()
    .select("subEntity.id")
    .from(SomeEntity.class, "subEntity")
    .where("subEntity.state").eq(someValue)
    .orderByAsc("subEntity.id")
    .setMaxResults(100)
  .end()

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

entityManager.createQuery(
    "select * from SomeEntity where id IN(LIMIT((" +
    "  select id " +
    "  from SomeEntity subEntity " +
    "  where subEntity.state = :someParam " +
    "  order by subEntity.id asc" +
    "),1)) "
)

或类似的东西

criteriaQuery.where(
   cb.in(yourClass.get(YourClass_.parentId)).value(cb.function("LIMIT", subquery));

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

【讨论】:

    猜你喜欢
    • 2021-02-03
    • 1970-01-01
    • 1970-01-01
    • 2012-02-26
    • 1970-01-01
    • 1970-01-01
    • 2011-10-17
    • 2015-03-04
    • 1970-01-01
    相关资源
    最近更新 更多