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

如何用Criteria API为日期加天数?PostgreSQL+Hibernate报错解决

问题描述

需要筛选出无法在客户指定截止日期前完成订单的DeliveryMethod,其中DeliveryMethod的deliveryDeadlineAmount字段(int类型)表示准备订单所需天数。

当前编写的JPA Criteria代码如下:

...

LocalDate deadlineByCustomer = LocalDate.now().plusDays(2);

...

Join<Site, DeliveryMethod> deliveryMethod = site.join(Site.Fields.deliveryMethods, JoinType.INNER);

if (deadlineByCustomer != null) {
    Predicate deliveryMethodCanSatisfyDeadline = criteriaBuilder.lessThanOrEqualTo(
           criteriaBuilder.function("DATEADD", LocalDate.class,
                   criteriaBuilder.literal(TemporalUnit.DAY),
                   deliveryMethod.get(DeliveryMethod.Fields.deliveryDeadlineAmount),
                   criteriaBuilder.literal(LocalDate.now())
           ),
            criteriaBuilder.literal(deadlineByCustomer));
    predicates.add(deliveryMethodCanSatisfyDeadline);
}

...

运行时抛出错误:

Cannot invoke "org.hibernate.metamodel.model.domain.internal.BasicSqmPathSource.getSqmPathType()" because the return value of "org.hibernate.query.sqm.tree.expression.SqmLiteral.getNodeType()" is null

此前尝试用criteriaBuilder.literal("day")替代criteriaBuilder.literal(TemporalUnit.DAY),但出现错误:

Parameter 1 of function timestampadd() has type TEMPORAL_UNIT, but argument is of type java.lang.Object

疑问:使用环境为PostgreSQL + Hibernate,不理解为何Hibernate会调用timestampadd()而非指定的原生SQL函数DATEADD。

解决方案

1. 为什么会调用timestampadd()而非DATEADD

Hibernate的JPA Criteria API中,function()方法并非直接映射原生SQL函数名称,而是优先匹配Hibernate预定义的JPQL标准函数别名。DATEADD是SQL Server专属函数,PostgreSQL没有该函数;Hibernate会将日期加减的标准操作映射为JPQL的timestampadd函数,最终再转换为PostgreSQL的原生语法(比如current_date + ?1 * interval '1 day')。

2. 正确实现方式

方式一:使用Hibernate封装的日期运算API(推荐,跨数据库)

直接调用criteriaBuilder.plus()完成日期加天数的计算,代码更简洁且无需关注数据库原生函数:

if (deadlineByCustomer != null) {
    // 计算当前日期加上准备天数后的截止日期
    Expression<LocalDate> calculatedDeadline = criteriaBuilder.plus(
        criteriaBuilder.literal(LocalDate.now()),
        deliveryMethod.get(DeliveryMethod.Fields.deliveryDeadlineAmount)
    );
    // 筛选"计算出的截止日期晚于客户指定日期"的记录(即无法按时完成)
    Predicate cannotMeetDeadline = criteriaBuilder.greaterThan(
        calculatedDeadline,
        criteriaBuilder.literal(deadlineByCustomer)
    );
    predicates.add(cannotMeetDeadline);
}

方式二:显式使用JPQL标准timestampadd函数

如果要手动调用函数,需遵循JPQL标准格式,传入正确的TemporalUnit参数:

if (deadlineByCustomer != null) {
    Predicate cannotMeetDeadline = criteriaBuilder.greaterThan(
        criteriaBuilder.function(
            "timestampadd", 
            LocalDate.class,
            criteriaBuilder.literal(TemporalUnit.DAY),
            deliveryMethod.get(DeliveryMethod.Fields.deliveryDeadlineAmount),
            criteriaBuilder.literal(LocalDate.now())
        ),
        criteriaBuilder.literal(deadlineByCustomer)
    );
    predicates.add(cannotMeetDeadline);
}

3. 关键注意事项

  • 原代码逻辑是筛选能按时完成的记录,若需求是筛选无法按时完成的,需将lessThanOrEqualTo改为greaterThan。
  • 不要用字符串"day"作为时间单位参数,必须传入TemporalUnit.DAY,Hibernate会自动处理参数类型映射,避免类型错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 18:20:02