首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用Hibernate/JPA和JDK Date的不必要的自动时区转换

使用Hibernate/JPA和JDK Date的不必要的自动时区转换
EN

Stack Overflow用户
提问于 2014-03-05 22:16:47
回答 3查看 17.3K关注 0票数 11

我使用Hibernate (4.2)作为我的持久性提供程序,并且我有一个包含日期字段的JPA实体:

代码语言:javascript
运行
复制
@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;
  }
  . . .
}

与START_DATE对应的列被定义为START_DATE TIMESTAMP (无时区)。

我在应用程序内部使用Joda-Time (2.3)来处理日期(总是在UTC中),并且在持久化实体之前,我使用Joda的toDate()类的DateTime方法来获取JDK Date对象,以便遵循映射:

代码语言:javascript
运行
复制
public void myMethod(DateTime startDateUTC) {
  . . .
  MyTable table = /* obtain somehow */
  table.setStartDate(startDateUTC.toDate());
  . . .
}

当我在DB中查看存储的值时,我注意到某个地方(JDK?冬眠?)使用运行代码的JVM的默认时区转换日期值。我的例子是“美国/芝加哥”。

这个问题在夏令储蓄时间(DST)附近确实表现出来。例如,如果内部时间是

代码语言:javascript
运行
复制
2014-03-09T02:55:00Z

它被存储为

代码语言:javascript
运行
复制
09-Mar-14 03:55:00

我想要的是,把它作为

代码语言:javascript
运行
复制
09-Mar-14 02:55:00

然而,在CDT中,3月9日凌晨2:55分并不存在("Spring forward")。所以有什么(JDK?冬眠?)把日期提前了。

我希望存储在DB中的瞬间处于UTC中。毕竟,这就是我在内部处理它给我的应用程序的方式,但是一旦我把它交给持久化,它就会被转换成我的默认时区。

注意:我无法使用设置默认的TimeZone

代码语言:javascript
运行
复制
TimeZone.setDefault(TimeZone.getTimeZone("UTC"))

,因为我正在运行的JVM是跨多个应用程序共享的.

如何在不将JVM默认时区设置为UTC的情况下将日期存储在UTC中?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2014-03-06 20:51:18

我自己也碰到这个的。我看到的是,即使您在日期中将UTC指定为时区(并且可以通过打印出来并在末尾看到'Z‘来查看它),但出于某种原因,JVM希望使用JVM的默认时区来接管和转换日期。

无论如何,您需要的是一个自定义映射来解决这个问题。尝试使用贾迪拉

代码语言:javascript
运行
复制
@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;
  }
  . . .
}

默认情况下,Jadira的PersistentDate类在将日期转换为存储在DB中的毫秒值时使用UTC作为时区。您可以指定其他时区,但听起来像是要存储UTC。

正如您的帖子中的注释所示,有时您用来查询DB的工具是对您进行愚蠢的自动--我的--默认-基于TZ的基于TZ的转换,这使您相信这个值仍然是不正确的。

您还可以尝试存储原始值(作为整数),以使自己确信正在存储正确的毫秒值。

HTH,

莫西

票数 8
EN

Stack Overflow用户

发布于 2016-11-08 12:47:55

有一篇关于这个意外时区移位问题的文章,您可以查看这里。它为问题的根源提供了解释,并说明了如何处理它。当然,主要的假设是我们希望将日期作为UTC存储在数据库中。

例如,每当您从数据库中读取日期(假设: 9:54 UTC)时,JDBC跳过有关时区的任何信息。因此,JVM通过JDBC接收到的是一个日期,它被解释为它位于本地时区(对于我的例子来说,是9:54 UTC+2)。如果本地时区与UTC不同(通常是这样),我们将以不正确的时移结束。

在写入数据库时也会发生类似的情况。

有一个小型开源项目DbAssist为不同版本的Hibernate提供修复。因此,如果您正在使用Hibernate 4.2.21,只需在POM文件中添加以下Maven依赖项,您的问题就解决了(可以在库github上找到安装的详细说明,例如Spring )。

代码语言:javascript
运行
复制
<dependency>
    <groupId>com.montrosesoftware</groupId>
    <artifactId>DbAssist-4.2.21</artifactId>
    <version>1.0-RELEASE</version>
</dependency>

应用此修复后,实体类中的java.util.Date字段将按预期的方式读取和持久化:就好像它们作为UTC存储在数据库中一样。如果使用JPA注释,则不必更改实体中的映射(如前面的响应之一);它是自动完成的。

在内部,修补程序使用自定义UTC日期类型,该类型覆盖Hibernate的日期类型,以强制它将DB中的所有日期视为UTC。然后,为了应用从java.util.DateUtcDateType的映射,它使用了@Typedef注释。见下文:

代码语言:javascript
运行
复制
@TypeDef(name = "UtcDateType", defaultForType = Date.class, typeClass = UtcDateType.class),
package com.montrosesoftware.dbassist.types;

如果您的项目依赖Hibernate HBM文件或其他版本的Hibernate,请访问项目的github wiki,了解如何安装正确的修补程序。

票数 5
EN

Stack Overflow用户

发布于 2014-03-05 22:28:54

您试过从getDateTime对象中使用DateTime (DateTimeZone x)吗?

就像这样:

代码语言:javascript
运行
复制
public void myMethod(DateTime startDateUTC) {
. . .
  MyTable table = /* obtain somehow */
  table.setStartDate(startDateUTC.toDateTime(DateTimeZone.UTC));
  . . .
}

希望它能帮到你!

票数 -2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/22210570

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档