J'ai une application démarrée sur Tomcat sur MACHINE_A avec le fuseau horaire GMT + 3.
J'utilise un serveur MySQL distant démarré sur MACHINE_B avec le fuseau horaire UTC.
Nous utilisons spring-data-jpa pour la persistance.
Comme exemple du problème, je montrerai le référentiel:
public interface MyRepository extends JpaRepository<MyInstance, Long> {
Optional<MyInstance> findByDate(LocalDate localDate);
}
Si je passe localDate pour 2018-09-06
, j'obtiens des entités dont la date est 2018-09-05
(jour précédent)
Dans les journaux, je vois:
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]
J'ai googlé cette question beaucoup et trouvé plusieurs articles avec le même contenu (par exemple https://moelholm.com/2016/11/09/spring-boot-controlling-timezones-with-hibernate/ )
Donc, j'ai le application.yml
suivant:
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
Mais ça n'aide pas.
Nous utilisons le connecteur suivant:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-Java</artifactId>
<version>8.0.12</version>
</dependency>
Comment puis-je résoudre mon problème?
J'ai essayé d'exécuter les deux applications avec le même fuseau horaire. Dans ce cas, tout fonctionne comme prévu.
J'ai essayé d'utiliser la version 6.0.6 du pilote MySQL, mais cela ne change rien.
Si vous utilisez LocalDate
en Java, vous devez utiliser une colonne DATE
dans MySQL. De cette façon, le problème sera résolu.
Si vous utilisez LocalDateTime
, essayez de définir la propriété comme ceci dans Spring Boot:
spring.jpa.properties.hibernate.jdbc.time_zone=UTC
Pour une explication plus détaillée, consultez cet article . Vous pouvez trouver un scénario de test dans mon référentiel GitHub Java Persistence hautes performances qui fonctionne parfaitement.
spring.jpa.properties.hibernate.jdbc.time_zone=UTC
Il est utilisé lorsque vous travaillez avec TimeZoned Date, mais d'après vos journaux, il semble que vous ne passez pas TimeZone:
paramètre de liaison [1] en tant que [DATE] - [2018-09-06]
Essayez de propriété à distance:
spring.jpa.properties.hibernate.jdbc.time_zone=UTC
J'ai rencontré des problèmes similaires lors de la création de tests d'intégration pour une application spring-boot
à l'aide de hibernate
. La base de données que j'ai utilisée ici était postgreSQL
.
Comme une autre réponse le souligne correctement, vous pouvez définir la propriété hibernate.jdbc.time_zone=UTC
comme décrit. Peu importe, cela ne résolvait pas mes problèmes, je devais donc définir le fuseau horaire par défaut JVM
à l'aide des éléments suivants dans la classe principale de mes applications 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
}
Cela devrait également résoudre vos problèmes. Vous pouvez collecter plus d'informations ici .
Raison
Je suppose que votre problème (récupération de la date - 1 jour) provient de votre configuration spécifique. Si votre application s'exécute en UTC et demande des horodatages à une base de données dans GMT + 3, elle est résolue à une date antérieure, car le contexte des applications (JVM
et Hibernate
sont responsables ici) a 3 heures de retard sur le contexte de la base de données UTC. Exemple simple:
2018-12-02 00:00:00
- 3heures = 2018-12-01 21:00:00
Comme vous ne regardez que les dates: 2018-12-02
- 3hours = 2018-12-01
En MySQL ...
TIMESTAMP
stocke en interne l'UTC, mais effectue la conversion vers/depuis le fuseau horaire du serveur en fonction de deux paramètres. Vérifiez ces paramètres via SHOW VARIABLES LIKE '%zone%';
Correctement configuré, le lecteur peut voir une heure différente de celle du rédacteur (en fonction des paramètres tz).
DATE
et DATETIME
prenez tout ce que vous lui donnez. Il n'y a pas de conversion tz entre la chaîne du client et ce qui est stocké dans la table. Pensez-y à stocker l'image d'une horloge. Le lecteur verra la chaîne même heure que l'auteur a écrite.
Idéalement, vos deux serveurs doivent se trouver dans le même fuseau horaire et l'un d'entre eux doit être au fuseau horaire UTC. Et pour montrer l’heure exacte à l’utilisateur dans son fuseau horaire; vous l'analyse dans le navigateur lui-même. Et lors de la récupération des données de la base de données; vous utilisez l'heure UTC. De cette façon, vous ne rencontrerez aucun problème lors de la récupération des données de la base de données.
Si vous ajoutez l'analyse suivante à votre requête HQL, il renverra une date sans format de fuseau horaire ni heure du jour. Ceci est une solution rapide à votre problème.
select DATE_FORMAT(date,'%Y-%m-%d') from Entity