【发布时间】:2020-01-09 15:21:19
【问题描述】:
我们正面临一个问题。在我们至少运行一年的代码中,我们使用带有自定义查询方法的 spring 存储库,使用 @Query 注释。在这些方法中,我们倾向于根据传入的 @Nullable 参数对查询应用过滤器。到目前为止,它运行良好,对于这样的查询:
@Query(value =
"select p FROM PersonOfInterestEntity p "
+ "where (?1 is null or p.upperPoiIdentifier like ?1) "
+ "and p.status in ?2 "
+ "and (?3 is null or p.createdDtm >= ?3) "
+ "and (?4 is null or p.createdDtm <= ?4) "
+ "and p.ixTenant.id=?5 ")
Page<PersonOfInterestEntity> findAllByNamesAndDateRangeAndStatusesAndTenantId(
@Nullable String identifier, List<StatusEnum> statuses, @Nullable Timestamp startDate, @Nullable Timestamp endDate, String tenantId, Pageable pageable);
正如在网上的文章中发现的那样,这是一种方便的方法,可以使用单个 dao 方法来查询不同的过滤器,并在传入的值为 null 的情况下跳过某些列上的条件。 但问题是,一旦我们升级到hibernate 5.4.10.Final(到目前为止我们使用的是hibernate 5.2.8.Final),我们突然开始报错:
[ WARN] [ org.hibernate.engine.jdbc.spi.SqlExceptionHelper] - SQL Error: 932, SQLState: 42000
[ERROR] [ org.hibernate.engine.jdbc.spi.SqlExceptionHelper] - ORA-00932: inconsistent datatypes: expected TIMESTAMP got BINARY
问题在于,升级的不仅是 hibernate,还有我们从某个第三方团队获得的一大堆依赖项——所以我们不能真正说明 Hibernate 是什么在这里有所作为。此外,我们不能在升级后的代码中将 Hibernate 版本降低回 5.2.8,我们也不能在旧代码中增加适用于这些查询的 Hibernate 版本,因为在这两种情况下我们都会遇到一些启动问题 -> 这很难隔离究竟是什么导致了行为的改变。 有趣的是,相同的语法似乎仍然适用于 String 类型的参数(在查询中的 where 关键字之后直接查看 ?1),问题似乎仅与时间戳有关。
我的问题是,我们应该看看 hibernate、spring-data-jpa 还是 Oracle 驱动程序的变化?我找不到任何可以说明这种向后不一致的文档。我们真的很想跳过解决方法的需要,将我们的方法拆分为每个空/非空参数的单独查询,因为这确实会增加代码中的混乱,因为我们有很多这些可选参数的组合。并且还想了解它到目前为止是如何工作的,以及为什么 Timestamp 与其他类型的不同。
更新:
我刚刚意识到我们还有一些特殊的类会影响代码中的时间戳如何序列化到数据库中。由于计划移植到 MySql,在 db 中需要在 3/100 ms 上进行舍入,所以我们有 UserType 应该负责这样做。这可能会以某种方式影响查询参数绑定,不确定...从调试休眠代码(这很困难,因为它没有进入 IDE 中的线程调用堆栈),我注意到 PreparedStatementParameter 用于部分查询,如下所示:
p.createdDtm >= ?3,将内部预期类型作为 CustomType,并带有添加的特殊类的名称。
具有相同索引的参数查询的一部分:
?3 is null,在没有预期类型的情况下被解析,后来在绑定中解析为(sqlTypeDescriptior=VarbinaryTypeDescriptor 和 javaTypeDescriptor=SerializableTypeDescriptor),后来出现我给出的错误结果。但也要说,调整 Timestamp 值的代码已经存在于以前版本的代码中,该代码在 hibernate 5.2.8 上运行良好......也许这种自定义类型需要对新版本的 hibernate 进行一些调整?
【问题讨论】:
-
请贴出PersonOfInterestEntity类的来源
-
理想情况下,将
org.hibernate.SQL的日志级别设置为DEBUG,将org.hibernate.type.descriptor.sql.BasicBinder设置为TRACE。顺便说一句,我们也使用了这样的语法,但有时会显着降低查询性能,因为数据库几乎没有机会提出正确的计划。所以我们放弃了这样的结构。 -
嗨,日志已经设置好了:
-
logging.level.org.hibernate.type.descriptor.sql=TRACE,在 hib 5.2.8.Final 中它产生这个日志:binding parameter [1] as [VARCHAR] - [null] binding parameter [2] as [VARCHAR] - [null] binding parameter [10] as [VARCHAR] - [4dqv9WCZenvofaDVIipeuA],而在 hib 5.4.0.Final 中它给出这个日志:binding parameter [1] as [VARBINARY] - [null] binding parameter [2] as [VARBINARY] - [null] binding parameter [6] as [VARBINARY] - [null] (same for [7, 8, ,9]... comment is limited in stackoverflow) binding parameter [10] as [VARCHAR] - [mi6MBSusOXjc17EuLBg-_A] -
PersonOfInterestEntity 相当大...它还继承了一些包含有问题的列的基类,我们没有它的来源...反编译代码显示了该列的这一点`@Column ( table = "", name = "CREATED_DTM" ) @Type( type = "timestamp" ) private Timestamp createdDtm;` 正如我所指出的,到目前为止工作得很好......关于性能,我们没有注意到问题,但是查询最多非常简单,一次连接,所以到目前为止没有问题