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

Spring Boot结合Spring Data JPA如何处理多可选请求参数过滤查询?

Spring Boot + Spring Data JPA 多参数组合动态过滤解决方案

首先明确:不需要为每种参数组合单独编写查询语句,Spring Data JPA提供了多种动态查询能力,可以灵活适配任意参数组合的过滤需求,下面给出3种常用实现方案:

方案1:JPA Specification(无额外依赖,灵活度最高)

这是Spring Data JPA原生支持的动态查询方案,无需引入第三方依赖,支持等值、模糊、范围等任意查询条件:

  1. 改造Repository接口,继承JpaSpecificationExecutor
public interface EmployeeRepository extends JpaRepository<Employee, Long>, JpaSpecificationExecutor<Employee> {
}
  1. 编写动态条件构建逻辑
    在Service层根据前端传入的非空参数,动态拼接查询条件:
import org.springframework.data.jpa.domain.Specification;
import jakarta.persistence.criteria.Predicate;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

public Specification<Employee> buildFilterSpec(Long id, String firstName, String lastName, 
                                               String designaton, Integer age, BigDecimal salary) {
    return (root, query, criteriaBuilder) -> {
        List<Predicate> predicates = new ArrayList<>();
        // 参数非空则加入查询条件
        if (id != null) {
            predicates.add(criteriaBuilder.equal(root.get("id"), id));
        }
        if (firstName != null && !firstName.isBlank()) {
            predicates.add(criteriaBuilder.like(root.get("firstName"), "%" + firstName + "%"));
        }
        if (lastName != null && !lastName.isBlank()) {
            predicates.add(criteriaBuilder.equal(root.get("lastName"), lastName));
        }
        if (designaton != null && !designaton.isBlank()) {
            predicates.add(criteriaBuilder.equal(root.get("designaton"), designaton));
        }
        if (age != null) {
            // 可根据需求调整为大于/小于/区间查询
            predicates.add(criteriaBuilder.equal(root.get("age"), age));
        }
        if (salary != null) {
            predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("salary"), salary));
        }
        return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
    };
}
  1. 执行查询
    直接调用Repository自带的findAll方法传入构造好的Specification即可:
Specification<Employee> spec = buildFilterSpec(id, firstName, lastName, designaton, age, salary);
// 支持分页、排序,传入Pageable参数即可
List<Employee> result = employeeRepository.findAll(spec);

方案2:Query By Example(QBE,代码最简,适合等值匹配场景)

如果你的过滤需求都是等值匹配,没有范围查询、复杂模糊匹配的要求,可以用QBE方案,无需手动判断参数非空:

// 把前端传入的参数直接赋值给Employee实体对象
Employee filterParam = new Employee();
filterParam.setFirstName(firstName);
filterParam.setLastName(lastName);
filterParam.setAge(age);
filterParam.setDesignaton(designaton);

// 配置匹配规则
ExampleMatcher matcher = ExampleMatcher.matching()
        .withIgnoreCase() // 忽略大小写
        .withIgnoreNullValues(); // 自动忽略null值字段
Example<Employee> example = Example.of(filterParam, matcher);

// 执行查询
List<Employee> result = employeeRepository.findAll(example);

方案3:QueryDSL(类型安全,避免硬编码字段名)

如果项目允许引入额外依赖,QueryDSL可以避免Specification方案中硬写字段名的问题,编译期就能检查字段正确性:

  1. 引入QueryDSL相关依赖并配置生成查询实体类
  2. 构建动态查询条件:
QEmployee qEmployee = QEmployee.employee;
BooleanBuilder condition = new BooleanBuilder();

if (firstName != null && !firstName.isBlank()) {
    condition.and(qEmployee.firstName.contains(firstName));
}
if (age != null) {
    condition.and(qEmployee.age.eq(age));
}
if (salary != null) {
    condition.and(qEmployee.salary.goe(salary));
}
// 其他参数同理

// 执行查询
List<Employee> result = employeeRepository.findAll(condition);

注意事项

  • 接口层所有@RequestParam都要设置required = false,且数值类型用包装类(比如Integer、BigDecimal),避免基础类型默认值干扰非空判断
  • 三种方案都原生支持分页、排序需求,只需要在调用findAll方法时传入Pageable参数即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 15:48:03