Jawaban:
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.
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.
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.
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.
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.
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.
java.sql.Datedapat ditugaskan ke suatu java.util.Dateketika yang pertama memperpanjang yang terakhir? Apa yang ingin Anda sampaikan?
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()));
Gunakan keduanya.
java.time.Instant menggantikan java.util.Datejava.time.LocalDate menggantikan java.sql.Datejava.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.DateYang pertama, java.util.Datedimaksudkan untuk mewakili momen dalam UTC, yang berarti offset dari UTC dari nol jam-menit-detik.
java.time.InstantSekarang digantikan oleh java.time.Instant.
Instant instant = Instant.now() ; // Capture the current moment as seen in UTC.
java.time.OffsetDateTimeInstantadalah 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.DateThe 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.LocalDateAlih-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 ) ;
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?
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());
}
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.