java - 对两个条件查询使用相同的谓词
问题描述
我想使用相同的数组运行一对查询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();
这行得通。我们得到了我们想要的,谓词是正确的等等。
但是,使用相同谓词创建另一个查询来确定计数会失败。我尝试了两种不同的方法:
(1) 重用Root
:
CriteriaQuery<Long> countQuery = criteriaBuilder.createQuery(Long.class);
countQuery.select(criteriaBuilder.count(entityRoot));
countQuery.where(predicates);
long count = entityManager.createQuery(countQuery).getSingleResult();
这不起作用并给了我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();
现在我们得到一个不同的错误: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
是Root
使用与CriteriaQuery<Long>
.
所以,如果这两种方式都不起作用,我将如何重新使用相同的数组Predicate
?我真的必须用第二个重新创建所有这些Root
吗?这对我来说似乎太过分了,尤其是因为他们都是Root<FooEntity>
. 我觉得必须有更好的方法。
解决方案
您必须为每个查询创建新Root
的CriteriaQuery
和。CriteriaBuiler
如果您使用Spring,则可以选择SpecificationPredicate
以使其可重用。否则,您可以像这样创建自己的Specification
功能界面
@FunctionalInterface
public interface Specification<T> {
Predicate toPredicate(
Root<T> root,
CriteriaQuery<?> query,
CriteriaBuilder criteriaBuilder);
}
用法:
public Specification<FooEntity> createSpecification(YourParameters parameters) {
return (root, query, criteriaBuilder) -> {
Predicate fullPredicate;
// create predicates using parameters, root, query, criteriaBuilder
// and concatenate them into one: fullPredicate = predicate.and(anotherPredicate);
return fullPredicate;
};
}
然后你可以通过这种方式为每个查询获取谓词
Predicate predicate = createSpecification(parameters)
.toPredicate(entityRoot, entityQuery, criteriaBuilder);
最好的方法是为每个规范创建具有单独方法的实用程序类,并使用Specification.and
方法将它们组合起来
推荐阅读
- java - spring boot 应用中根据请求头参数动态切换属性文件
- android - 等到所有权限都被授予
- java - Azure CLI 和 KubeCTL 的 Java 方式
- c# - 客户端 Blazor 中的自定义 IConfiguration 服务
- c++ - 如何避免这两个相互关联的线程之间的竞争条件
- google-apps-script - 滚动 Google Sheet 的脚本有效,直到我激活另一个单元格。为什么?
- phpunit - 于是,一个变种人逃了出来。怎么办?
- python - 创建/生成随机数的函数
- firebase - 将计划函数部署到 Firebase 函数
- xamarin.forms - ZXing.Mobile.Forms 因 UNHANDED 异常而崩溃