Java 8日期与数据库日期的映射关系是什么,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。
Java8中有很多新型的日期类型,比传统的日期类型好用。使用什么和数据库的日期进行映射,却是一个比较复杂的问题。
JDBC 4.2 规范
根据JDBC4.2的规范,Java日期类型和数据库日期类型关系如下:
Java 日期 | 数据库日期 |
---|---|
java.sql.Date | DATE |
java.sql.Time | TIME |
java.sql.Timestamp | TIMESTAMP |
java.util.Calendar | TIMESTAMP |
java.util.Date | TIMESTAMP |
java.time.LocalDate | DATE |
java.time.LocalTime | TIME |
java.time.LocalDateTime | TIMESTAMP |
java.time.OffsetTime | TIME_WITH_TIMEZONE |
java.time.OffsetDatetime | TIMESTAMP_WITH_TIMEZONE |
有两个是比较特别的。
TIMESTAMP_WITH_TIMEZONE:包含 Time Zone 的日期时间(DateTime),映射为OffsetDatetime。
TIME_WITH_TIMEZONE:包含 Time Zone 的时间(Time),映射为OffsetTime。
java8中新的日期类型代替旧日期类型
java.time.LocalDate代替java.sql.Date
java.time.LocalTime代替java.sql.Time
java.time.LocalDateTime代替java.sql.Timestamp
注意:JDBC4.2规范不支持Instant和ZonedOffsetDateTime
PostgreSQL JDBC 实现
Java 日期 | 数据库日期 |
---|---|
java.time.LocalDate | DATE |
java.time.LocalTime | TIME[ WITHOUT TIME ZONE ] |
java.time.LocalDateTime | TIMESTAMP [ WITHOUT TIME ZONE ] |
java.time.OffsetDatetime | TIMESTAMP WITH TIME ZONE |
除了不支持Instant和ZonedOffsetDateTime外,OffsetTime也不支持。
参考:PostgreSQL JDBC: Using Java 8 Date and Time classes
PostgreSQL 数据库
timestamp类似LocalDateTime,只是本地时间。要确保JVM的时区和数据库的时区一致,否则会出现时差。
timestamptz是TIMESTAMP WITH TIME ZONE类型,但并没有保存 Time Zone 信息,只是简单的使用UTC标准时间。理由是Time Zone只用于显示,而如何显示时间应该由应用程序处理,没有必要保存到数据库中。
mysql-">MySQL 数据库
MySQL甚至没有提供TIMESTAMP WITH TIME ZONE的类型,日期时间类型只有DateTime,没有 Time Zone 概念。必须使用jdbc连接中的serverTimezone确定时区。如jdbc:mysql://localhost/ujcms?serverTimezone=Asia/Shanghai。
Oracle 数据库
Date:本地时间。精度到秒。
Timestamp:本地时间。精度可以到纳秒。
TIMESTAMP WITH TIME ZONE:标准的OffsetDateTime,保存有Time Zone 信息。
TIMESTAMP WITH LOCAL TIME ZONE:和PostgresSQL的timestamptz类似,只保存标准的UTC时间,然后根据本地的 Time Zone 进行计算。
SQL Server 数据库
datetime2:本地时间。
datetimeoffset:标准的OffsetDateTime,保存有 Time Zone 信息。
JPA
JPA对日期的支持于JDBC规范是一致的。
Hibernate
Hibernate在JPA的基础上进行了扩展,支持Instant、ZonedDateTime。
但所有的Java8日期类型最后都转换成Timestamp进行处理。也就是说即使数据库支持TIMESTAMP WITH TIME ZONE并保存了时区信息,Hibernate也会将其丢弃,转而使用JVM的时区(时间是确保正确的)。
MyBatis
支持Instant。转为Timestamp处理。
支持ZonedDateTime,直接使用原生的。兼容性差,如PostgreSQL JDBC不支持这种类型的,会报错。
Freemarker
使用freemarker-java-8进行格式化。
支持OffsetDateTime和ZonedDateTime的格式化,使用对象中自带的时区。
不支持Instant格式化,会直接调用toString()方法。官方说会增加Instant的支持,但已经3年没有发布新版本。
Thymeleaf
支持OffsetDateTime和ZonedDateTime的格式化,使用对象中自带的时区。
支持Instant格式化,使用JVM默认时区。
Jackson
Instant:2008-08-08T08:00:00Z
OffsetDateTime:与Instant一致。
ZondDateTime:2008-08-08T08:00:00Z[UTC]
如何选择
LocalDateTime虽然日期显示友好,但时区不确定,取决于JVM的时区。这导致时间也不确定,不同时区的JVM访问数据库,会得到不一样的时间。这非常致命,使用LocalDateTime一定要确保JVM和数据库的时区一致。
按照JDBC规范,毫无疑问应该选择OffsetDateTime。OffsetDateTime是一个好选择,具有像LocalDateTime一样直观友好的日期显示,又能确保时间的确定性。
但由于MySQL和PostgreSQL都没有提供真正的保存时区的TIMESTAMP WITH TIME ZONE,OffsetDateTime其实已经降级为Instant(PostgresSQL的timestamptz本质上就是Instant)。特别是PostgreSQL提供的是一个标准UTC时区,而实际需要的是UTC+8的北京时间,这导致在Freemarker和Thymeleaf中都无法得到正确的格式化。
考虑到数据库兼容性的问题,Instant似乎是一个更好的选择。但JDBC4.2及JDBC4.3都不支持Instant,且Instant在Freemarker中也无法格式化。
大部分数据库都提供真正的TIMESTAMP WITH TIME ZONE,即使是MySQL也能通过设置serverTimezone得到时区正确的OffsetDateTime,再加上JDBC规范的要求,OffsetDateTime还是首选。至于PostgreSQL的兼容性,可以在FreeMarker和Thymeleaf中自定义日期格式化方法。
Java是什么
Java是一门面向对象编程语言,可以编写桌面应用程序、Web应用程序、分布式系统和嵌入式系统应用程序。
看完上述内容,你们掌握Java 8日期与数据库日期的映射关系是什么的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注编程网行业资讯频道,感谢各位的阅读!