在条件查询规范中合并不同类型的规范 [英] Merge specifications of different types in Criteria Query Specifications

查看:54
本文介绍了在条件查询规范中合并不同类型的规范的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 Activity 实体,该实体与 Event 实体及其相应的元模型处于 @ManyToOne 关系中- Activity_ Event _ 是由JPA模型生成器生成的.

I'm having an Activity entity which is in @ManyToOne relationship with Event entity and their corresponding metamodels - Activity_ and Event_ were generated by JPA model generator.

我已经创建了专门的类 ActivitySpecifications EventSpecifications .这些类仅包含返回 Specification 的静态方法.例如:

I've created specialized classes ActivitySpecifications and EventSpecifications. Those classes contain only static methods whose return Specification. For example:

public interface EventSpecifications {

   static Specification<Event> newerThan(LocalDateTime date) {
       return (root, cq, cb) -> cb.gt(Event_.date, date);
   }

  ...
}

因此,当我要构建匹配多个规范的查询时,可以在 JpaSpecificationExecutor< Event> 存储库上使用 findAll 执行以下语句.

so when I want to build query matching multiple specifications, I can execute following statement using findAll on JpaSpecificationExecutor<Event> repository.

EventSpecifications.newerThan(date).and(EventSpecifications.somethingElse())

ActivitySpecifications 示例:

static Specification<Activity> forActivityStatus(int status) { ... }

如何使用 ActivitySpecifications 中的 EventSpecifications ?我的意思是喜欢不同类型的合并规范.很抱歉,但是我什至不知道如何正确地提出问题,但是有一个简单的例子:

How do I use EventSpecifications from ActivitySpecifications ? I mean like merge specifications of different type. I'm sorry, but I don't even know how to ask it properly, but theres simple example:

我要选择状态为:status activity.event.date 大于:date

I want to select all activities with status = :status and where activity.event.date is greater than :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
}

这样可能吗?

我想到的最接近的东西是使用以下内容:

The closest thing that comes to my mind is using the following:

return forActivityStatus(status)
             .and((root, cq, cb) -> cb.isTrue(EventSpecifications.newerThan(date).toPredicate(???, cq, cb));

其中 ??? 需要 Root< Event> ,但是我只能使用 root.get获得 Path< Event> (Activity_.event).

where ??? requires Root<Event>, but I can only get Path<Event> using root.get(Activity_.event).

推荐答案

在规范的基本形式中,仅当规范引用相同的根时,规范才可以组合.

In its basic form, specifications are designed to be composable only if they refer to the same root.

但是,引入自己的接口应该很容易,该接口可以容易地转换为 Specification ,并且允许引用任意实体的规范组成.

However, it shouldn't be too difficult to introduce your own interface which is easily convertible to Specification and which allows for specifications refering to arbitrary entities to be composed.

首先,添加以下界面:

@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);
}

然后,您按如下所示重写规格:

You then rewrite the specifications as follows:

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);
    }
}

完成此操作后,您应该可以通过以下方式编写规范:

Once you've done that, you should be able to compose specifications in the following manner:

activityRepository.findAll(
    forActivityStatus(ActivityStatus.IN_PROGRESS).atRoot()
    .and(newerThan(LocalDateTime.of(2019, Month.AUGUST, 1, 0, 0)).atPath(Activity_.events))
)

上述解决方案的另一个优点是,将指定 WHERE 条件与指定路径分离开,因此,如果 Activity Event 之间存在多个关联,代码>,您可以为所有代码重用 Event 规范.

The above solution has the additional advantage in that specifying WHERE criteria is decoupled from specifying paths, so if you have multiple associations between Activity and Event, you can reuse Event specifications for all of them.

这篇关于在条件查询规范中合并不同类型的规范的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆