web-dev-qa-db-fra.com

Hibernate enregistre/récupère la date moins le jour si l'application utilise un fuseau horaire différent de MySQL

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?

P.S.

J'ai essayé d'exécuter les deux applications avec le même fuseau horaire. Dans ce cas, tout fonctionne comme prévu.

P.S.2

J'ai essayé d'utiliser la version 6.0.6 du pilote MySQL, mais cela ne change rien.

13
gstackoverflow

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.

11
Vlad Mihalcea

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

1
ValerioMC

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

1
git-flo

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.

0
Rick James

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.

0
Pavan

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
0
Alain Cruz