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



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


@Query("select b from #{#entityName} b where (trunc(b.date) <= trunc(:date))")
List<T> findByDate(LocalDateTime date);

虽然这在SpringBoot 2.7中非常有效,但在SpringBoot3中会抛出以下消息:

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

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

错误消息=ORA-00904:";DATETRUNC":ungültige ID

有人对此有解决方案吗?

向致以最良好的问候

因此,正如我在前面的回答中所指出的,没有用于日期截断的标准HQL函数。这是因为它在大多数SQL方言中都很难实现(我还没有真正研究过有多难,但它至少是不平凡的。)

但是,特别是对您来说,在Hibernate 6.2中,我所做的是在Postgres、DB2、Oracle和H2下添加了未记录的(但经过测试)对date_trunc()函数的支持。在Oracle上,这当然可以转换为trunc()

例如,您可以写:

date_trunc(year,current_timestamp)

当我说这是";"无证";我的意思是你使用它的风险自负。(记录在案的方法仍然是使用FunctionContributor。)

我希望这能有所帮助。

更新:

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

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

JPA标准方式

我在新的JPA3.1规范中添加的改进之一是让您编写:

extract(date from current_timestamp)

去掉时间戳中的时间部分。

HQL替代方案

但如果您喜欢,也可以使用HQLcast()功能:

cast(current_timestamp as Date)

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

在Hibernate 6中,我们开始检查HQL函数的参数类型。由于trunc(numeric,places)在SQL的许多方言中是一个非常常见的函数,我们将其注册为已知函数之一,尽管我们还没有将其提升为"标准"HQL函数(也许我们应该)。

另一方面,Oracle的trunc(date)非常特定于Oracle,更像DB2和Postgres上的date_trunc()函数。到目前为止,我们还没有尝试对任何类型的时间戳截断函数进行标准化,我认为这不是一个高优先级的函数。此函数不是由OracleDialect注册的。

因此,如果您尝试在Date上调用trunc(),则会出现键入错误。

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

  • 我们不保证HibernateDialect了解数据库中的每一个SQL函数,而且您不能依赖它
  • 相反,我们有一长串记录在案的HQL函数,我们承诺这些函数可以在我们支持的每个数据库中方便地工作。(现在trunc()不在名单上,但它几乎进入了名单。)

这很好,因为我们还提供了API,可以很容易地注册新的SQL函数,可以是像这样的特定于平台的函数,也可以是您自己编写的函数,通过:

  • 编写自定义Dialect,或
  • 提供CCD_ 18

https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-用户定义功能

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

我正在使用Oracle数据库,在迁移到新的hibernate后,我遇到了同样的问题。

我通过重写org.hibernate.dialect.OracleDialect并将ORACLE_TRUNC函数添加到functionRegistry中来解决这个问题。我使用了与Hibernate<6Oracle8iDialect

public class CustomOracleDialect extends OracleDialect {
public CustomOracleDialect() {
super();
}
@Override
public void initializeFunctionRegistry(FunctionContributions functionContributions) {
super.initializeFunctionRegistry(functionContributions);
functionContributions.getFunctionRegistry().register("ORACLE_TRUNC", new StandardSQLFunction("trunc"));
}
}

将hibernate配置为在application.properties/application.yml文件中使用此方言:

spring:
jpa:
database-platform: com.example.CustomOracleDialect

在查询中使用ORACLE_TRUNC()而不是TRUNC()。

正如Gavin King所说,我尝试使用hibernate 6.2 RC2中的DATE_TRUNC(date, my_date)函数,但它将我的查询转换为CAST(my_date AS DATE),而不是TRUNC(my_date),导致日期比较问题。

我找到了一种非常简单的方法来替换trunc函数,但它有点难看。

to_date(to_char(entity.date,'YYYY-MM-DD'), 'YYYY-MM-DD')

如果它能在等待更好的jpql解决方案时帮助某人。。。

我不得不用CriteriaBuilder这样解决这个问题:

cb.function("date_trunc", java.sql.Date.class, cb.literal("DD"), root.get("myAttribute"))

其中,root是实体的jakarta.persistence.criteria.Root的实例,myAttribute是要截断的时间戳类型的属性。

相关内容

  • 没有找到相关文章

最新更新