java.util.Date
vs java.sql.Date
: kiedy i z którego korzystać?
java.util.Date
vs java.sql.Date
: kiedy i z którego korzystać?
Odpowiedzi:
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.Date
odpowiada SQL DATE, co oznacza, że przechowuje lata, miesiące i dni, a godzina, minuta, sekunda i milisekunda są ignorowane. Dodatkowo sql.Date
nie jest związany ze strefami czasowymi.java.sql.Time
odpowiada CZASOWI SQL i jak powinno być oczywiste, zawiera tylko informacje o godzinie, minutach, sekundach i milisekundach .java.sql.Timestamp
odpowiada SQL TIMESTAMP, który jest dokładną datą w nanosekundach ( pamiętaj, że util.Date
obsł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.Date
jest specyficzna dla strefy czasowej,sql.Time
zawiera bieżący rok, miesiąc i dzień i tak dalej.
Zależy to od typu SQL pola. PreparedStatement
ma elementy ustawiające dla wszystkich trzech wartości, #setDate()
będące wartościami dla sql.Date
, #setTime()
dla sql.Time
i #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.
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.
PÓŹNA EDYCJA: Począwszy od Java 8 nie powinieneś używać ani, java.util.Date
ani w java.sql.Date
ogóle możesz tego uniknąć, a zamiast tego wolisz używać java.time
pakietu (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 Calendar
klasy. 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, long
jest 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.
java.sql.Date
z PreparedStatement
etc! Ale kiedy go przekazujesz, użyj narzędzia, LocalDate
które konwertujesz za pomocą java.sql.Date.valueOf
i java.sql.Date.valueOf
podczas 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.
Jedyny czas na użycie java.sql.Date
to PreparedStatement.setDate
. W przeciwnym razie użyj java.util.Date
. Mówi, że ResultSet.getDate
zwraca a, java.sql.Date
ale można ją przypisać bezpośrednio do java.util.Date
.
java.sql.Date
można przypisać a, java.util.Date
gdy pierwszy rozszerza drugi? Jaki sens starasz się osiągnąć?
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()));
Nie używaj żadnego.
java.time.Instant
zastępuje java.util.Date
java.time.LocalDate
zastępuje java.sql.Date
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.Date
ma 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
Instant
to podstawowa klasa elementów java.time . Aby uzyskać większą elastyczność, użyj OffsetDateTime
zestawu do ZoneOffset.UTC
tego 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::setObject
z JDBC 4.2 lub nowszej.
myPreparedStatement.setObject( … , odt ) ;
Odzyskać.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
java.sql.Date
java.sql.Date
Klasa 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.Date
po 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.LocalDate
do ś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 ) ;
Ś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?
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());
}
java.util.Date
reprezentuje 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.Time
i java.sql.Timestamp
interfejsów.
java.sql.Date
rozszerza 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.Date
instancję 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.Date
nie przechowuje informacji o czasie, zastępuje wszystkie operacje czasowe java.util.Date
i wszystkie te metody wyrzucają, java.lang.IllegalArgumentException
jeśli są wywoływane, jak wynika to ze szczegółów ich implementacji.