4.1 Excursus nella storia

Il compito di salvare gli oggetti Java nel database è stato rilevante quasi immediatamente dopo la creazione del linguaggio Java. A quel tempo, c'era solo un tipo di dati nel linguaggio Java, Date, che memorizzava il tempo secondo lo standard UNIX-time: come il numero di millisecondi dal 1970.

Bene, nei database a quel tempo c'erano già diversi tipi di dati per le date, almeno c'erano tipi separati per data, ora e data + ora:

  • DATA
  • TEMPO
  • TIMESTAMP

Pertanto, i creatori del linguaggio Java vi hanno aggiunto un pacchetto speciale: java.sql, che conteneva le classi:

  • java.sql.data
  • java.sql.Time
  • java.sql.timestamp

Mappare queste classi è un vero piacere:


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

Ma poiché i programmatori lavoravano con la classe java.util.Date, Hibernate ha aggiunto un'annotazione speciale @Temporalper controllare la mappatura del tipo Date.

Esempio:

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

Il tipo java.util.Calendare java.util.Dateil tipo predefinito utilizzano il tipo TIMESTAMP per rappresentarli nel database.

4.2 Nuovo orario

Al momento, con la mappatura, tutto è molto più semplice e migliore. Tutti i database supportano 4 tipi di dati per lavorare con il tempo:

  • DATE - data: anno, mese e giorno.
  • TIME - tempo: ore, minuti, secondi.
  • TIMESTAMP - data, ora e nanosecondi.
  • TIMESTAMP CON FUSO ORARIO - TIMESTAMP e fuso orario (nome o offset della zona).

Per rappresentare il tipo DATAin Java, è necessario utilizzare una classe java.time.LocalDatedall'API DateTime di JDK 8.

TipoTEMPOdal database può essere rappresentato da due tipi da Java: java.time.LocalTimee java.time.OffsetTime. Niente di complicato.

E la data e l'ora esatte rappresentate dal tipoTIMESTAMPnella base, in Java può essere rappresentato da 4 tipi:

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

E infineTIMESTAMP CON FUSO ORARIOpuò essere rappresentato da due tipi:

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

Dato che hai già familiarità con l'API DateTime , ricordare questa questione non sarà difficile per te :)

Mapparli è puro piacere:

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

L'annotazione @Basicsignifica che il campo dovrebbe essere elaborato automaticamente : Hibernate deciderà su quale colonna e tipo questo campo dovrebbe essere mappato.

4.3 Lavorare con i fusi orari

Se il fuso orario fa parte di una data, memorizzarli nel database è semplice, proprio come una data normale:

@Basic
private java.time.OffsetDateTime offsetDateTime;

@Basic
private java.time.ZonedDateTime zonedDateTime;

Tuttavia, se desideri memorizzare i fusi orari separatamente dalla data:

@Basic
private java.time.TimeZone timeZone;

@Basic
private java.time.ZoneOffset zonedOffset;

Quindi Hibernate li memorizzerà nel tipo VARCHAR per impostazione predefinita. Il che, in effetti, è logico, poiché il fuso orario di solito ha un nome di stringa come "UTC + 3" o "Cairo".

4.4 Impostare il proprio fuso orario

Quando lavori con il salvataggio delle date nel database, ti imbatterai nel fatto che ci sono già 4 posizioni in cui puoi impostare il fuso orario corrente:

  • Sistema operativo del server;
  • DBMS;
  • Applicazione java
  • Ibernazione.

Se il DBMS non specifica un fuso orario (TimeZone), lo prenderà dalle impostazioni del sistema operativo. Questo può essere scomodo, poiché i DBMS di backup si trovano spesso in altri data center che hanno il proprio fuso orario.

Pertanto, quasi tutti gli amministratori DBMS impostano una singola zona in modo che i dati possano essere facilmente trasferiti da un server all'altro.

La situazione è simile con un'applicazione Java. Può anche essere eseguito su server diversi in data center diversi, quindi di solito ha un fuso orario esplicito.


java -Duser.timezone=UTC ...

O mentre il programma è in esecuzione:

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

E, naturalmente, Hibernate ti consente di impostare il tuo fuso orario in modo esplicito.

Innanzitutto, può essere specificato durante la configurazione di SessionFactory:

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

In secondo luogo, è possibile specificare il fuso orarioper una sessione specifica:

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

4.5 Annotazione @TimeZoneStorage

Accade spesso che i programmatori abbiano iniziato a progettare un database basato sul lavoro in un paese (e un fuso orario), e poi dopo un paio d'anni hanno dovuto aggiungere il supporto per lavorare in diversi fusi orari.

Pertanto, hanno semplicemente aggiunto una colonna separata al database per memorizzare il fuso orario. Questa è una situazione così comune che Hibernate ha aggiunto un'annotazione speciale che consente di memorizzare il fuso orario di una data specifica in una colonna separata.

Esempio:

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

Questa è una stampella. Ma c'è anche una scusa per questo: è apparso in un momento in cui l'API DateTime non esisteva ancora. Ed era impossibile memorizzare TimeZone nella classe java.util.Date.

Spero davvero che non lo vedrai spesso nel tuo codice.