Как настроить jadira PersistentLocalDateTime с помощью java.time.LocalDateTime?

Я пытаюсь сохранить 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 и вот что нашел.

  1. PersistentLocalDateTime использует org.jadira.usertype.dateandtime.threeten.columnmapper.AbstractTimestampThreeTenColumnMapper
  2. AbstractTimestampThreeTenColumnMapper имеет поле ZoneOffset databaseZone по умолчанию, установленное на ZoneOffset.of("Z") (UTC).
  3. Поскольку он считает, что моя база данных находится в часовом поясе 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?


person Piotr Pradzynski    schedule 05.08.2015    source источник


Ответы (1)


Похоже на серьезный баг. Проблема в том, что параметр jadira.usertype.databaseZone анализируется как ZoneOffset вместо ZoneId. Таким образом, метод resolveCalendar сравнивает 2 разных типа Zone и Offset. Что забавно, параметр называется databaseZone, но не содержит зоны. Он содержит только смещение.

https://github.com/JadiraOrg/jadira/issues/42

https://github.com/JadiraOrg/jadira/issues/43

person Libre13    schedule 31.08.2015
comment
Это поведение исправлено в версии 5.0.0.GA, которая сопоставляется через ZoneId. Отказ от ответственности: я поддерживаю Jadira. - person Chris Pheby; 15.12.2015