CodeGym /课程 /SQL & Hibernate /日期映射

日期映射

SQL & Hibernate
第 12 级 , 课程 3
可用

4.1 历史的补充

将 Java 对象保存到数据库的任务几乎是在 Java 语言创建后立即发生的。当时,Java 语言中只有一种数据类型 Date,它按照 UNIX 时间标准存储时间:自 1970 年以来的毫秒数。

嗯,在当时的数据库中,日期已经有不同的数据类型,至少日期、时间和日期+时间有不同的类型:

  • 日期
  • 时间
  • 时间戳

因此,Java 语言的创建者为其添加了一个特殊的包——java.sql,其中包含类:

  • java.sql.日期
  • java.sql.时间
  • java.sql.时间戳

映射这些类是一种真正的乐趣:


@Entity
public class TemporalValues {
 
	@Basic
    private java.sql.Date sqlDate;
 
	@Basic
    private java.sql.Time sqlTime;
 
    @Basic
    private java.sql.Timestamp sqlTimestamp;
}

但是由于程序员习惯于使用类java.util.Date,Hibernate 添加了一个特殊的注释@Temporal来控制 Date 类型的映射。

例子:

// If the annotation is missing, then the database will have a TIMESTAMP type
Date dateAsTimestamp;

@Temporal(TemporalType.DATE) // will be mapped to DATE type
Date dateAsDate;

@Temporal(TemporalType.TIME) // will be mapped to TIME type
Date dateAsTime;

类型java.util.Calendarjava.util.Date默认类型使用TIMESTAMP类型在数据库中表示它们。

4.2 新时间

目前,有了地图,一切都变得更加简单和美好。所有数据库都支持 4 种类型的数据来处理时间:

  • DATE - 日期:年、月、日。
  • TIME - 时间:时、分、秒。
  • TIMESTAMP - 日期、时间和纳秒。
  • TIMESTAMP WITH TIME ZONE - 时间戳和时区(时区名称或偏移量)。

来表示类型 日期在 Java 中,您需要使用java.time.LocalDateJDK 8 DateTime API 中的一个类。

类型时间来自数据库的数据可以用 Java 中的两种类型表示:java.time.LocalTimejava.time.OffsetTime. 没什么复杂的。

以及类型所代表的确切日期和时间时间戳在基础中,在 Java 中它可以用 4 种类型表示:

  • java.time.Instant
  • java.time.LocalDateTime
  • java.time.OffsetDateTime
  • java.time.ZonedDateTime

最后带时区的时间戳可以用两种类型表示:

  • java.time.OffsetDateTime
  • java.time.ZonedDateTime

由于您已经熟悉DateTime API,因此记住这件事对您来说并不困难 :)

映射它们是纯粹的乐趣:

@Basic
private java.time.LocalDate localDate;

@Basic
private java.time.LocalTime localTime;

@Basic
private java.time.OffsetTime offsetTime;

@Basic
private java.time.Instant instant;

@Basic
private java.time.LocalDateTime localDateTime;

@Basic
private java.time.OffsetDateTime offsetDateTime;

@Basic
private java.time.ZonedDateTime zonedDateTime;

注释@Basic意味着该字段应该被自动处理:Hibernate 将决定该字段应该映射到哪个列和类型。

4.3 使用时区

如果时区是日期的一部分,那么将它们存储在数据库中就很简单——就像常规日期一样:

@Basic
private java.time.OffsetDateTime offsetDateTime;

@Basic
private java.time.ZonedDateTime zonedDateTime;

但是,如果您想将时区与日期分开存储:

@Basic
private java.time.TimeZone timeZone;

@Basic
private java.time.ZoneOffset zonedOffset;

然后 Hibernate 会默认将它们存储在 VARCHAR 类型中。事实上,这是合乎逻辑的,因为时区通常有一个字符串名称,如“UTC + 3”或“Cairo”。

4.4 设置自己的时区

当您将日期保存到数据库时,您会发现已经有 4 个地方可以设置当前时区:

  • 服务器操作系统;
  • 数据库管理系统;
  • Java应用程序
  • 休眠。

如果 DBMS 没有指定时区(TimeZone),那么它将从操作系统设置中获取。这可能很不方便,因为备份 DBMS 通常位于具有自己时区的其他数据中心。

因此,几乎所有的 DBMS 管理员都设置了一个区域,以便可以轻松地将数据从一台服务器传输到另一台服务器。

Java 应用程序的情况类似。它也可以在不同数据中心的不同服务器上运行,所以它通常有一个明确的时区。


java -Duser.timezone=UTC ...

或者在程序运行时:

TimeZone.setDefault(TimeZone.getTimeZone("UTC"));

当然,Hibernate 允许您明确设置时区。

首先,可以在配置SessionFactory时指定:

settings.put(
    AvailableSettings.JDBC_TIME_ZONE,
    TimeZone.getTimeZone("UTC")
);

其次,可以指定时区对于特定会话:

Session session = sessionFactory()
    .withOptions()
    .jdbcTimeZone(TimeZone.getTimeZone("UTC"))
    .openSession();

4.5 @TimeZoneStorage注解

经常发生的情况是,程序员开始设计一个基于在一个国家(和一个时区)工作的数据库,然后在几年后他们需要添加对在不同时区工作的支持。

因此,他们只是在数据库中添加了一个单独的列来存储时区。这种情况很常见,Hibernate 添加了一个特殊的注释,允许您将特定日期的时区存储在单独的列中。

例子:

@TimeZoneStorage(TimeZoneStorageType.COLUMN)
@TimeZoneColumn(name = "birthday_offset_offset")
@Column(name = "birthday_offset")
private OffsetDateTime offsetDateTimeColumn;

@TimeZoneStorage(TimeZoneStorageType.COLUMN)
@TimeZoneColumn(name = "birthday_zoned_offset")
@Column(name = "birthday_zoned")
private ZonedDateTime zonedDateTimeColumn;

这是拐杖。但它也有一个借口:它出现在 DateTime API 还不存在的时候。并且不可能将 TimeZone 存储在类中java.util.Date

我真的希望您不会经常在代码中看到这一点。

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION