在使用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);
答案 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
。