构造一个与 Postgresql 中的 tsrange 列类型重叠的 Criteria Api 谓词

问题描述 投票:0回答:1

我想创建一个 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")));

如何创建与这些日期重叠的谓词?

java spring criteria-api criteriaquery
1个回答
0
投票

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
准备的。

© www.soinside.com 2019 - 2024. All rights reserved.