10. Как мапятся даты (до Java 8 и после)?
UNKNOWNПри работе с датами рекомендуется установить определенный часовой пояс для драйвера JDBC. Таким образом, наше приложение будет независимым от текущего часового пояса системы.
Другой способ - настроить свойство hibernate.jdbc.time_zone в файле свойств Hibernate, который используется для создания фабрики сессий. Таким образом, мы можем указать часовой пояс один раз для всего приложения.
java.sql
Hibernate позволяет отображать различные классы даты/времени из Java в таблицах баз данных. Стандарт SQL определяет три типа даты/времени:
- DATE - Представляет календарную дату путем хранения лет, месяцев и дней. Эквивалентом JDBC является java.sql.Date.
- TIME - Представляет время дня и хранит часы, минуты и секунды. Эквивалентом JDBC является java.sql.Time.
- TIMESTAMP - Хранит как DATE, так и TIME плюс наносекунды. Эквивалентом JDBC является java.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 и содержат множество устаревших функций.
Чтобы избежать зависимостей от пакета java.sql, начали использовать классы даты/времени из пакета java.util вместо классов java.sql.Timestamp и java.sql.Time.
java.util
Точность представления времени составляет одну миллисекунду. Для большинства практических задач этого более чем достаточно, но иногда хочется иметь точность повыше.
Поскольку классы в данном API изменяемые (не immutable), использовать их в многопоточной среде нужно с осторожностью. В частности java.util.Date можно признать «эффективно» потоко-безопасным, если вы не вызываете у него устаревшие методы.
java.util.Date
Тип java.util.Date содержит информацию о дате и времени с точностью до миллисекунд. Но так как классы из этого пакета не имели прямого соответствия типам данных SQL, приходилось использовать над полями java.util.Date аннотацию @Temporal, чтобы дать понять SQL, с каким конкретно типом данных она работает. Для этого у аннотации @Temporal нужно было указать параметр TemporalType, который принимал одно из трёх значений: DATE, TIME или TIMESTAMP, что позволяло указать базе данных с какими конкретными типами данных она работает.
@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;
Тип java.util.Date имеет точность до миллисекунд, и недостаточно точен для обработки SQL-значения Timestamp, который имеет точность вплоть до наносекунд.
Поэтому, когда мы извлекаем сущность из базы данных, неудивительно, что в этом поле мы находим экземпляр java.sql.Timestamp, даже если изначально мы сохранили java.util.Date. Но это не страшно, так как Timestamp наследуется от Date.
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;
java.time
Начиная с Java 8, доступен новый API даты и времени для работы с временными значениями. Этот API-интерфейс устраняет многие проблемы классов java.util.Date и java.util.Calendar. Все классы в новом API неизменяемые (immutable) и, как следствие, потоко-безопасные. Точность представления времени составляет одну наносекунду, что в миллион раз точнее чем в пакете java.util. Типы данных из пакета 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]\"));
Предыдущий вопрос: 9. Как мапятся Enum`ы?
Следующий вопрос: 11. Как сохранять в базе данных коллекции базовых типов?
Все вопросы по теме: список
Все темы: список
Вопросы/замечания/предложения/нашли ошибку: напишите мне