在数据库中使用 Java 8 LocalDate 和 LocalDateTime 进行休眠

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/43476364/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 07:27:26  来源:igfitidea点击:

Hibernate with Java 8 LocalDate & LocalDateTime in Database

javahibernateutcjava-time

提问by adi

My requirement is to store all dates & date-times in UTC timezone in the database. I am using Java 8's LocalDate& LocalDateTimein my Hibernate entities.

我的要求是在数据库中以 UTC 时区存储所有日期和日期时间。我在我的 Hibernate 实体中使用 Java 8 的LocalDate& LocalDateTime

Is that correct as LocalDate& LocalDateTimedoesn't have timezone associated with them?

这是正确的,因为LocalDate&LocalDateTime没有与它们关联的时区吗?

If not, should I fall back to using good old (or legacy?) Date& Timestamp?

如果没有,我是否应该重新使用旧的(或遗留的?)Date& Timestamp

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?

或者我应该使用 Java 8Instant吗?如果使用Instant,是否有可能只存储日期部分而不存储时间?

The database is MySQL & SQL Server and this is a Spring Boot application.

数据库是 MySQL 和 SQL Server,这是一个 Spring Boot 应用程序。

回答by Basil Bourque

The “Local…” types purposely have no concept of time zone. So they do notrepresent a moment on the timeline. A LocalDateTimerepresents a vague range of possible moments but has no real meaning until assigning an offset or time zone. That means applying a ZoneIdto get a ZonedDateTime.

“本地...”类型故意没有时区的概念。所以它们并不代表时间线上的某个时刻。ALocalDateTime表示可能时刻的模糊范围,但在分配偏移量或时区之前没有真正的意义。这意味着应用 aZoneId来获得 a ZonedDateTime

For example, to say that Christmas this year starts at the first moment of December 25, we say:

例如,要说今年的圣诞节从 12 月 25 日的第一个时刻开始,我们说:

LocalDateTime ldt = LocalDateTime.of( 2017 , 12 , 25 , 0 , 0 , 0 , 0 );

But that stroke of midnight happens earlier in the east than in the west.

但是,午夜的中风在东部比在西部发生得更早。

That is why the elves' Logistics department maps out Santa's route starting at Kiribatiin the Pacific, the earliest time zone in the world at 14 hours ahead of UTC. After delivering there, they route Santa westward to places like New Zealand for its midnight later. Then on to Asia for their midnight later. Then India, and so on, reaching Europe for their midnight several hours later, and then the east coast of North America for their midnight a few hours more later. All of these places experienced that sameLocalDateTimeat different moments, each delivery represented by a differentZonedDateTimeobject.

这就是为什么精灵的后勤部门绘制了圣诞老人的路线,从太平洋的基里巴斯开始,这是世界上最早的时区,比 UTC 早 14 小时。在那里交付后,他们将圣诞老人向西运送到新西兰等地,等待午夜过后。然后在午夜后前往亚洲。然后是印度,以此类推,几个小时后到达欧洲的午夜,然后几个小时后到达北美东海岸的午夜。所有这些地方在不同的时刻都经历过同样的事情LocalDateTime,每次交付都由不同的ZonedDateTime对象代表。

So…

所以…

  • If you want to record the conceptof Christmas starting after midnight on the 25th, use a LocalDateTimeand write into a database column of type TIMESTAMP WITHOUT TIME ZONE.
  • If you want to record the exact moment of each delivery Santa makes, use a ZonedDateTimeand write into a database column of type TIMESTAMP WITH TIME ZONE.
  • 如果你想记录25日午夜之后开始的圣诞节的概念,使用aLocalDateTime并写入类型为的数据库列TIMESTAMP WITHOUT TIME ZONE
  • 如果您想记录圣诞老人每次交付的确切时刻,请使用 aZonedDateTime并写入类型为 的数据库列TIMESTAMP WITH TIME ZONE

About that second bullet, be aware that nearly every database system will use the zone information to adjust the date-time to UTC and store that UTC value. Some save the zone information as well, but some such as Postgres discard the zone info after using it to adjust into UTC. So “with time zone” is something of a misnomer, really meaning “with respectfor time zone”. If you care about remembering that original zone, you may need to store its name in a separate column alongside.

关于第二个要点,请注意几乎每个数据库系统都将使用区域信息将日期时间调整为 UTC 并存储该 UTC 值。有些也保存区域信息,但有些如 Postgres 在使用它调整为 UTC 后丢弃区域信息。所以“with time zone”有点用词不当,真正的意思是“尊重时区”。如果您想记住那个原始区域,您可能需要将其名称存储在一个单独的列中。

Another reason to use Local…types is for future appointments. Politicians enjoy frequently changing their time zone(s) of their jurisdiction. They like to adopt Daylight Saving Time (DST). The like to change the dates of their DST cutovers. They like to drop their adoption of DST. They like to redefine their time zones, changing the boundaries. They like to redefine their offset-from-UTC sometimes by amounts like 15 minutes. And they rarely give advance notice, making such changes with as little as a month or two of warning.

使用Local…类型的另一个原因是为了将来的约会。家喜欢经常改变他们管辖的时区。他们喜欢采用夏令时 (DST)。喜欢改变他们的 DST 转换日期。他们喜欢放弃采用 DST。他们喜欢重新定义他们的时区,改变界限。他们有时喜欢重新定义他们的 UTC 偏移量,比如 15 分钟。而且他们很少提前通知,只需一两个月的警告就做出这样的改变。

So to make medical check-up appointment for next year or in six months, the time zone definition cannot be predicted. So if you want an appointment of 9 AM, you should use a LocalTimeor LocalDateTimerecorded in a database column of type TIMESTAMP WITHOUT TIME ZONE. Otherwise that 9 AM appointment, if zoned where the DST cutover is postponed, may appear as 8 AM or 10 AM.

所以要预约明年或六个月后的体检,时区定义是无法预测的。因此,如果您想预约上午 9 点,则应使用LocalTimeLocalDateTime记录在类型为 的数据库列中TIMESTAMP WITHOUT TIME ZONE。否则,上午 9 点的约会,如果划在 DST 转换被推迟的地方,可能会显示为上午 8 点或上午 10 点。

When generating a projected schedule, you can apply a time zone (ZoneId) to those “local” (unzoned) values to create ZonedDateTimeobjects. But do not rely on those too far out in time when politicians may ruin their meaning by changing the zone(s).

生成计划时间表时,您可以将时区 ( ZoneId) 应用于那些“本地”(未分区)值以创建ZonedDateTime对象。但是,当家可能通过改变区域而破坏他们的意义时,不要依赖那些太远的时间。

Tip: These frequent changes to DST and time zones means you must keep your time zone tzdatadata base up to date. There is a tzdata in your host OS, your JVM, and perhaps in your database system such as Postgres. All three should be frequently updated. Sometimes the zones change faster than the planned update cycles of those products such as Turkey last year deciding to stay on DST with only several weeks notice. So you may occasionally need to manually update those tzdata files. Oracle provides a tool for updating the tzdata of their Java implementations.

提示:这些对 DST 和时区的频繁更改意味着您必须使时区tzdata数据库保持最新。在您的主机操作系统、JVM 以及您的数据库系统(例如 Postgres)中都有一个 tzdata。这三个都应该经常更新。有时区域的变化比这些产品的计划更新周期更快,例如土耳其去年决定继续使用夏令时,只有几周的通知。因此,您可能偶尔需要手动更新这些 tzdata 文件。Oracle 提供了一个工具来更新其 Java 实现的 tzdata。

The general best practice in handling exact moments is to track them in UTC. Apply a time zone only where necessary such as in presentation to a user where they expect to see values in their own parochial time zone. In java.time, the Instantclass represents a moment in the timeline. In UTC with a resolution of nanoseconds.

处理精确时刻的一般最佳实践是在 UTC 中跟踪它们。仅在必要时应用时区,例如在向用户展示时,他们希望看到他们自己狭隘时区的值。在 java.time 中,Instant该类代表时间线中的一个时刻。在 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.

By the way, drivers that comply with JDBC 4.2 and later can deal directly with the java.time types via:

顺便说一句,符合 JDBC 4.2 及更高版本的驱动程序可以通过以下方式直接处理 java.time 类型:

  • PreparedStatement::setObject
  • ResultSet::getObject
  • PreparedStatement::setObject
  • ResultSet::getObject

Oddly, the JDBC 4.2 spec does not require support for the two most common java.timetypes: Instant& ZonedDateTime. The spec does require support for OffsetDateTime. So you can easily convert back-and-forth.

奇怪的是,JDBC 4.2 规范不需要支持两种最常见的java.time类型:Instant& ZonedDateTime。该规范确实需要支持OffsetDateTime. 因此,您可以轻松地来回转换。

Avoid the old legacy data types such as java.util.Dateand java.sql.Timestampwhenever possible. They are poorly designed, confusing, and flawed.

避免旧的遗留数据类型,如java.util.Datejava.sql.Timestamp尽可能。它们设计不佳、令人困惑且有缺陷。

Understand that all of these four are representations of a moment on the timeline in UTC:

理解所有这四个都是 UTC 时间线上的一个时刻的表示:

  • Modern
    • java.time.Instant
    • java.time.OffsetDateTimewith an assigned offset of ZoneOffset.UTC
  • Legacy
    • java.util.Date
    • java.sql.Timestamp
  • 现代的
    • java.time.Instant
    • java.time.OffsetDateTime分配的偏移量为 ZoneOffset.UTC
  • 遗产
    • java.util.Date
    • java.sql.Timestamp

If you want a date-only value without time-of-day and without time zone, use java.time.LocalDate. This class supplants java.sql.Date.

如果您想要一个没有时间和时区的仅日期值,请使用java.time.LocalDate. 此类取代java.sql.Date

As for specific databases, be aware that the SQL standard barely touches on the topic of date-time types and their handling. Furthermore, the various databases vary widely, and I really mean widely, in their support of date-time features. Some have virtually no support. Some mix SQL standard types with proprietary types that either predate the standard types or are intended as alternatives to the standard types. In addition, JDBC drivers differ in their behavior with marshaling date-time values to/from the database. Be sure to study the documentation and practice, practice, practice.

对于特定的数据库,请注意 SQL 标准几乎没有涉及日期时间类型及其处理的主题。此外,各种数据库在支持日期时间特性方面差异很大,我的意思是广泛。有些几乎没有支持。有些将 SQL 标准类型与专有类型混合使用,这些专有类型要么早于标准类型,要么旨在作为标准类型的替代品。此外,JDBC 驱动程序在向/从数据库编组日期时间值的行为方面有所不同。一定要研究文档和练习,练习,练习。

Table of date-time types in Java (both legacy and modern) and in standard SQL

Java(旧版和现代版)和标准 SQL 中的日期时间类型表

回答by Justas

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?

还是我应该使用 Java 8 的 Instant?如果使用 Instant,是否可以只存储日期部分而不存储时间?

Instant should be suitable for the most operations.

Instant 应该适合大多数操作。

Add hibernate-java8to pom.xmlto support Java 8 time API:

添加hibernate-java8pom.xml支持 Java 8 时间 API:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-java8</artifactId>
    <version>${version.hibernate}</version>
</dependency>

Then you can use LocalDateor LocalDateTimeor Instantfor Hibernate entity fields. You need to remove @Temporal(TemporalType.TIMESTAMP).

然后您可以使用LocalDateLocalDateTimeInstant用于Hibernate 实体字段。您需要删除@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?

我的要求是在数据库中以 UTC 时区存储所有日期和日期时间。我在我的 Hibernate 实体中使用 Java 8 的 LocalDate 和 LocalDateTime。

这是否正确,因为 LocalDate 和 LocalDateTime 没有与之关联的时区?

You can set default JVM timezone somewhere in a configurational code:

您可以在配置代码中的某处设置默认 JVM 时区:

@PostConstruct
void setUTCTimezone() {
    TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
}

Then you'll operate UTC time in your code.

然后您将在代码中操作 UTC 时间。

To use Java 8 date types in DTO, you need to add Jsr310JpaConverters:

要在 DTO 中使用 Java 8 日期类型,您需要添加Jsr310JpaConverters

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

And:

和:

@EntityScan(basePackageClasses = { Application.class,    Jsr310JpaConverters.class })
SpringBootApplication
public class Application { … }

More options:

更多的选择:

回答by Jake O

Part of the new Date API they split the types of dates up. The correct class that includes timezones is ZonedDateTime

在新的 Date API 中,他们将日期类型分开。包含时区的正确类是 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);

Prints:

印刷:

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