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);
}
由于众所周知的 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
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);
}