java.util.Date
vs java.sql.Date
: quando usare quale e perché?
java.util.Date
vs java.sql.Date
: quando usare quale e perché?
Risposte:
Congratulazioni, hai colpito la mia pipì preferita con JDBC: gestione della classe di date.
Fondamentalmente i database di solito supportano almeno tre forme di campi datetime che sono data, ora e data e ora. Ognuno di questi ha una classe corrispondente in JDBC e ognuno di essi si estendejava.util.Date
. La semantica rapida di ciascuno di questi tre è il seguente:
java.sql.Date
corrisponde a SQL DATE, il che significa che memorizza anni, mesi e giorni, mentre ora, minuto, secondo e millisecondo vengono ignorati. Inoltre, sql.Date
non è legato ai fusi orari.java.sql.Time
corrisponde a SQL TIME e, come dovrebbe essere ovvio, contiene solo informazioni su ora, minuti, secondi e millisecondi .java.sql.Timestamp
corrisponde a SQL TIMESTAMP che è la data esatta del nanosecondo ( nota che util.Date
supporta solo millisecondi! ) con precisione personalizzabile.Uno dei bug più comuni quando si utilizzano i driver JDBC in relazione a questi tre tipi è che i tipi vengono gestiti in modo errato. Ciò significa che sql.Date
è specifico del fuso orario, sql.Time
contiene l'anno, il mese e il giorno attuali eccetera.
Dipende dal tipo SQL del campo, davvero. PreparedStatement
ha setter per tutti e tre i valori, #setDate()
essendo quello per sql.Date
, #setTime()
per sql.Time
e #setTimestamp()
per sql.Timestamp
.
Si noti che se si utilizza ps.setObject(fieldIndex, utilDateObject);
si può effettivamente dare un normale util.Date
alla maggior parte dei driver JDBC che li divorerà felicemente come se fosse del tipo corretto ma quando si richiedono i dati in seguito, è possibile notare che in realtà mancano cose.
Quello che sto dicendo che salva i millisecondi / nanosecondi come semplici long e li converte in qualsiasi oggetto tu stia usando ( plug joda-time obbligatorio ). Un modo bizzarro che può essere fatto è quello di memorizzare il componente data come componente long e time come un altro, ad esempio in questo momento sarebbe 20100221 e 154536123. Questi numeri magici possono essere utilizzati nelle query SQL e saranno trasferibili dal database all'altro e ti permetterà di evitare completamente questa parte dell'API Date JDBC / Java: s.
LATE EDIT: a partire da Java 8 non dovresti usare né java.util.Date
né java.sql.Date
se non puoi assolutamente evitarlo, e preferisci invece usare il java.time
pacchetto (basato su Joda) piuttosto che qualsiasi altra cosa. Se non sei su Java 8, ecco la risposta originale:
java.sql.Date
- quando chiami metodi / costruttori di librerie che lo usano (come JDBC). Non altrimenti. Non si desidera introdurre dipendenze nelle librerie del database per applicazioni / moduli che non gestiscono esplicitamente JDBC.
java.util.Date
- quando si usano librerie che lo usano. Altrimenti, il meno possibile, per diversi motivi:
È mutabile, il che significa che devi crearne una copia difensiva ogni volta che lo passi o lo restituisci da un metodo.
Non gestisce le date molto bene, cosa che le persone arretrate come la tua credono davvero che le lezioni di gestione delle date dovrebbero.
Ora, poiché juD non fa molto bene il suo lavoro, Calendar
sono state introdotte le classi orribili . Sono anche mutabili e terribili con cui lavorare e dovrebbero essere evitati se non si ha altra scelta.
Esistono alternative migliori, come l' API Joda Time ( che potrebbe persino trasformarsi in Java 7 e diventare la nuova API ufficiale di gestione delle date - una ricerca veloce dice che non lo farà).
Se ritieni che sia eccessivo introdurre una nuova dipendenza come Joda, long
non sono poi così male da usare per i campi timestamp negli oggetti, anche se io stesso li avvolgo di solito quando li passiamo in giro, per sicurezza del tipo e come documentazione.
java.sql.Date
con PreparedStatement
ecc! Ma quando lo passi in giro, usa un LocalDate
oggetto che converti usando java.sql.Date.valueOf
e java.sql.Date.valueOf
quando lo imposti e riconvertilo il prima possibile usando java.sql.Date.toLocalDate
- di nuovo, perché vuoi coinvolgere java.sql il meno possibile e perché è mutabile.
L'unica volta da usare java.sql.Date
è in a PreparedStatement.setDate
. Altrimenti, usa java.util.Date
. Sta dicendo che ResultSet.getDate
restituisce a java.sql.Date
ma può essere assegnato direttamente a java.util.Date
.
java.sql.Date
può essere assegnato a a java.util.Date
quando il primo estende il secondo? Qual è il punto che stai cercando di chiarire?
Ho avuto lo stesso problema, il modo più semplice che ho trovato per inserire la data corrente in una dichiarazione preparata è questo:
preparedStatement.setDate(1, new java.sql.Date(new java.util.Date().getTime()));
Non usare nessuno dei due.
java.time.Instant
sostituisce java.util.Date
java.time.LocalDate
sostituisce java.sql.Date
java.util.Date vs java.sql.Date: quando usare quale e perché?
Entrambe queste classi sono terribili, imperfette nel design e nella realizzazione. Evita come il Coronavirus della peste .
Utilizzare invece le classi java.time , definite in JSR 310. Queste classi sono un framework leader del settore per la gestione della data e dell'ora. Questi soppiantare completamente le sanguinose classi legacy terribile come Date
, Calendar
, SimpleDateFormat
, e così via.
java.util.Date
Il primo, java.util.Date
è pensato per rappresentare un momento in UTC, che significa un offset rispetto a UTC di zero ore-minuti-secondi.
java.time.Instant
Ora sostituito da java.time.Instant
.
Instant instant = Instant.now() ; // Capture the current moment as seen in UTC.
java.time.OffsetDateTime
Instant
è la classe base di java.time . Per una maggiore flessibilità, utilizzare OffsetDateTime
set to ZoneOffset.UTC
per lo stesso scopo: rappresentare un momento in UTC.
OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC ) ;
È possibile inviare questo oggetto a un database utilizzando PreparedStatement::setObject
con JDBC 4.2 o successivo.
myPreparedStatement.setObject( … , odt ) ;
Recuperare.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
java.sql.Date
La java.sql.Date
classe è anche terribile e obsoleta.
Questa classe intende rappresentare solo una data, senza ora del giorno e senza fuso orario. Sfortunatamente, in un terribile hack di un design, questa classe eredita da java.util.Date
cui rappresenta un momento (una data con l'ora del giorno in UTC). Quindi questa classe sta semplicemente fingendo di essere solo data, mentre in realtà trasporta un fuso orario e un offset implicito di UTC. Questo provoca molta confusione. Non usare mai questa classe.
java.time.LocalDate
Invece, usa java.time.LocalDate
per tracciare solo una data (anno, mese, giorno del mese) senza alcun orario del giorno né alcun fuso orario o offset.
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).
Invia al database.
myPreparedStatement.setObject( … , ld ) ;
Recuperare.
LocalDate ld = myResultSet.getObject( … , LocalDate.class ) ;
Il framework java.time è integrato in Java 8 e versioni successive. Queste classi soppiantare la vecchia fastidiosi legacy classi data-time come java.util.Date
, Calendar
, e SimpleDateFormat
.
Per saperne di più, consulta il tutorial Oracle . E cerca Stack Overflow per molti esempi e spiegazioni. La specifica è JSR 310 .
Il progetto Joda-Time , ora in modalità manutenzione , consiglia la migrazione alle classi java.time .
Puoi scambiare oggetti java.time direttamente con il tuo database. Utilizzare un driver JDBC conforme a JDBC 4.2 o successivo. Non c'è bisogno di stringhe, non c'è bisogno di java.sql.*
classi.
Dove ottenere le classi java.time?
La classe java.util.Date in Java rappresenta un momento particolare nel tempo (ad esempio, 25 nov 2013 16:30:45 fino a millisecondi), ma il tipo di dati DATE nel DB rappresenta solo una data (ad es. 25 novembre 2013). Per evitare di fornire per errore un oggetto java.util.Date al DB, Java non consente di impostare direttamente un parametro SQL su java.util.Date:
PreparedStatement st = ...
java.util.Date d = ...
st.setDate(1, d); //will not work
Ma ti consente ancora di farlo con la forza / intenzione (quindi ore e minuti saranno ignorati dal driver DB). Questo viene fatto con la classe java.sql.Date:
PreparedStatement st = ...
java.util.Date d = ...
st.setDate(1, new java.sql.Date(d.getTime())); //will work
Un oggetto java.sql.Date può memorizzare un momento nel tempo (in modo che sia facile da costruire da un java.util.Date) ma genererà un'eccezione se provi a chiederlo per le ore (per imporre il suo concetto di essere un solo data). Il driver DB dovrebbe riconoscere questa classe e usare solo 0 per le ore. Prova questo:
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
rappresenta un istante specifico nel tempo con precisione in millisecondi. Rappresenta sia la data sia l'ora senza fuso orario. La classe java.util.Date implementa l'interfaccia Serializable, Cloneable e Comparable. Esso viene ereditata da java.sql.Date
, java.sql.Time
e java.sql.Timestamp
le interfacce.
java.sql.Date
estende la classe java.util.Date che rappresenta la data senza informazioni sull'ora e dovrebbe essere utilizzata solo quando si tratta di database. Per conformarsi alla definizione di SQL DATE, i valori in millisecondi racchiusi in java.sql.Date
un'istanza devono essere "normalizzati" impostando le ore, i minuti, i secondi e i millisecondi su zero nel particolare fuso orario a cui è associata l'istanza.
Si eredita tutti i metodi pubblici di java.util.Date
come getHours()
, getMinutes()
, getSeconds()
, setHours()
, setMinutes()
, setSeconds()
. Poiché java.sql.Date
non memorizza le informazioni temporali, ha la priorità su tutte le operazioni temporali java.util.Date
e tutti questi metodi vengono generati java.lang.IllegalArgumentException
se richiamati come evidente dai dettagli di implementazione.