【DateTime】Spring Data JPA 3.x 升级 Hibernate 6.x 带来的一个坑
背景
由于惯用一段JPA代码,近期升级了一下 Spring Boot 3.x ,相应的 Spring Data JPA 也升级到了 3.x:
@CreationTimestamp
private Instant createTime; //SQL: create_time datetime default CURRENT_TIMESTAMP null,
现象
无论怎么排查时区、JDBC连接字符串,数据库插入的时间都是UTC的时间。
尝试
- 对于DateTime 类型,修改数据库属性的 URL 没有用处,无论是否设置。
- 影响时间显示的唯一设置是 Database - Data Editor and Viewer 设置 Display temporal data in time zone,这里和其他同事进行对比后发现,留空保持默认即可,不需要改动。
- 和数据库Connector 是 mysql-connector-j 还是 MariaDB Connector/J 无关。
新发现1
数据库DATETIME改成TIMESTAMP类型,仍然有问题,但是在 DataGrip当中直接插入一行,时间显示正常,createTime 和 updateTime. DEFAULT current_timestamp由数据库生成的均正常。
新发现2
JPA的类型 LocalDateTime, Instant 这两种类型目前是IDEA 的 JPA-Buddy 插件自动映射创建的Entity,映射规则是 DATETIME, TIMESTAMP 都映射为 Instant.

新发现3
使用 @CreationTimestamp 可以自动生成 createTime not null 这样的字段值,不用额外设置 insertable=false, updatable=false
Hibernate @CreationTimestamp and @UpdateTimestamp
新发现4
使用 @CreationTimestamp(SourceType.VM) 不正常,SourceType.DB 正常。
分析
经过上述排查和实验过程,可以认为不是数据库DateGrip的错误,也不是连接字符串的错误。
结果是CreationTimestamp 和 UpdateTimestamp 以及 CurrentTimestamp 注解都有个参数:SourceType.VM ,并且有些默认是DB,有些默认就是VM,这就很危险了。
- 如果是SourceType.DB 显式指定 ,则数据库创建的时间数值正确。
- 如果没有指定,SourceType.VM 会触发本文提到的坑,到数据库就会有问题。
结论
Hibernate 6.0 起 修改了 Instant 等数值的映射
6.0 Migration Guide
Github Gist

解决
spring.jpa.properties.hibernate.type.preferred_instant_jdbc_type=TIMESTAMP
DATETIME 是字面值,不受时区的影响
所以不需要在IDEA/DataGrip当中设置过多的URL参数,本身数据库连接信息会包含这些数据的。
Mysql-connector-J: 版本8.3.0, Java应用程序所用的时区相关参数
useJDBCCompliantTimezoneShift=true
useLegacyDatetimeCode=false
serverTimezone=GMT%2B8,
DataGrip/IDEA 数据库参数是,加上和去掉的结果一样
forceConnectionTimeZoneToSession=true
connectionTimeZone=Asia/Shanghai
forceConnectionTimeZone=true
connectionTimeZone=SERVER
preserveInstants=true
注意事项
需要在IDEA或者DataGrip当中的 Database - Data Editor and Viewer 设置当中,将
Display temporal data in time zone 设置为 ``,不能是:Etc/GMT+8 否则显示不正常。

浙公网安备 33010602011771号