JPA规范示例

sme*_*eeb 9 spring-data spring-data-jpa spring-boot jpa-criteria

Spring Boot在这里。我想换我的头周围JpaRepositories,并Specifications在执行复杂查询的上下文中使用,当我在努力“通过舍本逐末”的几个项目看。

a的一个典型示例Specification如下:

public class PersonSpecification implements Specification<Person> {
    private Person filter;

    public PersonSpecification(Person filter) {
        super();
        this.filter = filter;
    }

    public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> cq,
            CriteriaBuilder cb) {
        Predicate p = cb.disjunction();

        if (filter.getName() != null) {
            p.getExpressions()
                    .add(cb.equal(root.get("name"), filter.getName()));
        }

        if (filter.getSurname() != null && filter.getAge() != null) {
            p.getExpressions().add(
                    cb.and(cb.equal(root.get("surname"), filter.getSurname()),
                            cb.equal(root.get("age"), filter.getAge())));
        }

        return p;
    }
}
Run Code Online (Sandbox Code Playgroud)

在此toPredicate(...)方法中,Root<Person>CriteriaQuery代表什么?最重要的是,听起来您需要为要应用的每种过滤器类型创建一个Specificationimpl ,因为每个规范都转换为一个且只有一个谓词...因此,例如,如果我想查找所有姓为“ Smeeb”的年龄大于25岁,听起来我需要写以及。有人可以帮我确认或澄清吗?LastnameMatchingSpecification<Person>AgeGreaterThanSpecification<Person>

Jen*_*der 5

什么做Root<Person>CriteriaQuery代表什么?

Root是查询的根,基本上就是您要查询的内容。在中Specification,您可以使用它来对此动态地做出反应。例如,这将允许您通过检测类型并使用适当的属性来构建一个用a 和a OlderThanSpecification处理Car的。modelYearDriversdateOfBirth

Similiar CriteriaQuery是一个完整的查询,您可以再次使用它来检查它并根据它修改您正在构造的谓词。

如果我想找到所有姓氏为“ Smeeb”且年龄大于25岁的人,听起来我需要写一个LastnameMatchingSpecification<Person>AgeGreaterThanSpecification<Person>。有人可以帮我确认或澄清吗?

我想你错了。接受Specification的Spring Data接口仅接受一个Specification。因此,如果要查找Person具有特定名称和特定年龄的所有s,则可以创建一个Specification。与您引用的示例类似,该示例也结合了两个约束。

但是,您可以创建单独Specification的,然后创建另一个组合,如果您想分别使用,也可以组合使用。


jDu*_*ub9 5

刚开始时,这对我来说也很困难,但是现在我可以轻松地进行动态查询,并且每个表有一个规范(当需要高级搜索时)

将这些对象想像成:

  1. 根就是你的桌子。
  2. CriteriaQuery是您的查询,非常适合应用不重复,子查询,排序依据等。
  3. CriteriaBuilder是您的条件,非常适合创建where子句

-

始终从列表开始,然后根据您的需要在任何AND / OR条件下压缩它们。

public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
    List<Predicate> predicates = new ArrayList<>();

    if(filter.getName() != null) {
        predicates.add(cb.equal(root.get("name"), filter.getName());
    }
    if(filter.getSurname() != null) {
        predicates.add(cb.equal(root.get("surname"), filter.getSurname());
    }
    if(filter.getAge() != null) {
        predicates.add(cb.equal(root.get("age"), filter.getAge());
    }
    if(predicates.isEmpty()){
        predicates.add(cb.equal(root.get("id"), -1);
        /* 
         I like to add this because without it if no criteria is specified then 
         everything is returned. Because that's how queries work without where 
         clauses. However, if my user doesn't provide any criteria I want to 
         say no results found. 
        */
    }

    return query.where(cb.and(predicates.toArray(new Predicate[0])))
                .distinct(true).orderBy(cb.desc(root.get("name")).getRestriction();
}
Run Code Online (Sandbox Code Playgroud)

现在,我的用户可以在此处传递这3个字段的任意组合,并且此逻辑将动态构建查询以包括它们的条件。

例如,姓名= John,姓氏= Doe,年龄= 41,或者name = John,年龄= 41,或者name = John,等等。

最后,在搜索字符串时,我建议您使用cb.like而不是cb.equal,这样它就可以使用户或前端系统通过%进行部分搜索。

请记住,默认情况下cb.like不区分大小写,它需要与cb.lower或cb.upper结合使用,例如:

 predicates.add(cb.like(cb.lower(root.get("name"), filter.getName().toLowercase());
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助 !