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.