Spring @Query 在进行本机查询时无法处理位置参数
Posted
技术标签:
【中文标题】Spring @Query 在进行本机查询时无法处理位置参数【英文标题】:Spring @Query cannot handle positional parameters when doing native query 【发布时间】:2019-01-02 00:38:20 【问题描述】:我正在尝试使用 spring 数据进行本机查询。该查询具有 JPQL 不支持的子查询,因此我必须使用本机查询。我需要的最终查询如下所示:
@Query( value = """select count(*)
from (select * from image a where a.table_id = ?1) as img
inner join
(select b.id from image_set b where b.camera_id = ?2 and b.time between ?3 and ?4) as imgset
on img.image_set_id = imgset.id""",
nativeQuery = true)
fun countByTableIdAndImageSetTimeBetween(tableId: Long, cameraId: Long, fromDateTime: LocalDateTime, toDateTime: LocalDateTime): Long
这没有提供结果,所以我暂时用一个更简单的查询来尝试定位问题。下面我将引用这个查询,直到我至少可以运行它:
@Query( value = "select count(*) from image_set b where b.camera_id = ?2", nativeQuery = true)
fun countByTableIdAndImageSetTimeBetween(tableId: Long, cameraId: Long, fromDateTime: LocalDateTime, toDateTime: LocalDateTime): Long
我遇到的第一个问题是出现异常:
org.hibernate.QueryException: Named parameter [2] not set
经过一番搜索,我发现 JPA 的位置参数是 1 索引的,而 Hibernate 是 0 索引的。我认为错误可能来自尝试将 LocalDateTime 分配给查询中无法使用的位置,并且在执行本机查询时我应该使用 0 索引。这似乎非常值得怀疑,但将 ?2 更改为 ?1 确实 解决了异常。然而,它并没有解决问题。我现在总是得到 0 的结果,但事实并非如此。 我注意到在休眠日志中没有填写参数值:
Hibernate:
/* dynamic native SQL query */ select
count(*)
from
image_set b
where
b.camera_id=?
但是,其他查询似乎也是如此,它们有效,所以我猜 hibernate 不会记录它发送的实际查询?无论如何,现在我几乎迷路了。我不知道我在这里做错了什么,任何帮助将不胜感激。
重要附录: 我在进行本机查询时位置参数基于 0 的可疑假设实际上是不正确的。我刚刚尝试使用?0,我得到了 ArrayIndexOutOfBoundsException:-1。所以看起来 spring 会自己从索引中减去 1。
这改变了问题:我在使用 ?1 时返回 0 的原因确实是因为我使用了错误的值。 0 是在这种情况下的正确反应。 所以我需要弄清楚为什么我在使用 ?2 时会出现错误。
【问题讨论】:
Hibernate 从不使用参数记录。它总是打印没有参数的 sql。如果你的参数是正确的,它应该可以工作 这里countByTableIdAndImageSetTimeBetween,你在方法参数中提供了更多参数,它会在查询中搜索要替换的参数,这就是你得到那个错误的原因,如果你只有一个参数,给一个方法参数 @Karthik R 传递的参数是正确的,我检查过了。 @VinuBibin 当您自己定义查询时,我有点期望方法名称无关紧要。我不知道它将用于映射参数。无论如何,我将方法名称更改为 countByTableIdAndTableCameraIdAndImageSetTimeBetween,我看到的行为完全相同。 方法名也应该像 findByCameraId 和一个参数 【参考方案1】:解决了,这里有一点需要解压。主要是我的愚蠢。
让我们从我真正想要的查询开始,结果总是返回 0:
@Query( value = """select count(*)
from (select * from image a where a.table_id = ?1) as img
inner join
(select b.id from image_set b where b.camera_id = ?2 and b.time between ?3 and ?4) as imgset
on img.image_set_id = imgset.id""",
nativeQuery = true)
fun countByTableIdAndImageSetTimeBetween(tableId: Long, cameraId: Long, fromDateTime: LocalDateTime, toDateTime: LocalDateTime): Long
这不起作用的原因不是位置参数。这是时间的争论。事后看来,这很痛苦,但我花了一段时间才看到:当然,您不能在本地查询中使用 LocalDateTime 作为参数。您必须将其转换为 java.util.Date。
这样就解决了我真正想要解决的问题。其余的几乎是一场野鹅追逐。因为我认为查询不起作用的唯一合理假设是参数传递错误,所以我对同一方法进行了第二个简化查询,因为这是最省力的:
@Query( value = "select count(*) from image_set b where b.camera_id = ?2", nativeQuery = true)
fun countByTableIdAndImageSetTimeBetween(tableId: Long, cameraId: Long, fromDateTime: LocalDateTime, toDateTime: LocalDateTime): Long
问题是这个查询查询的不是同一个实体!因为我只是把它放在另一个方法上,所以它在错误的存储库中,并且它不起作用的原因又是不是由于位置参数。将该查询移至正确的存储库后,它实际上可以正常工作。 这最终使我得出结论,也许参数毕竟不是我的问题,经过一番严厉地查看我的原始查询后,我意识到了问题。
另外两件事需要记录: 一,不,使用 nativeQuery 时,位置参数不需要从零开始的索引。 Spring 自己从索引中减去 1。
二,不,在使用@Query 注解时,方法名称实际上不 很重要。您可以将您的方法命名为 fooBar 并使用您想要的多个参数执行任何查询,这没有什么区别。
感谢大家的努力!
【讨论】:
当查询需要超过 2 个参数时,我建议使用 @Param。以上是关于Spring @Query 在进行本机查询时无法处理位置参数的主要内容,如果未能解决你的问题,请参考以下文章
Postgres Interval 不适用于本机 Spring 数据 JPA 查询
在 Hibernate 中使用 @Query 进行带参数的本机查询
本机查询支持 SQL Server 的 Spring 数据 jpa 流