Java >> Java tutorial >  >> Tag >> Jdbc

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 i java.sql
  • java.time.LocalDateTime pauser på sommertid overgange ved konvertering gennem java.sql.Timestamp fordi sidstnævnte afhænger af JVM-tidszonen, se kode nedenfor
  • java.sql.Time har millisekund opløsning, men java.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());

Java tag