Hibernate - Date et heure de mappage

Hibernate - Date et heure de mappage

1. introduction

Dans cet article, nous allons montrer comment mapper les valeurs de colonnes temporelles dans Hibernate, y compris les classes des packagesjava.sql,java.util etjava.time.

2. Configuration du projet

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


    org.hibernate
    hibernate-core
    5.2.12.Final


    com.h2database
    h2
    1.4.194

Pour la version actuelle de la bibliothèquehibernate-core, accédez au référentielMaven Central.

3. Configuration du fuseau horaire

When dealing with dates, it’s a good idea to set a specific time zone for the JDBC driver. De cette façon, notre application serait indépendante du fuseau horaire actuel du système.

Pour notre exemple, nous allons le configurer 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 typesjava.sql

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

  • Date correspond au type SQLDATE, qui n'est qu'une date sans heure

  • Time correspond au type SQL deTIME, qui est une heure du jour spécifiée en heures, minutes et secondes

  • Timestamp inclut des informations sur la date et l'heure avec une précision allant jusqu'à nanosecondes et correspond au type SQL deTIMESTAMP

Comme ces types sont compatibles avec 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 typesjava.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 déconseillées.

5. Type de mappagejava.util.Date

The type java.util.Date contains both date and time information, up to millisecond precision. 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 typeTemporalType. Il peut s'agir deDATE,TIME ouTIMESTAMP, selon le type SQL sous-jacent souhaité à 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,the java.util.Date type (milliseconds precision) is not precise enough to handle the Timestamp value (nanoseconds precision).

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

6. Type de mappagejava.util.Calendar

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

La seule différence est que Hibernate ne prend pas en charge le mappage deCalendar à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 typesjava.time

Since Java 8, the new Java Date and Time API is available for dealing with temporal values. Cette API résout de nombreux problèmes des classesjava.util.Date etjava.util.Calendar.

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

  • LocalDate est mappé surDATE

  • LocalTime etOffsetTime sont mappés surTIME

  • Instant,LocalDateTime,OffsetDateTime etZonedDateTime sont mappés surTIMESTAMP

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 packagejava.time a une méthode statiqueparse() pour analyser la valeurString fournie en utilisant le format approprié. Voici donc comment 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 disponibleover on GitHub.