条件API限制导致子查询

时间:2016-05-12 12:40:58

标签: java jpa-2.0 criteria-api

我正在尝试编写类似于

的查询
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

施加限制

2 个答案:

答案 0 :(得分:8)

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

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

JPA API 中,简短的回答是你不能......在你提出的问题中,你建议使用Criteria API(以及SubQuery)。但是,直到您实际致电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();

答案 1 :(得分:0)

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

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

以下是展示带有子查询的限制/偏移量用例的文档:https://persistence.blazebit.com/documentation/1.2/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', ...)