Как остановить изменение 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). У вас есть две альтернативы:

  1. Установка JVM с использованием -Duser.timezone=Europe/Berlin или программно с помощью TimeZone.setDefault(TimeZone.getTimeZone("Europe/Berlin"));

  2. Создание зонированного 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;
    }
    

Я использовал второй вариант.