Подтвердить что ты не робот

Hibernate сохраняет/извлекает дату минус день, если приложение использует другой часовой пояс, чем MySQL

У меня есть приложение, запущенное на tomcat на MACHINE_A с часовым поясом GMT + 3.

Я использую удаленный сервер MySQL, запущенный на MACHINE_B с часовым поясом UTC.

Мы используем spring-data-jpa для настойчивости.

В качестве примера проблемы я покажу репозиторий:

public interface MyRepository extends JpaRepository<MyInstance, Long> {
    Optional<MyInstance> findByDate(LocalDate localDate);
}

Если я передам localDate для 2018-09-06, я получаю объекты, где дата равна 2018-09-05 (предыдущий день)

В журналах я вижу:

2018-09-06 18:17:27.783 TRACE 13676 --- [nio-8080-exec-3] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [DATE] - [2018-09-06]

Я много искал этот вопрос и нашел несколько статей с одним и тем же контентом (например, https://moelholm.com/2016/11/09/spring-boot-controlling-timezones-with-hibernate/)

Итак, у меня есть следующее application.yml:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/MYDB?useUnicode=true&characterEncoding=utf8&useSSL=false&useLegacyDatetimeCode=false&serverTimezone=UTC
    username: root
    password: *****
  jpa:
    hibernate:
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    properties:
      hibernate:
        show_sql: true
        use_sql_comments: true
        format_sql: true
        type: trace
        jdbc:
          time_zone: UTC

Но это не помогает.

Мы используем следующий разъем:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.12</version>
</dependency>

Как я могу решить свою проблему?

PS

Я попытался запустить оба приложения с одним и тем же часовым поясом. В этом случае все работает так, как ожидалось.

PS2

Я попытался использовать версию MySQL 6.0.6, но ничего не изменил.

4b9b3361

Ответ 1

Если вы используете LocalDate в Java, вы должны использовать столбец DATE в MySQL. Таким образом, проблема будет решена.

Если вы используете LocalDateTime, попробуйте установить свойство, подобное этому в Spring Boot:

spring.jpa.properties.hibernate.jdbc.time_zone=UTC

Для более подробного объяснения ознакомьтесь с этой статьей. Вы можете найти тестовый пример в моем хранилище GitHub с высокой производительностью Java Persistence, который работает отлично.

Ответ 2

Я столкнулся с подобными проблемами, создав некоторые интеграционные тесты для приложения spring-boot использующего hibernate. База данных, которую я использовал здесь, - postgreSQL.

Как правильно указывает другой ответ, вы можете установить свойство hibernate.jdbc.time_zone=UTC как описано. Nevermind, это не решило мои проблемы, поэтому мне пришлось установить часовой пояс JVM умолчанию с помощью следующего в моем основном классе spring-boot:

@PostConstruct
public void init(){
    TimeZone.setDefault(TimeZone.getTimeZone("UTC"));   // It will set UTC timezone
    System.out.println("Spring boot application running in UTC timezone :"+new Date());   // It will print UTC timezone
}

Это также должно решить ваши проблемы. Вы можете собрать больше информации здесь.

причина

Я думаю, ваша проблема (получение даты - 1 день) происходит от вашей конкретной настройки. Если ваше приложение работает в UTC и запрашивает отметки времени из базы данных в GMT + 3, оно разрешается в более раннюю дату, так как контекст приложений (JVM и Hibernate несут ответственность здесь) составляет 3 часа позади контекста базы данных в UTC. Простой пример:

2018-12-02 00:00:00 - 3 часа = 2018-12-01 21:00:00

Поскольку вы смотрите только на даты: 2018-12-02 - 2018-12-01= 2018-12-01

Ответ 3

spring.jpa.properties.hibernate.jdbc.time_zone=UTC

Он используется, когда вы работаете с DateZoned Date, но из ваших журналов кажется, что вы не пропустите TimeZone:

параметр привязки [1] в качестве [ДАТА] - [2018-09-06]

Попробуйте удаленную собственность:

spring.jpa.properties.hibernate.jdbc.time_zone=UTC

Ответ 4

В идеале, оба сервера должны находиться в одном и том же часовом поясе, а предпочтительный - в часовой пояс UTC. И показать правильное время для пользователя в его часовом поясе; вы разбираете его в самом браузере. И при получении данных из БД; вы используете время UTC. Таким образом, вы не будете иметь проблемы при извлечении данных из БД

Ответ 5

В MySQL...

TIMESTAMP внутренне хранит UTC, но конвертирует в/из часовой пояс сервера на основе двух настроек. Проверьте эти настройки с помощью SHOW VARIABLES LIKE '%zone%'; Правильно настроенный читатель может видеть другое время, чем писатель (на основе настроек tz).

DATE и DATETIME возьмите все, что вы им дадите. Нет никакого преобразования tz между строкой в клиенте и тем, что хранится в таблице. Подумайте об этом, сохранив изображение часов. Читатель увидит ту же самую строку времени, которую написал писатель.

Ответ 6

Если вы добавите следующий синтаксический анализ в ваш запрос HQL, он вернет дату без какого-либо формата часового пояса или времени суток. Это быстрое решение вашей проблемы.

select DATE_FORMAT(date,'%Y-%m-%d') from Entity

Ответ 7

Я попробовал все ответы, представленные здесь, и они не помогли. У меня есть столбец Дата на стороне MySql и установлен параметр spring.jpa.properties.hibernate.jdbc.time_zone = UTC.

Наконец, я заменил LocalDate на java.util.Date и добавил фильтрацию шаблонов, чтобы не проводить там время. Проблема исчезла. С LocalDate что-то не так, но я не хочу быть честным, поскольку обходной путь не так уж и плох :-)