Saya memiliki situasi di mana saya harus melampirkan kembali objek yang terpisah ke sesi hibernasi, meskipun objek dengan identitas yang sama DAPAT ada di sesi, yang akan menyebabkan kesalahan.
Saat ini, saya dapat melakukan satu dari dua hal.
getHibernateTemplate().update( obj )
Ini berfungsi jika dan hanya jika suatu objek belum ada di sesi hibernasi. Pengecualian dilemparkan yang menyatakan objek dengan pengenal yang diberikan sudah ada di sesi ketika saya membutuhkannya nanti.getHibernateTemplate().merge( obj )
Ini berfungsi jika dan hanya jika ada objek di sesi hibernasi. Pengecualian dilemparkan ketika saya membutuhkan objek dalam sesi nanti jika saya menggunakan ini.
Dengan dua skenario ini, bagaimana saya bisa secara umum melampirkan sesi ke objek? Saya tidak ingin menggunakan pengecualian untuk mengontrol aliran solusi masalah ini, karena harus ada solusi yang lebih elegan ...
refresh()
pada entitas yang terpisah? Melihat melalui spesifikasi 2.0, saya tidak melihat adanya pembenaran; Hanya saja itu tidak diizinkan.*Reattaching a modified detached instance* A detached instance may be reattached to a new Session (and managed by this new persistence context) by calling update() on the detached object. In our experience, it may be easier for you to understand the following code if you rename the update() method in your mind to reattach()—however, there is a good reason it’s called updating.
Lebih banyak dapat ditemukan di bagian 9.3.2lock(LockMode.NONE)
sebenarnya bisa dipanggil pada objek sementara, dan itu menempel kembali entitas ke sesi. Lihat stackoverflow.com/a/3683370/14379Semua jawaban ini kehilangan perbedaan penting. pembaruan () digunakan untuk (kembali) melampirkan grafik objek Anda ke Sesi. Objek yang Anda lewati adalah objek yang dikelola.
menggabungkan () sebenarnya bukan API lampiran (kembali). Perhatikan penggabungan () memiliki nilai pengembalian? Itu karena ia mengembalikan Anda grafik yang dikelola, yang mungkin bukan grafik yang Anda lewati. gabungan () adalah API JPA dan perilakunya diatur oleh spesifikasi JPA. Jika objek yang Anda lewati untuk menggabungkan () sudah dikelola (sudah dikaitkan dengan Sesi) maka itulah grafik yang berfungsi dengan Hibernate; objek yang diteruskan adalah objek yang sama yang dikembalikan dari gabungan (). Namun, jika objek yang Anda gabungkan () dilepaskan, Hibernate membuat grafik objek baru yang dikelola dan menyalin keadaan dari grafik Anda yang terlepas ke grafik terkelola yang baru. Sekali lagi, ini semua ditentukan dan diatur oleh spesifikasi JPA.
Dalam hal strategi generik untuk "memastikan entitas ini dikelola, atau membuatnya dikelola", itu tergantung pada apakah Anda ingin memperhitungkan data yang belum dimasukkan juga. Dengan asumsi Anda melakukannya, gunakan sesuatu seperti
Perhatikan saya menggunakan saveOrUpdate () daripada update (). Jika Anda tidak ingin data yang belum dimasukkan ditangani di sini, gunakan pembaruan () sebagai gantinya ...
sumber
Session.contains(Object)
memeriksa dengan referensi. Jika sudah ada Entitas lain yang mewakili baris yang sama dalam sesi dan Anda melewati instance terpisah Anda akan mendapatkan pengecualian.Session.contains(Object)
pemeriksaan dengan referensi, jika ada Entitas lain yang mewakili baris yang sama dalam sesi, itu akan mengembalikan false, dan itu akan memperbaruinya.Jawaban tidak jujur: Anda mungkin mencari konteks ketekunan yang diperpanjang. Ini adalah salah satu alasan utama di balik Seam Framework ... Jika Anda kesulitan untuk menggunakan Hibernate di Spring pada khususnya, lihat bagian ini dari dokumen Seam ini.
Jawaban diplomatik: Ini dijelaskan dalam dokumen Hibernate . Jika Anda membutuhkan klarifikasi lebih lanjut, lihat Bagian 9.3.2 Java Persistence dengan Hibernate yang disebut "Bekerja dengan Objek yang Dilepaskan." Saya sangat menyarankan Anda mendapatkan buku ini jika Anda melakukan lebih dari CRUD dengan Hibernate.
sumber
Jika Anda yakin entitas Anda belum dimodifikasi (atau jika Anda setuju modifikasi apa pun akan hilang), maka Anda dapat memasangnya kembali ke sesi dengan kunci.
Ini tidak akan mengunci apa pun, tetapi akan mendapatkan entitas dari cache sesi atau (jika tidak ditemukan di sana) membacanya dari DB.
Ini sangat berguna untuk mencegah LazyInitException ketika Anda menavigasi hubungan dari entitas "lama" (dari HttpSession misalnya). Anda pertama kali "melampirkan kembali" entitas.
Menggunakan get juga berfungsi, kecuali ketika Anda mendapatkan pemetaan warisan (yang sudah akan melempar pengecualian pada getId ()).
sumber
Session.lock(entity, LockMode.NONE)
gagal dengan pengecualian yang mengatakan: tidak dapat mengaitkan kembali koleksi transien yang tidak diinisialisasi. Bagaimana cara mengatasinya?Session.find()
metode API. Mungkin maksud AndaSession.load(Object object, Serializable id)
.Status entitas
JPA mendefinisikan status entitas berikut:
Baru (sementara)
Objek yang baru dibuat yang belum pernah dikaitkan dengan Hibernate
Session
(aliasPersistence Context
) dan tidak dipetakan ke baris tabel basis data apa pun dianggap berada dalam keadaan Baru (Transient).Untuk bertahan, kita perlu secara eksplisit memanggil
EntityManager#persist
metode atau memanfaatkan mekanisme kegigihan transitif.Gigih (Dikelola)
Entitas persisten telah dikaitkan dengan baris tabel database dan dikelola oleh Konteks Persistence yang sedang berjalan. Setiap perubahan yang dibuat untuk entitas seperti itu akan dideteksi dan disebarkan ke database (selama Sesi flush-time).
Dengan Hibernate, kita tidak lagi harus menjalankan pernyataan INSERT / UPDATE / DELETE. Hibernate menggunakan gaya kerja tulis-balik transaksional dan perubahan disinkronkan pada saat yang paling bertanggung jawab terakhir, selama waktu
Session
flush saat ini.Terpisah
Setelah Konteks Persistensi berjalan saat ini ditutup semua entitas yang sebelumnya dikelola menjadi terlepas. Perubahan yang berurutan tidak akan lagi dilacak dan tidak ada sinkronisasi basis data otomatis yang akan terjadi.
Transisi status entitas
Anda dapat mengubah status entitas menggunakan berbagai metode yang ditentukan oleh
EntityManager
antarmuka.Untuk memahami transisi status entitas JPA dengan lebih baik, pertimbangkan diagram berikut:
Saat menggunakan JPA, untuk menghubungkan kembali entitas yang terpisah ke aktif
EntityManager
, Anda dapat menggunakan operasi gabungan .Saat menggunakan API Hibernate asli, selain dari
merge
, Anda dapat memasang kembali entitas terpisah ke Sesi Hibernasi aktif menggunakan metode pembaruan, seperti yang ditunjukkan oleh diagram berikut:Menggabungkan entitas yang terpisah
Penggabungan akan menyalin status entitas terpisah (sumber) ke instance entitas terkelola (tujuan).
Anggaplah kita telah mempertahankan
Book
entitas berikut , dan sekarang entitas terlepas sepertiEntityManager
yang digunakan untuk bertahan entitas ditutup:Saat entitas dalam keadaan terpisah, kami memodifikasinya sebagai berikut:
Sekarang, kami ingin menyebarkan perubahan ke database, sehingga kami dapat memanggil
merge
metode:Dan Hibernate akan menjalankan pernyataan SQL berikut:
Jika entitas penggabungan tidak memiliki padanan dalam arus
EntityManager
, snapshot entitas baru akan diambil dari database.Setelah ada entitas manged, JPA menyalin status entitas terpisah ke entitas yang saat ini dikelola, dan selama Konteks Persistensi
flush
, UPDATE akan dihasilkan jika mekanisme pemeriksaan kotor menemukan bahwa entitas yang dikelola telah berubah.Pemasangan kembali entitas yang terpisah
Hibernasi, tetapi JPA tidak mendukung pemasangan kembali melalui
update
metode.A Hibernate
Session
hanya dapat mengaitkan satu objek entitas untuk baris database yang diberikan. Ini karena Konteks Persistensi bertindak sebagai cache di memori (cache level pertama) dan hanya satu nilai (entitas) yang dikaitkan dengan kunci yang diberikan (tipe entitas dan pengidentifikasi basis data).Suatu entitas dapat disambungkan kembali hanya jika tidak ada objek JVM lainnya (cocok dengan baris database yang sama) yang sudah dikaitkan dengan Hibernate saat ini
Session
.Mengingat kami telah bertahan
Book
entitas dan bahwa kami memodifikasinya ketikaBook
entitas dalam keadaan terpisah:Kami dapat memasang kembali entitas yang terlepas seperti ini:
Dan Hibernate akan menjalankan pernyataan SQL berikut:
Tidak seperti itu
merge
, entitas terpisah yang disediakan akan dihubungkan kembali dengan Konteks Persistensi saat ini dan UPDATE dijadwalkan selama flush apakah entitas telah dimodifikasi atau belum.Untuk mencegah hal ini, Anda dapat menggunakan
@SelectBeforeUpdate
anotasi Hibernate yang akan memicu pernyataan SELECT yang mengambil status dimuat yang kemudian digunakan oleh mekanisme pemeriksaan kotor.Waspadai NonUniqueObjectException
Salah satu masalah yang dapat terjadi
update
adalah jika Konteks Persistensi sudah berisi referensi entitas dengan id yang sama dan dari jenis yang sama seperti dalam contoh berikut:Sekarang, ketika menjalankan test case di atas, Hibernate akan melempar
NonUniqueObjectException
karena yang keduaEntityManager
sudah berisiBook
entitas dengan pengidentifikasi yang sama seperti yang kita lewatiupdate
, dan Persistence Context tidak dapat menampung dua representasi dari entitas yang sama.Kesimpulan
The
merge
metode adalah lebih disukai jika Anda menggunakan penguncian optimis karena memungkinkan Anda untuk mencegah update yang hilang. Untuk detail lebih lanjut tentang topik ini, lihat artikel ini .Ini
update
bagus untuk pembaruan batch karena dapat mencegah pernyataan SELECT tambahan yang dihasilkan olehmerge
operasi, sehingga mengurangi waktu eksekusi pembaruan batch.sumber
@SelectBeforeUpdate
anotasi. Kapan pilih dipicu? Saat meneleponupdate
, tepat sebelum memerah atau tidak terlalu penting (bisa jadi masalah jika hibernate mengambil semua entitas yang beranotasi dalam satu panggilan sebelum memerah)?@SelectBeforeUpdate
memicu SELECT selama Konteks Kegigihanflush
operasi. Periksa dengangetDatabaseSnapshot
metode dalamDefaultFlushEntityEventListener
untuk lebih jelasnya.Saya kembali ke JavaDoc untuk
org.hibernate.Session
dan menemukan yang berikut:Dengan demikian
update()
,saveOrUpdate()
,lock()
,replicate()
danmerge()
adalah pilihan kandidat.update()
: Akan melempar pengecualian jika ada instance persisten dengan pengidentifikasi yang sama.saveOrUpdate()
: Baik menyimpan atau memperbaruilock()
: Usangreplicate()
: Tetap kondisi instance terpisah yang diberikan, menggunakan kembali nilai pengidentifikasi saat ini.merge()
: Mengembalikan objek persisten dengan pengidentifikasi yang sama. Contoh yang diberikan tidak menjadi terkait dengan sesi.Oleh karena itu,
lock()
tidak boleh digunakan secara langsung dan berdasarkan persyaratan fungsional satu atau lebih dari mereka dapat dipilih.sumber
Saya melakukannya seperti itu di C # dengan NHibernate, tetapi harus bekerja dengan cara yang sama di Jawa:
Kunci Pertama dipanggil pada setiap objek karena Berisi selalu salah. Masalahnya adalah NHibernate membandingkan objek berdasarkan id dan jenis basis data. Berisi menggunakan
equals
metode, yang membandingkan dengan referensi jika tidak ditimpa. Denganequals
metode itu berfungsi tanpa Pengecualian:sumber
Session.contains(Object obj)
memeriksa referensi dan tidak akan mendeteksi instance berbeda yang mewakili baris yang sama dan sudah terlampir padanya.Di sini solusi generik saya untuk Entitas dengan properti pengidentifikasi.
Ini adalah salah satu dari beberapa aspek .Net EntityFramework yang saya suka, opsi melampirkan yang berbeda mengenai entitas yang berubah dan properti mereka.
sumber
Saya datang dengan solusi untuk "menyegarkan" objek dari toko kegigihan yang akan menjelaskan objek lain yang mungkin sudah dilampirkan pada sesi:
sumber
Maaf, sepertinya tidak dapat menambahkan komentar (belum?).
Menggunakan Hibernate 3.5.0-Final
Sementara
Session#lock
metode ini sudah usang, javadoc tidak menyarankan menggunakanSession#buildLockRequest(LockOptions)#lock(entity)
dan jika Anda memastikan asosiasi Anda milikicascade=lock
, pemuatan malas juga tidak menjadi masalah.Jadi, metode lampirkan saya agak mirip
Tes awal menunjukkan itu manjur.
sumber
Mungkin berperilaku sedikit berbeda di Eclipselink. Untuk melampirkan kembali objek yang terlepas tanpa mendapatkan data basi, biasanya saya lakukan:
dan sebagai langkah kedua opsional (untuk membuat cache tidak valid):
sumber
coba getHibernateTemplate (). replikasi (entitas, ReplicationMode.LATEST_VERSION)
sumber
Dalam posting asli, ada dua metode,
update(obj)
danmerge(obj)
yang disebutkan berfungsi, tetapi dalam situasi yang berlawanan. Jika ini benar, maka mengapa tidak menguji untuk melihat apakah objek sudah ada di sesi pertama, lalu panggilupdate(obj)
jika itu, jika tidak panggilmerge(obj)
.Tes untuk keberadaan dalam sesi ini adalah
session.contains(obj)
. Oleh karena itu, saya akan berpikir kode pseudo berikut ini akan berfungsi:sumber
untuk memasang kembali objek ini, Anda harus menggunakan gabungan ();
Metode ini menerima parameter entitas Anda terlepas dan mengembalikan entitas akan dilampirkan dan dimuat kembali dari Database.
sumber
memanggil penggabungan pertama () (untuk memperbarui instance persisten), lalu mengunci (LockMode.NONE) (untuk melampirkan instance saat ini, bukan yang dikembalikan oleh penggabungan ()) tampaknya berfungsi untuk beberapa kasus penggunaan.
sumber
Properti
hibernate.allow_refresh_detached_entity
melakukan trik untuk saya. Tetapi ini adalah aturan umum, jadi tidak cocok jika Anda ingin melakukannya hanya dalam beberapa kasus. Saya harap ini membantu.Diuji pada Hibernate 5.4.9
SessionFactoryOptionsBuilder
sumber
Dukungan Hibernate pasang kembali entitas yang terlepas dengan cara serval, lihat panduan pengguna Hibernate .
sumber
sumber