提问者:小点点

hibernate存储的本地日期错误


我试图在数据库中存储餐厅预订的日期,但即使我提交的日期是正确的,hibernate也会在数据库中保存我提交日期前一天的日期。我不知道为什么。。。这可能是时区问题,但我不明白为什么。。。日期不应受到时区的影响。

这是我的Spring引导属性文件:

spring:
  thymeleaf:
    mode: HTML5
    encoding: UTF-8
    cache: false
  jpa:
    database: MYSQL
    hibernate:
      ddl-auto: update
    properties:
      hibernate:
        locationId:
          new_generator_mappings: false
        dialect: org.hibernate.dialect.MySQL5InnoDBDialect
        jdbc:
          time_zone: UTC
  datasource:
    driver:
      class: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/databaseName?useSSL=false&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
    username: username
    password: **********

我来自意大利,所以我的时区是这样的:

  • 标准时间GMT/UTC 1h
  • 夏令时GMT/UTC 2h

目前我们是UTC 2h。

我存储的对象是这个:

@Entity
public class Dinner {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long dinnerId;

    private LocalDate date;
    ...

我用来拦截POST请求的控制器是这样的:

@PreAuthorize("hasRole('USER')")
@PostMapping
public String createDinner(@RequestParam(value="dinnerDate") String dinnerDate, Principal principal, Model model){
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate date = LocalDate.parse(dinnerDate, formatter);
        dinnerService.createDinner(date);
        return "redirect:/dinners?dinnerDate=" + dinnerDate;
}

哪个调用服务方法 createDinner,调用 Jpa 方法保存来存储对象。我正在使用百里香叶来处理 html 模板。如果我在数据库中提交日期 30/6/2019,我会得到 29/6/2019。当我按日期检索晚餐对象时,如果我插入 30/6/2019,我会得到日期为 29/6/2019 的晚餐。所以似乎Spring以一种奇怪的方式自己处理日期......考虑某种时区,但我不知道如何禁用或处理它。知道吗?


共3个答案

匿名用户

>

  • 您无需为模式 yyyy-MM-dd 定义格式。默认情况下,LocalDate#parse 使用 DateTimeFormatter.ISO_LOCAL_DATE,这意味着 LocalDate.parse(“2020-06-29”) 无需显式应用格式即可工作。

    由于您已经知道时区中的日期时间与 UTC 中的日期时间不同,因此您永远不应该只考虑日期;相反,您应该同时考虑日期和时间,例如,2020-06-29 UTC 晚上 11:30 将落在您的时区的 2020-06-30。因此,您应该做的第一件事是在数据库中将字段的类型更改为 TIMESTAMP

    将字段类型更改为TIMESTAMP后,更改方法,createDner如下所示:

    LocalDateTime dinnerDateTime = LocalDateTime.of(LocalDate.parse(dinnerDate), LocalTime.of(0, 0, 0, 0));
    OffsetDateTime odt = dinnerDateTime.atOffset(ZoneOffset.UTC);
    dinnerService.createDinner(odt);
    

    然后在DinnerService(或DinnerServiceDAO中,无论您在哪里编写了在数据库中插入/更新记录的逻辑):

    pst.setObject(index, odt);
    

    其中pst代表的对象,index代表插入/更新查询中该字段的索引(以1开头)。

  • 匿名用户

    同样的问题(和同一个国家!:-) ).

    我怀疑,如果hibernate或jpa设置了时区< code>UTC,而机器设置了默认时区== Europe/Rome,那么当一个日期被持久化时,它将自动从机器时区转换为数据库时区,如果所有日期都以UTC格式存储在数据库中,这是一个不错的特性。

    当您在持久化之前转换日期时会出现问题:它会被转换两次。至少,这是我的案子。

    仍在寻找最佳解决方案!如果我能找到一个,那么我稍后会将其添加到答案中。

    匿名用户

    假设您的时区是:欧洲/意大利,您必须像这样设置您的serverTimezone变量:

    url: jdbc:mysql://localhost:3306/databaseName?useSSL=false