Can any Java 8 + JDBC expert tell me if something's wrong in the following reasoning? And, if in the secrets of Gods, why this hasn't been done?
A java.sql.Date
is currently the type used by JDBC to map to the DATE SQL type, which represents a date without time, and without timezone. But this class is awfully designed, since it's in fact a subclass of java.util.Date
, which stores a precise instant in time, up to the millisecond.
To represent the date 2015-09-13 in database, we're thus forced to choose a timezone, parse the string "2015-09-13T00:00:00.000" in that timezone as a java.util.Date to get a millisecond value, then construct a java.sql.Date
from this millisecond value, and finally call setDate()
on the prepared statement, passing a Calendar holding the timezone chosen in order for the JDBC driver to be able to correctly recompute the date 2015-09-13 from this millisecond value. This process is made a bit simpler by using the default timezone everywhere, and not passing a Calendar.
Java 8 introduces a LocalDate class, which is a much better fit for the DATE database type, since it's not a precise moment in time, and is thus not dependent on the timezone. And Java 8 also introduces default methods, which would allow to make backward-compatible changes to the PreparedStatement and ResultSet interfaces.
So, haven't we missed a huge opportunity to clean up the mess in JDBC while still maintaining backward compatibility? Java 8 could simply have added those default methods to PreparedStatement and ResultSet:
default public void setLocalDate(int parameterIndex, LocalDate localDate) {
if (localDate == null) {
setDate(parameterIndex, null);
}
else {
ZoneId utc = ZoneId.of("UTC");
java.util.Date utilDate = java.util.Date.from(localDate.atStartOfDay(utc).toInstant());
Date sqlDate = new Date(utilDate.getTime());
setDate(parameterIndex, sqlDate, Calendar.getInstance(TimeZone.getTimeZone(utc)));
}
}
default LocalDate getLocalDate(int parameterIndex) {
ZoneId utc = ZoneId.of("UTC");
Date sqlDate = getDate(parameterIndex, Calendar.getInstance(TimeZone.getTimeZone(utc)));
if (sqlDate == null) {
return null;
}
java.util.Date utilDate = new java.util.Date(sqlDate.getTime());
return utilDate.toInstant().atZone(utc).toLocalDate();
}
Of course, the same reasoning applies to the support of Instant for the TIMESTAMP type, and the support of LocalTime for the TIME type.
setObject
method. If the object happens to be aLocalDate
, up-to-date JDBC drivers are supposed to know what to do with it. – RealSkeptic