Bagaimana cara mengkonfigurasi JPA / Hibernate untuk menyimpan tanggal / waktu dalam database sebagai zona waktu UTC (GMT)? Pertimbangkan entitas JPA beranotasi ini:
public class Event {
@Id
public int id;
@Temporal(TemporalType.TIMESTAMP)
public java.util.Date date;
}
Jika tanggal 2008-Feb-03 9:30 Pacific Standard Time (PST), maka saya ingin waktu UTC 2008-Feb-03 17:30 disimpan dalam database. Demikian juga, ketika tanggal diambil dari database, saya ingin itu ditafsirkan sebagai UTC. Jadi dalam hal ini jam 530 sore adalah jam 530 sore UTC. Saat itu ditampilkan, itu akan diformat sebagai 9:30 PST.
Jawaban:
Dengan Hibernate 5.2, Anda sekarang dapat memaksa zona waktu UTC menggunakan properti konfigurasi berikut:
Untuk lebih jelasnya, lihat artikel ini .
sumber
useTimezone=true
dalam string koneksi. Kemudian hanya properti pengaturan yanghibernate.jdbc.time_zone
akan bekerjauseLegacyDatetimeCode
ke falseSepengetahuan saya, Anda perlu meletakkan seluruh aplikasi Java Anda di zona waktu UTC (sehingga Hibernate akan menyimpan tanggal dalam UTC), dan Anda harus mengonversi ke zona waktu apa pun yang diinginkan saat Anda menampilkan sesuatu (setidaknya kami melakukannya cara ini).
Saat memulai, kami melakukan:
Dan atur zona waktu yang diinginkan ke DateFormat:
sumber
Hibernate mengabaikan hal-hal zona waktu di Tanggal (karena tidak ada), tetapi sebenarnya lapisan JDBC yang menyebabkan masalah.
ResultSet.getTimestamp
danPreparedStatement.setTimestamp
keduanya mengatakan di dokumen mereka bahwa mereka mengubah tanggal ke / dari zona waktu JVM saat ini secara default saat membaca dan menulis dari / ke database.Saya menemukan solusi untuk ini di Hibernate 3.5 dengan subclassing
org.hibernate.type.TimestampType
yang memaksa metode JDBC ini untuk menggunakan UTC, bukan zona waktu lokal:Hal yang sama harus dilakukan untuk memperbaiki TimeType dan DateType jika Anda menggunakan tipe tersebut. Kelemahannya adalah Anda harus menentukan secara manual bahwa tipe ini akan digunakan sebagai ganti default pada setiap bidang Tanggal di POJO Anda (dan juga merusak kompatibilitas JPA murni), kecuali seseorang mengetahui metode penggantian yang lebih umum.
UPDATE: Hibernate 3.6 telah mengubah jenis API. Di 3.6, saya menulis kelas UtcTimestampTypeDescriptor untuk mengimplementasikan ini.
Sekarang, saat aplikasi dimulai, jika Anda menyetel TimestampTypeDescriptor.INSTANCE ke instance UtcTimestampTypeDescriptor, semua stempel waktu akan disimpan dan diperlakukan sebagai dalam UTC tanpa harus mengubah anotasi pada POJO. [Saya belum menguji ini]
sumber
UtcTimestampType
?UtcTimestampType
kompatibel?Date
atauTimestamp
bergantung.Dengan Spring Boot JPA, gunakan kode di bawah ini di file application.properties Anda dan jelas Anda dapat mengubah zona waktu sesuai pilihan Anda
Kemudian di file kelas Entitas Anda,
sumber
Menambahkan jawaban yang sepenuhnya berdasarkan dan berhutang budi kepada divestoclimb dengan petunjuk dari Shaun Stone. Hanya ingin menjelaskannya secara detail karena ini masalah umum dan solusinya agak membingungkan.
Ini menggunakan Hibernate 4.1.4. Final, meskipun saya mencurigai apa pun setelah 3.6 akan berfungsi.
Pertama, buat UtcTimestampTypeDescriptor divestoclimb
Kemudian buat UtcTimestampType, yang menggunakan UtcTimestampTypeDescriptor alih-alih TimestampTypeDescriptor sebagai SqlTypeDescriptor dalam panggilan konstruktor super tetapi sebaliknya mendelegasikan semuanya ke TimestampType:
Terakhir, ketika Anda menginisialisasi konfigurasi Hibernate Anda, daftarkan UtcTimestampType sebagai penimpaan tipe:
Sekarang stempel waktu tidak harus peduli dengan zona waktu JVM dalam perjalanannya ke dan dari database. HTH.
sumber
Anda akan mengira masalah umum ini akan diatasi dengan Hibernate. Tapi tidak! Ada beberapa "cara" untuk melakukannya dengan benar.
Yang saya gunakan adalah untuk menyimpan Date as a Long di database. Jadi saya selalu bekerja dengan milidetik setelah 1/1/70. Saya kemudian memiliki getter dan setter di Kelas saya yang mengembalikan / hanya menerima Tanggal. Jadi API tetap sama. Sisi negatifnya adalah saya memiliki kerinduan di database. JADI dengan SQL saya hanya bisa melakukan <,>, = perbandingan - bukan operator tanggal yang mewah.
Pendekatan lainnya adalah menggunakan jenis pemetaan ubahsuaian seperti yang dijelaskan di sini: http://www.hibernate.org/100.html
Saya pikir cara yang benar untuk menangani ini adalah dengan menggunakan Kalender daripada Tanggal sekalipun. Dengan Kalender Anda dapat mengatur TimeZone sebelum bertahan.
CATATAN: Stackoverflow konyol tidak mengizinkan saya berkomentar, jadi berikut adalah tanggapan untuk david a.
Jika Anda membuat objek ini di Chicago:
Hibernate mempertahankannya sebagai "12/31/1969 18:00:00". Tanggal harus tanpa zona waktu, jadi saya tidak yakin mengapa penyesuaian akan dilakukan.
sumber
Calendar
masalah baca ini , saya pikir Hibernate atau JPA harus menyediakan beberapa cara untuk menentukan, untuk setiap pemetaan, zona waktu yang harus diterjemahkan Hibernate tanggal membaca dan menulis keTIMESTAMP
kolom.Timestamp
karena kita tidak dapat begitu saja mempercayai driver JDBC untuk menyimpan tanggal sebagai kami harapkan.Ada beberapa zona waktu yang beroperasi di sini:
Semua ini bisa berbeda. Hibernate / JPA memiliki kekurangan desain yang parah sehingga pengguna tidak dapat dengan mudah memastikan bahwa informasi zona waktu disimpan di server database (yang memungkinkan rekonstruksi waktu dan tanggal yang benar di JVM).
Tanpa kemampuan untuk (dengan mudah) menyimpan zona waktu menggunakan JPA / Hibernate maka informasi akan hilang dan begitu informasi hilang, membuatnya menjadi mahal (jika memungkinkan).
Saya berpendapat bahwa lebih baik untuk selalu menyimpan informasi zona waktu (harus default) dan pengguna kemudian harus memiliki kemampuan opsional untuk mengoptimalkan zona waktu (meskipun itu hanya benar-benar mempengaruhi tampilan, masih ada zona waktu implisit di tanggal mana pun).
Maaf, posting ini tidak memberikan solusi (yang telah dijawab di tempat lain) tetapi ini adalah rasionalisasi mengapa selalu menyimpan informasi zona waktu di sekitar itu penting. Sayangnya tampaknya banyak Ilmuwan Komputer dan praktisi pemrograman menentang perlunya zona waktu hanya karena mereka tidak menghargai perspektif "kehilangan informasi" dan bagaimana hal itu membuat hal-hal seperti internasionalisasi menjadi sangat sulit - yang sangat penting saat ini dengan situs web yang dapat diakses oleh klien dan orang di organisasi Anda saat mereka bergerak di seluruh dunia.
sumber
Silakan lihat proyek saya di Sourceforge yang memiliki tipe pengguna untuk tipe Tanggal dan Waktu SQL standar serta JSR 310 dan Joda Time. Semua tipe mencoba untuk mengatasi masalah pengimbangan. Lihat http://sourceforge.net/projects/usertype/
EDIT: Menanggapi pertanyaan Derek Mahar yang dilampirkan pada komentar ini:
"Chris, apakah tipe pengguna Anda berfungsi dengan Hibernate 3 atau lebih tinggi? - Derek Mahar 7 November '10 pukul 12:30"
Ya, tipe ini mendukung versi Hibernate 3.x termasuk Hibernate 3.6.
sumber
Tanggal tidak berada dalam zona waktu mana pun (ini adalah kantor milidetik dari momen yang ditentukan dalam waktu yang sama untuk semua orang), tetapi DB yang mendasari umumnya menyimpan stempel waktu dalam format politik (tahun, bulan, hari, jam, menit, detik,. ..) yang peka terhadap zona waktu.
Untuk lebih serius, Hibernate HARUS diizinkan untuk diberi tahu dalam beberapa bentuk pemetaan bahwa tanggal DB berada dalam zona waktu ini-dan-itu sehingga ketika memuat atau menyimpannya, ia tidak menganggapnya sendiri ...
sumber
Saya mengalami masalah yang sama ketika saya ingin menyimpan tanggal di DB sebagai UTC dan menghindari penggunaan
varchar
danString <-> java.util.Date
konversi eksplisit , atau mengatur seluruh aplikasi Java saya dalam zona waktu UTC (karena ini dapat menyebabkan masalah tak terduga lainnya, jika JVM adalah dibagikan di banyak aplikasi).Jadi, ada proyek sumber terbuka
DbAssist
, yang memungkinkan Anda untuk dengan mudah memperbaiki tanggal baca / tulis sebagai UTC dari database. Karena Anda menggunakan Anotasi JPA untuk memetakan bidang di entitas, yang harus Anda lakukan adalah menyertakan ketergantungan berikut kepom
file Maven Anda :Kemudian Anda menerapkan perbaikan (untuk contoh Hibernate + Spring Boot) dengan menambahkan
@EnableAutoConfiguration
anotasi sebelum kelas aplikasi Spring. Untuk instruksi instalasi setup lainnya dan contoh penggunaan lainnya, cukup merujuk ke github proyek .Hal baiknya adalah Anda tidak perlu memodifikasi entitas sama sekali; Anda bisa membiarkan
java.util.Date
ladang mereka apa adanya.5.2.2
harus sesuai dengan versi Hibernate yang Anda gunakan. Saya tidak yakin, versi mana yang Anda gunakan dalam proyek Anda, tetapi daftar lengkap perbaikan yang disediakan tersedia di halaman wiki github proyek . Alasan mengapa perbaikan berbeda untuk berbagai versi Hibernate adalah karena pembuat Hibernate mengubah API beberapa kali antara rilis.Secara internal, perbaikan menggunakan petunjuk dari divestoclimb, Shane dan beberapa sumber lain untuk membuat kebiasaan
UtcDateType
. Kemudian memetakan standarjava.util.Date
dengan kebiasaanUtcDateType
yang menangani semua penanganan zona waktu yang diperlukan. Pemetaan jenis dilakukan dengan menggunakan@Typedef
anotasi dipackage-info.java
file yang disediakan .Anda dapat menemukan artikel di sini yang menjelaskan mengapa pergeseran waktu seperti itu terjadi dan apa saja pendekatan untuk menyelesaikannya.
sumber
Hibernasi tidak memungkinkan untuk menentukan zona waktu dengan penjelasan atau cara lain. Jika Anda menggunakan Kalender dan bukan tanggal, Anda bisa menerapkan solusi menggunakan properti HIbernate AccessType dan menerapkan pemetaan sendiri. Solusi yang lebih canggih adalah menerapkan UserType kustom untuk memetakan Tanggal atau Kalender Anda. Kedua solusi dijelaskan dalam postingan blog saya di sini: http://www.joobik.com/2010/11/mapping-dates-and-time-zones-with.html
sumber