Bagaimana seharusnya model class's equals dan hashcode diimplementasikan dalam Hibernate? Apa saja jebakan yang umum? Apakah implementasi default cukup baik untuk kebanyakan kasus? Apakah ada gunanya menggunakan kunci bisnis?
Bagi saya tampaknya cukup sulit untuk membuatnya bekerja dengan benar dalam setiap situasi, ketika pengambilan lambat, pembuatan id, proxy, dll diperhitungkan.
Jawaban:
Hibernate memiliki penjelasan yang bagus dan panjang tentang kapan / bagaimana menimpa
equals()
/hashCode()
dalam dokumentasiIntinya adalah Anda hanya perlu mengkhawatirkannya jika entitas Anda akan menjadi bagian dari a
Set
atau jika Anda akan melepaskan / melampirkan instance-nya. Yang terakhir tidak begitu umum. Yang pertama biasanya paling baik ditangani melalui:equals()
/hashCode()
pada kunci bisnis - misalnya kombinasi unik dari atribut yang tidak akan berubah selama objek (atau, setidaknya, sesi) seumur hidup.equals()
/hashCode()
pada kunci primer JIKA itu disetel dan identitas objek /System.identityHashCode()
sebaliknya. Bagian penting di sini adalah Anda perlu memuat ulang Set Anda setelah entitas baru ditambahkan ke dalamnya dan dipertahankan; jika tidak, Anda mungkin akan mendapatkan perilaku aneh (yang pada akhirnya menyebabkan kesalahan dan / atau kerusakan data) karena entitas Anda mungkin dialokasikan ke keranjang yang tidak cocok dengan saat inihashCode()
.sumber
refresh()
? Bagaimana entitas Anda, yang mematuhiSet
kontrak berakhir di keranjang yang salah (dengan asumsi Anda memiliki implementasi kode hash yang cukup baik).Set.contains(entity)
dan Anda akan kembalifalse
. Hal yang sama berlaku untuk get () / put () / etc ...Saya tidak berpikir bahwa jawaban yang diterima itu akurat.
Untuk menjawab pertanyaan awal:
Jawabannya adalah ya, dalam banyak kasus memang demikian.
Anda hanya perlu mengganti
equals()
danhashcode()
jika entitas akan digunakan dalamSet
(yang sangat umum) DAN entitas akan terlepas dari, dan kemudian dilampirkan kembali ke, sesi hibernasi (yang merupakan penggunaan hibernate yang tidak umum).Jawaban yang diterima menunjukkan bahwa metode perlu diganti jika salah satu kondisinya benar.
sumber
Penerapan
equals
/ terbaikhashCode
adalah ketika Anda menggunakan kunci bisnis yang unik .Kunci bisnis harus konsisten di semua transisi status entitas (sementara, dilampirkan, dilepas, dihapus), itulah mengapa Anda tidak dapat mengandalkan id untuk kesetaraan.
Opsi lainnya adalah beralih menggunakan pengenal UUID , yang ditetapkan oleh logika aplikasi. Dengan cara ini, Anda dapat menggunakan UUID untuk
equals
/hashCode
karena id ditetapkan sebelum entitas dihapus.Anda bahkan dapat menggunakan pengenal entitas untuk
equals
danhashCode
, tetapi itu mengharuskan Anda untuk selalu mengembalikan nilai yang samahashCode
sehingga Anda memastikan bahwa nilai kode hash entitas konsisten di semua transisi status entitas. Lihat posting ini untuk lebih lanjut tentang topik ini .sumber
BaseEntity
dan jangan pernah berpikir lagi tentang masalah itu. Dibutuhkan sedikit ruang di sisi db tetapi harga itu Anda lebih baik membayar untuk kenyamanan :)Ketika sebuah entitas dimuat melalui pemuatan lambat, itu bukan instance dari tipe dasar, tetapi merupakan subtipe yang dibuat secara dinamis yang dibuat oleh javassist, jadi pemeriksaan pada tipe kelas yang sama akan gagal, jadi jangan gunakan:
sebagai gantinya gunakan:
yang juga merupakan praktik yang baik, seperti yang dijelaskan di Implementing Equals in Java Practices .
karena alasan yang sama, mengakses bidang secara langsung, mungkin tidak berfungsi dan mengembalikan null, bukan nilai yang mendasarinya, jadi jangan gunakan perbandingan pada properti, tetapi gunakan getter, karena mereka mungkin memicu untuk memuat nilai yang mendasarinya.
sumber
Ya, itu sulit. Dalam proyek saya sama dan hashCode keduanya bergantung pada id objek. Masalah dari solusi ini adalah bahwa keduanya tidak berfungsi jika objek belum dipertahankan, karena id dihasilkan oleh database. Dalam kasus saya, itu dapat ditoleransi karena di hampir semua kasus, objek langsung disimpan. Selain itu, ini berfungsi dengan baik dan mudah diterapkan.
sumber
Dalam dokumentasi Hibernate 5.2 dikatakan bahwa Anda mungkin tidak ingin mengimplementasikan kode hash dan yang sama sama sekali - tergantung pada situasi Anda.
https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#mapping-model-pojo-equalshashcode
Umumnya, dua objek yang dimuat dari sesi yang sama akan sama jika keduanya sama dalam database (tanpa menerapkan hashCode dan sama dengan).
Ini menjadi rumit jika Anda menggunakan dua sesi atau lebih. Dalam hal ini, persamaan dua objek bergantung pada implementasi metode-sama Anda.
Lebih lanjut, Anda akan mendapat masalah jika metode-sama Anda membandingkan ID yang hanya dibuat saat mempertahankan objek untuk pertama kalinya. Mereka mungkin belum ada di sana saat yang sama dipanggil.
sumber
Ada artikel yang sangat bagus di sini: https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/persistent-classes-equalshashcode.html
Mengutip baris penting dari artikel:
Secara sederhana
sumber
Jika Anda kebetulan menimpa
equals
, pastikan Anda memenuhi kontraknya: -Dan mengesampingkan
hashCode
, karena kontraknya bergantung padaequals
implementasi.Joshua Bloch (perancang kerangka Koleksi) sangat menyarankan aturan ini untuk diikuti.
Ada efek serius yang tidak diinginkan jika Anda tidak mengikuti kontrak ini. Misalnya
List#contains(Object o)
mungkin mengembalikanboolean
nilai yang salah karena kontrak umum tidak terpenuhi.sumber