2016-03-15 83 views
2

我已经阅读了许多关于类似问题的答案,但仍然无法理解整个逻辑。 我的目标是在我的应用程序内部处理日期(包括时间,不仅仅是年份等),并考虑夏时制转换和服务器的时区。我使用hibernate和postgresql db。我的错误是对来自数据库的所有日期使用 java.util.Date类型。不久我意识到它不包含时区信息。现在问题出现了,如何使用正确的日期(考虑日光和时区)?Java从数据库映射到休眠映射的日期(时区问题和夏令时)

什么是正确的方法来做到这一点?使用不同类型的类如Calendar?或者就这样离开它,但是在我使用日期的每个地方,我应该使用日历将它转换为本地时区。

让我困惑的是数据如何来自数据库到Date对象,如果它的安全将日期对象转换为日历以包含时区等?

回答

2

java.util.Date不带真正的时区信息。按照惯例,它是一个UTC时间戳(好吧,这不是因为Javadoc状态跳过了leap seconds)。

但是,如果您认为它是UTC,那么至少在Calendar.getTime()方法中您确实很好。

在构建/创建Date实例的应用程序逻辑中使用Calendar而不是Date是很好的第一步。使用日历方法setTimeZone来管理应用程序中的时区,它将为您重新计算字段值,如HOUR_OF_DAY

Hibernate使用JDBC来执行SQL语句,因此它绑定到JDBC API的可能性以及您使用的JDBC驱动程序的行为并导致数据库的可能性。

我的经验是,JDBC驱动程序和数据库确实在日期/时间处理方面有所不同,尤其是当涉及到时区时。例如,postgres sql具有带和不带时区的日期/时间字段类型。不幸的是,带时区的类型不属于SQL规范的一部分,因此它们不属于JDBC规范。所以Hibernate不支持它们。

简短解答的简短解释: 解决此问题的最常见方法是确保始终使用相同的时区来存储和加载来自Hibernate的数据。一个简单的方法是使用UTC时区,因为Calendar.getTime()方法总是将当前时间返回为UTC Date实例,您可以直接将其传递给Hibernate。 从数据库装载值,使用Calendar.setTime(Date)方法将它们转换为Calendar

+0

如果我不得不在服务器上使用UTC,我将显式设置日历时区,然后使用Calendar.setTime(Date)也可以吗? – maximus

+0

如果你想在一个非UTC时区有'Date'内容,你应该首先使用带有UTC的'Calendar.getInstance(TimeZone)'创建一个'Calendar',然后'setTime(Date)',然后将它改为任何你喜欢使用'setTimeZone(TimeZone)'的时区。这是否符合你的问题? – Alexander

+0

只是为了确认,我留下了现在使用的hibernate实体代码(使用java.util.Date),但是在那里我使用了那个日期,我只是将它们转换成Calendar来描述它的方式吗? – maximus

1

每当您读取/写入数据库时​​,确保您的日期(及其时区)一致的一种方法是确保您在数据库中使用UTC时区。如前所述,JDBC存在一个常见问题,因为当它从数据库中读取日期时,默认情况下会将日期视为本地JVM时区。因此,如果您的本地JVM时区与数据库中使用的时区不同,您将面临意外的时区转换(例如,在不同时区中的两个位置运行应用程序时)。

有一篇很好的文章解释了问题的根源here。还有一个小型的开源库DbAssist,你可以在你的项目中使用它来确保JDBC和Hibernate将数据库中的日期视为UTC时区。

要包括修复,添加下面的依赖关系到您的POM文件:

<dependency> 
    <groupId>com.montrosesoftware</groupId> 
    <artifactId>DbAssist-5.2.2</artifactId> 
    <version>1.0-RELEASE</version> 
</dependency> 

这种特殊的修补程序适用于Hibernate的5.2.2版本,但如果你使用任何其他的Hibernate,只是参照本link为适当的修复版本。它还包括安装指南,根据您是使用HBM文件还是JPA注释(带或不带Spring Boot)映射您的实体字段而有所不同。在这两种情况下,应用修复只是一行code的问题。

这个库(修复)的好处是你不必改变实体类中的任何东西。一旦添加了依赖关系并应用修复,实体中的java.util.Date字段就被认为是UTC时区(从DB写入/读取时)。