Spring jpa hibernate mysql LocalDate off one day after persist
Asked Answered
P

2

9

Whenever I persist LocalDate to MySQL Database, the Date is stored one day off (11 Nov 2017 becomes 10 Nov 2017). I've already tried to set the timezone in the application on MySQL server and set the legacyDateTimeCode to false but the problem still exists. Any idea on how to fix it? If I switch to local h2 Database, the Date is stored correctly.

Spring-boot-starter-parent: 1.5.7 hibernate 5.2.10 mysql: 5.7 LocalDate stored in DATE field

Parmenides answered 10/11, 2017 at 17:34 Comment(11)
When retrieved via JPA, the value is correct or still off?Holohedral
its off in db and in the application thats why i am curious. I thought it might be because of different timezone settings thats why i set them the same on mysql server and in the application. But even then its off.Parmenides
Hm... timezone of the actual machine (OS) on which it is running? I remember having this issue couple years back, unfortunately dont remember the details anymore.Holohedral
UTC both mysql and applicationserverParmenides
Does this answer help any?Boulanger
No i tried that as well but thank youParmenides
I'm having the same issue. Did you find a fix for it ? ThanksCappuccino
i forgot about this. I had this issue again later in another project and it was due to the docker image i created having different time settings that the host. So make sure Host,docker images, and applications do have the same time settingsParmenides
bugs.mysql.com/bug.php?id=85570Pluri
Possibly related: bugs.mysql.com/bug.php?id=93444Boulanger
Having the same problem even with the following configurations: application properties: 1) spring.datasource.url=jdbc:mysql://xxxx/database?serverTimezone=UTC 2) spring.jpa.properties.hibernate.jdbc.time_zone=UTC Spring main class: @PostConstruct void started() { TimeZone.setDefault(TimeZone.getTimeZone("UTC")); } Spring Boot 2.5.8 (w/ hibernate 5.4.33 and mysql connector 8.0.27) and MySQL 8.0Minutes
R
1

Add spring.jpa.properties.hibernate.jdbc.time_zone : UTC In Properties file

Roscoe answered 16/5, 2019 at 9:55 Comment(0)
C
1

I have the following environment:

  • Spring-Boot (v3.0.6)
  • Spring (v6.0.8)
  • Spring-Data (v3.0.5)
  • Hibernate (v6.1.7.Final)
  • MySQL Connector J (v8.0.33)

I saved a LocalDate in UTC timezone without any problems, but if I wanted to read the LocalDate from the database it always shifted one day off (my machine TimeZone is Europe/Berlin). Maybe someone has the opposite problem, so that their LocalDate is shifted one day on.

Most of the previous answers couldn't really help me. The only solution, which I did not want to use, because it was impractical, was the following start parameter -Duser.timezone=UTC.

1. Change TimeZone.getDefault()

I tried to change the default TimeZone via a simple @PostConstruct method in Spring-Boot in my @SpringBootApplication.

@PostConstruct
public void changeTimezone() {
  TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
}

Still one day off.

2. TimeZone in MySQL Connection String

I tried to set the serverTimezone via mysql connection string with the following parameter:

jdbc:mysql://{host}/{database}?serverTimezone=UTC

Still one day off.

3. Tell Hibernate the TimeZone

Next thing is to tell hibernate which TimeZone I wanted to use. I've done that via hibernate properties and passed them into my LocalContainerEntityManagerFactoryBean instead of setting them into my application.properties.

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

Surprise... Still one day off..

(Note: I also added the TimeZoneStorageType.NORMALIZE_UTC of hibernate 6, but it shouldn't effect the LocalDate java or java.sql.Date type.)

spring.jpa.properties.hibernate.timezone.default_storage=NORMALIZE_UTC

(HIBERNATE 6 ONLY)

4. Create debug hibernate type and check whats going on.

I debugged through a custom created hibernate descriptor type for LocalDate. It quickly became clear that hibernate was doing everything right and the "error" must lie deeper. So I jumped into mysql-connector-j source code.

The implementation of java.sql.ResultSet ResultSetImpl in the mysql-connector-j is parsing the java.sql.Date values with the TimeZone of the NativeServerSession.

See this method in ResultSetImpl

It uses the ServerSession#getDefaultTimeZone() method to retrieve the "wanted" TimeZone.

See the getDefaultTimeZone() method

As you can see, it checks if it should return the "cached" TimeZone defaultTimeZone field. If not, it recalls the TimeZone.getDefault() method.

Now the problem with Spring-Boot is, that the ServerSession object is already constructed and has cached the previous TimeZone.getDefault() (default jvm TimeZone) in the field linked above, before you can change the TimeZone in a @PostConstruct method via Spring like I've tried.

To fix the issue you can just add these parameters to the mysql connection string:

  • serverTimezone=UTC - Sets the TimeZone
  • useLegacyDatetimeCode=false - Disables the usage of legacy datetime codes
  • forceConnectionTimeZoneToSession=true - Forces the serverTimezone parameter to the session
  • cacheDefaultTimezone=false - Disables the caching TimeZone problem with spring-boot

Example connection string:

jdbc:mysql://{host}/{database}?serverTimezone=UTC&useLegacyDatetimeCode=false&forceConnectionTimeZoneToSession=true&cacheDefaultTimezone=false

I could not find any documentation for some parameters, so I link here again to the PropertyKey class where I found the parameters.

I hope I was able to help someone.

Cretic answered 11/5, 2023 at 9:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.