如何自定义QueryDsl Predicate实现createdOn日期范围查询
问题描述
当前在Rest Controller中使用以下代码实现基于QueryDsl的分页查询:
public Page<DemoDto> find( @QuerydsPredicate(root = DemoEntiy.class) final Predicate predicate, @PageableDefault(size = 100) @SortDefault(sort = "id", direction = Sort.Direction.ASC) Pageable pageable, @RequestParam MultiValueMap<String, String> parameters) { if (IRequestValidator.isValidParams(parameters)) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Search params have incorrect values or range is too high."); } return DemoEntiyService.findAllByQueryDsl( QueryDslUtil.validPredicate(predicate, parameters), pageable); }
QueryDsl自动生成的Predicate会忽略实体类中无匹配字段名的请求参数。现在需求是:请求传入startDate和endDate参数(格式:?startDate=<some_date>&endDate=<some_date2>),但实体类DemoEntity仅包含createdOn字段,需要自定义Predicate实现createdOn字段介于startDate和endDate之间的查询条件。
仓库定义如下:
public interface DemoRepository extends JpaRepository<DemoEntity, Long>, QuerydsPredicateExecutor<DemoEntity> {}
解决方案
步骤1:解析日期参数并转换为对应类型
从请求参数中提取startDate和endDate,转换为实体类createdOn对应的日期类型(以下示例假设createdOn为LocalDateTime,若为Date或其他类型可调整转换逻辑)。
步骤2:构建日期范围Predicate
利用QueryDsl自动生成的QDemoEntity类,构建createdOn的区间查询条件:
QDemoEntity qDemoEntity = QDemoEntity.demoEntity; Predicate dateRangePredicate = null; // 处理startDate:大于等于起始时间 String startDateStr = parameters.getFirst("startDate"); if (StringUtils.hasText(startDateStr)) { LocalDateTime startDate = LocalDateTime.parse(startDateStr, DateTimeFormatter.ISO_LOCAL_DATE_TIME); dateRangePredicate = qDemoEntity.createdOn.goe(startDate); } // 处理endDate:小于等于结束时间 String endDateStr = parameters.getFirst("endDate"); if (StringUtils.hasText(endDateStr)) { LocalDateTime endDate = LocalDateTime.parse(endDateStr, DateTimeFormatter.ISO_LOCAL_DATE_TIME); Predicate endPredicate = qDemoEntity.createdOn.loe(endDate); dateRangePredicate = dateRangePredicate != null ? dateRangePredicate.and(endPredicate) : endPredicate; }
步骤3:合并原有Predicate与自定义Predicate
将QueryDsl自动生成的predicate和我们构建的日期范围Predicate合并,兼容原有Predicate为空的场景:
Predicate finalPredicate = predicate; if (dateRangePredicate != null) { finalPredicate = finalPredicate != null ? finalPredicate.and(dateRangePredicate) : dateRangePredicate; }
步骤4:修改Controller调用逻辑
把合并后的finalPredicate传入Service层方法,完整Controller代码如下:
public Page<DemoDto> find( @QuerydsPredicate(root = DemoEntiy.class) final Predicate predicate, @PageableDefault(size = 100) @SortDefault(sort = "id", direction = Sort.Direction.ASC) Pageable pageable, @RequestParam MultiValueMap<String, String> parameters) { if (IRequestValidator.isValidParams(parameters)) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Search params have incorrect values or range is too high."); } // 构建日期范围Predicate QDemoEntity qDemoEntity = QDemoEntity.demoEntity; Predicate dateRangePredicate = null; String startDateStr = parameters.getFirst("startDate"); if (StringUtils.hasText(startDateStr)) { LocalDateTime startDate = LocalDateTime.parse(startDateStr, DateTimeFormatter.ISO_LOCAL_DATE_TIME); dateRangePredicate = qDemoEntity.createdOn.goe(startDate); } String endDateStr = parameters.getFirst("endDate"); if (StringUtils.hasText(endDateStr)) { LocalDateTime endDate = LocalDateTime.parse(endDateStr, DateTimeFormatter.ISO_LOCAL_DATE_TIME); Predicate endPredicate = qDemoEntity.createdOn.loe(endDate); dateRangePredicate = dateRangePredicate != null ? dateRangePredicate.and(endPredicate) : endPredicate; } // 合并Predicate Predicate finalPredicate = predicate; if (dateRangePredicate != null) { finalPredicate = finalPredicate != null ? finalPredicate.and(dateRangePredicate) : dateRangePredicate; } // 调用Service查询 return DemoEntiyService.findAllByQueryDsl(finalPredicate, pageable); }
可选优化建议
- 将日期解析、Predicate构建逻辑抽离到
QueryDslUtil工具类,精简Controller代码; - 日期格式可配置化,或根据请求头自适应;
- 扩展
IRequestValidator.isValidParams方法,增加startDate不晚于endDate的校验逻辑,避免非法参数。
注意事项
- 确保项目已正确配置QueryDsl插件(如Maven的
apt-maven-plugin),自动生成QDemoEntity类; - 若
createdOn为java.util.Date类型,需调整日期解析逻辑为SimpleDateFormat或对应转换方式; - 处理参数为空的场景,避免空指针异常。
内容的提问来源于stack exchange,提问作者DEEPAK MALIK
相关产品推荐
相关产品推荐

