繁体   English   中英

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

[英]How to make a dynamic Spring (Boot) JPA query with paging?

为了简化问题:我们有一个类/表 Wine(表“葡萄酒”),除其他属性外,它还具有:

  • 名称:字符串
  • 描述:字符串
  • 产地:原产地

...其中 Origin 是另一个 class (带有表“origins”),只有region: Stringcountry: 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 ......你明白了。

我尝试过的事情:

  1. (天真地)在我的存储库中构建一个大规模查询(实现 CrudRepository),就像这里一样。

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

    • 哪个(除了超级丑陋,特别是对于更多属性)可能不起作用,因为:
      1. 没有定义 OR 或 AND 是否更重要(没有括号)。
      2. 不知道我是否可以将“Origins” object 推入其中以使其工作。
      3. 如果这是一种自定义方法,不知道 Pageable 是否有效。
  2. 使用 spring.io 建议的“规范和Querydsl ”。

    • 我只是愚蠢地理解了这一点,尤其是对于帖子底部的那些 Q 类或开头的 _ 类。 对于它应该做的事情来说,这似乎太复杂了。
    • 也没有分页选项。 然而,有一个像这里这样的可能修复,但我没有得到它,因为使用那些 _ 和/或 Q 类开始时会产生很大的开销。
  3. 此外,这只是我从 2013 年发现的一个例子,我什至不理解,但看起来它会很合适。

如果您发现很难理解Specifications ,只有这两个搜索参数并假设Wine实体有一个@ManyToOneOrigin ,您可以执行以下操作:

    @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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM