如何解决如何使用 JPQL、Spring Data Repositories 和 Hibernate 为 TimescaleDB `time_bucket` 函数参数化 Postgresql 间隔
我在 PostgreSQL 13 上使用带有 TimescaleDB 扩展的 Spring Data JPA(下面带有 Hibernate,JPA 2.1),并希望使用 time_bucket
函数。这需要 bucket_width
是 INTERVAL
和 time
是数据的 TIMESTAMP
列。
我想将它放在 Spring Data Repository 中,并想使用 JPQL @Query
将数据提取到一个投影中,该投影表示返回时间段的聚合计数、平均值等。我不想使用本机查询,因为我想加入其他一些表,并自动填充它们的实体。
我将 time_bucket
函数注册到我要扩展的 PostgisPG95Dialect
,如下所示:
public class CustomPostgresqlDialect extends PostgisPG95Dialect {
public CustomPostgresqlDialect() {
super();
this.registerFunction("time_bucket",new StandardSQLFunction("time_bucket",new OffsetDateTimeType()));
}
}
如果 bucket_width
是硬编码的,所有这些都可以正常工作。但我希望 bucket_width
成为查询方法的参数。
以下工作正常:
@Query("select sys as system,"
+ "function('time_bucket','10 mins',vt.ts) as startTime,"
+ "count(vt) as total,avg(vt.speed) as avgSpeed "
+ "from Data vt "
+ "JOIN vt.system sys "
+ "where sys.sysId = :sysId and "
+ "function('time_bucket',vt.ts) between :from and :to "
+ "group by system,startTime "
+ "order by startTime")
List<SummaryAggregate> getSummaryData(
@Param("sysId") String sysId,@Param("from") OffsetDateTime from,@Param("to") OffsetDateTime to);
但是当我尝试参数化间隔时,我无法让它工作。我尝试将间隔作为字符串传递,因为这就是它在硬编码版本中的编写方式:
@Query("select sys as system,:grouping,@Param("to") OffsetDateTime to,@Param("grouping") String grouping);
其中 grouping
被传递一个类似于 10 mins
的值。
但为此我收到此错误:
SQL Error: 0,SQLState: 42883
ERROR: function time_bucket(character varying,timestamp with time zone) does not exist
Hint: No function matches the given name and argument types. You might need to add explicit type casts.
Position: 61
然后我尝试将其更改为 Duration
,因为 Hibernate translates Duration
to PostgreSQL Interval types
@Query("select sys as system,@Param("grouping") Duration grouping);
但我还是遇到了同样的错误,这次它认为 Duration 是 bigint
而不是 Interval
。
SQL Error: 0,SQLState: 42883
ERROR: function time_bucket(bigint,timestamp with time zone) does not exist
Hint: No function matches the given name and argument types. You might need to add explicit type casts.
Position: 61
有没有办法使用 JPQL 参数化 Interval
?
解决方法
有一种方法,但您必须为此目的注册一个自定义函数,因为您不能强制转换为任意 SQL 类型。
public class CastInterval implements SQLFunction {
@Override
public boolean hasArguments() {
return true;
}
@Override
public boolean hasParenthesesIfNoArguments() {
return true;
}
@Override
public Type getReturnType(Type firstArgumentType,Mapping mapping) throws QueryException {
return firstArgumentType;
}
@Override
public String render(Type firstArgumentType,List args,SessionFactoryImplementor factory) throws QueryException {
return "cast(" + args.get(0) + " as interval)";
}
}
您必须在方言中注册该函数。
因此,如果方言按照指示进行扩展,则可以通过以下方式完成:
this.registerFunction("castInterval",new CastInterval());
然后你可以这样使用它:function('time_bucket',castInterval(:grouping),vt.ts)
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。