迁移到 SpringBoot 3:使用 oracle 驱动程序截断日期不再有效(休眠)

Hel*_*rix 9 oracle spring hibernate spring-data spring-boot

我目前正在将 SpringBoot 2.7 应用程序迁移到 SpringBoot 3。在 SpringData 存储库中使用以下查询:

\n
\n@Query("select b from #{#entityName} b where (trunc(b.date) <= trunc(:date))")\nList<T> findByDate(LocalDateTime date);\n\n
Run Code Online (Sandbox Code Playgroud)\n

虽然这在 SpringBoot 2.7 中效果很好,但在 SpringBoot 3 中会抛出以下消息:

\n
\n

org.hibernate.QueryException:函数 trunc() 的参数 1 的类型为 NUMERIC,但参数的类型为 java.time.LocalDateTime

\n
\n

不幸的是,简单迁移到 datetrunc 并不成功:

\n
\n

错误消息 = ORA-00904: "DATETRUNC": ung\xc3\xbcltige ID

\n
\n

有人有解决方案吗?

\n

此致

\n

Gav*_*ing 6

因此,正如我在之前的回答中指出的那样,没有用于日期截断的标准 HQL 函数。这是因为在大多数 SQL 方言中实现起来相当困难(我还没有真正研究过有多,但它至少很重要。)

然而,特别是对你来说,在今天应该进入 CR 的 Hibernate 6.2 中,我所做的是添加了对Postgres、DB2、Oracle 和 H2 下的功能的未记录的(但经过测试的)支持。当然date_trunc(),在 Oracle 上,这会转化为。trunc()

例如,你可以写:

date_trunc(year,current_timestamp)
Run Code Online (Sandbox Code Playgroud)

当我说它“无证”时,我的意思是您使用它的风险由您自己承担。(已记录的执行此操作的方法仍然是使用FunctionContributor。)

我希望这有帮助。

更新:

哦,顺便说一句,我刚刚注意到您实际上并没有trunc()在查询中使用该函数的完整形式。实际上,您只是剥离了时间戳的时间部分。

实际上,在 HQL 中有多种方法可以做到这一点,而无需使用 Oracle 特定的trunc()函数。(但是当然trunc()/date_trunc()可以做更多你在这里没有使用的事情。)

JPA-标准方式

我在新的 JPA 3.1 规范中添加的改进之一是允许您编写:

extract(date from current_timestamp)
Run Code Online (Sandbox Code Playgroud)

去掉时间戳的时间部分。

HQL 替代方案

cast()但如果您愿意,也可以使用 HQL 函数来完成此操作:

cast(current_timestamp as Date)
Run Code Online (Sandbox Code Playgroud)

这两个选项在 Oracle 上转换为相同的 SQL。


Gav*_*ing 3

在 Hibernate 6 中,我们开始检查 HQL 函数的参数类型。\n由于它trunc(numeric,places)是跨多种 SQL 方言的非常常见的函数,因此我们将其注册为已知函数之一,尽管我们尚未将其提升为 \xe2\x80\ x9cstandard\xe2\x80\x9d HQL 函数(也许我们应该)。

\n

另一方面,Oracle\xe2\x80\x99strunc(date)是 Oracle 特有的,更像是date_trunc()DB2 和 Postgres 上的功能。我们(到目前为止)还没有尝试标准化任何类型的时间戳截断函数,并且我不认为它是一个高优先级。此功能由 注册OracleDialect

\n

因此,如果您尝试调用trunc()a Date,您将收到输入错误。

\n

现在,一般来说,在 Hibernate 6.x 中:

\n
    \n
  • 我们不保证HibernateDialect了解数据库中的每个 SQL 函数,并且您不能依赖这一点。
  • \n
  • 相反,我们有一个相当长的记录 HQL 函数列表,我们承诺这些函数可以跨我们支持的每个数据库移植。(现在trunc()不在该列表中,但它几乎进入了列表。)
  • \n
\n

这完全没问题,因为我们还提供了 API,可以非常轻松地注册新的 SQL 函数,无论是像这样的特定于平台的函数,还是您自己编写的函数,方法如下:

\n
    \n
  • 编写一个自定义Dialect,或者
  • \n
  • 提供一个FunctionContributor.
  • \n
\n

https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-user-defined-functions

\n

或者,如果工作量太大,您可以使用 JPA 标准语法来调用本机 SQL 函数,即function(\'trunc\', date, \'YEAR\'),但我认为这有点不可爱。

\n

  • 嗯,不,这一点也不奇怪:这是 Hibernate 6 的一个重要新功能!我一直直言不讳地谈论 HQL 的整个理念如何从“让垃圾进入数据库”转变为“让我们确保发送一些合理的东西”。我在这里讨论过,例如:https://www.youtube.com/watch?v=pc6QIwx0EL0&amp;t=1564s (2认同)