// Редактировать: я обновил ответ, чтобы отразить различия между JPA версии 2.1 и 2.2.
// Редактировать 2: добавлена ссылка на спецификацию JPA 2.2
Проблема с JPA 2.1
JPA v2.1 не знает о типах java 8 и попытается привести указанное значение в соответствие. Для LocalDateTime, Instant и OffsetDateTime он будет использовать метод toString () и сохранит соответствующую строку в целевом поле.
При этом вы должны указать JPA, как преобразовать ваше значение в соответствующий тип базы данных, например java.sql.Date
или java.sql.Timestamp
.
Реализуйте и зарегистрируйте интерфейс AttributeConverter
, чтобы сделать эту работу.
См:
Остерегайтесь ошибочной реализации Адама Бьена: сначала нужно зонировать LocalDate.
Использование JPA 2.2
Только не создавайте конвертеры атрибутов. Они уже включены.
// Обновление 2:
Вы можете увидеть это в спецификации здесь: JPA 2.2 spec . Прокрутите до самой последней страницы, чтобы увидеть, что типы времени включены.
Если вы используете выражения jpql, обязательно используйте объект Instant, а также используйте Instant в ваших классах PDO.
, например
// query does not make any sense, probably.
query.setParameter("createdOnBefore", Instant.now());
Это прекрасно работает.
Использование java.time.Instant
вместо других форматов
В любом случае, даже если у вас есть ZonedDateTime или OffsetDateTime, результат, считанный из базы данных, всегда будет в формате UTC, поскольку база данных хранится мгновенно во времени, независимо от часового пояса. Часовой пояс на самом деле просто отображает информацию (метаданные) .
Поэтому я рекомендую использовать Instant
вместо этого и конвертировать его в классы Zoned или Offset Time только при необходимости. Чтобы восстановить время в данной зоне или смещении, сохраните зону или смещение отдельно в своем поле базы данных.
Сравнения JPQL будут работать с этим решением, просто продолжайте работать с мгновенными моментами.
PS: Я недавно разговаривал с некоторыми парнями из Spring, и они также согласились, что вы никогда не сохраните ничего, кроме Instant. Только момент - это конкретный момент времени, который затем можно преобразовать с использованием метаданных.
Использование составного значения
Согласно спецификации JPA 2.2, спецификации , CompositeValues не упоминаются. Это означает, что они не вошли в спецификацию, и вы не можете сохранить одно поле в нескольких столбцах базы данных в настоящее время. Ищите «Композит» и смотрите только упоминания, связанные с идентификаторами.
Как бы то ни было, Hibernate мог бы сделать это, как указано в комментариях к этому ответу.
Пример реализации
Я создал этот пример с учетом этого принципа: открыт для расширения, закрыт для модификации. Подробнее об этом принципе читайте здесь: Открытый / закрытый принцип в Википедии .
Это означает, что вы можете сохранить свои текущие поля в базе данных (отметку времени), и вам нужно только добавить дополнительный столбец, который не должен мешать.
Кроме того, ваша сущность может сохранять установщики и получатели OffsetDateTime. Внутренняя структура не должна беспокоить абонентов. Это означает, что это предложение не должно навредить вашему API.
Реализация может выглядеть так:
@Entity
public class UserPdo {
@Column(name = "created_on")
private Instant createdOn;
@Column(name = "display_offset")
private int offset;
public void setCreatedOn(final Instant newInstant) {
this.createdOn = newInstant;
this.offset = 0;
}
public void setCreatedOn(final OffsetDateTime dt) {
this.createdOn = dt.toInstant();
this.offset = dt.getOffset().getTotalSeconds();
}
// derived display value
public OffsetDateTime getCreatedOnOnOffset() {
ZoneOffset zoneOffset = ZoneOffset.ofTotalSeconds(this.offset);
return this.createdOn.atOffset(zoneOffset);
}
}