2017-04-18 73 views
7

这可能是一个愚蠢的问题,带有隐式/直接的答案,但不知何故,我无法将其包围。在数据库中使用Java 8 LocalDate和LocalDateTime进行休眠

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

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

如果不是,我是否应该回到使用旧的(或遗产?)Date & Timestamp

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

如果它有什么不同,我希望它们工作的数据库是MySQL & SQL Server。

再一次,如果它做出更大的区别,那么它就是一个Spring Boot应用程序。

谢谢!

回答

12

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

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

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

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

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

所以......

  • 如果你想要录制的概念的圣诞节午夜25日之后出发,使用LocalDateTime和写入TIMESTAMP WITHOUT TIME ZONE类型的数据库列。
  • 如果您想记录圣诞老人每次送货的确切时刻,请使用ZonedDateTime并写入类型为TIMESTAMP WITH TIME ZONE的数据库列。

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

使用Local…类型的另一个原因是未来约会。政治家喜欢经常改变他们辖区的时区。他们喜欢采用夏令时(DST)。喜欢改变他们DST切换的日期。他们喜欢放弃采用DST。他们喜欢重新定义他们的时区,改变边界。他们喜欢重新定义它们的UTC偏移量,有时候会像15分钟那样。他们很少提前发出通知,在短短一个月或两个小时内发出警告。

因此,要进行下一年或六个月的体检预约,无法预测时区定义。因此,如果您要预约上午9点,则应使用记录在TIMESTAMP WITHOUT TIME ZONE类型的数据库列中的LocalTimeLocalDateTime。否则,上午9点的预约,如果在夏令时延期的区域划分,可能会显示为上午8点或上午10点。

生成投影计划时,可以将时区(ZoneId)应用于这些“本地”(未分区)值以创建ZonedDateTime对象。但是,当政客们通过改变区域可能毁掉他们的意义时,不要依赖那些时间太远的人。

提示:对DST和时区进行频繁更改意味着您必须保持您的时区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。时间类型通过:

  • PreparedStatement::setObject
  • ResultSet::getObject

避免旧旧的数据类型,如java.util.Datejava.sql.Timestamp只要有可能。它们设计不佳,混乱和缺陷。

明白,所有这四个都在UTC时间线上的时刻表示:

  • 现代
    • java.time.Instant
    • java.time.OffsetDateTimeZoneOffset.UTC
  • 分配的偏移
  • 传统
    • java.util.Date
    • java.sql.Timestamp

如果你想有一个日期,只值没有时间的天,没有时区,使用java.time.LocalDate。这个类代替java.sql.Date

至于具体的数据库,要知道,SQL标准几乎没有触及到的日期时间类型及其处理的话题。此外,各种数据库有很大的不同,我真的是广泛,在他们的支持的日期时间的功能。有些几乎没有支持。有些将SQL标准类型与专有类型混合使用,这些类型要么早于标准类型,要么作为标准类型的替代品。另外,JDBC驱动程序的行为与将日期时间值封送到数据库或从数据库封装日期时间值不同。一定要研究文件和练习,练习,练习。

+0

我非常同意这样的说法,即一段时间应该以UTC存储,并且只有在向用户显示时才划分区域。 –

+0

我真的很希望我8岁的女儿(谁声称圣诞老人是不真实的)有一段时间读这个。不仅是一个很好的答案,但是当我得到“这就是为什么精灵......”的时候upvoted。:) – NealeU

+0

TimeZone精美的解释。 Upvoted,谢谢:-) –

-4

他们将新日期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 
+0

我知道'ZonedDateTime'类。我的问题是关于在Hibernate中使用'LocalDate'和'LocalDateTime'类在UTC中将日期和日期时间存储在数据库中的问题。 – adi

+0

然后你会想使用最可能的DateFormatter –

+0

感谢您再次阅读问题。你能否更新你的答案,以便真正回答我问的问题? – adi

6

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

即时应该是适合大多数操作。

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

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

然后你可以使用对Hibernate实体领域LocalDateLocalDateTimeInstant。您需要删除@Temporal(TemporalType.TIMESTAMP)

我的要求是存储所有日期&日期时间在UTC时区在 数据库。我用我 休眠实体的Java 8的LOCALDATE的& LocalDateTime。

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

您可以在构型码的地方设置默认JVM时区:

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

然后你会在你的代码运行UTC时间。

使用Java在DTO 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 { … } 

更多选择: