使用Java 8 Predicates的JPA存储库过滤器

问题描述 投票:2回答:3

我在使用Spring Boot进行的一次面试测试中有一个要求,我必须创建一个端点,接受一堆可选的请求参数,然后根据这些参数返回汽车列表,如汽车型号,车牌,发动机类型,制造商,司机,它租给的公司等等。汽车,司机和制造商都是独立的实体。

我在JPARepository中使用单个JPQL查询实现此功能,实现LEFT JOINS并过滤where子句,如licensePlate = licensePlateParameter或licensePlatParameter为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);
java spring-boot spring-data-jpa predicate
3个回答
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)方法。


0
投票

您可以使用Criteria Api而不是JPQL。

例如,参考示例1

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


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

© www.soinside.com 2019 - 2024. All rights reserved.