繁体   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