You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot如何使用可选参数实现空参数自动忽略的搜索功能

Spring Boot 多可选参数分页搜索实现方案

针对JPA场景下多可选查询参数的动态搜索需求,最简便的实现方式是使用Spring Data JPA原生提供的Specification动态条件构造能力,自动忽略空值参数,无需拼接冗余SQL。


实现步骤

1. 扩展Repository接口

让你的InventoryRepository继承JpaSpecificationExecutor,获得动态查询执行能力:

public interface InventoryRepository extends JpaRepository<Inventory, Long>, JpaSpecificationExecutor<Inventory> {
    // 原有方法可保留
}

注:如果项目已经引入spring-boot-starter-data-jpa依赖,无需额外引入其他包即可使用该功能。

2. 改造Service层动态拼接查询条件

修改原有分页查询方法,接收所有可选搜索参数,仅当参数非空时才拼接对应查询条件:

@Override
public Page<Inventory> listAllCarIfShow(int pageNumber, int pageSize, String name, String owner, 
                                        String fuelType, String location, String insuranceValidity, 
                                        String rto, Integer minPrice, Integer maxPrice) {
    Pageable pageable = PageRequest.of(pageNumber - 1, pageSize);
    
    Specification<Inventory> spec = (root, query, cb) -> {
        List<Predicate> predicates = new ArrayList<>();
        // 固定条件:仅查询展示状态的车辆,和原有逻辑一致
        predicates.add(cb.equal(root.get("show"), true));

        // 可选条件:参数非空时才拼接
        if (name != null && !name.trim().isEmpty()) {
            // 名称模糊匹配,如需忽略大小写可统一转小写处理
            predicates.add(cb.like(root.get("name"), "%" + name.trim() + "%"));
        }
        if (owner != null && !owner.trim().isEmpty()) {
            predicates.add(cb.like(root.get("owner"), "%" + owner.trim() + "%"));
        }
        if (fuelType != null && !fuelType.trim().isEmpty()) {
            predicates.add(cb.equal(root.get("fuelType"), fuelType.trim()));
        }
        if (location != null && !location.trim().isEmpty()) {
            predicates.add(cb.equal(root.get("location"), location.trim()));
        }
        if (insuranceValidity != null && !insuranceValidity.trim().isEmpty()) {
            predicates.add(cb.equal(root.get("insuranceValidity"), insuranceValidity.trim()));
        }
        if (rto != null && !rto.trim().isEmpty()) {
            predicates.add(cb.equal(root.get("rto"), rto.trim()));
        }
        // 价格区间筛选,可根据表单需求调整
        if (minPrice != null) {
            predicates.add(cb.greaterThanOrEqualTo(root.get("price"), minPrice));
        }
        if (maxPrice != null) {
            predicates.add(cb.lessThanOrEqualTo(root.get("price"), maxPrice));
        }

        return cb.and(predicates.toArray(new Predicate[0]));
    };

    return carInventoryRepository.findAll(spec, pageable);
}

如果需要新增里程范围、车龄等其他筛选条件,按照相同逻辑在方法内追加判断即可。

3. 改造Controller层接收可选参数

将所有搜索参数设置为非必填,传入Service层,同时修正原有代码中分页参数写死的问题,增加搜索参数回显能力:

@GetMapping("/buy-car")
public String showBuyCarPageList(Model model,
                                 @RequestParam(value = "page", required = false) Integer page,
                                 @RequestParam(value = "size", required = false) Integer size,
                                 @RequestParam(value = "name", required = false) String name,
                                 @RequestParam(value = "owner", required = false) String owner,
                                 @RequestParam(value = "fuelType", required = false) String fuelType,
                                 @RequestParam(value = "location", required = false) String location,
                                 @RequestParam(value = "insuranceValidity", required = false) String insuranceValidity,
                                 @RequestParam(value = "rto", required = false) String rto,
                                 @RequestParam(value = "minPrice", required = false) Integer minPrice,
                                 @RequestParam(value = "maxPrice", required = false) Integer maxPrice
){
    int currentPage = page != null ? page : 1;
    int pageSize = size != null ? size : 16;
    Page<Inventory> pageObj = carInventoryService.listAllCarIfShow(currentPage, pageSize, name, owner, fuelType, location, insuranceValidity, rto, minPrice, maxPrice);
    
    model.addAttribute("currentPage", currentPage);
    model.addAttribute("totalPages", pageObj.getTotalPages());
    model.addAttribute("totalItems", pageObj.getTotalElements());
    model.addAttribute("carInfo", pageObj.getContent());
    // 回传搜索参数用于前端表单回显
    model.addAttribute("name", name);
    model.addAttribute("owner", owner);
    model.addAttribute("fuelType", fuelType);
    model.addAttribute("location", location);
    model.addAttribute("insuranceValidity", insuranceValidity);
    model.addAttribute("rto", rto);
    model.addAttribute("minPrice", minPrice);
    model.addAttribute("maxPrice", maxPrice);

    return "frontend/buy-car-list";
}

效果验证

改造完成后,你列出的三个请求场景都可以正常支持:

  • 传name参数时,仅按名称筛选,其他空参数自动忽略
  • 同时传name、owner参数时,叠加两个条件做精确筛选
  • 传fuelType、owner参数时,按燃油类型和所有者叠加筛选
    所有未传、值为空的参数都不会参与SQL条件拼接,完全符合可选搜索的需求。

优化建议

  • 如果搜索参数较多,可以单独定义InventorySearchDTO类封装所有搜索字段,Controller直接接收DTO对象,减少方法参数长度
  • 字符串匹配建议增加大小写忽略逻辑,提升搜索体验
  • 分页拼接时记得带上原有搜索参数,避免翻页后搜索条件丢失

内容的提问来源于stack exchange,提问作者Cipher

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 10:54:17