I am trying to persist java.time.LocalDateTime
using Hibernate and JPA. I used Jadira Framework ("org.jadira.usertype:usertype.core:3.2.0.GA" & "org.jadira.usertype:usertype.extended:3.2.0.GA"). I created package-info.java
file and created @TypeDefs({@TypeDef(defaultForType = java.time.LocalDateTime.class, typeClass = org.jadira.usertype.dateandtime.threeten.PersistentLocalDateTime.class)})
there. I tested the solution and the java.time.LocalDateTime
fields are stored/retrieved to my MySQL database in DATETIME
columns (almost) correctly.
The only problem is that the values in database are +2 hours to the correct time value from fields in Java. I'm in CEST (UTC+2) so I understood that this is some problem with time zones. I debugged the code of PersistentLocalDateTime
and this is what I found.
PersistentLocalDateTime
is usingorg.jadira.usertype.dateandtime.threeten.columnmapper.AbstractTimestampThreeTenColumnMapper
AbstractTimestampThreeTenColumnMapper
has fieldZoneOffset databaseZone
by default set toZoneOffset.of("Z")
(UTC).- Because it is thinking that my database is in UTC timezone (and the application is in UTC+2) it adds two hours to my time during conversion to database (and subtracts two hours from my time during conversion from database). So in the application I see the correct date and time but in database I not.
I found that a can add parameters to the @TypeDef
so I specified them as below:
@TypeDef(defaultForType = LocalDateTime.class, typeClass = PersistentLocalDateTime.class,
parameters = {
@Parameter(name = "databaseZone", value = "+02:00")
}),
but I've got an exception:
java.lang.IllegalStateException: Could not map Zone +02:00 to Calendar
at org.jadira.usertype.dateandtime.threeten.columnmapper.AbstractTimestampThreeTenColumnMapper.getHibernateType(AbstractTimestampThreeTenColumnMapper.java:59)
I debugged a little bit more. AbstractTimestampThreeTenColumnMapper
has two methods:
public final DstSafeTimestampType getHibernateType() {
if (databaseZone == null) {
return DstSafeTimestampType.INSTANCE;
}
Calendar cal = resolveCalendar(databaseZone);
if (cal == null) {
throw new IllegalStateException("Could not map Zone " + databaseZone + " to Calendar");
}
return new DstSafeTimestampType(cal);
}
private Calendar resolveCalendar(ZoneOffset databaseZone) {
String id = databaseZone.getId();
if (Arrays.binarySearch(TimeZone.getAvailableIDs(), id) != -1) {
return Calendar.getInstance(TimeZone.getTimeZone(id));
} else {
return null;
}
}
getHibernateType
method throws the exception because resolveCalendar
method returns null
. Why it returns null
? Because time zones IDs from java.time.ZoneOffset
and java.util.TimeZone
does not match. As far as I see the only possible value which match is Z
. Any other values causes exceptions.
Is there any way to setup this correctly? Or is it a bug in the Jadira Framework?