Я пытаюсь сохранить java.time.LocalDateTime
, используя Hibernate и JPA. Я использовал Jadira Framework ("org.jadira.usertype:usertype.core:3.2.0.GA" и "org.jadira.usertype:usertype.extended:3.2.0.GA"). Я создал файл package-info.java
и создал там @TypeDefs({@TypeDef(defaultForType = java.time.LocalDateTime.class, typeClass = org.jadira.usertype.dateandtime.threeten.PersistentLocalDateTime.class)})
. Я протестировал решение, и поля java.time.LocalDateTime
сохраняются/извлекаются в мою базу данных MySQL в столбцах DATETIME
(почти) правильно.
Единственная проблема заключается в том, что значения в базе данных составляют +2 часа к правильному значению времени из полей в Java. Я нахожусь в CEST (UTC+2), поэтому я понял, что это какая-то проблема с часовыми поясами. Я отладил код PersistentLocalDateTime
и вот что нашел.
PersistentLocalDateTime
используетorg.jadira.usertype.dateandtime.threeten.columnmapper.AbstractTimestampThreeTenColumnMapper
AbstractTimestampThreeTenColumnMapper
имеет полеZoneOffset databaseZone
по умолчанию, установленное наZoneOffset.of("Z")
(UTC).- Поскольку он считает, что моя база данных находится в часовом поясе UTC (а приложение находится в UTC+2), он добавляет два часа к моему времени во время преобразования в базу данных (и вычитает два часа из моего времени во время преобразования из базы данных). Так что в приложении я вижу правильную дату и время, а в базе данных нет.
Я обнаружил, что можно добавлять параметры к @TypeDef
, поэтому я указал их, как показано ниже:
@TypeDef(defaultForType = LocalDateTime.class, typeClass = PersistentLocalDateTime.class,
parameters = {
@Parameter(name = "databaseZone", value = "+02:00")
}),
но у меня есть исключение:
java.lang.IllegalStateException: Could not map Zone +02:00 to Calendar
at org.jadira.usertype.dateandtime.threeten.columnmapper.AbstractTimestampThreeTenColumnMapper.getHibernateType(AbstractTimestampThreeTenColumnMapper.java:59)
Я еще немного отладил. AbstractTimestampThreeTenColumnMapper
имеет два метода:
public final DstSafeTimestampType getHibernateType() {
if (databaseZone == null) {
return DstSafeTimestampType.INSTANCE;
}
Calendar cal = resolveCalendar(databaseZone);
if (cal == null) {
throw new IllegalStateException("Could not map Zone " + databaseZone + " to Calendar");
}
return new DstSafeTimestampType(cal);
}
private Calendar resolveCalendar(ZoneOffset databaseZone) {
String id = databaseZone.getId();
if (Arrays.binarySearch(TimeZone.getAvailableIDs(), id) != -1) {
return Calendar.getInstance(TimeZone.getTimeZone(id));
} else {
return null;
}
}
Метод getHibernateType
выдает исключение, поскольку метод resolveCalendar
возвращает null
. Почему он возвращает null
? Потому что идентификаторы часовых поясов от java.time.ZoneOffset
и java.util.TimeZone
не совпадают. Насколько я вижу, единственное возможное значение, которое соответствует Z
. Любые другие значения вызывают исключения.
Есть ли способ настроить это правильно? Или это ошибка в Jadira Framework?