Hibernate - Mappatura di data e ora

1. Introduzione

In questo articolo, mostreremo come mappare i valori delle colonne temporali in Hibernate, comprese le classi dei pacchetti java.sql , java.util e java.time .

2. Configurazione del progetto

Per dimostrare la mappatura dei tipi temporali, avremo bisogno del database H2 e dell'ultima versione della libreria hibernate-core :

 org.hibernate hibernate-core 5.4.12.Final   com.h2database h2 1.4.194 

Per la versione corrente della libreria hibernate-core , vai al repository Maven Central.

3. Configurazione del fuso orario

Quando si tratta di date, è una buona idea impostare un fuso orario specifico per il driver JDBC. In questo modo la nostra applicazione sarebbe indipendente dal fuso orario corrente del sistema.

Per il nostro esempio, lo configureremo per sessione:

session = HibernateUtil.getSessionFactory().withOptions() .jdbcTimeZone(TimeZone.getTimeZone("UTC")) .openSession();

Un altro modo sarebbe quello di impostare il hibernate.jdbc.time_zone proprietà a Hibernate file delle proprietà che viene utilizzato per costruire la fabbrica di sessione. In questo modo potremmo specificare il fuso orario una volta per l'intera applicazione.

4. Mappatura dei tipi java.sql

Il pacchetto java.sql contiene tipi JDBC allineati con i tipi definiti dallo standard SQL:

  • La data corrisponde al tipo SQL DATE , che è solo una data senza ora
  • L'ora corrisponde al tipo TIME SQL, che è un'ora del giorno specificata in ore, minuti e secondi
  • Timestamp include informazioni su data e ora con precisione fino a nanosecondi e corrisponde al tipo SQL TIMESTAMP

Poiché questi tipi sono in linea con SQL, la loro mappatura è relativamente semplice. Possiamo usare l' annotazione @Basic o @Column :

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

Potremmo quindi impostare i valori corrispondenti in questo modo:

temporalValues.setSqlDate(java.sql.Date.valueOf("2017-11-15")); temporalValues.setSqlTime(java.sql.Time.valueOf("15:30:14")); temporalValues.setSqlTimestamp( java.sql.Timestamp.valueOf("2017-11-15 15:30:14.332"));

Si noti che la scelta dei tipi java.sql per i campi entità potrebbe non essere sempre una buona scelta. Queste classi sono specifiche di JDBC e contengono molte funzionalità deprecate.

5. Mappatura del tipo java.util.Date

Il tipo java.util.Date contiene sia la data che l'ora, con una precisione fino al millisecondo. Ma non si riferisce direttamente a nessun tipo SQL.

Questo è il motivo per cui abbiamo bisogno di un'altra annotazione per specificare il tipo SQL desiderato:

@Basic @Temporal(TemporalType.DATE) private java.util.Date utilDate; @Basic @Temporal(TemporalType.TIME) private java.util.Date utilTime; @Basic @Temporal(TemporalType.TIMESTAMP) private java.util.Date utilTimestamp;

L' annotazione @Temporal ha il valore del parametro singolo di tipo TemporalType. Può essere DATE , TIME o TIMESTAMP , a seconda del tipo SQL sottostante che si desidera utilizzare per la mappatura.

Potremmo quindi impostare i campi corrispondenti in questo modo:

temporalValues.setUtilDate( new SimpleDateFormat("yyyy-MM-dd").parse("2017-11-15")); temporalValues.setUtilTime( new SimpleDateFormat("HH:mm:ss").parse("15:30:14")); temporalValues.setUtilTimestamp( new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS") .parse("2017-11-15 15:30:14.332"));

Come abbiamo visto, il tipo java.util.Date (precisione in millisecondi) non è sufficientemente preciso per gestire il valore Timestamp (precisione in nanosecondi).

Quindi, quando recuperiamo l'entità dal database, non sorprende trovare un'istanza java.sql.Timestamp in questo campo, anche se inizialmente abbiamo persistito un java.util.Date :

temporalValues = session.get(TemporalValues.class, temporalValues.getId()); assertThat(temporalValues.getUtilTimestamp()) .isEqualTo(java.sql.Timestamp.valueOf("2017-11-15 15:30:14.332"));

Questo dovrebbe andare bene per il nostro codice poiché Timestamp estende Date .

6. Mappatura del tipo java.util.Calendar

Come con java.util.Date , il tipo java.util.Calendar può essere mappato a diversi tipi SQL, quindi dobbiamo specificarli con @Temporal .

L'unica differenza è che Hibernate non supporta la mappatura di Calendar su TIME :

@Basic @Temporal(TemporalType.DATE) private java.util.Calendar calendarDate; @Basic @Temporal(TemporalType.TIMESTAMP) private java.util.Calendar calendarTimestamp;

Ecco come possiamo impostare il valore del campo:

Calendar calendarDate = Calendar.getInstance( TimeZone.getTimeZone("UTC")); calendarDate.set(Calendar.YEAR, 2017); calendarDate.set(Calendar.MONTH, 10); calendarDate.set(Calendar.DAY_OF_MONTH, 15); temporalValues.setCalendarDate(calendarDate);

7. Mappatura dei tipi java.time

A partire da Java 8, la nuova API Java Date and Time è disponibile per gestire i valori temporali . Questa API risolve molti dei problemi delle classi java.util.Date e java.util.Calendar .

I tipi dal pacchetto java.time vengono mappati direttamente ai tipi SQL corrispondenti. Quindi non è necessario specificare esplicitamente l' annotazione @Temporal :

  • LocalDate è mappato su DATE
  • LocalTime e OffsetTime sono mappati su TIME
  • Instant , LocalDateTime , OffsetDateTime e ZonedDateTime vengono mappati su TIMESTAMP

Ciò significa che possiamo contrassegnare questi campi solo con l' annotazione @Basic (o @Column ), in questo modo:

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

Ogni classe temporale nel pacchetto java.time ha un metodo parse () statico per analizzare il valore String fornito utilizzando il formato appropriato. Quindi ecco come possiamo impostare i valori dei campi entità:

temporalValues.setLocalDate(LocalDate.parse("2017-11-15")); temporalValues.setLocalTime(LocalTime.parse("15:30:18")); temporalValues.setOffsetTime(OffsetTime.parse("08:22:12+01:00")); temporalValues.setInstant(Instant.parse("2017-11-15T08:22:12Z")); temporalValues.setLocalDateTime( LocalDateTime.parse("2017-11-15T08:22:12")); temporalValues.setOffsetDateTime( OffsetDateTime.parse("2017-11-15T08:22:12+01:00")); temporalValues.setZonedDateTime( ZonedDateTime.parse("2017-11-15T08:22:12+01:00[Europe/Paris]"));

8. Conclusione

In questo articolo, abbiamo mostrato come mappare valori temporali di diversi tipi in Hibernate.

Il codice sorgente dell'articolo è disponibile su GitHub.