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ę, @Temporal
aby kontrolować mapowanie typu Date.
Przykład:
// Jeśli brakuje adnotacji, baza danych będzie miała typ TIMESTAMP
Date dateAsTimestamp;
@Temporal(TemporalType.DATE) // zostanie odwzorowany na typ DATE
Date dateAsDate;
@Temporal(TemporalType.TIME) // zostanie odwzorowany na typ TIME
Date dateAsTime;
Typ java.util.Calendar
i java.util.Date
typ 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.LocalDate
z API JDK 8 DateTime.
TypCZASz bazy danych mogą być reprezentowane przez dwa typy z Javy: java.time.LocalTime
i 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 @Basic
oznacza, ż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.
GO TO FULL VERSION