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