[英]JPA Specifications by Example
Spring Boot 在這里。 當在實現復雜查詢的上下文中使用時,我試圖圍繞JpaRepositories
和Specifications
進行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>
。 有人可以為我確認或澄清這一點嗎?!
一開始這對我來說也很困難,但現在我可以輕松地進行動態查詢,每個表只有一個規范(當需要高級搜索時)
想想這些對象,如:
——
始終以列表開頭,然后根據您的需要在最后使用 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 modelYear
和Drivers
與dateOfBirth
通過檢測的類型,並使用相應的屬性。
Similiar CriteriaQuery
是完整的查詢,您可以再次使用它來檢查它並根據它調整您正在構建的 Predicate。
如果我想找到所有姓氏“Smeeb”且年齡大於 25 歲的人,聽起來我需要編寫
LastnameMatchingSpecification<Person>
和AgeGreaterThanSpecification<Person>
。 有人可以為我確認或澄清這一點嗎?!
我認為你錯了。 接受Specification
的 Spring Data 接口只接受單個Specification
。 因此,如果您想查找具有特定名稱和特定年齡的所有Person
,您可以創建一個Specification
。 類似於您引用的示例,它也結合了兩個約束。
但是您可以創建單獨的Specification
s,然后創建另一個將它們組合在一起的,如果您想單獨使用它們,但也可以組合使用。
如果您還需要使用連接,則必須編寫如下內容:
@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;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.