How to stop LocalDate from changing when being saved to a mySQL database

眉间皱痕 提交于 2020-02-21 09:48:26

问题


When saving a LocalDate field (e.g. '2017-09-27') to a mySQL Date column using JPA CriteriaBuilder API, the result is different (e.g. '2017-09-26').

I have validated that my database's timezone is set to UTC using SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP) as the result is '00:00:00'.

I am testing this locally, and I have a timezone of GMT + 2, so my suspicion is that when the conversion occurs from LocalDate to Date, 2 hours are being deducted and producing a date 1 day before the requested date (assuming that the LocalDate field, as a result of it having no time information, is being treated as 00:00:00.

What is the best way to save LocalDates in this situation? Should I be following the advice here https://stackoverflow.com/a/29751575/3832047 and explicitly setting all LocalDate fields to UTC or something similar?

I ran a test to see what happens when converting them in code and got the following result:

Date convertedDate = Date.valueOf(localDate);

conversion-result

EDIT

Here is an example of the code I use to retrieve the data, where the odd date change occurs as well. If I request data for 2017-06-27, I receive results for 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:


Since LocalDate has no TimeZone, you can map the column_date as long in your database schema, and use AttributeConverter to convert LocalDate to long to avoid time zone conversion problems :

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:


I had the same issue and I solved it seeing the hibernate atlassian issues ( here is the thread: https://hibernate.atlassian.net/browse/HHH-11396) . You have two alternatives:

  1. Setting the JVM using -Duser.timezone=Europe/Berlin or programmatically by TimeZone.setDefault(TimeZone.getTimeZone("Europe/Berlin"));

  2. Creating a zoned DateTime and then converting it back to 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;
    }
    

I used the second alternative.



来源:https://stackoverflow.com/questions/46446598/how-to-stop-localdate-from-changing-when-being-saved-to-a-mysql-database

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!