У меня есть приложение, запущенное на 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>
Как я могу решить мою проблему?
Я пробовал запускать оба приложения с одним часовым поясом. В этом случае все работает как положено.
Я пробовал использовать версию драйвера MySQL 6.0.6, но ничего не меняет.
@Boris, весенние данные jpa с ним не работают. Я вижу ошибку: значение параметра [2018-10-02T00: 00Z] не соответствует ожидаемому типу [java.time.LocalDate (n / a)]; вложенное исключение - java.lang.IllegalArgumentException: значение параметра [2018-10-02T00: 00Z] не соответствует ожидаемому типу [java.time.LocalDate (n / a)]
Но согласно этому отвечать это должно быть возможно. Spring Data JPA 1.11 поддерживает Hibernate 5.2, в котором поддерживает Java 8 Date / Time Values.
@Boris, я использую более новую версию. Мой эксперимент показывает, что это неправильно.
Когда вы пытаетесь использовать ZonedDateTime, почему мы видим исключение с жалобой на LocalDate? Вы оставили тип даты в MyInstance как LocalDate?
@ Борис, хорошее замечание. Смогу проверить в понедельник
Пожалуйста, предоставьте SHOW VARIABLES LIKE '%zone%';
Как я уже сказал в своем ответе, я думаю, что часовой пояс JVM должен быть установлен на UTC, и это касается не только Hibernate.
@ Рик Джеймс, system_time_zone UTC time_zone SYSTEM
@gstackoverflow вы можете попробовать, передав локальную дату с идентификатором зоны UTC, например, для текущей даты LocalDate = LocalDate.now (ZoneId.of ("UTC")); для вашей даты, пожалуйста, просто используйте с ней идентификатор зоны, а затем перейдите к методу репозитория.




Если вы используете LocalDate в Java, вам следует использовать столбец DATE в MySQL. Так проблема будет решена.
Если вы используете LocalDateTime, попробуйте установить это свойство в Spring Boot:
spring.jpa.properties.hibernate.jdbc.time_zone=UTC
Чтобы увидеть, как он работает в действии, вы можете найти тестовый пример в моем Высокопроизводительный репозиторий Java Persistence GitHub, который демонстрирует, как этот параметр работает с MySQL.
Но похоже, что он у меня в файле yml. Не могли бы вы проверить, правильно ли я его установил?
Как я могу проверить, есть ли у спящего режима это свойство?
Добавьте точку останова в эта линия в SessionFactoryOptionsBuilder.
Я вижу в зависимостях hibernate-core: 5.2.17
Я нашел строку: ** Object jdbcTimeZoneValue = configurationSettings.get (JDBC_TIME_ZONE); ** и оценивается как UTC в org.hibernate.boot.internal.SessionFactoryBuilderImpl
Затем вам нужно отладить его, чтобы понять, почему он не работает для вас, и сравнить с этот тест в моем репозитории высокопроизводительного java-persistence, который отлично работает.
Вы хотя бы используете более новую версию спящего режима. и более старая версия mysql (вы используете 6, но я использую 8)
Попробуйте обновить драйвер в моем репозитории и посмотрите, не сломается ли тест.
Кроме того, вы проверяете данные и время. В моем случае я использую localDate, у которого нет времени
Затем вы должны сопоставить его со столбцом MySQL DATE, и проблема решена.
В базе данных есть столбец ДАТА.
Любые идеи? Вы понимаете, что предоставленный вами совет был применен в моем вопросе и у меня он не работает?
это не работает: особенно в этом случае «LocalDate в Java, вы должны использовать DATE» и использование «spring.jpa.properties.hibernate.jdbc.time_zone = CET» не помогает.
В идеале оба сервера должны находиться в одном часовом поясе, а предпочтительный - в часовом поясе UTC. И чтобы показать правильное время пользователю в его часовом поясе; вы разбираете его в самом браузере. И при извлечении данных из БД; вы используете время UTC. Таким образом, у вас не будет проблем при извлечении данных из БД.
Собственно, в идеале часовой пояс ваших серверов должен быть не имеющий отношения. Ваше программирование должно явно указывать желаемые / ожидаемые часовые пояса в качестве необязательных аргументов для классов даты и времени, а не полагаться неявно на текущие часовые пояса по умолчанию.
Я согласен с комментарием Базиля Бурка. Во времена (глобальных) распределенных баз данных и микросервисных архитектур вы должны четко понимать тему часового пояса как вашего приложения, так и базовой базы данных. Тем не менее, часовые пояса должны быть абстрагированными и, следовательно, неуместными.
В MySQL ...
TIMESTAMP внутренне хранит UTC, но преобразует в / из часового пояса сервера на основе двух настроек. Проверьте эти настройки через SHOW VARIABLES LIKE '%zone%';. При правильной настройке считыватель может видеть время, отличное от времени записи (в зависимости от настроек tz).
DATE и DATETIME берут все, что вы им даете. Нет преобразования tz между строкой в клиенте и тем, что хранится в таблице. Подумайте об этом, как о хранении изображения часов. Читатель увидит строку времени такой же, которую написал писатель.
system_time_zone UTC time_zone SYSTEM
Я столкнулся с аналогичными проблемами при создании некоторых интеграционных тестов для приложения 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 здесь не должно быть проблем, потому что описанный метод init() устанавливает часовой пояс Java в UTC, независимо от того, в каком часовом поясе находится сервер.
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
Я сделал все, как указано в ответах раньше, например
TimeZone.setDefault(TimeZone.getTimeZone("America/Sao_Paulo"))но ни один из них не работал, пока я не добавлю параметр serverTimezone = Америка / Сан-Паулу в свой URL-адрес JDBC:
jdbc:mysql://localhost:3306/aurorabuzz-test?serverTimezone=America/Sao_Paulo
Теперь все работает нормально!
В соединителе MySQL до версии 8.0.22 была ошибка, см. Запрос данных Spring для localdate возвращает неправильные записи - минус один день
Вы пробовали использовать ZonedDateTime вместо LocalDate в
MyRepository?