[英]How to make a dynamic Spring (Boot) JPA query with paging?
为了简化问题:我们有一个类/表 Wine(表“葡萄酒”),除其他属性外,它还具有:
...其中 Origin 是另一个 class (带有表“origins”),只有region: String
和country: String
。
我想要做的是我的存储库中供 RestController 使用的搜索方法。
RestController 中的方法声明是这样的:
@GetMapping("/search")
public Wine searchProduct(
@RequestParam Optional<String> searchTerm,
@RequestParam Optional<Origin> origin) {
// ???
}
我现在要做的是:为使用 searchTerm 的数据库创建一个查询,如果给定的话,与 origin 相同。 它应该是可分页的。 例子:
SELECT * FROM wines JOIN origins ON wines.origin_id = origins.id
WHERE (name LIKE $searchTerm OR description LIKE $searchTerm) AND (/*origin check*/)
如果没有给出搜索词,则整个“() AND”部分不应出现在查询中。 如果没有给出 Origin ......你明白了。
我尝试过的事情:
(天真地)在我的存储库中构建一个大规模查询(实现 CrudRepository),就像这里一样。
Page<Wine> findWinesByNameLikeOrDescriptionLikeAndOriginEquals(..., Pageable pageable);
使用 spring.io 建议的“规范和Querydsl ”。
此外,这只是我从 2013 年发现的一个例子,我什至不理解,但看起来它会很合适。
如果您发现很难理解Specifications
,只有这两个搜索参数并假设Wine
实体有一个@ManyToOne
到Origin
,您可以执行以下操作:
@Query("SELECT w FROM wines w join w.origin o where " +
"( :searchTerm is null or " +
" w.name like CONCAT('%',:searchTerm ,'%') or " +
" w.description like CONCAT('%', :searchTerm ,'%')" +
") " +
"AND (:origin is null or o = :origin)")
Page<Wine> searchWines(String searchTerm, Origin origin, Pageable p);
当你调用它时,
wineRepository.searchWines(searchTerm.orElse(null), origin.orElse(null));
所以我实际上得到了一个解决方案,对于好奇的人:我是这样做的:
产品控制器.java
@GetMapping("search")
public Page<Wine> searchProducts(
@RequestParam(name = "text", required = false) String searchTerm,
@RequestParam(required = false) Origin origin,
@RequestParam(required = false) Integer page) {
// generate PageRequest based on Integer page if given:
Pageable pageRequest = PageRequest.of(page != null ? page : 0, 10);
if(Objects.isNull(searchTerm) && Objects.isNull(origin)) {
return wineService.findAll(pageRequest);
}
return wineService.searchWines(
searchTerm,
origin,
pageRequest
);
}
WineService.java
public Page<Wine> searchWines(String searchTerm, Origin origin, Pageable pageable) {
List<Specification<Wine>> specifications = new LinkedList<>();
if (searchTerm != null) {
specifications.add(ProductSpecification.hasSearchStringInNameOrDescription(searchTerm));
}
if (origin != null) {
specifications.add(ProductSpecification.hasOrigin(origin));
}
if (specifications.isEmpty()) {
return wineRepository.findAll(pageable);
} else {
Specification<Wine> query = Specification.where(specifications.remove(0));
for (Specification<Wine> wineSpecification : specifications) {
query = query.and(wineSpecification);
}
return wineRepository.findAll(query, pageable);
}
}
例如,这是名称/描述字符串的规范:
产品规格.java
public static Specification<Wine> hasSearchStringInNameOrDescription(String input) {
final String searchTerm = input.toLowerCase();
return (root, criteriaQuery, criteriaBuilder) -> {
log.info("SearchTerm: " + searchTerm);
Predicate pName = criteriaBuilder.like(
criteriaBuilder.lower(root.get(Wine_.NAME)),
"%" + searchTerm + "%"
);
Predicate pDescription = criteriaBuilder.like(
criteriaBuilder.lower(root.get(Wine_.DESCRIPTION)),
"%" + searchTerm + "%"
);
return criteriaBuilder.or(pName, pDescription);
};
}
这些Wine_类的生成是通过 Maven 插件自动生成的。 看到这个。 此外,为了将请求正文片段转换为复杂对象(如 Origin),您需要像这里一样包含一个转换器 class 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.