java.util.Date vs java.sql.Date

501

java.util.Datevs java.sql.Date: kapan harus menggunakan yang mana dan mengapa?

flybywire
sumber

Jawaban:

585

Selamat, Anda telah memukul hewan peliharaan kesayangan saya dengan JDBC: Penanganan kelas tanggal.

Pada dasarnya database biasanya mendukung setidaknya tiga bentuk bidang datetime yaitu tanggal, waktu dan cap waktu. Masing-masing memiliki kelas yang sesuai di JDBC dan masing-masing dari mereka memperpanjangjava.util.Date . Semantik cepat dari masing-masing dari ketiganya adalah sebagai berikut:

  • java.sql.Dateberkorespondensi dengan SQL DATE yang artinya menyimpan bertahun-tahun, bulan dan hari sementara jam, menit, detik dan milidetik diabaikan. Selain sql.Dateitu tidak terikat dengan zona waktu.
  • java.sql.Timesesuai dengan SQL TIME dan sebagaimana seharusnya jelas, hanya berisi informasi tentang jam, menit, detik, dan milidetik .
  • java.sql.Timestampsesuai dengan SQL TIMESTAMP yang merupakan tanggal yang tepat untuk nanodetik ( perhatikan bahwa util.Datehanya mendukung milidetik! ) dengan presisi yang dapat disesuaikan.

Salah satu bug yang paling umum ketika menggunakan driver JDBC dalam kaitannya dengan ketiga jenis ini adalah bahwa jenis-jenisnya ditangani secara tidak benar. Ini berarti sql.Datespesifik zona waktu, sql.Timeberisi tahun saat ini, bulan dan hari dan lain-lain.

Akhirnya: Yang mana yang digunakan?

Tergantung pada tipe SQL dari lapangan, sungguh. PreparedStatementmemiliki setter untuk ketiga nilai, #setDate()menjadi satu untuk sql.Date, #setTime()untuk sql.Timedan #setTimestamp()untuk sql.Timestamp.

Perhatikan bahwa jika Anda menggunakan, ps.setObject(fieldIndex, utilDateObject);Anda benar-benar dapat memberikan yang normal util.Dateuntuk sebagian besar driver JDBC yang dengan senang hati akan melahapnya seolah-olah itu adalah tipe yang benar tetapi ketika Anda meminta data setelah itu, Anda mungkin melihat bahwa Anda benar-benar kehilangan barang.

Saya benar-benar mengatakan bahwa tidak ada Tanggal yang boleh digunakan sama sekali.

Apa yang saya katakan bahwa menyimpan milidetik / nanodetik sebagai panjang biasa dan mengubahnya menjadi benda apa pun yang Anda gunakan ( plug joda-waktu wajib ). Salah satu cara hacky yang dapat dilakukan adalah dengan menyimpan komponen tanggal sebagai satu komponen panjang dan waktu sebagai yang lain, misalnya saat ini adalah 20100221 dan 154536123. Angka ajaib ini dapat digunakan dalam query SQL dan akan portabel dari database ke yang lain dan akan membiarkan Anda menghindari bagian dari JDBC / Java Date API ini: sepenuhnya.

Esko
sumber
17
Jawaban bagus. Tapi bukankah menyimpan tanggal selama sedikit tidak ramah untuk DBA?
cherouvim
26
Mungkin, bagaimanapun DBA: umumnya cenderung RDBMS pilihan mereka dan menolak segala sesuatu yang bukan tentang RDBMS itu secara langsung ( saya melihat Anda, penggemar Oracle ) sementara aplikasi Java diharapkan bekerja dengan mereka semua. Secara pribadi saya tidak suka memasukkan logika saya ke DB sama sekali.
Esko
2
Kolom mysql saya adalah datetime, tetapi melakukan ps.setDate (java.sql.Date baru (myObject.getCreatedDate (). GetTime ())); Saya kehilangan bagian milidetik, bagaimana cara memperbaikinya?
Blankman
2
Untuk tidak kehilangan milidetik Anda: java.sql.Timestamp baru (utilDate.getTime ())
Kieveli
3
Saya menyebutkan bahwa itu adalah bug umum yang merupakan TZ spesifik sedangkan menurut spesifikasi tidak boleh.
Esko
60

EDIT TERAKHIR: Dimulai dengan Java 8 Anda sebaiknya tidak menggunakan java.util.Dateatau java.sql.Datejika Anda bisa menghindarinya, dan sebaliknya lebih memilih menggunakan java.timepaket (berdasarkan Joda) daripada yang lainnya. Jika Anda tidak menggunakan Java 8, inilah respons aslinya:


java.sql.Date- ketika Anda memanggil metode / konstruktor perpustakaan yang menggunakannya (seperti JDBC). Tidak sebaliknya. Anda tidak ingin memperkenalkan dependensi ke pustaka database untuk aplikasi / modul yang tidak secara eksplisit berurusan dengan JDBC.

java.util.Date- saat menggunakan perpustakaan yang menggunakannya. Kalau tidak, sesedikit mungkin, karena beberapa alasan:

  • Ini bisa berubah, yang berarti Anda harus membuat salinan defensif setiap kali Anda meneruskannya atau mengembalikannya dari suatu metode.

  • Ini tidak menangani kencan dengan sangat baik, yang membuat orang-orang seperti Anda mundur benar-benar berpikir kelas penanganan kencan seharusnya.

  • Sekarang, karena JuD tidak melakukan pekerjaannya dengan baik, Calendarkelas-kelas yang mengerikan diperkenalkan. Mereka juga bisa berubah, dan mengerikan untuk bekerja dengan, dan harus dihindari jika Anda tidak punya pilihan.

  • Ada alternatif yang lebih baik, seperti Joda Time API ( yang bahkan mungkin membuatnya menjadi Java 7 dan menjadi tanggal resmi penanganan API baru - pencarian cepat mengatakan itu tidak akan).

Jika Anda merasa sulit untuk memperkenalkan ketergantungan baru seperti Joda, longs tidak terlalu buruk untuk digunakan untuk bidang timestamp pada objek, meskipun saya sendiri biasanya membungkusnya dalam juD ketika melewati mereka di sekitar, untuk keamanan jenis dan sebagai dokumentasi.

Gustaf
sumber
5
Mengapa kita lebih suka java.time daripada java.sql saat menyimpan dalam database? Saya benar-benar menarik dalam pernyataan itu, tapi saya ingin mengerti mengapa :)
Jean-François Savard
@ Jean-FrançoisSavard Saya harap Anda telah menemukan jawaban untuk pertanyaan Anda sejak Anda memposting komentar itu - tetapi inilah jawabannya, hanya demi kelengkapan: Masih baik-baik saja java.sql.Date dengan PreparedStatementdll! Tetapi ketika Anda menyebarkannya, gunakan LocalDateyang Anda konversi menggunakan java.sql.Date.valueOfdan java.sql.Date.valueOfketika mengaturnya, dan mengubahnya kembali sedini mungkin menggunakan java.sql.Date.toLocalDate - lagi, karena Anda ingin melibatkan java.sql sesedikit mungkin, dan karena itu bisa berubah.
gustafc
19

Satu-satunya waktu untuk menggunakan java.sql.Dateadalah a PreparedStatement.setDate. Kalau tidak, gunakan java.util.Date. Ia memberi tahu bahwa ResultSet.getDatemengembalikan a java.sql.Datetetapi dapat ditugaskan langsung ke a java.util.Date.

Paul Tomblin
sumber
3
Ehm, ResultSet # getDate () mengembalikan sql.Date (yang meluas util.Date).
Esko
3
@ Esko - "Ehm", saya memperbaikinya sebelum Anda berkomentar (dan downvoted).
Paul Tomblin
1
Penting untuk dicatat bahwa alasan java.sql.Date dapat ditetapkan ke java.util.Date adalah karena yang pertama adalah subkelas dari yang kedua.
dj18
2
Mengapa 'mengatakan' bahwa a java.sql.Datedapat ditugaskan ke suatu java.util.Dateketika yang pertama memperpanjang yang terakhir? Apa yang ingin Anda sampaikan?
Marquis of Lorne
11

Saya memiliki masalah yang sama, cara termudah yang saya temukan untuk memasukkan tanggal saat ini ke dalam pernyataan yang disiapkan adalah yang ini:

preparedStatement.setDate(1, new java.sql.Date(new java.util.Date().getTime()));
Israel
sumber
2
tidak bisa mendapatkan zona waktu dengan ini.
erhanasikoglu
4

tl; dr

Gunakan keduanya.

Tidak juga

java.util.Date vs java.sql.Date: kapan harus menggunakan yang mana dan mengapa?

Kedua kelas ini mengerikan, cacat dalam desain dan implementasi. Hindari seperti Wabah Coronavirus .

Alih-alih menggunakan kelas java.time , didefinisikan dalam JSR 310. Kelas-kelas ini adalah kerangka kerja industri terkemuka untuk bekerja dengan penanganan tanggal-waktu. Menggantikan ini seluruhnya kelas warisan mengerikan berdarah seperti Date, Calendar, SimpleDateFormat, dan semacamnya.

java.util.Date

Yang pertama, java.util.Datedimaksudkan untuk mewakili momen dalam UTC, yang berarti offset dari UTC dari nol jam-menit-detik.

java.time.Instant

Sekarang digantikan oleh java.time.Instant.

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

java.time.OffsetDateTime

Instantadalah kelas dasar-blok bangunan java.time . Untuk fleksibilitas lebih, gunakan OffsetDateTimeset ke ZoneOffset.UTCuntuk tujuan yang sama: mewakili momen di UTC.

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

Anda dapat mengirim objek ini ke database dengan menggunakan PreparedStatement::setObjectdengan JDBC 4.2 atau yang lebih baru.

myPreparedStatement.setObject(  , odt ) ;

Ambil kembali.

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

java.sql.Date

The java.sql.Datekelas juga mengerikan dan usang.

Kelas ini dimaksudkan untuk mewakili tanggal saja, tanpa waktu-hari dan tanpa zona waktu. Sayangnya, dalam peretasan desain yang mengerikan, kelas ini mewarisi dari java.util.Datemana yang mewakili momen (tanggal dengan waktu sehari di UTC). Jadi kelas ini hanya berpura-pura sebagai tanggal saja, sementara sebenarnya membawa waktu of-hari dan offset implisit UTC. Ini menyebabkan banyak kebingungan. Jangan pernah gunakan kelas ini.

java.time.LocalDate

Alih-alih, gunakan java.time.LocalDateuntuk melacak hanya tanggal (tahun, bulan, hari-bulan) tanpa batas waktu atau zona waktu atau offset apa pun.

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

Kirim ke database.

myPreparedStatement.setObject(  , ld ) ;

Ambil kembali.

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

Tabel tipe waktu tanggal di Jawa (baik yang lama maupun yang modern) dan dalam SQL standar


Tentang java.time

The java.time kerangka dibangun ke Jawa 8 dan kemudian. Kelas-kelas ini menggantikan tua merepotkan warisan kelas tanggal-waktu seperti java.util.Date, Calendar, & SimpleDateFormat.

Untuk mempelajari lebih lanjut, lihat Tutorial Oracle . Dan cari Stack Overflow untuk banyak contoh dan penjelasan. Spesifikasi adalah JSR 310 .

Proyek Joda-Time , sekarang dalam mode pemeliharaan , menyarankan migrasi ke kelas java.time .

Anda dapat bertukar objek java.time secara langsung dengan database Anda. Gunakan driver JDBC yang sesuai dengan JDBC 4.2 atau yang lebih baru. Tidak perlu untuk string, tidak perlujava.sql.* kelas.

Di mana mendapatkan kelas java.time?

Tabel mana perpustakaan java.time untuk digunakan dengan versi Java atau Android

Basil Bourque
sumber
1
Suara positif untuk mengganti referensi Wabah oleh virus Corona. Lebih nyaman sekarang.
ankuranurag2
2

Kelas java.util.Date di Java mewakili momen tertentu dalam waktu (e, .g., 2013 25 Nov 16:30:45 hingga milidetik), tetapi tipe data DATE dalam DB hanya mewakili tanggal (misalnya, 2013 25 November). Untuk mencegah Anda menyediakan objek java.util.Date ke DB karena kesalahan, Java tidak memungkinkan Anda untuk mengatur parameter SQL ke java.util.Date secara langsung:

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

Tetapi masih memungkinkan Anda untuk melakukannya dengan paksa / niat (maka jam dan menit akan diabaikan oleh driver DB). Ini dilakukan dengan kelas java.sql.Date:

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

Objek java.sql.Date dapat menyimpan momen dalam waktu (sehingga mudah dibangun dari java.util.Date) tetapi akan melempar pengecualian jika Anda mencoba untuk meminta waktu (untuk menegakkan konsepnya menjadi hanya tanggal). Driver DB diharapkan untuk mengenali kelas ini dan hanya menggunakan 0 selama berjam-jam. Coba ini:

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

java.util.Datemewakili instan spesifik dalam waktu dengan presisi milidetik. Ini mewakili informasi tanggal dan waktu tanpa zona waktu. Kelas java.util.Date mengimplementasikan antarmuka Serializable, Cloneable dan Sebanding. Ini diwarisi oleh java.sql.Date, java.sql.Timedan java.sql.Timestampantarmuka.

java.sql.Datememperluas kelas java.util.Date yang mewakili tanggal tanpa informasi waktu dan itu harus digunakan hanya ketika berhadapan dengan database. Untuk menyesuaikan dengan definisi SQL DATE, nilai milidetik yang dibungkus oleh sebuah java.sql.Dateinstance harus 'dinormalisasi' dengan menetapkan jam, menit, detik, dan milidetik ke nol dalam zona waktu tertentu yang terkait dengan instance tersebut.

Itu mewarisi semua metode umum dari java.util.Dateseperti getHours(), getMinutes(), getSeconds(), setHours(), setMinutes(), setSeconds(). Karena java.sql.Datetidak menyimpan informasi waktu, itu menimpa semua operasi waktu dari java.util.Datedan semua metode ini membuang java.lang.IllegalArgumentExceptionjika dipanggil sebagai bukti dari rincian implementasi mereka.

Abdul Alim Shakir
sumber