Spring / Jhipster QueryService如何构建深度规范

Spring / Jhipster QueryService how to build a deep specification

我是使用 Jhispter 生成的 QueryService 的新手,我正在尝试使用它们来过滤实体列表。

当我过滤这个实体的字段时它工作正常,但现在我必须做一些更复杂的事情。

我有这个数据集:

我要做的是:

到目前为止我所做的是:

private Specification<Child> createSpecification(ChildCriteria criteria) {
    Specification<Child> specification = Specification.where(null);
    if (criteria != null) {
        if (criteria.getParentName() != null) {
            speficiation = specification.and(buildReferringEntitySpecification(criteria.getParentName(), Child_.parent, Parent_.name));
        }
        if (criteria.getGrandParentName() != null) {
            specification.and(buildJoinSpecification(criteria.getGrandParentName(), Child_.parent, Parent_.grandParent, GrandParent_.name));
        }
    }
}

Child、Parent、GrandParent 和 Child 标准的摘录:

@Entity
@Table(name = "Child")
public class Child extends EntityObject {
    @ManyToOne(fetch = FetchType.LAZY)
    @JsonIgnoreProperties("childs")
    @QueryInit("GrandParent")
    private Parent parent;
}


@Entity
@Table(name = "Parent")
public class Parent extends EntityObject {
    @Column(name = "name")
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    @JsonIgnoreProperties("parents")
    private GrandParent grandParent;

    @OneToMany(mappedBy = "Parent", fetch = FetchType.LAZY)
    private Set<Child> childs = new HashSet<>();
}


@Entity
@Table(name = "GrandParent")
public class GrandParent extends EntityObject {
    @Column(name = "name")
    private String name;

    @OneToMany(mappedBy = "GrandParent", fetch = FetchType.LAZY)
    private Set<Parent> parrents = new HashSet<>();
}



public class ChildCriteria implements Serializable {
    private StringFilter parentName;
    private StringFilter grandParentName;

    public StringFilter getParentName() {
        return parentName;
    }

    public void setParentName(StringFilter parentName) {
        this.parentName= parentName;
    }

    public StringFilter getGrandParentName() {
        return grandParentName;
    }

    public void setGrandParentName(StringFilter grandParentName) {
        this.grandParentName= grandParentName;
    }
}

parent 名称的过滤器有效,我想对盛大的 parent 名称做同样的事情。 对于那个,我尝试了一些我在 SO 上找到的技巧,比如 ,但没有任何效果。

根据 link,这是我目前的情况:

public class ExtendedQueryService<ENTITY> extends QueryService<ENTITY>{
    protected <REFERENCE, JOIN, FILTER extends Comparable<? super FILTER>> Specification<ENTITY> buildJoinSpecification(StringFilter filter, SingularAttribute<? super ENTITY, REFERENCE> reference, SingularAttribute<REFERENCE, JOIN> joinField, SingularAttribute<JOIN, FILTER> valueField) {
        Specification<ENTITY> result = Specification.where((Specification) null);
        if (filter.getContains() != null) {
            result = this.containsSpecification(reference, joinField, valueField, filter.getContains());
        }
        return result;
    }

    protected  <REFERENCE, JOIN, FILTER> Specification<ENTITY> containsSpecification(SingularAttribute<? super ENTITY, REFERENCE> reference, SingularAttribute<REFERENCE, JOIN> joinField, SingularAttribute<JOIN, FILTER> idField, String value) {
        return (root, query, builder) ->
            builder.equal(root.join(reference).join(joinField).get(idField), value);
    }
}

我在我的 ChildQueryService 中扩展了这个 class,我在其中使用了 buildJoinSpecification 方法。

如果我尝试使用它进行过滤,它 returns 是一个空列表。

好的,我在 Jhipster 文档中找到了一些东西。问题是我使用了 StringFilter,当我明白这一点时,我从 Jhipster 函数中修改了一些东西,如下所示:

扩展查询服务:

protected Specification<ENTITY> buildSpecification(StringFilter filter, Function<Root<ENTITY>, Expression<String>> metaclassFunction) {
        if (filter.getEquals() != null) {
            return equalsSpecification(metaclassFunction, filter.getEquals());
        } else if (filter.getIn() != null) {
            return valueIn(metaclassFunction, filter.getIn());
        } else if (filter.getNotIn() != null) {
            return valueNotIn(metaclassFunction, filter.getNotIn());
        } else if (filter.getContains() != null) {
            return likeUpperSpecification(metaclassFunction, filter.getContains());
        } else if (filter.getDoesNotContain() != null) {
            return doesNotContainSpecification(metaclassFunction, filter.getDoesNotContain());
        } else if (filter.getNotEquals() != null) {
            return notEqualsSpecification(metaclassFunction, filter.getNotEquals());
        } else if (filter.getSpecified() != null) {
            return byFieldSpecified(metaclassFunction, filter.getSpecified());
        }
        return null;
    }

以便我可以在我的 ChildQueryService 中执行此操作:

if (criteria.getGrandParentName() != null) {
    specification = specification.and(
        buildSpecification(
            criteria.getGrandParentName(),
            root -> root.join(Child_.parent, JoinType.INNER).join(Parent_.grandParent, JoinType.INNER).get(GrandParent_.name)
        )
    );
}

现在可以正常使用了!看来你可以通过连接深入到你想要的深度,尽管我没有测试它超过 2 级深度。

郑重声明,这是我在 Jhipster 文档中找到的,以及我的函数基于的内容:

protected <OTHER, MISC, X> Specification<ENTITY> buildReferringEntitySpecification(
    Filter<X> filter,
    Function<Root<ENTITY>, SetJoin<MISC, OTHER>> functionToEntity,
    Function<SetJoin<MISC, OTHER>, Expression<X>> entityToColumn
) {
    if (filter.getEquals() != null) {
        return equalsSpecification(functionToEntity.andThen(entityToColumn), filter.getEquals());
    } else if (filter.getSpecified() != null) {
        return byFieldSpecified(root -> functionToEntity.apply(root), filter.getSpecified());
    }
    return null;
}

例如这样使用的:

buildReferringEntitySpecification(
    criteria.getGrandParentName(),
    root ->  root.get(Child_.parent).join(Parent_.grandParent),
    GrandParent_.name
)

同时使用两者可以处理 StringFilter 和其他过滤器。不过,您可能必须为某些过滤器执行特定功能 class。