[英]JPA Repository filter using Java 8 Predicates
我在使用 Spring Boot 的一次面試測試中有一個要求,我必須創建一個端點,該端點接受一堆可選的請求參數,然后根據這些參數返回汽車列表,例如汽車型號、車牌、發動機類型、制造商,司機,公司等。汽車,司機和制造商都是獨立的實體。
我在 JPARepository 中使用單個 JPQL 查詢實現了這個功能,該查詢實現了 LEFT JOINS 並在 where 子句中過濾,如 licensePlate = licensePlateParameter OR licensePlatParameter is null 等。
該解決方案有效,但面試官表示該解決方案具有可擴展性和可維護性。 我應該使用謂詞來實現它。 有人可以向我展示一個示例,我如何使用更易於維護的謂詞來實現此類功能? 一些帶有代碼的示例將不勝感激。
我認為我很聰明,通過檢查參數是否為空來滿足可選參數和在單個調用中找到的記錄。 我想到的另一個與此相關的問題是,從 DB 獲取所有記錄然后使用謂詞對其進行過濾真的是一個好習慣嗎? 還有當我們涉及多個對象/實體時如何過濾,可以為單個類型創建謂詞。
@Query("SELECT d FROM Driver d LEFT JOIN d.car c WHERE (d.name = :name OR :name is null) "
+ "and (c.licensePlate = :licensePlate OR :licensePlate is null) "
+ "and (c.rating = :rating OR :rating is null) " and so on
List<Driver> findByAttributes(@Param("name") String name,
@Param("licensePlate") String licensePlate,
@Param("rating") Integer rating,
and so on);
Spring 對 JPA 標准 API(使用謂詞)有一個包裝器,稱為規范 API。
編寫規范時您可以執行以下操作,為每個標准編寫規范:
public static Specification<Car> withLicensePlate(String licensePlate) {
return (root, query, cb) -> licensePlate == null ? null : cb.equal(root.get("licensePlate"), licensePlate);
}
public static Specification<Car> withRating(String rating) {
return (root, query, cb) -> rating == null ? null : cb.equal(root.get("rating"), rating);
}
public static Specification<Car> withName(String name) {
return (root, query, cb) -> name == null ? null : cb.equal(root.get("name"), name);
}
它還允許您編寫連接操作:
public static Specification<Car> withSeatType(String type) {
return (root, query, cb) -> {
return type == null ? null : cb.equal(root.join("interior", JoinType.LEFT).get("type"), type);
};
}
您可以在條件內返回null
,這允許您將這些規范設為“可選”。 之后,您可以使用Specifications.where()
組合這些條件:
Specification<Car> spec = Specifications
.where(withLicensePlate(licensePlate))
.and(withRating(rating))
.and(withName(name))
.and(withSeatType(seatType));
如果你像我在這個例子中所做的那樣編寫單獨的規范,你可以在必要時重新使用它們。 否則,您將不得不編寫特定於操作的規范,而面試官也可能不會發現它具有可擴展性。
編寫規范后,您必須從JpaSpecificationExecutor
接口擴展您的存儲庫並使用findAll(Specification)
方法。
您可以像這樣在 Spring JPA 中使用動態查詢:
public List<Employee> findByCriteria(String employeeName,String employeeRole){
return employeeDAO.findAll(new Specification<Employee>() {
@Override
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicates = new ArrayList<>();
if(employeeName!=null) {
predicates.add(criteriaBuilder.and(criteriaBuilder.like(root.get("employeeName"), "%"+employeeName+"%")));
}
if(employeeRole!=null){
predicates.add(criteriaBuilder.and(criteriaBuilder.equal(root.get("employeeRole"), employeeRole)));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
}
});
}
為此,您需要在存儲庫中實現JpaSpecificationExecutor
。
您可以使用 Criteria Api 代替 JPQL。
例如參考例子1
https://www.programcreek.com/java-api-examples/index.php?api=javax.persistence.criteria.Predicate
Car和Driver實體是如何映射的? @短跑男孩
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.