使用Java 8谓词的JPA信息库过滤器

时间:2018-09-11 16:17:44

标签: java spring-boot spring-data-jpa predicate

在使用Spring Boot进行的一项采访测试中,我有一个要求,我必须创建一个接受一堆可选请求参数的终结点,然后根据这些参数(例如汽车型号,车牌,发动机)返回汽车列表。类型,制造商,驾驶员,租给的公司等。汽车,驾驶员和制造商都是单独的实体。

我在JPARepository中使用单个JPQL查询实现了此功能,该查询实现了LEFT JOINS并在where子句(如licensePlate = licensePlateParameter或licensePlatParameter为null等)中进行过滤。

该解决方案正在运行,但是面试官说该解决方案具有可伸缩性和可维护性。我应该使用谓词实现它。谁能给我一个例子,我如何使用更易于维护的谓词实现这种功能?一些带有代码的示例将不胜感激。

我认为我很聪明,因为同时提供了两个可选参数,并通过检查参数是否为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);

3 个答案:

答案 0 :(得分:1)

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)方法。

答案 1 :(得分:0)

您可以使用Criteria Api代替JPQL。

例如,请参考示例1

https://www.programcreek.com/java-api-examples/index.php?api=javax.persistence.criteria.Predicate

答案 2 :(得分:0)

您可以像这样在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

Here is detail explanation for Spring JPA dynamic query