JPA规范示例

时间:2018-02-06 16:39:50

标签: spring-boot spring-data spring-data-jpa jpa-criteria

Spring Boot here。当我在实施复杂查询的环境中使用时,我正试图绕过JpaRepositoriesSpecifications,并且正在努力通过树木看到"森林。关于几个项目。

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

在此toPredicate(...)方法中,Root<Person>CriteriaQuery代表什么?最重要的是,声音就像您需要为每个类型的过滤器创建一个Specification impl,因为每个规范都会被转换为一个且只有一个一个谓词......所以,例如,如果我想找到所有姓氏为&#34; Smeeb&#34;年龄大于25岁,听起来我需要写一个LastnameMatchingSpecification<Person>和一个AgeGreaterThanSpecification<Person>。有人可以为我确认或澄清这个吗?!

3 个答案:

答案 0 :(得分:4)

  

Root<Person>CriteriaQuery代表什么?

Root是您查询的根,基本上是您正在查询的。在Specification中,您可以使用它对此动态做出反应。例如,通过检测类型和使用,您可以构建一个OlderThanSpecification来处理Car modelYearDrivers dateOfBirth LastnameMatchingSpecification<Person>适当的财产。

类似CriteriaQuery是完整的查询,您可以再次使用它来检查它并根据它调整您正构建的谓词。

  

如果我想找到所有姓氏为&#34; Smeeb&#34;年龄大于25岁,听起来我需要写AgeGreaterThanSpecification<Person>Specification。有人可以为我确认或澄清这个吗?!

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

但是你可以创建单独的<a href=""></a>,然后创建另一个组合,如果你想单独使用它们,但也可以组合使用。

答案 1 :(得分:3)

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

考虑这些对象,例如:

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

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

例如 姓名=约翰,姓=母鹿,年龄= 41 要么 名称=约翰,年龄= 41 要么 名字=约翰 等

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

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

 predicates.add(cb.like(cb.lower(root.get("name"), filter.getName().toLowercase());

希望这会有所帮助!

答案 2 :(得分:0)

如果您还需要使用联接,则必须编写如下内容:



    @Override
    public Predicate toPredicate(Root<Opportunity> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
        Predicate predicate = cb.conjunction();
        ...
        predicate.getExpressions().add(cb.equal(root.join("address").get("streetName"), person.getAddress().getStreetName()));
        ...
        return predicate;
    }