簡體   English   中英

JPA 規范示例

[英]JPA Specifications by Example

Spring Boot 在這里。 當在實現復雜查詢的上下文中使用時,我試圖圍繞JpaRepositoriesSpecifications進行JpaRepositories ,並且正在努力在幾個項目上看到“穿過樹林的森林”。

一的典型的例子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實現,因為每個規范都被翻譯成一個且只有一個謂詞……例如,如果我想找到所有有姓氏的人“Smeeb”和年齡大於 25 歲,聽起來我需要寫一個LastnameMatchingSpecification<Person>以及一個AgeGreaterThanSpecification<Person> 有人可以為我確認或澄清這一點嗎?!

一開始這對我來說也很困難,但現在我可以輕松地進行動態查詢,每個表只有一個規范(當需要高級搜索時)

想想這些對象,如:

  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 個字段的任意組合,此邏輯將動態構建查詢以包含它們的條件。

例如,姓名 = 約翰和姓氏 = Doe 和年齡 = 41 或姓名 = 約翰和年齡 = 41 或姓名 = 約翰等。

最后,在搜索字符串時,我建議使用 cb.like 而不是 cb.equal ,以便它可以使您的搜索能夠部分搜索 % 由用戶或前端系統傳遞。

請記住,默認情況下 cb.like 不區分大小寫,它需要與 cb.lower 或 cb.upper 結合使用,例如:

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

希望這有幫助!

Root<Person>CriteriaQuery代表什么?

Root是您查詢的根,基本上您要查詢的內容。 Specification ,您可以使用它對此做出動態反應。 這將允許你,例如,建立一個OlderThanSpecification處理Car ,以及符合S modelYearDriversdateOfBirth通過檢測的類型,並使用相應的屬性。

Similiar CriteriaQuery是完整的查詢,您可以再次使用它來檢查它並根據它調整您正在構建的 Predicate。

如果我想找到所有姓氏“Smeeb”且年齡大於 25 歲的人,聽起來我需要編寫LastnameMatchingSpecification<Person>AgeGreaterThanSpecification<Person> 有人可以為我確認或澄清這一點嗎?!

我認為你錯了。 接受Specification的 Spring Data 接口只接受單個Specification 因此,如果您想查找具有特定名稱和特定年齡的所有Person ,您可以創建一個Specification 類似於您引用的示例,它也結合了兩個約束。

但是您可以創建單獨的Specification s,然后創建另一個將它們組合在一起的,如果您想單獨使用它們,但也可以組合使用。

如果您還需要使用連接,則必須編寫如下內容:



    @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