Hibernate - отображение даты и времени

Hibernate - отображение даты и времени

1. Вступление

В этой статье мы покажем, как сопоставить значения временных столбцов в Hibernate, включая классы из пакетовjava.sql,java.util иjava.time.

2. Настройка проекта

Чтобы продемонстрировать отображение временных типов, нам понадобится база данных H2 и последняя версия библиотекиhibernate-core:


    org.hibernate
    hibernate-core
    5.2.12.Final


    com.h2database
    h2
    1.4.194

Для текущей версии библиотекиhibernate-core перейдите в репозиторийMaven Central.

3. Настройка часового пояса

When dealing with dates, it’s a good idea to set a specific time zone for the JDBC driver. Таким образом, наше приложение не будет зависеть от текущего часового пояса системы.

В нашем примере мы настроим его для каждого сеанса:

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

Другой способ - настроить свойствоhibernate.jdbc.time_zone в файле свойств Hibernate, который используется для создания фабрики сеанса. Таким образом, мы можем указать часовой пояс один раз для всего приложения.

4. Сопоставление типовjava.sql

Пакетjava.sql содержит типы JDBC, которые согласованы с типами, определенными стандартом SQL:

  • Date соответствует типу SQLDATE, который представляет собой только дату без времени

  • Time соответствует типу SQLTIME, который представляет собой время дня, указанное в часах, минутах и ​​секундах

  • Timestamp включает информацию о дате и времени с точностью до наносекунд и соответствует типу SQLTIMESTAMP

Поскольку эти типы соответствуют 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 Тип

The type java.util.Date contains both date and time information, up to millisecond precision. Но это не имеет прямого отношения ни к какому типу 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"));

Как мы видели,the java.util.Date type (milliseconds precision) is not precise enough to handle the Timestamp value (nanoseconds precision).

Поэтому, когда мы извлекаем объект из базы данных, неудивительно, что в этом поле мы обнаруживаем экземпляр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

Since Java 8, the new Java Date and Time API is available for dealing with temporal values. Этот 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.

Исходный код статьи доступенover on GitHub.