当使用 spring-data-jpa 2.6.2 使用 java.sql.Timestamp 查询 IS NULL 时,hsqldb 中的转换中的数据类型不兼容

问题描述 投票:0回答:2

spring-boot-starter-data-jpa 2.6.4 依赖于 spring-data-jpa 2.6.2,后者依赖于 hsqldb 2.5.2 和 hibernate 5.6.7.Final

当我们想要查询 CrudRepository 中 java.sql.Timestamp 类型的多个值时,即

@Query("SELECT dm " +
        " FROM DataModel dm " +
        "WHERE (dm.ts1 IS NULL OR :ts1 IS NULL OR dm.ts1 < :ts1) " +
        "  AND (dm.ts2 IS NULL OR :ts2 IS NULL OR dm.ts2 < :ts2)")
List<DataModel> findByTimestamps(@Param("ts1") Timestamp ts1, @Param("ts2") Timestamp ts2);

它失败了

org.hsqldb.HsqlException: incompatible data type in conversion

spring-data-jpa 2.6.3 也会发生这种情况

降级到 hsqldb 2.5.1 可以工作

用于重现的存储库: https://github.com/daveyx/spring-data-hsqldb-timestamp-issue

问题出现在

org.hsqldb.jdbc.JDBCPreparedStatement#setTimestamp(int, java.sql.Timestamp, java.util.Calendar)
,因为参数类型错误。

看起来 @Param 注释参数的“IS NULL”部分导致 JDBCPreparedStatement#setTimestamp 中的“CharacterType”而不是“DateTimeType”。

hibernate spring-data-jpa timestamp hsqldb
2个回答
2
投票

正如您提到的,问题是由条件

OR :ts1 IS NULL
OR :ts2 IS NULL
引起的。 Hibernate 将这些以
? IS NULL
的形式应用于 SQL 查询,并且 HSQLDB 将参数解析为字符串。然后 Hibernate 假设参数类型解析为 java.sql.Timestamp 并调用 setTimestamp(int, Timestamp) 方法,这会导致问题。

可以添加CAST来确定参数的类型。

@Query("SELECT dm " +
    " FROM DataModel dm " +
    "WHERE (dm.ts1 IS NULL OR CAST(:ts1 AS TIMESTAMP) IS NULL OR dm.ts1 < :ts1) " +
    "  AND (dm.ts2 IS NULL OR CAST(:ts2 AS TIMESTAMP) IS NULL OR dm.ts2 < :ts2)")

HSQLDB 版本 2.5.1 对

setTimestamp(int, Timestamp)
比较宽松,当目标不是时间戳时,将时间戳转换为字符串。 2.5.2 和 2.6.0 版本中的回归使其更加严格。 HSQLDB 的未来版本将恢复到宽松的方法。


0
投票

@fredt 我对日期也有同样的问题,将有助于修复以下查询的空检查:

@Query("SELECT p FROM PromotionCodeDraft p WHERE " +
        "(:personId IS NULL OR p.requester.id = :personId) AND " +
        "(:promotionCode IS NULL OR p.promotionCode LIKE %:promotionCode%) AND " +
        "(:workflowStatus IS NULL OR p.workflowStatus = :workflowStatus) AND " +
        "(:customerLabel IS NULL OR p.customerLabel LIKE %:customerLabel%) AND " +
        "((:active IS NULL) OR " +
        "(:active = false AND (p.active = false OR p.active IS NULL)) OR " +
        "(:active = true AND p.active = true)) AND " +
        "((:startDate IS NULL AND :endDate IS NULL) OR " +
        "(:startDate IS NOT NULL AND :endDate IS NULL AND p.startDate = :startDate) OR " +
        "(:endDate IS NOT NULL AND :startDate IS NULL AND p.endDate = :endDate) OR " +
        "(:startDate IS NOT NULL AND :endDate IS NOT NULL AND p.startDate >= :startDate AND p.endDate <= :endDate))")
© www.soinside.com 2019 - 2024. All rights reserved.