Я пытаюсь сохранить в базе данных дату бронирования ресторана, но, хотя дата, которую я отправляю, верна, спящий режим хранит в базе данных дату за день до той, которую я отправил. Я не знаю, почему... это, вероятно, проблема с часовым поясом, но я не могу понять, почему... часовой пояс не должен влиять на дату.
Вот мой файл свойств весенней загрузки:
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: **********
Я из Италии, поэтому мой часовой пояс:
В настоящее время мы 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;
}
Который вызывает метод службы создатьУжин, который вызывает метод Jpa спасти для сохранения объекта. Я использую тимелеаф для обработки html-шаблонов. Если я отправлю дату 30.06.2019 в базу данных, я получу 29.06.2019. Когда я получаю объект «Ужин» по дате, если я вставляю 30.06.2019, я получаю ужин с датой 29.06.2019. Так что кажется, что spring странно обрабатывает дату сама по себе... учитывая какой-то часовой пояс, но я не знаю, как отключить или справиться с этим. Есть идеи?
Тип — DATE, как я уже сказал, createDinner просто вызывает save.
еще несколько вопросов: вы проверяли, что на самом деле находится в базе данных после сохранения объекта? И как вы получаете объект Dinner?
Как я уже сказал: «Если я отправлю дату 30.06.2019 в базу данных, я получу 29.06.2019», так что да, я проверил ... в этом все дело! Я получаю объект обеда, вызывая метод findById JpaRepository. Но проблема в базе данных... она спасает не тот день...
Какую версию MySQL и коннектор вы используете? Вы также используете сторонний пул соединений? Это может быть актуально: bugs.mysql.com/bug.php?id=71084
Вы можете попробовать это: stackoverflow.com/a/60906163/8536903 для правильной настройки локального
Предполагая, что ваш часовой пояс: Европа/Италия, вы должны настроить переменную serverTimezone следующим образом:
URL: jdbc:mysql://localhost:3306/databaseName?useSSL=false&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Европа/Италия
та же проблема (и та же страна! :-)).
Я подозреваю, что если hibernate или jpa установлены с часовым поясом UTC, в то время как для машины установлен часовой пояс по умолчанию == Europe/Rome, когда дата сохраняется, она будет автоматически преобразована из часового пояса машины в часовой пояс базы данных, что неплохо, если у вас есть все даты, хранящиеся в формате UTC в БД.
Проблема возникает, когда вы конвертируете дату перед сохранением: она преобразуется дважды. По крайней мере, это мой случай.
Все еще в поисках лучшего решения! Если я найду его, я добавлю его позже в ответ.
Вы нашли решение? На мой взгляд, это ошибка: bugs.mysql.com/bug.php?id=104822&thanks=4
Нет.. Я больше не работал над этим проектом. Но я использовал PostgreSql, ссылка есть на форуме mysql... это может иметь отношение, но я не настолько эксперт по Hibernate, чтобы рассказывать!
Вам не нужно определять формат шаблона yyyy-MM-dd. LocalDate#разбор по умолчанию использует DateTimeFormatter.ISO_LOCAL_DATE, что означает, что LocalDate.parse("2020-06-29") работает без явного применения формата.
Поскольку вы уже знаете, что дата-время в вашем часовом поясе отличается от времени в UTC, вы никогда не должны рассматривать только дату; скорее вы должны учитывать как дату, так и время, например. 23:30 по всемирному координированному времени 29 июня 2020 года выпадает на 30 июня 2020 года в вашем часовом поясе. Поэтому первое, что вы должны сделать, это изменить тип поля как TIMESTAMP в базе данных.
После того, как вы изменили тип поля на 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) этого поля в вашем запросе на вставку/обновление.
какой тип базы данных столбца у
date? Также добавьте код для методаcreateDinner(LocalDate date). + посмотрите здесь: stackoverflow.com/questions/43476364/… : "... Итак, если вы хотите назначить встречу на 9 утра, вы должны использовать LocalTime или LocalDateTime, записанные в столбце базы данных типа TIMESTAMP WITHOUT TIME ZONE..."