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

如何自定义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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 21:59:50