簡體   English   中英

使用 Java 8 謂詞的 JPA 存儲庫過濾器

[英]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

這是Spring JPA動態查詢的詳細解釋

您可以使用 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM