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 进行带参数的本机查询

hibernate + spring数据中的本机插入查询

本机查询支持 SQL Server 的 Spring 数据 jpa 流

带有分页的 Spring Data 和 Native Query

如何在 Spring Boot 的本机查询中使用 all(array[])?