Spring多@RequestParam参数动态查询筛选的优化方案探讨
多条件筛选接口的优化方案
问题场景
现有一个获取全部Post的接口,通过多个@RequestParam可选参数实现筛选与搜索(分页参数已省略),具体参数定义如下:
@RequestParam(required=false) List<String> brand - 多品牌筛选 @RequestParam(required=false) String province - 省份筛选 @RequestParam(required=false) String city - 城市筛选 // 价格区间筛选参数 @RequestParam(defaultValue = "0", required = false) String minValue - 最低价格筛选 @RequestParam(defaultValue = "5000000", required = false) String maxValue - 最高价格筛选
当前Controller通过条件分支判断调用对应的Service方法,代码示例:
if(query != null) { pageTuts = postService.findAllPosts(query, pagingSort); } else if(brand != null) { pageTuts = postService.findAllByBrandIn(brand, pagingSort); } else if(minValue != null && maxValue != null) { pageTuts = postService.findAllPostsByPriceBetween(minValue, maxValue, pagingSort); } else if(brand != null & minValue != null & maxValue != null) { pageTuts = postService.findAllPostsByPriceBetween(minValue, maxValue, pagingSort); } else { // 无参数时获取全部Post pageTuts = postService.findAllPosts(pagingSort); } // 需要补充更多判断以覆盖所有参数组合
这种方式存在明显问题:要覆盖所有参数组合,必须编写大量条件判断逻辑,同时还要在Repository层实现对应数量的查询方法,例如:
Page<Post> findAllByProvince(String province, Pageable pageable); Page<Post> findAllByCity(String city, Pageable pageable); Page<Post> findAllByProvinceAndCity(String province, String city, Pageable pageable); Page<Post> findAllByBrandInAndProvince(List<String> brand, String province, Pageable pageable);
还需额外实现findAllByPriceBetween()、findAllByCityAndPriceBetween()等更多组合方法,代码冗余度极高。
优化实现
参考@M. Deinum的方案,通过重写toPredicate方法动态构建查询条件,完美覆盖所有参数组合场景,实现代码如下:
@Override public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder builder) { List<Predicate> predicates = new ArrayList<>(); // min/max始终有默认值,直接加入价格区间条件 predicates.add(builder.between(root.get("price"), params.getMinValue(), params.getMaxValue())); if (params.getProvince() != null) { predicates.add(builder.equal(root.get("province"), params.getProvince())); } if (params.getCity() != null) { predicates.add(builder.equal(root.get("city"), params.getCity())); } if (!CollectionUtils.isEmpty(params.getBrand())) { Expression<String> brandExpression = root.get("brand"); Predicate brandPredicate = brandExpression.in(params.getBrand()); predicates.add(brandPredicate); } // 将所有有效条件组合为AND查询 return builder.and(predicates.toArray(new Predicate[0])); }
内容的提问来源于stack exchange,提问作者ServletException
相关产品推荐
相关产品推荐

