4.1 Wycieczka do historii

Zadanie zapisywania obiektów Java do bazy danych było aktualne niemal natychmiast po stworzeniu języka Java. W tamtym czasie w języku Java istniał tylko jeden typ danych, Date, który zapisywał czas zgodnie ze standardem czasu UNIX: jako liczbę milisekund od 1970 roku.

Cóż, w bazach danych w tamtym czasie istniały już różne typy danych dla dat, przynajmniej były osobne typy dla daty, czasu i daty + czasu:

  • DATA
  • CZAS
  • ZNAK CZASU

Dlatego twórcy języka Java dodali do niego specjalny pakiet - java.sql, który zawierał klasy:

  • java.sql.data
  • java.sql.Time
  • znacznik czasu java.sql

Mapowanie tych klas to prawdziwa przyjemność:


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

Ale ponieważ programiści pracowali z klasą java.util.Date, Hibernate dodał specjalną adnotację, @Temporalaby kontrolować mapowanie typu Date.

Przykład:

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

Typ java.util.Calendari java.util.Datetyp domyślny używają typu TIMESTAMP do reprezentowania ich w bazie danych.

4.2 Nowy czas

Obecnie dzięki mapowaniu wszystko jest dużo prostsze i lepsze. Wszystkie bazy danych obsługują 4 typy danych do pracy z czasem:

  • DATA - data: rok, miesiąc i dzień.
  • CZAS - czas: godziny, minuty, sekundy.
  • TIMESTAMP - data, godzina i nanosekundy.
  • TIMESTAMP WITH TIME ZONE - TIMESTAMP i strefa czasowa (nazwa strefy lub przesunięcie).

Reprezentować typ DATAw Javie musisz użyć klasy java.time.LocalDatez API JDK 8 DateTime.

TypCZASz bazy danych mogą być reprezentowane przez dwa typy z Javy: java.time.LocalTimei java.time.OffsetTime. Nic skomplikowanego.

Dokładna data i godzina reprezentowana przez typZNAK CZASUw bazie, w Javie może być reprezentowany przez 4 typy:

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

I w końcuZNACZNIK CZASU ZE STREFĄ CZASOWĄmoże być reprezentowany przez dwa typy:

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

Ponieważ znasz już API DateTime , zapamiętanie tej kwestii nie będzie dla Ciebie trudne :)

Mapowanie ich to czysta przyjemność:

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

Adnotacja @Basicoznacza, że ​​pole powinno zostać przetworzone automatycznie : Hibernate zdecyduje, na którą kolumnę i typ ma być zmapowane to pole.

4.3 Praca ze strefami czasowymi

Jeśli strefa czasowa jest częścią daty, przechowywanie ich w bazie danych jest proste - podobnie jak zwykła data:

@Basic
private java.time.OffsetDateTime offsetDateTime;

@Basic
private java.time.ZonedDateTime zonedDateTime;

Jeśli jednak chcesz przechowywać strefy czasowe oddzielnie od daty:

@Basic
private java.time.TimeZone timeZone;

@Basic
private java.time.ZoneOffset zonedOffset;

Następnie Hibernate domyślnie zapisze je w typie VARCHAR. Co w rzeczywistości jest logiczne, ponieważ strefa czasowa ma zwykle nazwę ciągu, taką jak „UTC + 3” lub „Cairo”.

4.4 Ustawianie własnej strefy czasowej

Pracując z zapisem dat do bazy natkniesz się na fakt, że są już 4 miejsca, w których możesz ustawić aktualną strefę czasową:

  • System operacyjny serwera;
  • DBMS;
  • Aplikacja Javy
  • Hibernować.

Jeśli DBMS nie określa strefy czasowej (TimeZone), to weźmie ją z ustawień systemu operacyjnego. Może to być niewygodne, ponieważ zapasowe systemy DBMS często znajdują się w innych centrach danych, które mają własną strefę czasową.

Dlatego prawie wszyscy administratorzy DBMS ustawiają pojedynczą strefę, aby dane można było łatwo przenosić z jednego serwera na drugi.

Podobnie sytuacja wygląda w przypadku aplikacji Java. Może być również uruchamiany na różnych serwerach w różnych centrach danych, dlatego zwykle jest mu wyraźnie nadawana strefa czasowa.


java -Duser.timezone=UTC ...

Lub podczas działania programu:

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

I, oczywiście, Hibernate umożliwia jawne ustawienie strefy czasowej.

Po pierwsze, można to określić podczas konfigurowania SessionFactory:

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

Po drugie, można określić strefę czasowąna konkretną sesję:

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

4.5 Adnotacja @TimeZoneStorage

Często zdarza się, że programiści zaczynali projektować bazę danych od pracy w jednym kraju (i jednej strefie czasowej), a potem po kilku latach musieli dodać obsługę pracy w różnych strefach czasowych.

Dlatego po prostu dodali oddzielną kolumnę do bazy danych, aby przechowywać strefę czasową. Jest to tak powszechna sytuacja, że ​​Hibernate dodał specjalną adnotację, która pozwala przechowywać strefę czasową określonej daty w osobnej kolumnie.

Przykład:

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

To jest kula. Ale jest też na to wymówka: pojawiła się w czasie, gdy API DateTime jeszcze nie istniało. I nie można było przechowywać TimeZone w klasie java.util.Date.

Naprawdę mam nadzieję, że nie będziesz często widzieć tego w swoim kodzie.