Неправильный LocalDate хранится в спящем режиме

Я пытаюсь сохранить в базе данных дату бронирования ресторана, но, хотя дата, которую я отправляю, верна, спящий режим хранит в базе данных дату за день до той, которую я отправил. Я не знаю, почему... это, вероятно, проблема с часовым поясом, но я не могу понять, почему... часовой пояс не должен влиять на дату.

Вот мой файл свойств весенней загрузки:

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.06.2019 в базу данных, я получу 29.06.2019. Когда я получаю объект «Ужин» по дате, если я вставляю 30.06.2019, я получаю ужин с датой 29.06.2019. Так что кажется, что spring странно обрабатывает дату сама по себе... учитывая какой-то часовой пояс, но я не знаю, как отключить или справиться с этим. Есть идеи?


person Stefano Sambruna    schedule 23.06.2019    source источник
comment
что такое тип базы данных столбца date? Также добавьте код для метода createDinner(LocalDate date). + посмотрите здесь: stackoverflow.com/questions/43476364/ : ... Итак, если вы хотите назначить встречу в 9 утра, вы должны использовать LocalTime или LocalDateTime, записанные в столбце базы данных типа TIMESTAMP WITHOUT TIME ZONE...   -  person Dmitrii Bocharov    schedule 23.06.2019
comment
Тип — DATE, как я уже сказал, createDinner просто вызывает save.   -  person Stefano Sambruna    schedule 23.06.2019
comment
еще несколько вопросов: вы проверяли, что на самом деле находится в базе данных после сохранения сущности? И как вы получаете объект Dinner?   -  person Dmitrii Bocharov    schedule 23.06.2019
comment
Как я уже сказал: если я отправлю дату 30.06.2019 в базу данных, я получу 29.06.2019, так что да, я проверил ... в этом все дело! Я получаю объект ужина, вызывая метод JpaRepository findById. Но проблема в базе данных... она спасает не тот день...   -  person Stefano Sambruna    schedule 23.06.2019
comment
Какую версию MySQL и коннектор вы используете? Вы также используете сторонний пул соединений? Это может иметь значение: bugs.mysql.com/bug.php?id=71084   -  person vphilipnyc    schedule 24.06.2019
comment
Вы можете попробовать это: stackoverflow.com/a/60906163/8536903 для правильной настройки локального   -  person Lukk17s    schedule 30.03.2020


Ответы (3)


  1. Вам не нужно определять формат шаблона yyyy-MM-dd. LocalDate#parse по умолчанию использует DateTimeFormatter.ISO_LOCAL_DATE, что означает, что LocalDate.parse("2020-06-29") работает без явного применения формата.

  2. Поскольку вы уже знаете, что дата-время в вашем часовом поясе отличается от времени в UTC, вы никогда не должны рассматривать только дату; скорее вы должны учитывать как дату, так и время, например. 23:30 по всемирному координированному времени 29 июня 2020 года выпадает на 30 июня 2020 года в вашем часовом поясе. Поэтому первое, что вы должны сделать, это изменить тип поля на 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) этого поля в запросе на вставку/обновление.

person Arvind Kumar Avinash    schedule 29.06.2020

та же проблема (и та же страна! :-)).

Я подозреваю, что если hibernate или jpa установлены с часовым поясом UTC, в то время как для машины установлен часовой пояс по умолчанию == Europe/Rome, когда дата сохраняется, она будет автоматически преобразована из часового пояса машины в часовой пояс базы данных, что неплохо, если у вас есть все даты, хранящиеся в формате UTC в БД.

Проблема возникает, когда вы конвертируете дату перед сохранением: она преобразуется дважды. По крайней мере, это мой случай.

Все еще в поисках лучшего решения! Если я найду его, я добавлю его позже в ответ.

person funder7    schedule 26.06.2020

Предполагая, что ваш часовой пояс: Европа/Италия, вы должны настроить переменную serverTimezone следующим образом:

URL: jdbc:mysql://localhost:3306/databaseName?useSSL=false&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Европа/Италия

person chafik gouasmia    schedule 05.12.2019