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>

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

P.S.

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

P.S.2

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

Вы пробовали использовать ZonedDateTime вместо LocalDate в MyRepository?

Boris 06.09.2018 17:59

@Boris, весенние данные jpa с ним не работают. Я вижу ошибку: значение параметра [2018-10-02T00: 00Z] не соответствует ожидаемому типу [java.time.LocalDate (n / a)]; вложенное исключение - java.lang.IllegalArgumentException: значение параметра [2018-10-02T00: 00Z] не соответствует ожидаемому типу [java.time.LocalDate (n / a)]

gstackoverflow 06.09.2018 19:44

Но согласно этому отвечать это должно быть возможно. Spring Data JPA 1.11 поддерживает Hibernate 5.2, в котором поддерживает Java 8 Date / Time Values.

Boris 07.09.2018 13:46

@Boris, я использую более новую версию. Мой эксперимент показывает, что это неправильно.

gstackoverflow 07.09.2018 15:19

Когда вы пытаетесь использовать ZonedDateTime, почему мы видим исключение с жалобой на LocalDate? Вы оставили тип даты в MyInstance как LocalDate?

Boris 07.09.2018 17:58

@ Борис, хорошее замечание. Смогу проверить в понедельник

gstackoverflow 08.09.2018 06:08

Пожалуйста, предоставьте SHOW VARIABLES LIKE '%zone%';

Rick James 12.09.2018 03:29

Как я уже сказал в своем ответе, я думаю, что часовой пояс JVM должен быть установлен на UTC, и это касается не только Hibernate.

git-flo 15.09.2018 18:21

@ Рик Джеймс, system_time_zone UTC time_zone SYSTEM

gstackoverflow 05.10.2018 09:28

@gstackoverflow вы можете попробовать, передав локальную дату с идентификатором зоны UTC, например, для текущей даты LocalDate = LocalDate.now (ZoneId.of ("UTC")); для вашей даты, пожалуйста, просто используйте с ней идентификатор зоны, а затем перейдите к методу репозитория.

kj007 05.10.2018 09:47
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
19
10
7 164
8

Ответы 8

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

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

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

Чтобы увидеть, как он работает в действии, вы можете найти тестовый пример в моем Высокопроизводительный репозиторий Java Persistence GitHub, который демонстрирует, как этот параметр работает с MySQL.

Но похоже, что он у меня в файле yml. Не могли бы вы проверить, правильно ли я его установил?

gstackoverflow 06.09.2018 23:14

Как я могу проверить, есть ли у спящего режима это свойство?

gstackoverflow 06.09.2018 23:19

Добавьте точку останова в эта линия в SessionFactoryOptionsBuilder.

Vlad Mihalcea 07.09.2018 07:07

Я вижу в зависимостях hibernate-core: 5.2.17

gstackoverflow 07.09.2018 09:45

Я нашел строку: ** Object jdbcTimeZoneValue = configurationSettings.get (JDBC_TIME_ZONE); ** и оценивается как UTC в org.hibernate.boot.internal.SessionFactoryBuilderImpl

gstackoverflow 07.09.2018 09:52

Затем вам нужно отладить его, чтобы понять, почему он не работает для вас, и сравнить с этот тест в моем репозитории высокопроизводительного java-persistence, который отлично работает.

Vlad Mihalcea 07.09.2018 09:57

Вы хотя бы используете более новую версию спящего режима. и более старая версия mysql (вы используете 6, но я использую 8)

gstackoverflow 07.09.2018 10:40

Попробуйте обновить драйвер в моем репозитории и посмотрите, не сломается ли тест.

Vlad Mihalcea 07.09.2018 10:54

Кроме того, вы проверяете данные и время. В моем случае я использую localDate, у которого нет времени

gstackoverflow 07.09.2018 10:55

Затем вы должны сопоставить его со столбцом MySQL DATE, и проблема решена.

Vlad Mihalcea 07.09.2018 12:24

В базе данных есть столбец ДАТА.

gstackoverflow 08.09.2018 06:09

Любые идеи? Вы понимаете, что предоставленный вами совет был применен в моем вопросе и у меня он не работает?

gstackoverflow 05.10.2018 09:16

это не работает: особенно в этом случае «LocalDate в Java, вы должны использовать DATE» и использование «spring.jpa.properties.hibernate.jdbc.time_zone = CET» не помогает.

dermoritz 09.11.2020 17:00

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

Собственно, в идеале часовой пояс ваших серверов должен быть не имеющий отношения. Ваше программирование должно явно указывать желаемые / ожидаемые часовые пояса в качестве необязательных аргументов для классов даты и времени, а не полагаться неявно на текущие часовые пояса по умолчанию.

Basil Bourque 09.09.2018 01:01

Я согласен с комментарием Базиля Бурка. Во времена (глобальных) распределенных баз данных и микросервисных архитектур вы должны четко понимать тему часового пояса как вашего приложения, так и базовой базы данных. Тем не менее, часовые пояса должны быть абстрагированными и, следовательно, неуместными.

git-flo 15.09.2018 17:53

В MySQL ...

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

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

system_time_zone UTC time_zone SYSTEM

gstackoverflow 05.10.2018 09:33

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

Как правильно указывает другой ответ, вы можете установить свойство hibernate.jdbc.time_zone=UTC, как описано. Тем не менее, это не решило мои проблемы, поэтому мне пришлось установить часовой пояс 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) отстает от контекста базы данных в UTC на 3 часа. Простой пример:

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

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

ваш подход будет работать, но мы не можем гарантировать, что все серверы будут работать в UTC

gstackoverflow 05.10.2018 09:33

@gstackoverflow здесь не должно быть проблем, потому что описанный метод init() устанавливает часовой пояс Java в UTC, независимо от того, в каком часовом поясе находится сервер.

git-flo 05.10.2018 11:00

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

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

binding parameter [1] as [DATE] - [2018-09-06]

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

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

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

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

Я сделал все, как указано в ответах раньше, например

  • Добавление spring.jpa.properties.hibernate.jdbc.time_zone = America / Sao_Paulo
  • Установка часового пояса по умолчанию: TimeZone.setDefault(TimeZone.getTimeZone("America/Sao_Paulo"))

но ни один из них не работал, пока я не добавлю параметр serverTimezone = Америка / Сан-Паулу в свой URL-адрес JDBC:

jdbc:mysql://localhost:3306/aurorabuzz-test?serverTimezone=America/Sao_Paulo

Теперь все работает нормально!

Другие вопросы по теме