如果应用程序使用的时区不是MySQL,则Hibernate会保存/检索日期减去日期

时间:2018-09-06 15:11:54

标签: java mysql hibernate spring-boot timezone

我在MACHINE_A的tomcat上启动了一个具有时区GMT + 3的应用程序。

我使用时区UTC在MACHINE_B上启动的远程MySQL服务器。

我们使用spring-data-jpa进行持久化。

作为问题的一个示例,我将显示存储库:

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

如果我为2018-09-06传递localDate,我将获得日期为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版本,但它没有任何改变。

8 个答案:

答案 0 :(得分:11)

如果在Java中使用LocalDate,则应在MySQL中使用DATE列。这样就可以解决问题。

如果您使用LocalDateTime,请尝试在Spring Boot中这样设置属性:

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

有关详细说明,请查看this article。您可以在我的High-Performance Java Persistence GitHub repository中找到一个很好的测试用例。

答案 1 :(得分:2)

在使用spring-boothibernate应用程序创建一些集成测试时,我遇到了类似的问题。我在这里使用的数据库是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
}

这也应该解决您的问题。您可以收集更多信息here

原因

我想您的问题(检索日期-1天)来自您的特定设置。如果您的应用程序以UTC运行,并从GMT + 3中的数据库请求时间戳,则它会在较早的日期解决,因为应用程序上下文(JVMHibernate在此处负责)比数据库晚了3个小时。 UTC中的上下文。简单的例子:

2018-12-02 00:00:00-3小时= 2018-12-01 21:00:00

仅查看日期:2018-12-02-3hours = 2018-12-01

答案 2 :(得分:2)

版本8.0.22之前的MySQL连接器中存在一个错误,请参见Spring data query for localdate returns wrong entries - minus one day

答案 3 :(得分:1)

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

在您使用“时区日期”时会用到它,但是从您的日志中看,您似乎没有通过“时区”:

  

将参数[1]绑定为[DATE]-[2018-09-06]

尝试访问媒体资源:

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

答案 4 :(得分:0)

理想情况下,您的两台服务器应位于同一时区,首选服务器应位于UTC时区。并向用户显示其所在时区的正确时间;您可以在浏览器本身中对其进行解析。并且在从数据库检索数据时;您使用UTC时间。这样,从数据库中获取数据时就不会有问题

答案 5 :(得分:0)

在MySQL中...

TIMESTAMP在内部存储UTC,但基于两个设置在服务器时区之间进行转换。通过SHOW VARIABLES LIKE '%zone%';检查这些设置。正确配置后,阅读器可能会看到与写入器不同的时间(基于tz设置)。

DATEDATETIME随您便。客户端中的字符串与表中存储的字符串之间没有tz转换。可以认为它是存储时钟的图片。读者将看到作者编写的相同时间字符串。

答案 6 :(得分:0)

如果将以下解析添加到HQL查询,它将返回没有任何时区格式或一天中的时间的日期。这是解决问题的快速解决方案。

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

答案 7 :(得分:0)

我已经按照之前的答案进行了所有操作,例如

  • 添加spring.jpa.properties.hibernate.jdbc.time_zone = America / Sao_Paulo
  • 设置默认时区:TimeZone.setDefault(TimeZone.getTimeZone("America/Sao_Paulo"))

但是它们都不起作用,直到我在JDBC URL中添加参数 serverTimezone = America / Sao_Paulo 为止:

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

现在一切正常!