对两个条件查询使用相同的谓词

Dep*_*sio 4 java hibernate jpa criteria-api

我想使用相同的数组运行一对查询Predicate:一个用于计算记录,一个用于获取某一页记录。对我来说,这似乎是一个非常正常的用例,所以一定有一个好的方法来做到这一点,但我还没有找到它。

这是获取实体的部分:

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
EntityType<ENTITY> entityType = entityManager.getMetamodel().entity(FooEntity.class);
CriteriaQuery<FooEntity> entityQuery = criteriaBuilder.createQuery(FooEntity.class);
Root<FooEntity> entityRoot = entityQuery.from(FooEntity.class);

// Use the criteria builder, root, and type to create some predicates.
Predicate[] predicates = createPredicates(criteriaBuilder, entityRoot, entityType );

// Fetch the entities.
entityQuery.select(entityRoot);
entityQuery.where(predicates);
List<FooEntity> entities = entityManager.createQuery(entityQuery)
    .setFirstResult(0) // Just get the first page
    .setMaxResults(50)
    .getResultList();
Run Code Online (Sandbox Code Playgroud)

这有效。我们得到了我们想要的,谓词是正确的等等。

但是,创建另一个查询来使用相同谓词确定计数会失败。我尝试了两种不同的方法:

(1) 重复使用Root

CriteriaQuery<Long> countQuery = criteriaBuilder.createQuery(Long.class);
countQuery.select(criteriaBuilder.count(entityRoot));
countQuery.where(predicates);
long count = entityManager.createQuery(countQuery).getSingleResult();
Run Code Online (Sandbox Code Playgroud)

这不起作用并给了我java.lang.IllegalStateException: No criteria query roots were specified。奇怪的是,因为我明确指定了Root,但也许我无法重复使用Root从不同的 创建的CriteriaQuery?好吧,让我们用同样的东西创建一个CriteriaQuery......

(2) 创建一个新的Root

CriteriaQuery<Long> countQuery = criteriaBuilder.createQuery(Long.class);
countQuery.select(criteriaBuilder.count(countQuery.from(FooEntity.class));
countQuery.where(predicates);
long count = entityManager.createQuery(countQuery).getSingleResult();
Run Code Online (Sandbox Code Playgroud)

现在我们得到一个不同的错误:org.hibernate.hql.internal.ast.QuerySyntaxException: Invalid path: 'generatedAlias1.fooProperty' [select count(generatedAlias0) from com.foo.FooEntity as generatedAlias0 where ( generatedAlias1.fooProperty = :param0 )]

查看创建的 HQL,似乎“from”子句设置了 a generatedAlias0,但“where”子句中的所有内容都引用了generatedAlias1。我的猜测是因为 的 数组Predicate是使用RootCriteriaQuery<Long>.

那么,如果这两种方法都不起作用,我将如何重新使用相同的数组Predicate?我真的必须用第二个重新创建所有这些吗Root?这对我来说似乎太过分了,尤其是因为他们都是Root<FooEntity>. 我觉得一定有更好的方法。

小智 5

您应该对两个 Root 元素使用相同的别名,例如:

Root<FooEntity> entityRoot = entityQuery.from(FooEntity.class);
entityRoot.alias("alias1") // Property alias

Predicate[] predicates = createPredicates(criteriaBuilder, entityRoot, entityType );

// Fetch the entities.
entityQuery.select(entityRoot);
entityQuery.where(predicates);
List<FooEntity> entities = entityManager.createQuery(entityQuery)
    .setFirstResult(0)
    .setMaxResults(50)
    .getResultList();


CriteriaQuery<Long> countQuery = builder.createQuery(Long.class);
Root<FooEntity> newRoot = countQuery.from(FooEntity.class);
newRoot.alias("alias1"); // Same property alias

countQuery.select(builder.count(newRoot));
countQuery.where(predicates);
Long count = em.createQuery(countQuery).getSingleResult();
Run Code Online (Sandbox Code Playgroud)

这样 JPA 就可以理解并使用相同的限制进行查询