通过Hibernate将Java Date转SQL Server datetime2:仍出现datetime式毫秒舍入问题?
我之前也踩过一模一样的坑!虽然你把表字段改成了datetime2(3)来支持精确毫秒,但Hibernate的默认配置可能还在沿用旧的datetime类型逻辑,导致明明插入语句显示正确,数据库里的结果却不对——比如你提到的2017-01-31 01:23:00.999这种边缘值异常。
下面是我亲测有效的解决步骤:
修正实体类的字段映射
别只改数据库字段,要确保Hibernate知道这个列是datetime2(3)类型。如果还在使用旧的java.util.Date,需要同时指定@Temporal和columnDefinition:@Temporal(TemporalType.TIMESTAMP) @Column(name = "your_column_name", columnDefinition = "datetime2(3)") private Date myDateTime;更推荐直接用Java 8+的
java.time.LocalDateTime(Date已经过时了),它的映射更直观,不需要@Temporal:@Column(name = "your_column_name", columnDefinition = "datetime2(3)") private LocalDateTime myDateTime;升级JDBC驱动并配置正确的方言
旧版本的Microsoft JDBC Driver for SQL Server对datetime2的支持有瑕疵,建议升级到6.0及以上版本。同时,Hibernate方言必须用支持datetime2的版本,比如org.hibernate.dialect.SQLServer2008Dialect或者更高(比如SQLServer2012Dialect),旧的SQLServerDialect默认只处理datetime类型,会自动做奇怪的舍入。检查参数绑定的SQL类型
可以开启Hibernate的SQL类型日志,确认参数是按datetime2绑定的,而不是datetime。在日志配置里加一行:logging.level.org.hibernate.type.descriptor.sql=TRACE查看日志输出,如果看到参数类型是
datetime而不是datetime2,说明映射还是没配置对,回到第一步调整。特殊处理0.999毫秒值
偶尔会碰到0.999毫秒被进位成下一秒的情况,这大概率是JDBC驱动的旧bug导致的,升级驱动到最新版就能解决——我之前升完驱动后,2017-01-31 01:23:00.999就能正常存入数据库了。
总的来说,核心就是让Hibernate、JDBC驱动和数据库的类型完全匹配,别让任何一层默认用旧的datetime逻辑处理你的时间值。
内容的提问来源于stack exchange,提问作者Kevin C

