Hibernate - відображення дати та часу

1. Вступ

У цій статті ми покажемо, як зіставити значення тимчасових стовпців у режимі глибокого сну, включаючи класи з пакетів java.sql , java.util та java.time .

2. Налаштування проекту

Для демонстрації відображення тимчасових типів нам знадобиться база даних H2 та остання версія бібліотеки hibernate-core :

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

Для поточної версії hibernate-core бібліотеки перейдіть до сховища Maven Central.

3. Налаштування часового поясу

При роботі з датами непогано встановити певний часовий пояс для драйвера JDBC. Таким чином наш додаток буде незалежним від поточного часового поясу системи.

Для нашого прикладу ми встановимо його для кожного сеансу:

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

Іншим способом було б встановити властивість hibernate.jdbc.time_zone у файлі властивостей Hibernate, який використовується для побудови фабричної сесії. Таким чином ми могли б вказати часовий пояс один раз для всієї програми.

4. Зіставлення типів java.sql

Пакет java.sql містить типи JDBC, які вирівняні з типами, визначеними стандартом SQL:

  • Дата відповідає типу DATE SQL, який є лише датою без часу
  • Час відповідає типу TIME SQL, який є часом доби, вказаним у годинах, хвилинах та секундах
  • Відмітка часу включає інформацію про дату та час з точністю до наносекунд і відповідає типу TIMESTAMP SQL

Оскільки ці типи відповідають SQL, то їх відображення є відносно прямим. Ми можемо використовувати або анотацію @Basic, або @Column :

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

Потім ми могли б встановити відповідні значення таким чином:

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

Зверніть увагу, що вибір типів java.sql для полів сутності не завжди може бути хорошим вибором. Ці класи специфічні для JDBC і містять багато застарілих функціональних можливостей.

5. Зіставлення типу java.util.Date

Тип java.util.Date містить інформацію про дату та час з точністю до мілісекунд. Але це безпосередньо не стосується жодного типу SQL.

Ось чому нам потрібна ще одна анотація, щоб вказати бажаний тип SQL:

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

@Temporal анотації мають одне значення параметра типу TemporalType. Це може бути DATE , TIME або TIMESTAMP , залежно від базового типу SQL, який ми хочемо використовувати для зіставлення.

Потім ми могли б встановити відповідні поля так:

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

Як ми вже бачили, java.util.Date типу (мілісекунди точність) НЕ є досить точним , щоб обробляти значення Відмітки (наносекунди точності).

Отже, коли ми отримуємо сутність із бази даних, ми не дивно знайдемо в цьому полі екземпляр java.sql.Timestamp , навіть якщо спочатку зберігався 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"));

Це має бути добре для нашого коду, оскільки Timestamp продовжує дату .

6. Картування java.util.Calendar Type

Як і у випадку з java.util.Date , тип java.util.Calendar може бути зіставлений з різними типами SQL, тому ми повинні вказати їх за допомогою @Temporal .

Єдина відмінність полягає в тому, що Hibernate не підтримує відображення календаря на TIME :

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

Ось як ми можемо встановити значення поля:

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. Зіставлення типів java.time

Починаючи з Java 8, новий API дати та часу Java доступний для роботи з тимчасовими значеннями . Цей API виправляє багато проблем класів java.util.Date та java.util.Calendar .

Типи з пакета java.time безпосередньо відображаються у відповідні типи SQL. Тож немає необхідності явно вказувати анотацію @Temporal :

  • LocalDate відображається на DATE
  • LocalTime і OffsetTime відображаються на TIME
  • Миттєвий , LocalDateTime , OffsetDateTime і ZonedDateTime відображаються у TIMESTAMP

Це означає, що ми можемо позначати ці поля лише анотацією @Basic (або @Column ), наприклад:

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

Кожен тимчасовий клас у пакеті java.time має статичний метод parse () для синтаксичного аналізу наданого значення String за допомогою відповідного формату. Отже, ось як ми можемо встановити значення полів сутності:

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. Висновок

У цій статті ми показали, як зіставити часові значення різних типів у режимі глибокого сну.

Вихідний код статті доступний на GitHub.