4.1 Excurs în istorie

Sarcina de a salva obiectele Java în baza de date a fost relevantă aproape imediat după crearea limbajului Java. La acel moment, exista un singur tip de date în limbajul Java, Date, care stoca timpul conform standardului UNIX: ca număr de milisecunde din 1970.

Ei bine, în bazele de date la acea vreme existau deja diferite tipuri de date pentru date, cel puțin existau tipuri separate pentru dată, oră și dată + oră:

  • DATA
  • TIMP
  • TIMESTAMP-UL

Prin urmare, creatorii limbajului Java i-au adăugat un pachet special - java.sql, care conținea clase:

  • java.sql.date
  • java.sql.Time
  • java.sql.timestamp

Cartografierea acestor clase este o adevărată plăcere:


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

Dar, deoarece programatorii obișnuiau să lucreze cu clasa java.util.Date, Hibernate a adăugat o adnotare specială @Temporalpentru a controla maparea tipului Date.

Exemplu:

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

Tipul java.util.Calendarși java.util.Datetipul implicit folosesc tipul TIMESTAMP pentru a le reprezenta în baza de date.

4.2 Timp nou

În prezent, cu cartografierea, totul este mult mai simplu și mai bun. Toate bazele de date acceptă 4 tipuri de date pentru a lucra cu timpul:

  • DATA - data: an, luna si zi.
  • TIMP - timp: ore, minute, secunde.
  • TIMESTAMP - data, ora si nanosecunde.
  • TIMESTAMP WITH TIME ZONE - TIMESTAMP și fus orar (numele sau decalajul zonei).

Pentru a reprezenta tipul DATAîn Java, trebuie să utilizați o clasă java.time.LocalDatedin API-ul JDK 8 DateTime.

TipTIMPdin baza de date poate fi reprezentată prin două tipuri din Java: java.time.LocalTimeși java.time.OffsetTime. Nimic complicat.

Și data și ora exactă reprezentate de tipTIMESTAMP-ULîn bază, în Java poate fi reprezentat prin 4 tipuri:

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

Și, în sfârșitTIMESTAMP CU FUS ORARpoate fi reprezentat de două tipuri:

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

Deoarece sunteți deja familiarizat cu API-ul DateTime , să vă amintiți această problemă nu vă va fi dificil :)

Cartografierea lor este o plăcere pură:

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

Adnotarea @Basicînseamnă că câmpul ar trebui procesat automat : Hibernate va decide ce coloană și tipul acestui câmp trebuie mapat.

4.3 Lucrul cu fusurile orare

Dacă fusul orar face parte dintr-o dată, atunci stocarea lor în baza de date este simplă - la fel ca o dată obișnuită:

@Basic
private java.time.OffsetDateTime offsetDateTime;

@Basic
private java.time.ZonedDateTime zonedDateTime;

Cu toate acestea, dacă doriți să stocați fusurile orare separat de dată:

@Basic
private java.time.TimeZone timeZone;

@Basic
private java.time.ZoneOffset zonedOffset;

Apoi Hibernate le va stoca implicit în tipul VARCHAR. Ceea ce, de fapt, este logic, deoarece TimeZone are de obicei un nume de șir precum „UTC + 3” sau „Cairo”.

4.4 Setarea propriului fus orar

Când lucrați cu salvarea datelor în baza de date, veți da peste faptul că există deja 4 locuri în care puteți seta fusul orar actual:

  • Sistem de operare server;
  • SGBD;
  • aplicație Java
  • Hibernează.

Dacă DBMS nu specifică un fus orar (TimeZone), atunci îl va prelua din setările sistemului de operare. Acest lucru poate fi incomod, deoarece SGBD-urile de rezervă sunt adesea situate în alte centre de date care au propriul fus orar.

Prin urmare, aproape toți administratorii DBMS stabilesc o singură zonă, astfel încât datele să poată fi transferate cu ușurință de la un server la altul.

Situația este similară cu o aplicație Java. De asemenea, poate fi rulat pe servere diferite din centre de date diferite, deci are de obicei un fus orar explicit.


java -Duser.timezone=UTC ...

Sau în timp ce programul rulează:

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

Și, desigur, Hibernate vă permite să vă setați fusul orar în mod explicit.

În primul rând, poate fi specificat la configurarea SessionFactory:

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

În al doilea rând, poate fi specificat fusul orarpentru o anumită sesiune:

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

4.5 Adnotare @TimeZoneStorage

Se întâmplă adesea ca programatorii să înceapă să proiecteze o bază de date bazată pe lucrul într-o țară (și un fus orar), iar apoi, după câțiva ani, au nevoie să adauge suport pentru lucrul în diferite fusuri orare.

Prin urmare, pur și simplu au adăugat o coloană separată la baza de date pentru a stoca fusul orar. Aceasta este o situație atât de comună încât Hibernate a adăugat o adnotare specială care vă permite să stocați fusul orar al unei anumite date într-o coloană separată.

Exemplu:

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

Aceasta este o cârjă. Dar există și o scuză pentru asta: a apărut într-un moment în care API-ul DateTime nu exista încă. Și era imposibil să stocați TimeZone în clasă java.util.Date.

Sper cu adevărat că nu veți vedea adesea acest lucru în codul dvs.