java - 在数据库中使用 Java 8 LocalDate 和 LocalDateTime 进行 hibernate

标签 java hibernate utc java-time

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

是否正确为 LocalDate & LocalDateTime没有与它们关联的时区?

如果没有,我应该回退使用旧的(或遗留的?)Date & Timestamp ?

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

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

最佳答案

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

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

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

但是,午夜的钟声在东方比在西方发生得更早。

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

所以…
  • 如果你想记录25日午夜之后开始的圣诞节的概念,使用LocalDateTime并写入 TIMESTAMP WITHOUT TIME ZONE 类型的数据库列中.
  • 如果您想记录圣诞老人每次送货的确切时刻,请使用 ZonedDateTime并写入 TIMESTAMP WITH TIME ZONE 类型的数据库列中.

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

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

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

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

    提示:这些对 DST 和时区的频繁更改意味着您必须保留时区 tzdata数据库是最新的。在您的主机操作系统、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.Datejava.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 驱动程序在向/从数据库编码(marshal)日期时间值的行为方面有所不同。一定要研究文档和练习,练习,练习。

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

    关于java - 在数据库中使用 Java 8 LocalDate 和 LocalDateTime 进行 hibernate ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43476364/

    相关文章:

    ruby-on-rails - 在单个模型上设置default_timezone

    javascript - 在angularjs中获取全局(UTC或GMT)时间而不是本地时间

    java - 数据未保存在数据库中

    java - Autowiring 到非 Spring 托管类 (POJO) 时出现 NullPointerException - Spring Boot

    database - 在构建应用程序时,您首先考虑数据库或对象吗?

    java - Hibernate 和 jOOQ 共享事务

    java - Maven 错误 :Perhaps you are running on a JRE rather than a JDK?

    java - 错误 : cannot assign a value to final variable

    java - 关于 hibernate 中的数据检索

    oracle - 如何获取 Oracle 上 SYSDATE 的 UTC 值