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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在数据库中使用Java 8 LocalDate和LocalDateTime进行Hibernate相关的知识,希望对你有一定的参考价值。

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

这是正确的,因为LocalDateLocalDateTime没有与它们相关的时区吗?

如果没有,我应该回到使用好旧(或传统?)DateTimestamp

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

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

答案

“Local ...”类型故意没有时区概念。所以他们并不代表时间表上的时刻。 LocalDateTime代表了一系列模糊的可能时刻,但在指定偏移或时区之前没有任何实际意义。这意味着应用ZoneId来获得ZonedDateTime

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

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

但午夜时分发生在东部早于西部。

这就是为什么精灵的物流部门在太平洋地区的Kiribati开始绘制圣诞老人的路线,这是世界上最早的时区,比UTC早14个小时。在那里交付之后,他们将圣诞老人向西行驶到新西兰等地的午夜。然后去亚洲过夜。然后是印度,等等,几个小时后到达欧洲的午夜,然后是北美东海岸的午夜几个小时之后。所有这些地方在不同的时刻经历了相同的LocalDateTime,每个交付由不同的ZonedDateTime对象代表。

所以…

  • 如果你想在25日午夜之后记录圣诞节的概念,请使用LocalDateTime并写入TIMESTAMP WITHOUT TIME ZONE类型的数据库列。
  • 如果要记录Santa发送的每个交付的确切时刻,请使用ZonedDateTime并写入TIMESTAMP WITH TIME ZONE类型的数据库列。

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

使用Local…类型的另一个原因是将来的约会。政客们经常喜欢改变其管辖范围的时区。他们喜欢采用夏令时(DST)。更改DST转换日期的类似内容。他们喜欢放弃采用DST。他们喜欢重新定义他们的时区,改变边界。他们喜欢重新定义他们的UTC偏移量,有时候是15分钟。他们很少提前通知,只需要一两个月的警告即可进行此类更改。

因此,为了明年或六个月进行体检,无法预测时区定义。因此,如果您想预约上午9点,您应该使用LocalTime类型的数据库列中记录的LocalDateTimeTIMESTAMP WITHOUT TIME ZONE。否则上午9点的预约,如果划分到DST转换推迟的地方,可能会显示为上午8点或上午10点。

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

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

处理精确时刻的一般最佳实践是以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.

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

  • PreparedStatement::setObject
  • ResultSet::getObject

尽可能避免旧的遗留数据类型,如java.util.Datejava.sql.Timestamp。它们设计糟糕,容易混淆,有缺陷。

了解所有这四个都是UTC时间轴上片刻的表示:

  • 现代 java.time.Instant java.time.OffsetDateTime指定的ZoneOffset.UTC偏移量
  • 遗产 java.util.Date java.sql.Timestamp

如果您想要一个没有时间且没有时区的仅限日期的值,请使用java.time.LocalDate。这个班级取代了java.sql.Date

至于特定的数据库,请注意SQL标准几乎没有涉及日期时间类型及其处理的主题。此外,各种数据库的差异很大,我支持日期时间功能的确非常广泛。有些人几乎没有支持。有些将SQL标准类型与专有类型混合在一起,这些类型要么早于标准类型,要么作为标准类型的替代品。此外,JDBC驱动程序的行为与编组数据库的日期时间值或来自数据库的日期时间值不同。务必学习文档和练习,练习,练习。

另一答案

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

瞬间应该适合大多数操作。

hibernate-java8添加到pom.xml以支持Java 8时间API:

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

然后你可以使用LocalDateLocalDateTimeInstant用于Hibernate实体字段。你需要删除@Temporal(TemporalType.TIMESTAMP)

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

这是正确的,因为LocalDate和LocalDateTime没有与它们相关的时区?

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

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

然后,您将在代码中运行UTC时间。

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

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

和:

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

更多的选择:

另一答案

它们是新的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);

打印:

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

以上是关于在数据库中使用Java 8 LocalDate和LocalDateTime进行Hibernate的主要内容,如果未能解决你的问题,请参考以下文章

在 mongoDB 中存储 java 8 LocalDate

Java日期LocalDate使用

Java 8 Time Api 使用(LocalDate,LocalTime和LocalDateTime等)

Java 8 Time Api 使用(LocalDate,LocalTime和LocalDateTime等)

如何比较 LocalDate 实例 Java 8

Grails 3.2 JSON 视图中的 Java 8 LocalDate