Obecny stan rzeczy w czasie

Od czasu wynalezienia JDBC i ujednolicenia jego interfejsów minęło 20 lat iw tym czasie wiele się zmieniło.

Po pierwsze, świat stał się globalny i teraz jeden serwer może obsługiwać użytkowników z całego świata. Szybkość internetu wzrosła. Dlatego do SQL dodano inny typ danych, aby pracować z czasem. Teraz typy wyglądają tak:

  • DATA - przechowuje datę: rok, miesiąc, dzień.
  • TIME - przechowuje czas: godziny, minuty, sekundy.
  • TIMESTAMP - przechowuje określony punkt w czasie: datę, godzinę i milisekundy.
  • TIMESTAMP WITH TIME ZONE - TIMESTAMP i strefa czasowa (nazwa strefy lub przesunięcie).

Po drugie, Java wprowadziła API DateTime do globalnego zarządzania czasem. Ma następujące klasy:

  • Data i godzina :
    • Data lokalna
    • Czas lokalny
  • Dokładny moment :
    • java.time.Instant
    • java.time.LocalDateTime
    • java.time.OffsetDateTime
    • java.time.ZonedDateTime
  • Czas ze strefą czasową :
    • java.time.OffsetDateTime
    • java.time.ZonedDateTime

Trzecią interesującą kwestią jest to, że wielu klientów SQL chciałoby otrzymywać czas z serwera znajdującego się już w ich strefie lokalnej . Oczywiście możesz konwertować czas w locie, ale nie jest to wygodne i zdarzają się błędy.

Na przykład chcę pobrać wszystkie zadania na dziś z bazy danych. SQL Server ma do tego funkcję CURDATE() . Tylko tutaj serwer jest w USA, a ja w Japonii. I chciałbym, żeby zwrócił wszystkie zapisy na „moje dzisiaj”, a nie „jego dzisiaj”.

Ogólnie rzecz biorąc, serwer SQL musi być w stanie inteligentnie współpracować z klientami w różnych strefach czasowych.

Współczesne problemy wymagają nowoczesnych rozwiązań

W zasadzie nowe typy z Java DateTime API i typy z SQL mogą być wygodnie mapowane. Aby reprezentować typ DATE w Javie, należy użyć klasy java.time.LocalDate z interfejsu API DateTime pakietu JDK 8.

Typ TIME z bazy danych może być reprezentowany przez dwa typy z Javy: java.time.LocalTime i java.time.OffsetTime . Nic skomplikowanego też.

Określony punkt w czasie, reprezentowany przez typ TIMESTAMP w bazie danych, może być reprezentowany w Javie przez 4 typy:

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

I wreszcie TIMESTAMP WITH TIME ZONE może być reprezentowany przez dwa typy:

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

Ponieważ znasz już API DateTime, zapamiętanie tej kwestii nie będzie dla Ciebie trudne :)

Napiszę to w formie tabeli, więc będzie łatwiej:

TYP SQL Typ Javy
DATA java.time.LocalDate
CZAS java.time.LocalTime
java.time.OffsetTime
ZNAK CZASU java.time.Instant
java.time.LocalDateTime
java.time.OffsetDateTime
java.time.ZonedDateTime
ZNACZNIK CZASU ZE STREFĄ CZASOWĄ java.time.OffsetDateTime
_

Uzyskiwanie daty

Mam dobre wiadomości dla Ciebie. Pierwszy od dłuższego czasu. Możemy obejść ograniczenia metody getDate() , która zwraca typ daty java.sql.

Rzecz w tym, że przedmiotzestaw wynikówjest jeszcze jedna interesująca metoda - getObject() . Ta metoda przyjmuje dwa parametry: kolumnę i typ oraz zwraca wartość kolumny przekonwertowanej na dany typ. Ogólna postać metody jest następująca:

ClassName Name = getObject(column, ClassName);

A jeśli chcesz przekonwertować typ DATE na typ java.time.LocalDate , musisz napisać coś takiego:

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

I ogólnie każdy TIMESTAMP można przekonwertować na kilka typów:

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);

Ważny! Ten kod nie zadziała, jeśli masz przestarzały sterownik MySQL JDBC . Zwróć uwagę na wersję „mysql-connector-java” zapisaną w pom.xml lub dodaną do Bibliotek w ustawieniach projektu.

Nawiasem mówiąc, w ten sam sposób można obejść brak możliwości przechowywania wartości null dla typów pierwotnych. Jeśli kolumna tabeli jest typu INT, istnieje kilka sposobów uzyskania z niej wartości null. Zobacz przykład poniżej:

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

Ustawienie strefy czasowej w MySQL

Wiele interesujących rzeczy wydarzyło się również z MySQL. Jak wiesz, podczas tworzenia połączenia MySQL możesz dodać do niego różne parametry :
mysql://localhost:3306/db_scheme?Name=meaning&Name=meaning

Tak więc dodano trzy parametry do pracy ze strefami czasowymi w MySQL. Parametry te można przekazać podczas nawiązywania połączenia z serwerem.

Poniżej podam tabelkę z nimi:

Parametr Wartości Domyślna wartość
połączenieStrefa Czasowa LOKALNE | SERWER | strefa użytkownika SERWER
forceConnectionTimeZoneToSession prawda | FAŁSZ PRAWDA
zachowajInstancje prawda | FAŁSZ FAŁSZ

Za pomocą parametru connectionTimeZone wybieramy strefę czasową (strefę czasową), w której będą wykonywane wszystkie żądania. Z punktu widzenia klienta serwer działa w określonej strefie czasowej.

Parametr forceConnectionTimeZoneToSession powoduje, że zmienna time_zone sesji jest ignorowana i zastępowana przez connectionTimeZone.

Na koniec parametr keepInstants steruje konwersją dokładnego czasu między strefą czasową maszyny JVM a strefą czasową połączenia.

Najczęstsze konfiguracje to:

  • connectionTimeZone=LOCAL & forceConnectionTimeZoneToSession=false - odpowiada staremu sterownikowi MySQL JDBC w wersji 5.1 z useLegacyDatetimeCode=true.

  • connectionTimeZone=LOCAL & forceConnectionTimeZoneToSession=true to nowy tryb, który zapewnia najbardziej naturalny sposób obsługi wartości daty i godziny.

  • connectionTimeZone=SERVER &serverInstants=true — Odpowiada staremu sterownikowi JDBC MySQL w wersji 5.1 z parametrem useLegacyDatetimeCode=false.

  • connectionTimeZone=user_defined & keepInstants=true — pomaga przezwyciężyć sytuację, w której strefa czasowa serwera nie może zostać rozpoznana przez łącznik, ponieważ jest ustawiona jako ogólny skrót, taki jak CET/CEST.

Tak, daty to ciekawy temat i jest z nimi wiele problemów. Jak to się mówi: to straszne, oczywiście, ale też nie jestem wkurzony! :)