我目前正在将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:中
- 我们不保证Hibernate
Dialect
了解数据库中的每一个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
是要截断的时间戳类型的属性。