4.1 Du hành vào lịch sử

Nhiệm vụ lưu các đối tượng Java vào cơ sở dữ liệu có liên quan gần như ngay lập tức sau khi tạo ngôn ngữ Java. Vào thời điểm đó, chỉ có một kiểu dữ liệu trong ngôn ngữ Java, Date, lưu trữ thời gian theo tiêu chuẩn thời gian UNIX: dưới dạng số mili giây kể từ năm 1970.

Chà, trong cơ sở dữ liệu vào thời điểm đó đã có các loại dữ liệu khác nhau cho ngày tháng, ít nhất là có các loại riêng biệt cho ngày, giờ và ngày + giờ:

  • NGÀY
  • THỜI GIAN
  • DẤU THỜI GIAN

Do đó, những người tạo ra ngôn ngữ Java đã thêm một gói đặc biệt vào nó - java.sql, chứa các lớp:

  • java.sql.date
  • java.sql.Thời gian
  • java.sql.timestamp

Lập bản đồ các lớp này là một niềm vui thực sự:


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

Nhưng vì các lập trình viên đã từng làm việc với lớp java.util.Datenên Hibernate đã thêm một chú thích đặc biệt @Temporalđể kiểm soát ánh xạ của loại Ngày.

Ví dụ:

// 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;

Loại java.util.Calendarjava.util.Dateloại mặc định sử dụng loại DẤU THỜI GIAN để thể hiện chúng trong cơ sở dữ liệu.

4.2 Thời gian mới

Hiện tại, với việc lập bản đồ, mọi thứ trở nên đơn giản và tốt hơn rất nhiều. Tất cả các cơ sở dữ liệu đều hỗ trợ 4 loại dữ liệu để hoạt động theo thời gian:

  • NGÀY - ngày: năm, tháng và ngày.
  • TIME - thời gian: giờ, phút, giây.
  • DẤU THỜI GIAN - ngày, giờ và nano giây.
  • DẤU THỜI GIAN VỚI Múi giờ - DẤU THỜI GIAN và múi giờ (tên hoặc phần bù của múi giờ).

Để đại diện cho loại NGÀYtrong Java, bạn cần sử dụng một lớp java.time.LocalDatetừ API DateTime của JDK 8.

KiểuTHỜI GIANtừ cơ sở dữ liệu có thể được biểu diễn bằng hai loại từ Java: java.time.LocalTimejava.time.OffsetTime. Không có gì phức tạp.

Và ngày giờ chính xác được đại diện bởi loạiDẤU THỜI GIANtrong cơ sở, trong Java, nó có thể được biểu diễn bằng 4 loại:

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

Và cuối cùngDẤU THỜI GIAN VỚI Múi giờcó thể được đại diện bởi hai loại:

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

Vì bạn đã quen thuộc với API DateTime nên việc ghi nhớ vấn đề này sẽ không khó đối với bạn :)

Lập bản đồ chúng là niềm vui thuần túy:

@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;

Chú thích @Basiccó nghĩa là trường sẽ được xử lý tự động : Hibernate sẽ quyết định cột nào và loại trường này sẽ được ánh xạ.

4.3 Làm việc với múi giờ

Nếu múi giờ là một phần của ngày, thì việc lưu trữ chúng trong cơ sở dữ liệu rất đơn giản - giống như một ngày thông thường:

@Basic
private java.time.OffsetDateTime offsetDateTime;

@Basic
private java.time.ZonedDateTime zonedDateTime;

Tuy nhiên, nếu bạn muốn lưu trữ các múi giờ riêng biệt với ngày:

@Basic
private java.time.TimeZone timeZone;

@Basic
private java.time.ZoneOffset zonedOffset;

Sau đó, Hibernate sẽ lưu trữ chúng theo kiểu VARCHAR theo mặc định. Trên thực tế, điều này là hợp lý, vì Múi giờ thường có tên chuỗi như "UTC + 3" hoặc "Cairo".

4.4 Đặt múi giờ của riêng bạn

Khi bạn làm việc với việc lưu ngày vào cơ sở dữ liệu, bạn sẽ thấy rằng đã có 4 nơi bạn có thể đặt múi giờ hiện tại:

  • Hệ điều hành máy chủ;
  • Hệ quản trị cơ sở dữ liệu;
  • ứng dụng Java
  • Ngủ đông.

Nếu DBMS không chỉ định múi giờ (TimeZone), thì nó sẽ lấy múi giờ đó từ cài đặt hệ điều hành. Điều này có thể bất tiện vì các DBMS dự phòng thường được đặt ở các trung tâm dữ liệu khác có múi giờ riêng.

Do đó, hầu hết tất cả các quản trị viên DBMS đều đặt một vùng duy nhất để dữ liệu có thể dễ dàng chuyển từ máy chủ này sang máy chủ khác.

Tình huống tương tự với một ứng dụng Java. Nó cũng có thể chạy trên các máy chủ khác nhau ở các trung tâm dữ liệu khác nhau, do đó, nó thường có múi giờ rõ ràng.


java -Duser.timezone=UTC ...

Hoặc trong khi chương trình đang chạy:

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

Và, tất nhiên, Hibernate cho phép bạn đặt múi giờ của mình một cách rõ ràng.

Đầu tiên, nó có thể được chỉ định khi định cấu hình SessionFactory:

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

Thứ hai, múi giờ có thể được chỉ địnhcho một phiên cụ thể:

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

4.5 Chú thích @TimeZoneStorage

Điều thường xảy ra là các lập trình viên bắt đầu thiết kế cơ sở dữ liệu dựa trên việc làm việc ở một quốc gia (và một múi giờ), sau đó vài năm, họ cần thêm hỗ trợ để làm việc ở các múi giờ khác nhau.

Do đó, họ chỉ cần thêm một cột riêng vào cơ sở dữ liệu để lưu trữ múi giờ. Đây là một tình huống phổ biến đến mức Hibernate đã thêm một chú thích đặc biệt cho phép bạn lưu trữ Múi giờ của một ngày cụ thể trong một cột riêng biệt.

Ví dụ:

@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;

Đây là một cái nạng. Nhưng cũng có một cái cớ cho nó: nó xuất hiện vào thời điểm API DateTime chưa tồn tại. Và không thể lưu trữ TimeZone trong lớp java.util.Date.

Tôi thực sự hy vọng rằng bạn sẽ không thường thấy điều này trong mã của mình.