JPA Criteria API + PostgreSQL 按日期过滤,精度为分钟

JPA Criteria API + PostgreSQL filter by date with minute precision

由于众所周知的 Hibernate+Postgres bug,我决定使用 Criteria API,因为我的许多论点都可以 null(来自 REST 调用)。

由于 Criteria API 对每个数据库客户端的行为不同,我无法在涉及日期(yyyy-MM-dd HH:mm:ss 格式)时获得解决方案。对我有用的任何东西都不令人满意,因为我失去了精度或者我得到了例外。重点:

这是我的实体的 field/column 定义,它映射到 Postgres 数据库中的 timestamp 列类型:

@Column(name = "last_modified_date",
        nullable = false,
        updatable = false,
        columnDefinition = "TIMESTAMP WITHOUT TIME ZONE")
@Temporal(TemporalType.DATE)
protected Date lastModifiedDate;

这是我使用 Criteria 的 JpaRepository 接口API:

public 接口 PageHistoryRepository 扩展 JpaRepository, JpaSpecificationExecutor {

default Page<PageHistoryEntity> findPages(Date fromDate,
                                          Date toDate,
                                          Pageable pageable) {
    return findAll(search(fromDate, toDate), pageable);
}

static Specification<PageHistoryEntity> search(Date fromDate, Date toDate) {
    return (root, cq, cb) -> {
        //To ensure we start with a predicate
        Predicate predicate = cb.isTrue(cb.literal(true));

        // this works but has no time precision
        if (fromDate != null) {
            Predicate _predicate = cb.greaterThanOrEqualTo(root.get(PageHistoryEntity_.lastModifiedDate.getName()).as(java.util.Date.class), fromDate);
            predicate = cb.and(predicate,_predicate);
        }

        // this works aswell but again looses time precision
        if (toDate != null) {
            Predicate _predicate = cb.lessThanOrEqualTo(root.get(PageHistoryEntity_.lastModifiedDate.getName()), toDate);
            predicate = cb.and(predicate,_predicate);
        }

        // this does not work
        if (toDate != null) {
            Predicate _predicate = cb.lessThanOrEqualTo(
                    cb.function("date_trunc", Calendar.class, cb.literal("minute"), root.get(PageHistoryEntity_.lastModifiedDate.getName()).as(Calendar.class)),
                    cb.function("date_trunc", Calendar.class, cb.literal("minute"), cb.literal(toDate)).as(Calendar.class));
            predicate = cb.and(predicate,_predicate);
        }

        return predicate;
    };
}

}

我希望它起作用的最后一个谓词抛出异常:

org.postgresql.util.PSQLException: ERROR: function date_trunc(character varying, unknown) is not unique
  Wskazówka: Could not choose a best candidate function. You might need to add explicit type casts.

传入日期格式没问题,调试时有值(对应Postman发送的值所以一切正常):

这里我还提供了数据在 Postgres 中的显示方式:

我的问题是,如果知道用户可能不提供任何日期或仅提供其中一个或两个使用 Criteria API+PostgreSQL,我如何按日期过滤我的搜索结果?理想情况下,我希望我的最后一个谓词使用 date_trunc 来工作,但这现在对我来说太雄心勃勃了。

我想我的实验还不够,建立这样的标准以保持时间精度的正确方法:

if (toDate != null) {
    Predicate _predicate = cb.lessThanOrEqualTo(
            cb.function("date_trunc", Date.class, cb.literal("minute"), root.get(PageHistoryEntity_.lastModifiedDate.getName())),
            toDate);
    predicate = cb.and(predicate,_predicate);
}