java.util.Date
vs java.sql.Date
: kapan harus menggunakan yang mana dan mengapa?
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.Date
berkorespondensi dengan SQL DATE yang artinya menyimpan bertahun-tahun, bulan dan hari sementara jam, menit, detik dan milidetik diabaikan. Selain sql.Date
itu tidak terikat dengan zona waktu.java.sql.Time
sesuai dengan SQL TIME dan sebagaimana seharusnya jelas, hanya berisi informasi tentang jam, menit, detik, dan milidetik .java.sql.Timestamp
sesuai dengan SQL TIMESTAMP yang merupakan tanggal yang tepat untuk nanodetik ( perhatikan bahwa util.Date
hanya 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.Date
spesifik zona waktu, sql.Time
berisi tahun saat ini, bulan dan hari dan lain-lain.
Tergantung pada tipe SQL dari lapangan, sungguh. PreparedStatement
memiliki setter untuk ketiga nilai, #setDate()
menjadi satu untuk sql.Date
, #setTime()
untuk sql.Time
dan #setTimestamp()
untuk sql.Timestamp
.
Perhatikan bahwa jika Anda menggunakan, ps.setObject(fieldIndex, utilDateObject);
Anda benar-benar dapat memberikan yang normal util.Date
untuk 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.Date
ataujava.sql.Date
jika Anda bisa menghindarinya, dan sebaliknya lebih memilih menggunakanjava.time
paket (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,
Calendar
kelas-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,
long
s 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.sumber
java.sql.Date
denganPreparedStatement
dll! Tetapi ketika Anda menyebarkannya, gunakanLocalDate
yang Anda konversi menggunakanjava.sql.Date.valueOf
danjava.sql.Date.valueOf
ketika mengaturnya, dan mengubahnya kembali sedini mungkin menggunakanjava.sql.Date.toLocalDate
- lagi, karena Anda ingin melibatkan java.sql sesedikit mungkin, dan karena itu bisa berubah.Satu-satunya waktu untuk menggunakan
java.sql.Date
adalah aPreparedStatement.setDate
. Kalau tidak, gunakanjava.util.Date
. Ia memberi tahu bahwaResultSet.getDate
mengembalikan ajava.sql.Date
tetapi dapat ditugaskan langsung ke ajava.util.Date
.sumber
java.sql.Date
dapat ditugaskan ke suatujava.util.Date
ketika 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:
sumber
tl; dr
Gunakan keduanya.
java.time.Instant
menggantikanjava.util.Date
java.time.LocalDate
menggantikanjava.sql.Date
Tidak juga
Kedua kelas ini mengerikan, cacat dalam desain dan implementasi. Hindari seperti
WabahCoronavirus .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.Date
dimaksudkan untuk mewakili momen dalam UTC, yang berarti offset dari UTC dari nol jam-menit-detik.java.time.Instant
Sekarang digantikan oleh
java.time.Instant
.java.time.OffsetDateTime
Instant
adalah kelas dasar-blok bangunan java.time . Untuk fleksibilitas lebih, gunakanOffsetDateTime
set keZoneOffset.UTC
untuk tujuan yang sama: mewakili momen di UTC.Anda dapat mengirim objek ini ke database dengan menggunakan
PreparedStatement::setObject
dengan JDBC 4.2 atau yang lebih baru.Ambil kembali.
java.sql.Date
The
java.sql.Date
kelas 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.Date
mana 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.LocalDate
untuk melacak hanya tanggal (tahun, bulan, hari-bulan) tanpa batas waktu atau zona waktu atau offset apa pun.Kirim ke database.
Ambil kembali.
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 perlu
java.sql.*
kelas.Di mana mendapatkan kelas java.time?
sumber
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:
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:
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:
sumber
java.util.Date
mewakili 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 olehjava.sql.Date
,java.sql.Time
danjava.sql.Timestamp
antarmuka.java.sql.Date
memperluas 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 sebuahjava.sql.Date
instance 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.Date
sepertigetHours()
,getMinutes()
,getSeconds()
,setHours()
,setMinutes()
,setSeconds()
. Karenajava.sql.Date
tidak menyimpan informasi waktu, itu menimpa semua operasi waktu darijava.util.Date
dan semua metode ini membuangjava.lang.IllegalArgumentException
jika dipanggil sebagai bukti dari rincian implementasi mereka.sumber