我想创建一个 Java Criteria Api Predicate,它在 Postgres 15 中执行等效的 SQL 查询,如下所示:
SELECT time_range_column FROM public.address_table
WHERE time_range_column && '[2014-09-23, 2015-09-24]'::tsrange;
我在我的实体中声明了
time_range_column
,如下:
@Type(PostgreSQLRangeType.class)
@Column(name = "time_range_column")
private Range\<LocalDateTime\> timeRangeColumn;
我的解决方案如下,但没有返回结果:
String dateTimeString1 = "2014-09-15 00:00:00";
String dateTimeString2 = "2015-09-15 00:00:00";
String dateTimeFormat = "yyyy-MM-dd HH:mm:ss";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateTimeFormat);
LocalDateTime d1 = LocalDateTime.parse(dateTimeString1, formatter);
LocalDateTime d2 = LocalDateTime.parse(dateTimeString2, formatter);
String rangeLiteral = "[" + d1 + "," + d2 + ")";
Predicate dateRangePredicate = criteriaBuilder.and(criteriaBuilder.literal(rangeLiteral)
.in(root.<LocalDateTime>get("time_range_column")));
如何创建与这些日期重叠的谓词?
TL;博士
::
或 &&
不属于 JPA Criteria API
标准的一部分。 您可以使用本机 SQL 查询。
如果您想使用像
PostgreSQL
这样的数据库系统特有的特定特性或功能,可能无法直接在 JPA Criteria API 中使用它们。
有些查询,你可以通过完全指定转义序列来避免头痛,但是对于这个查询;无法同时转义特殊类型,例如
overlap (&&)
、double colon (::)
和 tsrange (cast(string as tsrange))
。
使用本机查询 (@Query) 而不是 Criteria API 创建此查询是合适的,或者;如果您使用
Hibernate
,您可以为此语句创建一个 FunctionContributor
。
示例可能如下所示:
public class PostgreSQLTsrangeOverlapFunction implements FunctionContributor {
@Override
public void contributeFunctions(FunctionContributions functionContributions) {
functionContributions.getFunctionRegistry().registerPattern(
"fn_tsrange_overlap",
"?1 && ?2::tsrange",
functionContributions
.getTypeConfiguration()
.getBasicTypeRegistry()
.resolve(StandardBasicTypes.BOOLEAN)
);
}
}
public static Specification> getSpecification() {
return (root, query, cb) -> {
String dateTimeString1 = "2014-09-15 00:00:00";
String dateTimeString2 = "2015-09-15 00:00:00";
String dateTimeFormat = "yyyy-MM-dd HH:mm:ss";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateTimeFormat);
LocalDateTime d1 = LocalDateTime.parse(dateTimeString1, formatter);
LocalDateTime d2 = LocalDateTime.parse(dateTimeString2, formatter);
Path<Object> field = root.get("time_range_column");
Expression<String> bound = cb.literal("[" + d1 + "," + d2 + "]");
Expression<Boolean> function = cb.function(
"fn_tsrange_overlap",
Boolean.class,
field,
bound);
return cb.isTrue(function);
};
}
(这是一个简单的函数定义方法,可以让它变得更可用。)
然后必须通过 Java ServiceLoader 机制注册该类,方法是将类的全名及其包添加到名为
org.hibernate.boot.model.FunctionContributor
的文件中,并添加到 java 模块的 META-INF/services
目录中。
您使用此规范,您的查询将如下所示:
select ... from table t1
where t1.time_range_column && '[2014-09-15T00:00,2015-09-15T00:00]'::tsrange
它是用
Spring Boot 3.2.1
和Hibernate 6.2.7.Final
准备的。