Hibernate - Mappage de la date et de l'heure

1. Introduction

Dans cet article, nous montrerons comment mapper les valeurs de colonne temporelle dans Hibernate, y compris les classes des packages java.sql , java.util et java.time .

2. Configuration du projet

Pour démontrer le mappage des types temporels, nous allons avoir besoin de la base de données H2 et de la dernière version de la bibliothèque hibernate-core :

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

Pour la version actuelle de la bibliothèque hibernate-core , accédez au référentiel Maven Central.

3. Configuration du fuseau horaire

Lorsqu'il s'agit de dates, il est judicieux de définir un fuseau horaire spécifique pour le pilote JDBC. De cette façon, notre application serait indépendante du fuseau horaire actuel du système.

Pour notre exemple, nous le configurerons sur une base par session:

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

Une autre façon serait de configurer la propriété hibernate.jdbc.time_zone dans le fichier de propriétés Hibernate qui est utilisé pour construire la fabrique de session. De cette façon, nous pourrions spécifier le fuseau horaire une fois pour toute l'application.

4. Mappage des types java.sql

Le package java.sql contient des types JDBC alignés sur les types définis par la norme SQL:

  • La date correspond au type SQL DATE , qui n'est qu'une date sans heure
  • L'heure correspond au type TIME SQL, qui est une heure du jour spécifiée en heures, minutes et secondes
  • L'horodatage comprend des informations sur la date et l'heure avec une précision allant jusqu'à nanosecondes et correspond au type SQL TIMESTAMP

Comme ces types sont conformes à SQL, leur mappage est relativement simple. Nous pouvons utiliser l' annotation @Basic ou @Column :

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

Nous pourrions alors définir les valeurs correspondantes comme ceci:

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"));

Notez que choisir les types java.sql pour les champs d'entité n'est pas toujours un bon choix. Ces classes sont spécifiques à JDBC et contiennent de nombreuses fonctionnalités obsolètes.

5. Mappage du type java.util.Date

Le type java.util.Date contient des informations de date et d'heure, avec une précision de l'ordre de la milliseconde. Mais cela ne concerne directement aucun type SQL.

C'est pourquoi nous avons besoin d'une autre annotation pour spécifier le type SQL souhaité:

@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' annotation @Temporal a la valeur de paramètre unique de type TemporalType. Il peut s'agir de DATE , TIME ou TIMESTAMP , selon le type SQL sous-jacent que nous voulons utiliser pour le mappage.

Nous pourrions alors définir les champs correspondants comme ceci:

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"));

Comme nous l'avons vu, le type java.util.Date (précision en millisecondes) n'est pas assez précis pour gérer la valeur Timestamp (précision en nanosecondes).

Ainsi, lorsque nous récupérons l'entité de la base de données, nous trouverons sans surprise une instance java.sql.Timestamp dans ce champ, même si nous avons initialement conservé 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"));

Cela devrait convenir à notre code car Timestamp étend la date .

6. Mappage du type java.util.Calendar

Comme pour java.util.Date , le type java.util.Calendar peut être mappé à différents types SQL, nous devons donc les spécifier avec @Temporal .

La seule différence est qu'Hibernate ne prend pas en charge le mappage du calendrier à TIME :

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

Voici comment nous pouvons définir la valeur du champ:

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. Mappage des types java.time

Depuis Java 8, la nouvelle API Java Date and Time est disponible pour traiter les valeurs temporelles . Cette API résout de nombreux problèmes des classes java.util.Date et java.util.Calendar .

Les types du package java.time sont directement mappés aux types SQL correspondants. Il n'est donc pas nécessaire de spécifier explicitement l' annotation @Temporelle :

  • LocalDate est mappé sur DATE
  • LocalTime et OffsetTime sont mappés sur TIME
  • Instant , LocalDateTime , OffsetDateTime et ZonedDateTime sont mappés à TIMESTAMP

Cela signifie que nous ne pouvons marquer ces champs qu'avec l' annotation @Basic (ou @Column ), comme ceci:

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

Chaque classe temporelle du package java.time a une méthode statique parse () pour analyser la valeur String fournie en utilisant le format approprié. Alors, voici comment nous pouvons définir les valeurs des champs d'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. Conclusion

Dans cet article, nous avons montré comment mapper des valeurs temporelles de différents types dans Hibernate.

Le code source de l'article est disponible à l'adresse over sur GitHub.