Glemte du muligheden for at rette JDBC-datohåndtering i Java 8?
For at repræsentere datoen 2015-09-13 i databasen, er vi derfor tvunget til at vælge en tidszone, parse strengen "2015-09-13T00:00:00.000" i den tidszone som en java.util.Date for at få et millisekund værdi, konstruer derefter en java.sql.Date fra denne millisekundværdi, og kald til sidst setDate() på den forberedte sætning, idet du passerer en kalender, der indeholder den valgte tidszone, for at JDBC-driveren skal være i stand til korrekt at genberegne datoen 2015-09 -13 fra denne millisekundværdi
Hvorfor? Bare ring
Date.valueOf("2015-09-13"); // From String
Date.valueOf(localDate); // From java.time.LocalDate
Opførselen vil være den korrekte i alle JDBC-drivere:Den lokale dato uden tidszone. Den omvendte operation er:
date.toString(); // To String
date.toLocalDate(); // To java.time.LocalDate
Du bør aldrig stole på java.sql.Date
s afhængighed af java.util.Date
, og det faktum, at den dermed arver semantikken af en java.time.Instant
via Date(long)
eller Date.getTime()
Så har vi ikke gået glip af en kæmpe mulighed for at rydde op i rodet i JDBC og samtidig bevare bagudkompatibiliteten? [...]
Det kommer an på. JDBC 4.2 specificerer, at du er i stand til at binde en LocalDate
skriv via setObject(int, localDate)
, og for at hente en LocalDate
skriv via getObject(int, LocalDate.class)
, hvis chaufføren er klar til det. Ikke så elegant som mere formel default
metoder, som du selvfølgelig foreslog.
Kort svar:nej, håndtering af dato og klokkeslæt er rettet i Java 8 / JDBC 4.2, fordi den understøtter Java 8 dato- og klokkeslætstyper. Dette kan ikke emuleres ved hjælp af standardmetoder.
Java 8 kunne simpelthen have tilføjet disse standardmetoder til PreparedStatement og ResultSet:
Dette er ikke muligt af flere årsager:
java.time.OffsetDateTime
har ikke tilsvarende ijava.sql
java.time.LocalDateTime
pauser på sommertid overgange ved konvertering gennemjava.sql.Timestamp
fordi sidstnævnte afhænger af JVM-tidszonen, se kode nedenforjava.sql.Time
har millisekund opløsning, menjava.time.LocalTime
har nanosekund opløsning
Derfor har du brug for ordentlig driversupport, standardmetoder skal konvertere gennem java.sql
typer, der introducerer tab af data.
Hvis du kører denne kode i en JVM-tidszone med sommertid, går den i stykker. Den søger den næste overgang, hvor urene er "sat frem" og vælger en LocalDateTime
det er lige midt i overgangen. Dette er helt gyldigt, fordi Java LocalDateTime
eller SQL TIMESTAMP
har ingen tidszone og derfor ingen tidszoneregler og derfor ingen sommertid. java.sql.Timestamp
på den anden side er bundet til JVM-tidszonen og derfor underlagt sommertid.
ZoneId systemTimezone = ZoneId.systemDefault();
Instant now = Instant.now();
ZoneRules rules = systemTimezone.getRules();
ZoneOffsetTransition transition = rules.nextTransition(now);
assertNotNull(transition);
if (!transition.getDateTimeBefore().isBefore(transition.getDateTimeAfter())) {
transition = rules.nextTransition(transition.getInstant().plusSeconds(1L));
assertNotNull(transition);
}
Duration gap = Duration.between(transition.getDateTimeBefore(), transition.getDateTimeAfter());
LocalDateTime betweenTransitions = transition.getDateTimeBefore().plus(gap.dividedBy(2L));
Timestamp timestamp = java.sql.Timestamp.valueOf(betweenTransitions);
assertEquals(betweenTransitions, timestamp.toLocalDateTime());