简体   繁体   English

JPA 规范示例

[英]JPA Specifications by Example

Spring Boot here. Spring Boot 在这里。 I'm trying to wrap my head around JpaRepositories and Specifications when used in the context of implementing complex queries and am struggling to see the "forest through the trees" on several items.当在实现复杂查询的上下文中使用时,我试图围绕JpaRepositoriesSpecifications进行JpaRepositories ,并且正在努力在几个项目上看到“穿过树林的森林”。

A canonical example of a Specification is as follows:一的典型的例子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;
    }
}

In this toPredicate(...) method, what do the Root<Person> and CriteriaQuery represent?在这个toPredicate(...)方法中, Root<Person>CriteriaQuery代表什么? Most importantly, it sounds like you need to create one Specification impl for each type of filter you want to apply, because each spec gets translated into one and only one predicate...so for instance if I wanted to find all people with a surname of "Smeeb" and an age greater than 25, it sounds like I would need to write a LastnameMatchingSpecification<Person> as well as a AgeGreaterThanSpecification<Person> .最重要的是,听起来您需要为要应用的每种类型的过滤器创建一个Specification实现,因为每个规范都被翻译成一个且只有一个谓词……例如,如果我想找到所有有姓氏的人“Smeeb”和年龄大于 25 岁,听起来我需要写一个LastnameMatchingSpecification<Person>以及一个AgeGreaterThanSpecification<Person> Can someone confirm or clarify this for me?!有人可以为我确认或澄清这一点吗?!

This was hard for me too at first, but now I'm making dynamic queries with ease and a single Specification per Table (when Advanced Searching is necessary)一开始这对我来说也很困难,但现在我可以轻松地进行动态查询,每个表只有一个规范(当需要高级搜索时)

Think of these objects like:想想这些对象,如:

  1. Root is your table.根是你的桌子。
  2. CriteriaQuery is your query, good for applying distinct, subqueries, order by, etc. CriteriaQuery 是您的查询,适用于应用不同的、子查询、排序依据等。
  3. CriteriaBuilder is your conditions, good for creating your where clauses CriteriaBuilder 是你的条件,有利于创建你的 where 子句

-- ——

Always start with a List then condense them at the end with either AND/OR conditions based on your needs.始终以列表开头,然后根据您的需要在最后使用 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();
}

Now my user can pass any combination of these 3 fields here and this logic would dynamically build the query to include conditions for them.现在我的用户可以在此处传递这 3 个字段的任意组合,此逻辑将动态构建查询以包含它们的条件。

eg name = John and surname = Doe and age = 41 or name = John and age = 41 or name = John etc.例如,姓名 = 约翰和姓氏 = Doe 和年龄 = 41 或姓名 = 约翰和年龄 = 41 或姓名 = 约翰等。

Lastly, when searching strings I would recommend using cb.like and not cb.equal so that it would make your search capable of partial searching with % is passed by user or frontend system.最后,在搜索字符串时,我建议使用 cb.like 而不是 cb.equal ,以便它可以使您的搜索能够部分搜索 % 由用户或前端系统传递。

Keep in mind cb.like is not case sensitive by default it needs to be used in conjunction with cb.lower or cb.upper such as:请记住,默认情况下 cb.like 不区分大小写,它需要与 cb.lower 或 cb.upper 结合使用,例如:

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

Hope this helps !希望这有帮助!

what do the Root<Person> and CriteriaQuery represent? Root<Person>CriteriaQuery代表什么?

Root is the root of your query, basically What you are querying for. Root是您查询的根,基本上您要查询的内容。 In a Specification , you might use it to react dynamically on this.Specification ,您可以使用它对此做出动态反应。 This would allow you, for example, to build one OlderThanSpecification to handle Car s with a modelYear and Drivers with a dateOfBirth by detecting the type and using the appropriate property.这将允许你,例如,建立一个OlderThanSpecification处理Car ,以及符合S modelYearDriversdateOfBirth通过检测的类型,并使用相应的属性。

Similiar CriteriaQuery is the complete query which you again might use to inspect it and adapt the Predicate you are constructing based on it. Similiar CriteriaQuery是完整的查询,您可以再次使用它来检查它并根据它调整您正在构建的 Predicate。

if I wanted to find all people with a surname of "Smeeb" and an age greater than 25, it sounds like I would need to write a LastnameMatchingSpecification<Person> as well as an AgeGreaterThanSpecification<Person> .如果我想找到所有姓氏“Smeeb”且年龄大于 25 岁的人,听起来我需要编写LastnameMatchingSpecification<Person>AgeGreaterThanSpecification<Person> Can someone confirm or clarify this for me?!有人可以为我确认或澄清这一点吗?!

I think you have that wrong.我认为你错了。 The Spring Data interfaces accepting Specification s only accept a single Specification .接受Specification的 Spring Data 接口只接受单个Specification So if you want to find all Person s with a certain name and a certain age you would create one Specification .因此,如果您想查找具有特定名称和特定年龄的所有Person ,您可以创建一个Specification Similar to the example you quote which also combines two constraints.类似于您引用的示例,它也结合了两个约束。

But you may create separate Specification s and then create another one combining those if you want to use each separately, but also combined.但是您可以创建单独的Specification s,然后创建另一个将它们组合在一起的,如果您想单独使用它们,但也可以组合使用。

If you need to also use joins, you will have to write something like this:如果您还需要使用连接,则必须编写如下内容:



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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM