1. Введение
В этой статье мы покажем, как отображать значения временных столбцов в Hibernate, включая классы из пакетов java.sql
, java.util
и java.time
.
2. Настройка проекта
Чтобы продемонстрировать сопоставление временных типов, нам понадобится база данных H2 и последняя версия библиотеки hibernate-core :
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.12.Final</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.194</version>
</dependency>
Текущую версию библиотеки 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:
Дата
соответствует типу SQLDATE
, который представляет собой только дату без времени.Время
соответствует типу SQLTIME
, который представляет собой время дня, указанное в часах, минутах и секундах.Отметка времени
включает информацию о дате и времени с точностью до наносекунд и соответствует SQL-типуTIMESTAMP .
Поскольку эти типы соответствуют 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
(с точностью до миллисекунд) недостаточно точен для обработки значения Timestamp (с точностью до наносекунд).
Поэтому, когда мы извлекаем объект из базы данных, мы неудивительно, что в этом поле мы найдем экземпляр 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
расширяет Date
.
6. Сопоставление типа java.util.Calendar
Как и в случае с java.util.Date
, тип java.util.Calendar
может быть сопоставлен с различными типами SQL, поэтому мы должны указать их с помощью @Temporal
.
Единственное отличие состоит в том, что Hibernate не поддерживает сопоставление Calendar
с 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, новый Java Date and Time API доступен для работы с временными значениями . Этот API устраняет многие проблемы классов java.util.Date
и java.util.Calendar
.
Типы из пакета java.time
напрямую сопоставляются с соответствующими типами SQL. Поэтому нет необходимости явно указывать аннотацию @Temporal
:
LocalDate
сопоставляется сDATE
LocalTime
иOffsetTime
сопоставляются сTIME
Instant
,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. Заключение
В этой статье мы показали, как отображать временные значения разных типов в Hibernate.
Исходный код статьи доступен на GitHub .