休眠存储的LocalDate错误

时间:2019-06-23 00:02:51

标签: spring hibernate jpa thymeleaf localdate

我正在尝试将数据库中的餐厅预订日期存储在数据库中,但是,即使我提交的日期是正确的,休眠状态也会在数据库中存储我提交日期之前一天的日期。我不知道为什么...这可能是时区问题,但我不明白为什么...日期不应该受时区影响。

这是我的spring boot属性文件:

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 + 1小时(标准时间)
  • 夏令时,格林尼治标准时间(GMT)/ UTC + 2小时

当前我们是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方法 save 来存储对象。 我正在使用百里香处理html模板。 如果我在数据库中提交日期30/6/2019,则会得到29/6/2019。当我按日期检索Dinner对象时,如果插入30/6/2019,则会得到日期为29/6/2019的Dinner。因此,似乎Spring自己以一种奇怪的方式来处理日期...考虑某种时区,但我不知道如何禁用或处理它。有想法吗?

3 个答案:

答案 0 :(得分:0)

相同的问题(和相同的国家!:-))。

我怀疑如果在保留日期后将休眠或jpa设置为时区UTC,而将机器设置为默认时区== Europe/Rome,那么它将自动从机器时区转换到数据库时区,如果您将所有日期都以UTC格式存储在数据库中,那么这不是一个坏功能。

当您在保留日期之前转换日期时,就会发生问题:它被转换了两次。至少这是我的情况。

仍在寻找最佳解决方案!以防万一,然后再将其添加到答案中。

答案 1 :(得分:0)

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

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

  3. 将字段类型更改为TIMESTAMP后,请按以下方式更改方法createDinner

    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代表PreparedStatement的对象,而index代表插入/更新查询中此字段的索引(从1开始)。

答案 2 :(得分:-1)

您必须像这样设置serverTimezone变量:

url:jdbc:mysql:// localhost:3306 / databaseName?useSSL = false&useUnicode = true&useJDBCCompliantTimezoneShift = true&useLegacyDatetimeCode = false&serverTimezone = Europe / Italy