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 @Temporal
per 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.Calendar
e java.util.Date
il 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.LocalDate
dall'API DateTime di JDK 8.
TipoTEMPOdal database può essere rappresentato da due tipi da Java: java.time.LocalTime
e 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 @Basic
significa 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.