如何在分层架构的REST API中通过请求参数实现动态搜索?
问题:REST API添加搜索参数的实现方案
我现在的REST API采用Controller->Service->Repository分层架构,已经实现了获取所有司机的GET接口:
@GetMapping public ResponseEntity<Set<DriverDTO>> getAll(@PageableDefault(value = 15) Pageable pageable) { Page<DriverEntity> driverEntities = driverService.getAll(pageable); Set<DriverDTO> driverDTOSet = modelMapperService.mapPageToSetOfEnteredClass(driverEntities, DriverDTO.class); return new ResponseEntity<>(driverDTOSet, HttpStatus.OK); }
现在想添加name、ageUpTo、ageOver等搜索参数,已经在接口中添加了@RequestParams:
@GetMapping public ResponseEntity<Set<DriverDTO>> getAll(@PageableDefault(value = 15) Pageable pageable, @RequestParam String name, @RequestParam int ageUpTo, @RequestParam int ageOver) { Page<DriverEntity> driverEntities = driverService.getAll(pageable); Set<DriverDTO> driverDTOSet = modelMapperService.mapPageToSetOfEnteredClass(driverEntities, DriverDTO.class); return new ResponseEntity<>(driverDTOSet, HttpStatus.OK); }
Service层的getAll方法如下:
public Page<DriverEntity> getAll(Pageable pageable) { return driverEntityRepository.findAll(pageable); }
接下来该如何实现?是否需要将每个请求参数都传入getAll方法,通过if判断参数是否为空,还是使用switch语句?
实现步骤
1. 调整Controller的请求参数配置
当前的@RequestParam都是必填规则,不符合搜索场景(用户通常只会传部分参数),需要改成可选配置,同时把基本类型换成包装类,避免默认值干扰判断:
@GetMapping public ResponseEntity<Set<DriverDTO>> getAll(@PageableDefault(value = 15) Pageable pageable, @RequestParam(required = false) String name, @RequestParam(required = false) Integer ageUpTo, @RequestParam(required = false) Integer ageOver) { Page<DriverEntity> driverEntities = driverService.getAll(pageable, name, ageUpTo, ageOver); Set<DriverDTO> driverDTOSet = modelMapperService.mapPageToSetOfEnteredClass(driverEntities, DriverDTO.class); return new ResponseEntity<>(driverDTOSet, HttpStatus.OK); }
如果后续参数增多,建议封装成查询DTO来优化代码整洁度:
// 定义查询参数DTO public class DriverQueryDTO { private String name; private Integer ageUpTo; private Integer ageOver; // getter、setter方法 } // Controller修改为 @GetMapping public ResponseEntity<Set<DriverDTO>> getAll(@PageableDefault(value = 15) Pageable pageable, DriverQueryDTO queryDTO) { Page<DriverEntity> driverEntities = driverService.getAll(pageable, queryDTO); // 后续转换逻辑不变 }
2. 修改Service层,实现动态条件查询
不需要用switch,直接用if判断参数是否存在即可。推荐用JPA的Specification实现动态查询,比硬写SQL更灵活适配多条件场景:
// Service层方法修改(直接传参数版) public Page<DriverEntity> getAll(Pageable pageable, String name, Integer ageUpTo, Integer ageOver) { Specification<DriverEntity> spec = (root, query, cb) -> { List<Predicate> predicates = new ArrayList<>(); // 处理name模糊匹配 if (StringUtils.isNotBlank(name)) { predicates.add(cb.like(root.get("name"), "%" + name + "%")); } // 处理ageUpTo:年龄≤指定值 if (ageUpTo != null) { predicates.add(cb.lessThanOrEqualTo(root.get("age"), ageUpTo)); } // 处理ageOver:年龄>指定值 if (ageOver != null) { predicates.add(cb.greaterThan(root.get("age"), ageOver)); } return cb.and(predicates.toArray(new Predicate[0])); }; return driverEntityRepository.findAll(spec, pageable); } // 如果用DTO的话,方法签名和逻辑调整为 public Page<DriverEntity> getAll(Pageable pageable, DriverQueryDTO queryDTO) { Specification<DriverEntity> spec = (root, query, cb) -> { List<Predicate> predicates = new ArrayList<>(); if (StringUtils.isNotBlank(queryDTO.getName())) { predicates.add(cb.like(root.get("name"), "%" + queryDTO.getName() + "%")); } if (queryDTO.getAgeUpTo() != null) { predicates.add(cb.lessThanOrEqualTo(root.get("age"), queryDTO.getAgeUpTo())); } if (queryDTO.getAgeOver() != null) { predicates.add(cb.greaterThan(root.get("age"), queryDTO.getAgeOver())); } return cb.and(predicates.toArray(new Predicate[0])); }; return driverEntityRepository.findAll(spec, pageable); }
3. 让Repository支持Specification
需要让你的DriverEntityRepository继承JpaSpecificationExecutor接口,才能使用Specification查询:
public interface DriverEntityRepository extends JpaRepository<DriverEntity, Long>, JpaSpecificationExecutor<DriverEntity> { }
替代方案:自定义Repository查询方法
如果查询逻辑简单,也可以直接在Repository中定义带条件的查询方法:
// Repository中定义 Page<DriverEntity> findByNameContainingAndAgeLessThanOrEqualToAndAgeGreaterThan(String name, Integer ageUpTo, Integer ageOver, Pageable pageable);
但这种方式的局限性是:当参数为null时,会将null作为匹配条件,而非忽略该条件,所以更适合固定条件的查询,多可选条件场景还是Specification更实用。
总结
- 不需要用switch,用
if判断参数是否存在即可实现动态条件拼接 - 推荐用JPA Specification适配多条件可选的搜索场景,灵活性更高
- 参数较多时封装成DTO,提升代码可读性和维护性
内容的提问来源于stack exchange,提问作者Lulex97
相关产品推荐
相关产品推荐

