J S*_*rry 8 java datetime hibernate jpa date
我使用Hibernate(4.2)作为我的持久性提供程序,我有一个包含Date字段的JPA实体:
@Entity
@Table(name = "MY_TABLE")
public class MyTable implements Serializable {
. . .
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "START_DATE")
private Date startDate;
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
. . .
}
Run Code Online (Sandbox Code Playgroud)
与START_DATE对应的列定义为START_DATE TIMESTAMP(无时区).
我在我的应用程序内部使用Joda-Time(2.3)来处理日期(总是在UTC中),并且在持久化实体之前,我使用toDate()Joda的DateTime类的方法来获取JDK Date对象以遵守映射:
public void myMethod(DateTime startDateUTC) {
. . .
MyTable table = /* obtain somehow */
table.setStartDate(startDateUTC.toDate());
. . .
}
Run Code Online (Sandbox Code Playgroud)
当我在DB中查看存储的值时,我注意到某处(JDK?Hibernate?)使用代码运行的JVM的默认时区转换Date值.在我的情况下是"美国/芝加哥".
问题确实在夏令时(DST)附近显现出来.例如,如果内部时间是
2014-03-09T02:55:00Z
Run Code Online (Sandbox Code Playgroud)
它被存储为
09-Mar-14 03:55:00
Run Code Online (Sandbox Code Playgroud)
我想要的是将它存储为
09-Mar-14 02:55:00
Run Code Online (Sandbox Code Playgroud)
但是,在CDT,3月9日凌晨2:55不存在("春季前进").所以(JDK?Hibernate?)正在向前推进日期.
我希望存储在数据库中的瞬间是UTC.毕竟,这就是我在内部处理我的应用程序的方式,但是一旦我将其交给持久化,它就会转换为我的默认时区.
注意:我无法使用默认TimeZone
TimeZone.setDefault(TimeZone.getTimeZone("UTC"))
Run Code Online (Sandbox Code Playgroud)
因为我运行的JVM在多个应用程序之间共享.
如何在不将JVM默认时区设置为UTC的情况下以UTC格式存储日期?
小智 7
我自己也碰到了这个.我看到的是,即使您已将UTC指定为日期中的时区(并且可以通过将其打印出来并在末尾看到'Z'来看到这一点),出于某种原因,JVM希望接管并且使用JVM的默认时区为您转换日期.
无论如何,你需要的是一个自定义映射来解决这个问题.尝试使用 Jadira:
@Entity
@Table(name = "MY_TABLE")
public class MyTable implements Serializable {
. . .
@Column(name = "START_DATE")
@Type(type="org.jadira.usertype.dateandtime.legacyjdk.PersistentDate")
private Date startDate;
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
. . .
}
Run Code Online (Sandbox Code Playgroud)
默认情况下,Jadira的PersistentDate类在将日期转换为存储在DB中的毫秒值时使用UTC作为时区.您可以指定其他时区,但听起来像是您要存储的UTC.
正如您对帖子的评论所暗示的那样,有时您用来查询数据库的工具正在为您进行盲目的愚蠢的自动 - 我 - 我 - JDK - 默认 - TZ转换,导致您相信该值仍然不正确.
您也可以尝试存储原始值(作为INTEGER),以说服自己存储正确的毫秒值.
HTH,
摩西
小智 5
有一篇关于这个意外时区偏移问题的文章,您可以在此处查看。它提供了问题根源的解释并说明了如何处理它。当然,主要的假设是我们希望将日期作为 UTC 存储在数据库中。
例如,每当您从数据库中读取日期时(比方说:UTC 时间 9:54),JDBC 都会跳过有关时区的任何信息。因此,JVM 通过 JDBC 接收到的日期是解释为本地时区的日期(对我而言,9:54 UTC+2)。如果本地时区与 UTC 不同(通常如此),我们最终会得到不正确的时移。
写入数据库时也会出现类似情况。
有一个小型开源项目 DbAssist 为不同版本的 Hibernate 提供修复。因此,如果您使用的是 Hibernate 4.2.21,只需将以下 Maven 依赖项添加到您的 POM 文件中,您的问题就解决了(使用例如 Spring Boot 进行设置的详细安装说明可以在库github上找到)。
<dependency>
<groupId>com.montrosesoftware</groupId>
<artifactId>DbAssist-4.2.21</artifactId>
<version>1.0-RELEASE</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)
应用此修复程序后,java.util.Date实体类中的字段将按预期读取和持久化:就好像它们以 UTC 格式存储在数据库中。如果您使用的是 JPA 注释,则不必更改实体中的映射(如上一个响应中的一个);它是自动完成的。
在内部,此修复程序使用自定义 UTC 日期类型,该类型覆盖 Hibernate 的日期类型,以强制其将数据库中的所有日期视为 UTC。然后,为了应用从java.util.Dateto的映射UtcDateType,它使用@Typedef注释。见下文:
@TypeDef(name = "UtcDateType", defaultForType = Date.class, typeClass = UtcDateType.class),
package com.montrosesoftware.dbassist.types;
Run Code Online (Sandbox Code Playgroud)
如果您的项目依赖于 Hibernate HBM 文件或其他版本的 Hibernate,请转到项目的 github wiki 以获取更详细的说明如何安装正确的修复程序。