spring - 在 Criteria Query Specifications 中合并不同类型的规范
问题描述
我有一个与Activity
实体及其相应元模型相关的实体 -并且由 JPA 模型生成器生成。@ManyToOne
Event
Activity_
Event_
我创建了专门的课程ActivitySpecifications
和EventSpecifications
. 这些类仅包含其 return 的静态方法Specification
。例如:
public interface EventSpecifications {
static Specification<Event> newerThan(LocalDateTime date) {
return (root, cq, cb) -> cb.gt(Event_.date, date);
}
...
}
因此,当我想构建匹配多个规范的查询时,我可以使用存储库执行以下findAll
语句JpaSpecificationExecutor<Event>
。
EventSpecifications.newerThan(date).and(EventSpecifications.somethingElse())
和ActivitySpecifications
例子:
static Specification<Activity> forActivityStatus(int status) { ... }
我如何使用EventSpecifications
from ActivitySpecifications
?我的意思是像合并不同类型的规范。对不起,但我什至不知道如何正确地问它,但有一个简单的例子:
我想选择所有状态 =:status
并且activity.event.date
大于的活动:date
static Specification<Activity> forStatusAndNewerThan(int status, LocalDateTime date) {
return forActivityStatus(status)
.and((root, cq, cb) -> root.get(Activity_.event) ....
// use EventSpecifications.newerThan(date) somehow up there
}
这样的事情可能吗?
我想到的最接近的事情是使用以下内容:
return forActivityStatus(status)
.and((root, cq, cb) -> cb.isTrue(EventSpecifications.newerThan(date).toPredicate(???, cq, cb));
哪里???
需要Root<Event>
,但我只能得到Path<Event>
使用root.get(Activity_.event)
。
解决方案
在其基本形式中,规范被设计为仅当它们引用相同的根时才可组合。
但是,引入您自己的接口应该不难,该接口易于转换,Specification
并允许组合引用任意实体的规范。
首先,添加以下接口:
@FunctionalInterface
public interface PathSpecification<T> {
default Specification<T> atRoot() {
return this::toPredicate;
}
default <S> Specification<S> atPath(final SetAttribute<S, T> pathAttribute) {
// you'll need a couple more methods like this one for all flavors of attribute types in order to make it fully workable
return (root, query, cb) -> {
return toPredicate(root.join(pathAttribute), query, cb);
};
}
@Nullable
Predicate toPredicate(Path<T> path, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder);
}
然后重写规范如下:
public class ActivitySpecifications {
public static PathSpecification<Activity> forActivityStatus(ActivityStatus status) {
return (path, query, cb) -> cb.equal(path.get(Activity_.status), cb.literal(status));
}
}
public class EventSpecifications {
public static PathSpecification<Event> newerThan(LocalDateTime date) {
return (path, cq, cb) -> cb.greaterThanOrEqualTo(path.get(Event_.createdDate), date);
}
}
完成此操作后,您应该能够按以下方式编写规范:
activityRepository.findAll(
forActivityStatus(ActivityStatus.IN_PROGRESS).atRoot()
.and(newerThan(LocalDateTime.of(2019, Month.AUGUST, 1, 0, 0)).atPath(Activity_.events))
)
上述解决方案的额外优势在于,指定WHERE
标准与指定路径是分离的,因此如果您在Activity
和之间有多个关联Event
,则可Event
以为所有这些关联重用规范。
推荐阅读
- excel - 目标搜索功能中的“编译错误:需要对象”
- dll - 如何防止代码分析 dll 在生产服务器上发布?
- java - 我们可以在java中传递一个字符作为整数数组的索引吗
- excel - MS Excel TRUE/FALSE 公式未返回预期结果
- docker - 运行“ansible-container build”时出错
- python - 从 CSV 文件加载虹膜数据集?
- html - SmoothScroll、Safari 支持和响应式导航
- c# - InstaSharper 令牌
- asp.net-core - 如何在 ASP .NET Core 的 Area 中使用 css 文件或 js
- java - 计算给定整数元素数组中范围 * {0, ..., r} 中整数的出现次数