使用数据库中的 Java 8 LocalDate 和 LocalDateTime 休眠
Hibernate with Java 8 LocalDate & LocalDateTime in Database
我的要求是在数据库中以 UTC 时区存储所有日期和日期时间。我在我的 Hibernate 实体中使用 Java 8 的 LocalDate
& LocalDateTime
。
这是否正确,因为 LocalDate
和 LocalDateTime
没有与之关联的时区?
如果不是,我是否应该退回到使用旧的(或遗留的?)Date
& Timestamp
?
或者我应该使用 Java 8 的 Instant
?如果使用Instant
,是否可以只存储日期部分,而不存储时间?
数据库是 MySQL & SQL 服务器,这是一个 Spring 引导应用程序。
新日期的一部分 API 他们将日期类型分开。包含时区的正确 class 是 ZonedDateTime
// Get the current date and time
ZonedDateTime date1 = ZonedDateTime.parse("2007-12-03T10:15:30+05:30[Asia/Karachi]");
System.out.println("date1: " + date1);
ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println("Zoned Date Time: " + zonedDateTime);
ZoneId id = ZoneId.of("Europe/Paris");
System.out.println("ZoneId: " + id);
ZoneId currentZone = ZoneId.systemDefault();
System.out.println("CurrentZone: " + currentZone);
打印:
date1: 2007-12-03T10:15:30+05:00[Asia/Karachi]
Zoned Date Time: 2017-04-18T11:36:09.126-04:00[America/New_York]
ZoneId: Europe/Paris
CurrentZone: America/New_York
“本地...”类型故意没有时区概念。所以他们 而不是 代表时间轴上的一个时刻。 LocalDateTime
表示可能时刻的模糊范围,但在分配偏移量或时区之前没有实际意义。这意味着应用 ZoneId
得到 ZonedDateTime
。
比如说今年的圣诞节从12月25日的第一刻开始,我们说:
LocalDateTime ldt = LocalDateTime.of( 2017 , 12 , 25 , 0 , 0 , 0 , 0 );
但午夜那声钟声,东比西早。
这就是为什么精灵的后勤部门将圣诞老人的路线绘制在Kiribati太平洋,世界上最早的时区,比UTC早14小时。送到那里后,他们将圣诞老人向西运送到新西兰等地,等到午夜时分。然后在午夜后前往亚洲。然后是印度,依此类推,在几个小时后的午夜到达欧洲,然后在几个小时后的午夜到达北美东海岸。所有这些地方在不同时刻都经历了 相同 LocalDateTime
,每次交付都由 不同 ZonedDateTime
对象表示。
所以……
- 如果要记录 25 日午夜后开始的圣诞节的 概念,请使用
LocalDateTime
并写入类型为 [=18= 的数据库列].
- 如果您想记录圣诞老人每次送货的确切时间,请使用
ZonedDateTime
并写入 TIMESTAMP WITH TIME ZONE
. 类型的数据库列
关于第二个项目符号,请注意几乎每个数据库系统都会使用区域信息将日期时间调整为 UTC 并存储该 UTC 值。有些也保存时区信息,但有些如 Postgres 在使用它调整为 UTC 后丢弃时区信息。所以“with time zone”有点用词不当,真正的意思是“with respect for time zone”。如果您想记住那个原始区域,您可能需要将其名称存储在旁边的单独列中。
使用 Local…
类型的另一个原因是为了将来的约会。政客们喜欢经常改变他们管辖范围内的时区。他们喜欢采用夏令时 (DST)。他们喜欢更改 DST 转换的日期。他们喜欢放弃采用 DST。他们喜欢重新定义时区,改变界限。他们有时喜欢重新定义他们与 UTC 的偏移量,例如 15 分钟。而且他们很少提前通知,在一两个月的警告前就做出了这样的改变。
因此要预约明年或六个月后的体检,无法预测时区定义。因此,如果您想预约上午 9 点,您应该使用 LocalTime
或 LocalDateTime
记录在类型为 TIMESTAMP WITHOUT TIME ZONE
的数据库列中。否则,上午 9 点的约会,如果在 DST 切换被推迟的地方进行分区,可能会显示为上午 8 点或 10 点。
生成预计时间表时,您可以将时区 (ZoneId
) 应用于那些“本地”(未分区)值以创建 ZonedDateTime
对象。但是,当政客们可能会通过改变区域来破坏他们的意义时,不要依赖那些太晚的时间。
提示:DST 和时区的这些频繁更改意味着您必须使时区 tzdata 数据库保持最新。在您的主机 OS、您的 JVM 中以及可能在您的数据库系统(如 Postgres)中有一个 tzdata。这三个都应该经常更新。有时,这些区域的变化速度比这些产品的计划更新周期更快,例如土耳其去年决定继续使用夏令时,但只通知了几周。所以你可能偶尔需要手动更新那些 tzdata 文件。 Oracle 提供了一个工具来更新其 Java 实现的 tzdata。
处理精确时刻的一般最佳做法是在 UTC 中跟踪它们。仅在必要时应用时区,例如在向用户展示时,他们希望在他们自己的狭隘时区中看到值。在java.time中,Instant
class表示时间轴中的一个时刻。在 UTC 中,分辨率为纳秒。
Instant instant = Instant.now() ; // Current moment on the timeline in UTC.
ZonedDateTime zdt = instant.atZone( z ) ; // Assign a time zone to view the same moment through the lens of a particular region’s wall-clock time.
Instant instant = zdt.toInstant(); // revert back to UTC, stripping away the time zone. But still the same moment in the timeline.
顺便说一句,符合 JDBC 4.2 及更高版本的驱动程序可以通过以下方式直接处理 java.time 类型:
PreparedStatement::setObject
ResultSet::getObject
奇怪的是,JDBC 4.2 规范不需要支持两种最常见的 java.time 类型:Instant
和 ZonedDateTime
.该规范确实需要支持 OffsetDateTime
。因此,您可以轻松地来回转换。
尽可能避免使用旧的遗留数据类型,例如 java.util.Date
和 java.sql.Timestamp
。它们设计不佳、令人困惑且存在缺陷。
了解所有这四个都是 UTC 时间轴上的时刻的表示:
- 现代
java.time.Instant
java.time.OffsetDateTime
分配的偏移量为 ZoneOffset.UTC
- 旧版
java.util.Date
java.sql.Timestamp
如果您想要一个没有时间和时区的纯日期值,请使用 java.time.LocalDate
。 class 取代 java.sql.Date
.
至于特定的数据库,请注意 SQL 标准几乎没有涉及日期时间类型及其处理的主题。此外,各种数据库差异很大,我的意思是广泛,因为它们支持日期时间功能。有些人几乎没有支持。有些将 SQL 标准类型与早于标准类型或旨在替代标准类型的专有类型混合使用。此外,JDBC 驱动程序在将日期时间值 to/from 编组到数据库时的行为有所不同。一定要研究文档和实践,实践,实践。
Or should I be using Java 8's Instant? If using Instant, will there be
a possibility to store only the date part, without time?
Instant应该适合大多数操作。
在pom.xml
中添加hibernate-java8
以支持Java8次API:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-java8</artifactId>
<version>${version.hibernate}</version>
</dependency>
然后你可以对 Hibernate 实体字段使用 LocalDate
或 LocalDateTime
或 Instant
。您需要删除 @Temporal(TemporalType.TIMESTAMP)
。
My requirement is to store all dates & date-times in UTC timezone in
the database. I am using Java 8's LocalDate & LocalDateTime in my
Hibernate entities.
Is that correct as LocalDate & LocalDateTime doesn't have timezone
associated with them?
您可以在配置代码的某处设置默认 JVM 时区:
@PostConstruct
void setUTCTimezone() {
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
}
然后您将在代码中操作 UTC 时间。
要在DTO中使用Java 8种日期类型,需要添加:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
并且:
@EntityScan(basePackageClasses = { Application.class, Jsr310JpaConverters.class })
SpringBootApplication
public class Application { … }
更多选项:
- Force Java timezone as GMT/UTC
- How to set java timezone?
- How to set a JVM TimeZone Properly
我的要求是在数据库中以 UTC 时区存储所有日期和日期时间。我在我的 Hibernate 实体中使用 Java 8 的 LocalDate
& LocalDateTime
。
这是否正确,因为 LocalDate
和 LocalDateTime
没有与之关联的时区?
如果不是,我是否应该退回到使用旧的(或遗留的?)Date
& Timestamp
?
或者我应该使用 Java 8 的 Instant
?如果使用Instant
,是否可以只存储日期部分,而不存储时间?
数据库是 MySQL & SQL 服务器,这是一个 Spring 引导应用程序。
新日期的一部分 API 他们将日期类型分开。包含时区的正确 class 是 ZonedDateTime
// Get the current date and time
ZonedDateTime date1 = ZonedDateTime.parse("2007-12-03T10:15:30+05:30[Asia/Karachi]");
System.out.println("date1: " + date1);
ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println("Zoned Date Time: " + zonedDateTime);
ZoneId id = ZoneId.of("Europe/Paris");
System.out.println("ZoneId: " + id);
ZoneId currentZone = ZoneId.systemDefault();
System.out.println("CurrentZone: " + currentZone);
打印:
date1: 2007-12-03T10:15:30+05:00[Asia/Karachi]
Zoned Date Time: 2017-04-18T11:36:09.126-04:00[America/New_York]
ZoneId: Europe/Paris
CurrentZone: America/New_York
“本地...”类型故意没有时区概念。所以他们 而不是 代表时间轴上的一个时刻。 LocalDateTime
表示可能时刻的模糊范围,但在分配偏移量或时区之前没有实际意义。这意味着应用 ZoneId
得到 ZonedDateTime
。
比如说今年的圣诞节从12月25日的第一刻开始,我们说:
LocalDateTime ldt = LocalDateTime.of( 2017 , 12 , 25 , 0 , 0 , 0 , 0 );
但午夜那声钟声,东比西早。
这就是为什么精灵的后勤部门将圣诞老人的路线绘制在Kiribati太平洋,世界上最早的时区,比UTC早14小时。送到那里后,他们将圣诞老人向西运送到新西兰等地,等到午夜时分。然后在午夜后前往亚洲。然后是印度,依此类推,在几个小时后的午夜到达欧洲,然后在几个小时后的午夜到达北美东海岸。所有这些地方在不同时刻都经历了 相同 LocalDateTime
,每次交付都由 不同 ZonedDateTime
对象表示。
所以……
- 如果要记录 25 日午夜后开始的圣诞节的 概念,请使用
LocalDateTime
并写入类型为 [=18= 的数据库列]. - 如果您想记录圣诞老人每次送货的确切时间,请使用
ZonedDateTime
并写入TIMESTAMP WITH TIME ZONE
. 类型的数据库列
关于第二个项目符号,请注意几乎每个数据库系统都会使用区域信息将日期时间调整为 UTC 并存储该 UTC 值。有些也保存时区信息,但有些如 Postgres 在使用它调整为 UTC 后丢弃时区信息。所以“with time zone”有点用词不当,真正的意思是“with respect for time zone”。如果您想记住那个原始区域,您可能需要将其名称存储在旁边的单独列中。
使用 Local…
类型的另一个原因是为了将来的约会。政客们喜欢经常改变他们管辖范围内的时区。他们喜欢采用夏令时 (DST)。他们喜欢更改 DST 转换的日期。他们喜欢放弃采用 DST。他们喜欢重新定义时区,改变界限。他们有时喜欢重新定义他们与 UTC 的偏移量,例如 15 分钟。而且他们很少提前通知,在一两个月的警告前就做出了这样的改变。
因此要预约明年或六个月后的体检,无法预测时区定义。因此,如果您想预约上午 9 点,您应该使用 LocalTime
或 LocalDateTime
记录在类型为 TIMESTAMP WITHOUT TIME ZONE
的数据库列中。否则,上午 9 点的约会,如果在 DST 切换被推迟的地方进行分区,可能会显示为上午 8 点或 10 点。
生成预计时间表时,您可以将时区 (ZoneId
) 应用于那些“本地”(未分区)值以创建 ZonedDateTime
对象。但是,当政客们可能会通过改变区域来破坏他们的意义时,不要依赖那些太晚的时间。
提示:DST 和时区的这些频繁更改意味着您必须使时区 tzdata 数据库保持最新。在您的主机 OS、您的 JVM 中以及可能在您的数据库系统(如 Postgres)中有一个 tzdata。这三个都应该经常更新。有时,这些区域的变化速度比这些产品的计划更新周期更快,例如土耳其去年决定继续使用夏令时,但只通知了几周。所以你可能偶尔需要手动更新那些 tzdata 文件。 Oracle 提供了一个工具来更新其 Java 实现的 tzdata。
处理精确时刻的一般最佳做法是在 UTC 中跟踪它们。仅在必要时应用时区,例如在向用户展示时,他们希望在他们自己的狭隘时区中看到值。在java.time中,Instant
class表示时间轴中的一个时刻。在 UTC 中,分辨率为纳秒。
Instant instant = Instant.now() ; // Current moment on the timeline in UTC.
ZonedDateTime zdt = instant.atZone( z ) ; // Assign a time zone to view the same moment through the lens of a particular region’s wall-clock time.
Instant instant = zdt.toInstant(); // revert back to UTC, stripping away the time zone. But still the same moment in the timeline.
顺便说一句,符合 JDBC 4.2 及更高版本的驱动程序可以通过以下方式直接处理 java.time 类型:
PreparedStatement::setObject
ResultSet::getObject
奇怪的是,JDBC 4.2 规范不需要支持两种最常见的 java.time 类型:Instant
和 ZonedDateTime
.该规范确实需要支持 OffsetDateTime
。因此,您可以轻松地来回转换。
尽可能避免使用旧的遗留数据类型,例如 java.util.Date
和 java.sql.Timestamp
。它们设计不佳、令人困惑且存在缺陷。
了解所有这四个都是 UTC 时间轴上的时刻的表示:
- 现代
java.time.Instant
java.time.OffsetDateTime
分配的偏移量为ZoneOffset.UTC
- 旧版
java.util.Date
java.sql.Timestamp
如果您想要一个没有时间和时区的纯日期值,请使用 java.time.LocalDate
。 class 取代 java.sql.Date
.
至于特定的数据库,请注意 SQL 标准几乎没有涉及日期时间类型及其处理的主题。此外,各种数据库差异很大,我的意思是广泛,因为它们支持日期时间功能。有些人几乎没有支持。有些将 SQL 标准类型与早于标准类型或旨在替代标准类型的专有类型混合使用。此外,JDBC 驱动程序在将日期时间值 to/from 编组到数据库时的行为有所不同。一定要研究文档和实践,实践,实践。
Or should I be using Java 8's Instant? If using Instant, will there be a possibility to store only the date part, without time?
Instant应该适合大多数操作。
在pom.xml
中添加hibernate-java8
以支持Java8次API:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-java8</artifactId>
<version>${version.hibernate}</version>
</dependency>
然后你可以对 Hibernate 实体字段使用 LocalDate
或 LocalDateTime
或 Instant
。您需要删除 @Temporal(TemporalType.TIMESTAMP)
。
My requirement is to store all dates & date-times in UTC timezone in the database. I am using Java 8's LocalDate & LocalDateTime in my Hibernate entities.
Is that correct as LocalDate & LocalDateTime doesn't have timezone associated with them?
您可以在配置代码的某处设置默认 JVM 时区:
@PostConstruct
void setUTCTimezone() {
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
}
然后您将在代码中操作 UTC 时间。
要在DTO中使用Java 8种日期类型,需要添加
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
并且:
@EntityScan(basePackageClasses = { Application.class, Jsr310JpaConverters.class })
SpringBootApplication
public class Application { … }
更多选项:
- Force Java timezone as GMT/UTC
- How to set java timezone?
- How to set a JVM TimeZone Properly