Det aktuella läget över tid

Sedan tiden då JDBC uppfanns och dess gränssnitt standardiserades har det gått 20 år, och under denna tid har mycket saker förändrats.

För det första har världen blivit global och nu kan en server betjäna användare från hela världen. Internethastigheten är uppe. Därför lades ytterligare en datatyp till SQL för att fungera med tiden. Nu ser typerna ut så här:

  • DATUM - lagrar datum: år, månad, dag.
  • TID - lagrar tid: timmar, minuter, sekunder.
  • TIMESTAMP - lagrar en specifik tidpunkt: datum, tid och millisekunder.
  • TIDSTÄMPEL MED TIDZON - TIDSTÄMPEL och tidszon (zonnamn eller offset).

För det andra introducerade Java DateTime API för global tidshantering. Den har följande klasser:

  • Datum och tid :
    • LocalDate
    • Lokal tid
  • Exakt ögonblick :
    • java.time.Instant
    • java.time.LocalDateTime
    • java.time.OffsetDateTime
    • java.time.ZonedDateTime
  • Tid med tidszon :
    • java.time.OffsetDateTime
    • java.time.ZonedDateTime

Den tredje intressanta punkten är att många SQL-klienter skulle vilja ta emot tid från servern redan i sin lokala zon . Naturligtvis kan du konvertera tid i farten, men det är inte bekvämt, och det finns misstag.

Till exempel vill jag hämta alla uppgifter för idag från databasen. SQL Server har en CURDATE() -funktion för detta. Bara här är servern i USA, och jag är i Japan. Och jag skulle vilja att han lämnade tillbaka alla skivor för "min idag", och inte "hans idag".

Generellt sett måste SQL-servern också kunna arbeta smart med klienter i olika tidszoner.

Moderna problem kräver moderna lösningar

I princip kan nya typer från Java DateTime API och typer från SQL enkelt mappas. För att representera DATE -typen i Java måste du använda klassen java.time.LocalDate från JDK 8 DateTime API.

TIME-typen från databasen kan representeras av två typer från Java: java.time.LocalTime och java.time.OffsetTime . Inget komplicerat heller.

En specifik tidpunkt, representerad av TIMESTAMP -typen i databasen, kan representeras i Java med 4 typer:

  • java.time.Instant
  • java.time.LocalDateTime
  • java.time.OffsetDateTime
  • java.time.ZonedDateTime

Och slutligen, TIDSTÄMPEL MED TIDZON kan representeras av två typer:

  • java.time.OffsetDateTime
  • java.time.ZonedDateTime

Eftersom du redan är bekant med DateTime API kommer det inte att vara svårt för dig att komma ihåg den här frågan :)

Jag kommer att skriva det i form av en tabell, så det blir lättare:

SQL TYP Java typ
DATUM java.time.LocalDate
TID java.time.LocalTime
java.time.OffsetTime
TIDSSTÄMPEL java.time.Instant
java.time.LocalDateTime
java.time.OffsetDateTime
java.time.ZonedDateTime
TIDSTÄMPEL MED TIDZON java.time.OffsetDateTime
_

Får datumet

Jag har goda nyheter till dig. Först på länge. Vi kan komma runt begränsningen för metoden getDate() , som returnerar en java.sql-datumtyp.

Poängen är att objektetresultatet sattdet finns en annan intressant metod - getObject() . Denna metod tar två parametrar: en kolumn och en typ, och returnerar värdet på kolumnen som konverterats till den givna typen. Metodens allmänna form är följande:

ClassName Name = getObject(column, ClassName);

Och om du vill konvertera DATE- typen till typen java.time.LocalDate , måste du skriva något i stil med:

LocalDate localDate = results.getObject(4, LocalDate.class);

Och vilken TIDSTÄMP som helst kan konverteras till ett gäng typer:

java.time.Instant instant = results.getObject(9, java.time.Instant.class);
java.time.LocalDateTime local = results.getObject(9, java.time. LocalDateTime.class);
java.time.OffsetDateTime offset = results.getObject(9, java.time. OffsetDateTime.class);
java.time.ZonedDateTime zoned = results.getObject(9, java.time. ZonedDateTime.class);

Viktig! Den här koden fungerar inte om du har en föråldrad MySQL JDBC-drivrutin . Var uppmärksam på versionen av "mysql-connector-java" skriven i din pom.xml, eller lagt till i biblioteken i projektinställningarna.

Förresten, på samma sätt kan du komma runt oförmågan att lagra null för primitiva typer. Om en tabellkolumn är av typen INT, finns det ett par sätt att få null från den. Se exempel nedan:

Integer id1 = results.getObject(8, Integer.class);    	 // this will work
Integer id2 = results.getObject(8, int.class);                 //this will also work
int id3 = results.getObject(8,  Integer.class);            	//method will return null, JVM will throw NPE
int id4 = results.getObject(8,  int.class);                    	//method will return null, JVM will throw NPE

Tidszonsinställning i MySQL

Många intressanta saker hände också med MySQL. Som du vet, när du skapar en MySQL-anslutning, kan du lägga till olika parametrar till den :
mysql://localhost:3306/db_scheme?Name=meaning&Name=meaning

Så tre parametrar har lagts till för att fungera med tidszoner i MySQL. Du kan skicka dessa parametrar när du upprättar en anslutning till servern.

Nedan kommer jag att ge en tabell med dem:

Parameter Värderingar Standardvärde
anslutningstidszon LOKAL | SERVER | användarzon SERVER
forceConnectionTimeZoneToSession sant | falsk Sann
bevaraInstants sant | falsk falsk

Med parametern connectionTimeZone väljer vi tidszonen (tidszonen) där alla förfrågningar kommer att exekveras. Ur klientens synvinkel körs servern i den angivna tidszonen.

Parametern forceConnectionTimeZoneToSession gör att variabeln session time_zone ignoreras och ersätts med connectionTimeZone.

Slutligen styr parametern preserveInstants den exakta tidskonverteringen mellan JVM:s tidszon och connectionTimeZone.

De vanligaste konfigurationerna är:

  • connectionTimeZone=LOCAL & forceConnectionTimeZoneToSession=false - motsvarar den gamla MySQL JDBC-drivrutinen version 5.1 med useLegacyDatetimeCode=true.

  • connectionTimeZone=LOCAL & forceConnectionTimeZoneToSession=true är ett nytt läge som ger det mest naturliga sättet att hantera datum- och tidsvärden.

  • connectionTimeZone=SERVER & preserveInstants=true - Motsvarar den gamla MySQL JDBC-drivrutinen version 5.1 med useLegacyDatetimeCode=false.

  • connectionTimeZone=user_defined & preserveInstants=true - Hjälper till att övervinna situationen där serverns tidszon inte kan identifieras av anslutningen eftersom den är inställd som en generisk förkortning såsom CET/CEST.

Ja, datum är ett intressant ämne och det finns många problem med dem. Som man säger: det är läskigt såklart, men jag är inte heller förbannad! :)