如何使用分页进行动态Spring(Boot)JPA查询?

Panossa:

为了简化这个问题:我们有一个类/表Wine(表“ wines”),它具有以下属性:

  • 名称:字符串
  • 描述:字符串
  • 起源:起源

...其中Origin是另一个具有just region: String和的类(带有表“ origins”)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”部分都不应出现在查询中。如果没有给出原产地,您就会得到它。

我尝试过的事情:

  1. (天真)建设我的仓库(实现CrudRepository)一个巨大的查询关键词,比如这里

    Page<Wine> findWinesByNameLikeOrDescriptionLikeAndOriginEquals(..., Pageable pageable);

    • 哪一个(除了非常丑陋,尤其是对于更多属性而言)可能不起作用,原因是:
      1. 还没有定义OR还是AND更重要(没有括号)。
      2. 不知道我是否可以在其中插入“ Origins”对象以使其工作。
      3. 如果这是一个自定义方法,不知道Pageable是否能正常工作。
  2. 使用spring.io 在此处建议的“ Specifications and Querydsl”

    • 我只是愚蠢的理解,特别是在帖子底部的Q类或开头的_类中。似乎太复杂了,应该怎么做。
    • 也没有分页选项。但是,这里有一个可能的解决方法,但是由于使用这些_和/或Q类会产生很多开销,因此我没有得到解决
  3. 同样,只是我从2013年发现的一个示例,我什至不大体上理解,但有点像它非常合适。

Panossa:

所以我实际上找到了解决方案!对于好奇的人,这是我的做法:

ProductController.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);
    }
}

例如,这是名称/描述字符串的规范:

产品规格书

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之类的复杂对象,您需要包括一个类似here的转换器类

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章