java.util.Date vs java.sql.Date

501

java.util.Datevs java.sql.Date: kiedy i z którego korzystać?

Lecieć jak po sznurku
źródło

Odpowiedzi:

585

Gratulacje, trafiłeś w moją ulubioną psotkę z JDBC: obsługa klasy randkowej.

Zasadniczo bazy danych zwykle obsługują co najmniej trzy formy pól datetime, którymi są data, godzina i znacznik czasu. Każda z nich ma odpowiednią klasę w JDBC i każda z nich ma rozszerzeniejava.util.Date . Szybka semantyka każdego z tych trzech jest następująca:

  • java.sql.Dateodpowiada SQL DATE, co oznacza, że ​​przechowuje lata, miesiące i dni, a godzina, minuta, sekunda i milisekunda są ignorowane. Dodatkowo sql.Datenie jest związany ze strefami czasowymi.
  • java.sql.Timeodpowiada CZASOWI SQL i jak powinno być oczywiste, zawiera tylko informacje o godzinie, minutach, sekundach i milisekundach .
  • java.sql.Timestampodpowiada SQL TIMESTAMP, który jest dokładną datą w nanosekundach ( pamiętaj, że util.Dateobsługuje tylko milisekundy! ) z dostosowywaną precyzją.

Jednym z najczęstszych błędów podczas używania sterowników JDBC w odniesieniu do tych trzech typów jest nieprawidłowe obsługiwanie typów. Oznacza to, że sql.Datejest specyficzna dla strefy czasowej,sql.Time zawiera bieżący rok, miesiąc i dzień i tak dalej.

Wreszcie: z którego wybrać?

Zależy to od typu SQL pola. PreparedStatementma elementy ustawiające dla wszystkich trzech wartości, #setDate()będące wartościami dla sql.Date, #setTime()dla sql.Timei #setTimestamp()dla sql.Timestamp.

Pamiętaj, że jeśli używasz ps.setObject(fieldIndex, utilDateObject);, możesz dać normalneutil.Date większości sterowników JDBC, które chętnie pożrą je tak, jakby były poprawnego typu, ale kiedy poprosisz o dane później, możesz zauważyć, że w rzeczywistości brakuje ci rzeczy.

Naprawdę mówię, że żadna z dat nie powinna być w ogóle używana.

To, co mówię, zapisuje milisekundy / nanosekundy jako zwykłe długości i konwertuje je na dowolne obiekty, których używasz ( obowiązkowa wtyczka czasu Joda ). Jednym z hackerskich sposobów, które można zrobić, jest przechowywanie komponentu daty jako jednego komponentu długiego i czasowego jako innego, na przykład teraz będzie to 20100221 i 154536123. Te magiczne liczby mogą być używane w zapytaniach SQL i będą przenośne z bazy danych na inną i pozwoli ci całkowicie ominąć tę część JDBC / Java Date API: s.

Esko
źródło
17
Niezła odpowiedź. Ale czy przechowywanie dat tak długo nie jest trochę nieprzyjazne dla DBA?
cherouvim
26
Być może jednak DBA: zazwyczaj wybierają RDBMS i odrzucają wszystko, co nie dotyczy bezpośrednio RDBMS ( patrzę na ciebie, fani Oracle ), podczas gdy aplikacje Java powinny działać z nimi wszystkimi. Osobiście nie lubię w ogóle logować się w DB.
Esko
2
Moja kolumna mysql jest datetime, ale robi ps.setDate (nowa java.sql.Date (myObject.getCreatedDate (). GetTime ())); Tracę część milisekund, jak to naprawić?
Blankman
2
Aby nie stracić milisekund: nowy plik java.sql.Timestamp (utilDate.getTime ())
Kieveli
3
Wspomniałem, że jest to powszechny błąd, który jest specyficzny dla TZ, podczas gdy według specyfikacji nie powinien być.
Esko
60

PÓŹNA EDYCJA: Począwszy od Java 8 nie powinieneś używać ani, java.util.Dateani w java.sql.Dateogóle możesz tego uniknąć, a zamiast tego wolisz używać java.timepakietu (opartego na Jodzie) niż czegokolwiek innego. Jeśli nie korzystasz z języka Java 8, oto oryginalna odpowiedź:


java.sql.Date- gdy wywołujesz metody / konstruktory bibliotek, które go używają (jak JDBC). Nie inaczej Nie chcesz wprowadzać zależności do bibliotek baz danych dla aplikacji / modułów, które nie dotyczą jawnie JDBC.

java.util.Date- podczas korzystania z bibliotek, które go używają. W przeciwnym razie, jak najmniej, z kilku powodów:

  • Można go modyfikować, co oznacza, że ​​musisz wykonać jego obronną kopię za każdym razem, gdy go przekażesz lub zwrócisz z metody.

  • Nie radzi sobie zbyt dobrze z datami, co zdaniem zacofanych ludzi, takich jak twoja, powinny być obsługiwane przez klasy zajmujące się datowaniem.

  • Ponieważ juD nie radzi sobie zbyt dobrze, wprowadzono upiorne Calendarklasy. Są również zmienne i okropne w pracy i należy ich unikać, jeśli nie masz wyboru.

  • Istnieją lepsze alternatywy, takie jak Joda Time API ( który może nawet przekształcić się w Javę 7 i stać się nowym oficjalnym API do obsługi dat - szybkie wyszukiwanie mówi, że nie będzie).

Jeśli uważasz, że wprowadzanie nowej zależności, takiej jak Joda, longjest przesadą, nie jest wcale tak źle używać pól znaczników czasu w obiektach, chociaż ja zwykle owijam je w JuD podczas ich przekazywania, dla bezpieczeństwa typu i dokumentacji.

gustafc
źródło
5
Dlaczego powinniśmy preferować java.time niż java.sql podczas przechowywania w bazie danych? Jestem naprawdę ciekawa w tym oświadczeniu, ale chcę zrozumieć, dlaczego :)
Jean-François Savard
@ Jean-FrançoisSavard Mam nadzieję, że znalazłeś odpowiedź na swoje pytanie, odkąd opublikowałeś ten komentarz - ale oto odpowiedź, dla samej kompletności: Nadal jest w porządku java.sql.Date z PreparedStatementetc! Ale kiedy go przekazujesz, użyj narzędzia, LocalDatektóre konwertujesz za pomocą java.sql.Date.valueOfi java.sql.Date.valueOfpodczas jego ustawiania, i przekonwertuj go z powrotem tak wcześnie, jak to możliwe za pomocą java.sql.Date.toLocalDate - ponownie, ponieważ chcesz zaangażować java.sql w jak najmniejszym stopniu, a ponieważ jest on zmienny.
gustafc
19

Jedyny czas na użycie java.sql.Dateto PreparedStatement.setDate. W przeciwnym razie użyj java.util.Date. Mówi, że ResultSet.getDatezwraca a, java.sql.Dateale można ją przypisać bezpośrednio do java.util.Date.

Paul Tomblin
źródło
3
Ehm, ResultSet # getDate () zwraca sql.Date (która rozszerza util.Date).
Esko
3
@Esko - „Ehm”, naprawiłem to, zanim skomentowałeś (i przegłosowałem).
Paul Tomblin
1
Ważne jest, aby pamiętać, że powodem java.sql.Date można przypisać do java.util.Date dlatego, że pierwsza jest podklasą drugiej.
dj18
2
Dlaczego „mówi”, że java.sql.Datemożna przypisać a, java.util.Dategdy pierwszy rozszerza drugi? Jaki sens starasz się osiągnąć?
Markiz Lorne
11

Miałem ten sam problem, najłatwiejszy sposób na wstawienie bieżącej daty w przygotowanym wyciągu to:

preparedStatement.setDate(1, new java.sql.Date(new java.util.Date().getTime()));
Izrael
źródło
2
nie mogę uzyskać strefy czasowej z tym.
erhanasikoglu
4

tl; dr

Nie używaj żadnego.

Ani

java.util.Date vs java.sql.Date: kiedy używać której i dlaczego?

Obie te klasy są okropne, mają wadliwy projekt i wdrożenie. Unikaj jak koronawirus zarazy .

Zamiast tego należy użyć klas java.time zdefiniowanych w JSR 310. Te klasy są wiodącym w branży środowiskiem do pracy z obsługą daty i czasu. Są one w całości zastąpić krwawe straszne klas starszych, takich jak Date, Calendar, SimpleDateFormat, i takie.

java.util.Date

Pierwszy java.util.Datema reprezentować moment w UTC, co oznacza przesunięcie w stosunku do UTC wynoszące zero godzin-minut-sekund.

java.time.Instant

Teraz zastąpiony przez java.time.Instant.

Instant instant = Instant.now() ;  // Capture the current moment as seen in UTC.

java.time.OffsetDateTime

Instantto podstawowa klasa elementów java.time . Aby uzyskać większą elastyczność, użyj OffsetDateTimezestawu do ZoneOffset.UTCtego samego celu: reprezentowania chwili w UTC.

OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC ) ;

Możesz wysłać ten obiekt do bazy danych przy użyciu PreparedStatement::setObjectz JDBC 4.2 lub nowszej.

myPreparedStatement.setObject(  , odt ) ;

Odzyskać.

OffsetDateTime odt = myResultSet.getObject(  , OffsetDateTime.class ) ;

java.sql.Date

java.sql.DateKlasa jest straszny i przestarzałe.

Ta klasa ma reprezentować tylko datę, bez pory dnia i bez strefy czasowej. Niestety, w wyniku strasznego włamania do projektu klasa ta dziedziczy java.util.Datepo chwili (data z porą dnia w UTC). Tak więc ta klasa udaje, że jest tylko datą, a jednocześnie ma ukrytą porę dnia i ukryte przesunięcie UTC. To powoduje tyle zamieszania. Nigdy nie używaj tej klasy.

java.time.LocalDate

Zamiast tego użyj java.time.LocalDatedo śledzenia tylko daty (roku, miesiąca, dnia miesiąca) bez żadnej pory dnia, żadnej strefy czasowej ani przesunięcia.

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
LocalDate ld = LocalDate.now( z ) ;    // Capture the current date as seen in the wall-clock time used by the people of a particular region (a time zone).

Wyślij do bazy danych.

myPreparedStatement.setObject(  , ld ) ;

Odzyskać.

LocalDate ld = myResultSet.getObject(  , LocalDate.class ) ;

Tabela typów daty i godziny w Javie (zarówno starszej, jak i nowoczesnej) i standardowym SQL


O java.time

Środowisko java.time jest wbudowane w Javę 8 i nowsze wersje . Klasy te kłopotliwe zastąpić stary starszych klas Date-Time, takich jak java.util.Date, Calendar, i SimpleDateFormat.

Aby dowiedzieć się więcej, zobacz samouczek Oracle . I przeszukaj stos przepełnienia dla wielu przykładów i wyjaśnień. Specyfikacja to JSR 310 .

Projekt Joda-Time , teraz w trybie konserwacji , zaleca migrację do klas java.time .

Możesz wymieniać obiekty java.time bezpośrednio ze swoją bazą danych. Użyj sterownika JDBC zgodnego z JDBC 4.2 lub nowszym. Nie potrzeba ciągów, nie ma potrzebyjava.sql.* klas.

Gdzie można uzyskać klasy java.time?

Tabela, z której biblioteki java.time należy korzystać, w której wersji Java lub Android

Basil Bourque
źródło
1
Głosowanie za zastąpieniem odniesienia zarazy wirusem Corona. Bardziej powiązany.
ankuranurag2
2

Klasa java.util.Date w Javie reprezentuje konkretny moment w czasie (np. 25 listopada 2013 16:30:45 do milisekund), ale typ danych DATE w DB reprezentuje tylko datę (np. 25 listopada 2013). Aby zapobiec przypadkowemu dostarczeniu obiektu java.util.Date do bazy danych przez pomyłkę, Java nie pozwala na bezpośrednie ustawienie parametru SQL na java.util.Date:

PreparedStatement st = ...
java.util.Date d = ...
st.setDate(1, d); //will not work

Ale nadal pozwala ci to robić siłą / intencją (wtedy godziny i minuty będą ignorowane przez sterownik DB). Odbywa się to za pomocą klasy java.sql.Date:

PreparedStatement st = ...
java.util.Date d = ...
st.setDate(1, new java.sql.Date(d.getTime())); //will work

Obiekt java.sql.Date może przechowywać moment w czasie (dzięki czemu można go łatwo zbudować z java.util.Date), ale zgłasza wyjątek, jeśli spróbujesz zapytać go o godziny (w celu wymuszenia jego koncepcji bycia tylko data). Oczekuje się, że sterownik DB rozpozna tę klasę i użyje 0 dla godzin. Spróbuj tego:

public static void main(String[] args) {
  java.util.Date d1 = new java.util.Date(12345);//ms since 1970 Jan 1 midnight
  java.sql.Date d2 = new java.sql.Date(12345);
  System.out.println(d1.getHours());
  System.out.println(d2.getHours());
}
Kent Tong
źródło
0

java.util.Datereprezentuje określony moment w czasie z precyzją milisekundową. Reprezentuje zarówno datę, jak i godzinę bez strefy czasowej. Klasa java.util.Date implementuje interfejs Serializable, Cloneable i Comparable. Jest dziedziczona przez java.sql.Date, java.sql.Timei java.sql.Timestampinterfejsów.

java.sql.Daterozszerza klasę java.util.Date, która reprezentuje datę bez informacji o czasie i powinna być używana tylko w przypadku baz danych. Aby zachować zgodność z definicją SQL DATE, wartości milisekund owinięte przez java.sql.Dateinstancję należy „znormalizować”, ustawiając godziny, minuty, sekundy i milisekundy na zero w konkretnej strefie czasowej, z którą instancja jest powiązana.

Dziedziczy wszystkie metody publiczne java.util.Date, takie jak getHours(), getMinutes(), getSeconds(), setHours(), setMinutes(), setSeconds(). Ponieważ java.sql.Datenie przechowuje informacji o czasie, zastępuje wszystkie operacje czasowe java.util.Datei wszystkie te metody wyrzucają, java.lang.IllegalArgumentExceptionjeśli są wywoływane, jak wynika to ze szczegółów ich implementacji.

Abdul Alim Shakir
źródło