java.util.Date vs java.sql.Date

501

java.util.Datevs java.sql.Date: wann und warum?

Flybywire
quelle

Antworten:

585

Herzlichen Glückwunsch, Sie haben mit JDBC meinen Lieblings-Pet-Peeve getroffen: Umgang mit Datumsklassen.

Grundsätzlich unterstützen Datenbanken normalerweise mindestens drei Arten von Datums- / Uhrzeitfeldern: Datum, Uhrzeit und Zeitstempel. Jeder von ihnen hat eine entsprechende Klasse in JDBC und jeder von ihnen wird erweitertjava.util.Date . Die schnelle Semantik jeder dieser drei ist die folgende:

  • java.sql.Dateentspricht SQL DATE, dh es werden Jahre, Monate und Tage gespeichert, während Stunde, Minute, Sekunde und Millisekunde ignoriert werden. Darüber hinaus sql.Dateist nicht an Zeitzonen gebunden.
  • java.sql.Timeentspricht SQL TIME und enthält, wie offensichtlich sein sollte, nur Informationen zu Stunden, Minuten, Sekunden und Millisekunden .
  • java.sql.Timestampentspricht SQL TIMESTAMP, dem genauen Datum der Nanosekunde ( beachten Sie, dass util.Datenur Millisekunden unterstützt werden! ) mit anpassbarer Genauigkeit.

Einer der häufigsten Fehler bei der Verwendung von JDBC-Treibern in Bezug auf diese drei Typen ist, dass die Typen falsch behandelt werden. Dies bedeutet, dass dies sql.Datezeitzonenspezifisch ist und sql.Timedas aktuelle Jahr, den aktuellen Monat und den aktuellen Tag usw. enthält.

Endlich: Welches verwenden?

Kommt wirklich auf den SQL-Typ des Feldes an. PreparedStatementhat Setter für alle drei Werte, #setDate()die für sql.Date, #setTime()für sql.Timeund #setTimestamp()für sql.Timestamp.

Beachten Sie, dass Sie bei Verwendung der meisten JDBC-Treibern ps.setObject(fieldIndex, utilDateObject);tatsächlich einen normalen util.DateWert geben können, der ihn gerne verschlingt, als wäre er vom richtigen Typ. Wenn Sie die Daten anschließend anfordern, werden Sie möglicherweise feststellen, dass Ihnen tatsächlich etwas fehlt.

Ich sage wirklich, dass keines der Daten überhaupt verwendet werden sollte.

Was ich damit sagen möchte , dass Sie die Millisekunden / Nanosekunden als einfache Longs speichern und sie in die von Ihnen verwendeten Objekte konvertieren ( obligatorischer Joda-Time-Plug ). Eine hackige Möglichkeit, die durchgeführt werden kann, besteht darin, die Datumskomponente als eine lange und eine Zeitkomponente als eine andere zu speichern. Diese lauten derzeit beispielsweise 20100221 und 154536123. Diese magischen Zahlen können in SQL-Abfragen verwendet werden und können von der Datenbank in eine andere und portiert werden Damit können Sie diesen Teil der JDBC / Java-Datums-API: s vollständig vermeiden.

Esko
quelle
17
Gute Antwort. Aber ist das Speichern von Daten für den DBA nicht etwas unfreundlich?
Cherouvim
26
Vielleicht tendieren DBAs jedoch im Allgemeinen dazu, das RDBMS ihrer Wahl zu wählen und alles, was nicht mit diesem RDBMS zu tun hat, direkt abzulehnen ( ich sehe Sie an, Oracle-Fans ), während von Java-Anwendungen erwartet wird, dass sie mit allen funktionieren. Persönlich mag ich es überhaupt nicht, meine Logik in die DB zu integrieren.
Esko
2
Meine MySQL-Spalte ist eine Datums- / Uhrzeitspalte, führt jedoch ps.setDate aus (new java.sql.Date (myObject.getCreatedDate (). GetTime ())); Ich verliere den Millisekunden-Teil. Wie kann ich das beheben?
Blankman
2
Um Ihre Millisekunden nicht zu verlieren: neuer java.sql.Timestamp (utilDate.getTime ())
Kieveli
3
Ich erwähnte, dass es ein häufiger Fehler ist, dass es TZ-spezifisch ist, während es nach Spezifikation nicht sein sollte.
Esko
60

LATE EDIT: Ab Java 8 sollten Sie weder verwenden java.util.Datenoch java.sql.Datewenn Sie es überhaupt vermeiden können, und stattdessen lieber das java.timePaket (basierend auf Joda) als irgendetwas anderes verwenden. Wenn Sie nicht mit Java 8 arbeiten, finden Sie hier die ursprüngliche Antwort:


java.sql.Date- wenn Sie Methoden / Konstruktoren von Bibliotheken aufrufen, die sie verwenden (wie JDBC). Nicht anders. Sie möchten keine Abhängigkeiten in die Datenbankbibliotheken für Anwendungen / Module einführen, die sich nicht explizit mit JDBC befassen.

java.util.Date- bei Verwendung von Bibliotheken, die es verwenden. Ansonsten so wenig wie möglich aus mehreren Gründen:

  • Es ist veränderlich, was bedeutet, dass Sie jedes Mal, wenn Sie es an eine Methode übergeben oder von einer Methode zurückgeben, eine defensive Kopie davon erstellen müssen.

  • Es geht nicht sehr gut mit Datumsangaben um, was Leute wie Ihre wirklich rückwärts denken, dass Datumsbehandlungsklassen dies tun sollten.

  • Nun, weil juD seine Arbeit nicht sehr gut macht, wurden die schrecklichen CalendarKlassen eingeführt. Sie sind auch veränderlich und schrecklich zu bearbeiten und sollten vermieden werden, wenn Sie keine Wahl haben.

  • Es gibt bessere Alternativen, wie die Joda Time API ( die es möglicherweise sogar in Java 7 schafft und zur neuen offiziellen API für die Datumsverarbeitung wird - eine schnelle Suche sagt, dass dies nicht der Fall ist).

Wenn Sie der Meinung sind, dass es übertrieben ist, eine neue Abhängigkeit wie Joda einzuführen, ist longes gar nicht so schlecht, sie für Zeitstempelfelder in Objekten zu verwenden, obwohl ich sie normalerweise selbst in juD einwickle, wenn ich sie weitergebe, zur Typensicherheit und als Dokumentation.

gustafc
quelle
5
Warum sollten wir java.time java.sql beim Speichern in der Datenbank vorziehen? Ich bin wirklich interessant in der Aussage, aber ich möchte verstehen, warum :)
Jean-François Savard
@ Jean-FrançoisSavard Ich hoffe, Sie haben eine Antwort auf Ihre Frage gefunden, seit Sie diesen Kommentar gepostet haben - aber hier ist eine Antwort, nur der Vollständigkeit halber: Es ist immer noch vollkommen in Ordnung java.sql.Date mit PreparedStatementetc! Wenn Sie es jedoch weitergeben, verwenden Sie ein, LocalDatedas Sie mit java.sql.Date.valueOfund java.sql.Date.valueOfbeim Festlegen konvertieren, und konvertieren Sie es so früh wie möglich mit java.sql.Date.toLocalDate - wieder, weil Sie java.sql so wenig wie möglich einbeziehen möchten und weil es veränderbar ist.
Gustafc
19

Die einzige Zeit zu verwenden java.sql.Dateist in a PreparedStatement.setDate. Andernfalls verwenden Sie java.util.Date. Es ist bezeichnend, dass ResultSet.getDatea zurückgegeben wird, java.sql.Dateaber es kann direkt a zugewiesen werden java.util.Date.

Paul Tomblin
quelle
3
Ehm, ResultSet # getDate () gibt sql.Date zurück (was util.Date erweitert).
Esko
3
@Esko - "Ehm", das habe ich behoben, bevor du es kommentiert (und herabgestimmt) hast.
Paul Tomblin
1
Es ist wichtig zu beachten, dass der Grund, warum ein java.sql.Date einem java.util.Date zugewiesen werden kann, darin besteht, dass das erste eine Unterklasse des zweiten ist.
DJ 18
2
Warum ist es "bezeichnend", dass a einem zugeordnet werden java.sql.Datekann, java.util.Datewenn das erstere das letztere erweitert? Was ist der Punkt, den Sie versuchen zu machen?
Marquis von Lorne
11

Ich hatte das gleiche Problem. Der einfachste Weg, das aktuelle Datum in eine vorbereitete Erklärung einzufügen, ist folgender:

preparedStatement.setDate(1, new java.sql.Date(new java.util.Date().getTime()));
Israelm
quelle
2
Ich kann damit keine Zeitzone bekommen.
Erhanasikoglu
4

tl; dr

Verwenden Sie keine.

Weder

java.util.Date vs java.sql.Date: wann und warum?

Beide Klassen sind schrecklich, fehlerhaft im Design und in der Implementierung. Vermeiden Sie wie das Pest- Coronavirus .

Verwenden Sie stattdessen java.time- Klassen, die in JSR 310 definiert sind. Diese Klassen sind ein branchenführendes Framework für die Arbeit mit der Datums- und Uhrzeitbehandlung. Dieser supplant völlig die blutigen schreckliches Erbe Klassen wie Date, Calendar, SimpleDateFormat, und so weiter .

java.util.Date

Der erste java.util.Datesoll einen Moment in UTC darstellen, dh einen Versatz von UTC von null Stunden-Minuten-Sekunden.

java.time.Instant

Jetzt ersetzt durch java.time.Instant.

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

java.time.OffsetDateTime

Instantist die grundlegende Bausteinklasse von java.time . Verwenden Sie für mehr Flexibilität OffsetDateTimeset to ZoneOffset.UTCfür denselben Zweck: einen Moment in UTC darstellen.

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

Sie können dieses Objekt mithilfe PreparedStatement::setObjectvon JDBC 4.2 oder höher an eine Datenbank senden .

myPreparedStatement.setObject(  , odt ) ;

Abrufen.

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

java.sql.Date

Die java.sql.DateKlasse ist auch schrecklich und veraltet.

Diese Klasse soll nur ein Datum ohne Tageszeit und ohne Zeitzone darstellen. Leider erbt diese Klasse in einem schrecklichen Hack eines Entwurfs, von java.util.Datedem ein Moment dargestellt wird (ein Datum mit Uhrzeit in UTC). Diese Klasse gibt also lediglich vor, nur ein Datum zu sein, während sie tatsächlich eine Tageszeit und einen impliziten Versatz von UTC enthält. Das verursacht so viel Verwirrung. Verwenden Sie niemals diese Klasse.

java.time.LocalDate

Verwenden Sie stattdessen, um java.time.LocalDatenur ein Datum (Jahr, Monat, Tag des Monats) ohne Tageszeit oder Zeitzone oder Versatz zu verfolgen.

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

An die Datenbank senden.

myPreparedStatement.setObject(  , ld ) ;

Abrufen.

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

Tabelle der Datums- und Uhrzeittypen in Java (sowohl Legacy als auch Modern) und in Standard-SQL


Über java.time

Das java.time- Framework ist in Java 8 und höher integriert. Diese Klassen verdrängen die lästigen alten Legacy - Datum-Zeit - Klassen wie java.util.Date, Calendar, & SimpleDateFormat.

Weitere Informationen finden Sie im Oracle-Lernprogramm . Suchen Sie im Stapelüberlauf nach vielen Beispielen und Erklärungen. Die Spezifikation ist JSR 310 .

Das Joda-Time- Projekt, das sich jetzt im Wartungsmodus befindet , empfiehlt die Migration zu den Klassen java.time .

Sie können java.time- Objekte direkt mit Ihrer Datenbank austauschen . Verwenden Sie einen JDBC-Treiber, der mit JDBC 4.2 oder höher kompatibel ist . Keine Notwendigkeit für Zeichenfolgen, keine Notwendigkeit für java.sql.*Klassen.

Woher bekomme ich die java.time-Klassen?

Tabelle, welche java.time-Bibliothek mit welcher Java- oder Android-Version verwendet werden soll

Basil Bourque
quelle
1
Upvote für das Ersetzen der Pestreferenz durch das Corona-Virus. Jetzt mehr zuordenbar.
ankuranurag2
2

Die Klasse java.util.Date in Java repräsentiert einen bestimmten Zeitpunkt (z. B. 2013, 25. November, 16:30:45 Uhr bis auf Millisekunden), aber der Datentyp DATE in der Datenbank repräsentiert nur ein Datum (z. 2013, 25. November). Um zu verhindern, dass Sie versehentlich ein java.util.Date-Objekt für die Datenbank bereitstellen, können Sie in Java keinen SQL-Parameter direkt auf java.util.Date setzen:

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

Sie können dies jedoch weiterhin mit Gewalt / Absicht tun (dann werden Stunden und Minuten vom DB-Treiber ignoriert). Dies geschieht mit der Klasse java.sql.Date:

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

Ein java.sql.Date-Objekt kann einen Moment in der Zeit speichern (so dass es einfach ist, aus einem java.util.Date zu konstruieren), löst jedoch eine Ausnahme aus, wenn Sie versuchen, es nach den Stunden zu fragen (um sein Konzept, ein zu sein, durchzusetzen nur Datum). Es wird erwartet, dass der DB-Treiber diese Klasse erkennt und nur 0 für die Stunden verwendet. Versuche dies:

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
quelle
0

java.util.Daterepräsentiert einen bestimmten Zeitpunkt mit Millisekundengenauigkeit. Es repräsentiert sowohl Datums- als auch Zeitinformationen ohne Zeitzone. Die Klasse java.util.Date implementiert die Schnittstelle Serializable, Cloneable und Comparable. Es wird vererbt java.sql.Date, java.sql.Timeund java.sql.TimestampSchnittstellen.

java.sql.Dateerweitert die Klasse java.util.Date, die ein Datum ohne Zeitangabe darstellt und nur beim Umgang mit Datenbanken verwendet werden sollte. Um der Definition von SQL DATE zu entsprechen, müssen die von einer java.sql.DateInstanz umschlossenen Millisekundenwerte "normalisiert" werden, indem die Stunden, Minuten, Sekunden und Millisekunden in der bestimmten Zeitzone, der die Instanz zugeordnet ist, auf Null gesetzt werden.

Es erbt alle öffentlichen Methoden java.util.Datewie getHours(), getMinutes(), getSeconds(), setHours(), setMinutes(), setSeconds(). Da java.sql.Datedie Zeitinformationen nicht gespeichert werden, werden alle Zeitoperationen von überschrieben java.util.Dateund alle diese Methoden werden ausgelöst, java.lang.IllegalArgumentExceptionwenn sie aufgerufen werden, wie aus den Implementierungsdetails hervorgeht.

Abdul Alim Shakir
quelle