Как остановить изменение LocalDate при сохранении в базе данных mySQL
При сохранении поля LocalDate
(например, "2017-09-27") в столбце mySQL Date
с использованием API JPA CriteriaBuilder результат будет другим (например, "2017-09-26").
Я подтвердил, что часовой пояс моей базы данных установлен на UTC с использованием SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP)
в результате "00: 00: 00".
Я тестирую это локально, и у меня есть часовой пояс GMT + 2, поэтому я подозреваю, что когда происходит преобразование из LocalDate
в Date
, вычитается 2 часа и производится дата за 1 день до запрошенной даты (при условии, что LocalDate
поле, в результате которого отсутствует информация о времени, обрабатывается как 00:00:00.
Каков наилучший способ сохранить LocalDates в этой ситуации? Должен ли я следовать совету здесь qaru.site/info/133886/... и явно устанавливать все поля LocalDate в UTC или что-то подобное?
Я запустил тест, чтобы увидеть, что происходит при преобразовании их в код, и получил следующий результат:
Date convertedDate = Date.valueOf(localDate);
конверсионный результат
РЕДАКТИРОВАТЬ
Вот пример кода, который я использую для извлечения данных, где также происходит нечетное изменение даты. Если я запрашиваю данные за 2017-06-27
, я получаю результаты за 2017-06-26
.
CriteriaBuilder criteriaBuilder = sessionFactory.getCriteriaBuilder();
CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(HorseAndTrailerRequest.class);
Root<HorseAndTrailerRequest> criteria = criteriaQuery.from(HorseAndTrailerRequest.class);
ParameterExpression<LocalDate> effectiveDateParameter = criteriaBuilder.parameter(LocalDate.class);
criteriaQuery.select(criteria)
.where(
criteriaBuilder.equal(criteria.get("effectiveDate"), effectiveDateParameter)
);
TypedQuery<HorseAndTrailerRequest> query = sessionFactory.getCurrentSession().createQuery(criteriaQuery);
query.setParameter(effectiveDateParameter, date);
return query.getResultList();
Ответы
Ответ 1
Поскольку у LocalDate
нет TimeZone, вы можете надолго сопоставить column_date в схеме базы данных и использовать AttributeConverter
для преобразования LocalDate
в long, чтобы избежать проблем с изменением часового пояса:
import javax.persistence.Converter;
import java.time.LocalDate;
import javax.persistence.AttributeConverter;
@Converter
public class LocalDateToLong implements AttributeConverter<LocalDate, Long> {
@Override
public Long convertToDatabaseColumn(LocalDate date) {
if (date != null) {
long epochDay = date.toEpochDay();
return epochDay;
}
return null;
}
@Override
public LocalDate convertToEntityAttribute(Long epochDay) {
// TODO Auto-generated method stub
if (epochDay != null) {
LocalDate date = LocalDate.ofEpochDay(epochDay);
return date;
}
return null;
}
}
Ответ 2
У меня была такая же проблема, и я решил ее, увидев проблемы спящего atlassian (вот ветка: https://hibernate.atlassian.net/browse/HHH-11396). У вас есть две альтернативы:
-
Установка JVM с использованием -Duser.timezone=Europe/Berlin
или программно с помощью TimeZone.setDefault(TimeZone.getTimeZone("Europe/Berlin"));
-
Создание зонированного DateTime, а затем преобразование его обратно в Date
public static final ZoneId ZONE_EUROPE_BERLIN = ZoneId.of("Europe/Berlin");
@Override
public Date convertToDatabaseColumn(LocalDate locDate) {
if (locDate == null) {
return null;
}
ZonedDateTime zonedDateTime = locDate.atStartOfDay(ZONE_EUROPE_BERLIN);
LocalDate producerLocalDate = zonedDateTime.toLocalDate();
Date date = Date.valueOf(producerLocalDate);
return date;
}
Я использовал второй вариант.