EntityManager.merge()
dapat menyisipkan objek baru dan memperbarui yang sudah ada.
Mengapa seseorang ingin menggunakan persist()
(yang hanya dapat membuat objek baru)?
jpa
merge
entitymanager
persist
java-persistence-api
Aaron Digulla
sumber
sumber
Jawaban:
Apa pun cara akan menambahkan entitas ke PersistenceContext, perbedaannya adalah apa yang Anda lakukan dengan entitas setelahnya.
Persist mengambil instance entitas, menambahkannya ke konteks dan membuat instance itu dikelola (yaitu pembaruan masa depan untuk entitas akan dilacak).
Gabungkan mengembalikan contoh terkelola yang statusnya digabungkan. Itu mengembalikan sesuatu yang ada di PersistenceContext atau menciptakan instance baru dari entitas Anda. Bagaimanapun, itu akan menyalin negara dari entitas yang disediakan, dan mengembalikan salinan yang dikelola. Contoh yang Anda lewati tidak akan dikelola (perubahan apa pun yang Anda lakukan tidak akan menjadi bagian dari transaksi - kecuali jika Anda memanggil penggabungan lagi). Anda dapat melalui penggunaan instance yang dikembalikan (dikelola satu).
Mungkin contoh kode akan membantu.
Skenario 1 dan 3 kira-kira setara, tetapi ada beberapa situasi di mana Anda ingin menggunakan Skenario 2.
sumber
merge
salinan penuh dari suatu objek sebelum mengelolanya memiliki kinerja yang baik?@GeneratedId
bisakah saya mendapatkannya di skenario 2?Bertahan dan bergabung adalah untuk dua tujuan yang berbeda (mereka bukan alternatif sama sekali).
(diedit untuk memperluas informasi perbedaan)
bertahan:
menggabungkan:
bertahan () efisiensi:
tahan () semantik:
Contoh:
Cara ini hanya ada 1 objek terlampir untuk register apa pun di manajer entitas.
menggabungkan () untuk entitas dengan id adalah sesuatu seperti:
Meskipun jika terhubung ke gabungan MySQL () bisa seefisien bertahan () menggunakan panggilan ke INSERT dengan opsi ON DUPLICATE KEY UPDATE, JPA adalah pemrograman tingkat yang sangat tinggi dan Anda tidak dapat menganggap ini akan terjadi di mana-mana.
sumber
em.persist(x)
denganx = em.merge(x)
?merge()
juga bisa melemparEntityExistsException
RuntimeException
, tetapi tidak disebutkan di Javadoc.Jika Anda menggunakan generator yang ditetapkan, menggunakan gabungan alih-alih bertahan dapat menyebabkan pernyataan SQL yang berlebihan , sehingga memengaruhi kinerja.
Selain itu, memanggil penggabungan untuk entitas yang dikelola juga merupakan kesalahan karena entitas yang dikelola secara otomatis dikelola oleh Hibernate dan statusnya disinkronkan dengan catatan database dengan mekanisme pemeriksaan kotor setelah membilas Konteks Persistence .
Untuk memahami bagaimana semua ini bekerja, Anda harus terlebih dahulu tahu bahwa Hibernate menggeser pola pikir pengembang dari pernyataan SQL ke transisi status entitas .
Setelah suatu entitas dikelola secara aktif oleh Hibernate, semua perubahan akan secara otomatis disebarkan ke database.
Monitor Hibernate yang saat ini terpasang entitas. Tetapi agar suatu entitas dikelola, ia harus dalam status entitas yang benar.
Untuk memahami transisi status JPA dengan lebih baik, Anda dapat memvisualisasikan diagram berikut:
Atau jika Anda menggunakan API khusus Hibernate:
Seperti diilustrasikan oleh diagram di atas, suatu entitas dapat berada di salah satu dari empat negara berikut:
Baru (sementara)
Objek yang baru dibuat yang belum pernah dikaitkan dengan Hibernate
Session
(aliasPersistence Context
) dan tidak dipetakan ke baris tabel database 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 paling bertanggung jawab terakhir, selama waktu
Session
flush saat ini.Terpisah
Setelah Konteks Persistensi berjalan saat ini ditutup semua entitas yang sebelumnya dikelola menjadi terpisah. Perubahan yang berurutan tidak akan lagi dilacak dan tidak ada sinkronisasi basis data otomatis yang akan terjadi.
Untuk mengaitkan entitas yang terlepas ke Sesi Hibernasi aktif, Anda dapat memilih salah satu opsi berikut:
Pemasangan kembali
Hibernate (tapi bukan JPA 2.1) mendukung pemasangan kembali melalui metode pembaruan Session #. Sesi Hibernate hanya dapat mengaitkan satu objek Entitas untuk baris database yang diberikan. Ini karena Konteks Persistensi bertindak sebagai cache dalam 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 Sesi Hibernasi saat ini.
Penggabungan
Penggabungan akan menyalin status entitas terpisah (sumber) ke instance entitas terkelola (tujuan). Jika entitas penggabungan tidak memiliki yang setara dalam Sesi saat ini, satu akan diambil dari database. Contoh objek yang dilepaskan akan terus tetap terlepas bahkan setelah operasi gabungan.
Dihapus
Meskipun JPA menuntut agar entitas yang dikelola hanya diizinkan untuk dihapus, Hibernate juga dapat menghapus entitas yang terpisah (tetapi hanya melalui panggilan metode penghapusan Sesi #). Entitas yang dihapus hanya dijadwalkan untuk dihapus dan pernyataan DELETE database aktual akan dieksekusi selama Sesi flush-time.
sumber
Saya perhatikan bahwa ketika saya menggunakan
em.merge
, saya mendapatSELECT
pernyataan untuk setiapINSERT
, bahkan ketika tidak ada bidang yang dihasilkan JPA untuk saya - bidang kunci utama adalah UUID yang saya tetapkan sendiri. Saya beralih keem.persist(myEntityObject)
dan mendapatINSERT
pernyataan saat itu.sumber
merge()
. Saya memiliki database PostgreSQL dengan tampilan rumit : tampilan agregat data dari beberapa tabel (tabel memiliki struktur yang identik tetapi nama yang berbeda). Jadi JPA coba lakukanmerge()
, tetapi sebenarnya JPA pertama kali dibuatSELECT
(database karena pengaturan tampilan dapat mengembalikan beberapa catatan dengan kunci primer yang sama dari tabel yang berbeda!), Kemudian JPA (Hibernate adalah implementasi) gagal: ada beberapa catatan dengan kunci yang sama (org.hibernate.HibernateException: More than one row with the given identifier was found
). Dalam kasus sayapersist()
membantu saya.Spesifikasi JPA mengatakan hal berikut tentang
persist()
.Jadi menggunakan
persist()
akan cocok ketika objek tidak harus menjadi objek yang terpisah. Anda mungkin lebih suka kode dilemparkanPersistenceException
sehingga gagal cepat.Meskipun spesifikasinya tidak jelas ,
persist()
mungkin mengatur@GeneratedValue
@Id
untuk objek.merge()
Namun harus memiliki objek dengan yang@Id
sudah dibuat.sumber
merge()
namun harus memiliki objek dengan yang@Id
sudah dibuat . " Setiap kali EntityManager tidak menemukan nilai untuk bidang ID objek, itu tetap (dimasukkan) ke dalam DB.Beberapa detail lebih lanjut tentang penggabungan yang akan membantu Anda menggunakan penggabungan bertahan:
Semua informasi di atas diambil dari "Pro JPA 2 Mastering the Java ™ Persistence API" oleh Mike Keith dan Merrick Schnicariol. Bab 6. Bagian detasemen dan penggabungan. Buku ini sebenarnya adalah buku kedua yang dikhususkan untuk JPA oleh penulis. Buku baru ini memiliki banyak informasi baru dari yang sebelumnya. Saya benar-benar merekomendasikan untuk membaca buku ini untuk orang-orang yang akan terlibat secara serius dengan JPA. Saya minta maaf karena secara anonim mengirimkan jawaban pertama saya.
sumber
Ada beberapa perbedaan lagi di antara
merge
danpersist
(saya akan sebutkan lagi yang sudah diposting di sini):D1.
merge
tidak membuat entitas yang lulus dikelola, melainkan mengembalikan contoh lain yang dikelola.persist
di sisi lain akan membuat entitas yang lulus dikelola:D2. Jika Anda menghapus suatu entitas dan kemudian memutuskan untuk bertahan kembali, Anda dapat melakukannya hanya dengan bertahan (), karena
merge
akan melemparIllegalArgumentException
.D3. Jika Anda memutuskan untuk mengurus ID Anda secara manual (misalnya dengan menggunakan UUID), maka suatu
merge
operasi akan memicuSELECT
kueri berikutnya untuk mencari entitas yang ada dengan ID itu, sementarapersist
mungkin tidak memerlukan kueri tersebut.D4. Ada beberapa kasus ketika Anda tidak mempercayai kode yang memanggil kode Anda, dan untuk memastikan tidak ada data yang diperbarui, melainkan dimasukkan, Anda harus menggunakan
persist
.sumber
Saya mendapatkan pengecualian lazyMemuat pada entitas saya karena saya mencoba mengakses koleksi malas yang ada di sesi.
Apa yang akan saya lakukan adalah dalam permintaan terpisah, mengambil entitas dari sesi dan kemudian mencoba mengakses koleksi di halaman jsp saya yang bermasalah.
Untuk meringankan ini, saya memperbarui entitas yang sama di controller saya dan meneruskannya ke jsp saya, meskipun saya bayangkan ketika saya disimpan kembali dalam sesi itu juga akan dapat diakses
SessionScope
dan tidak membuangLazyLoadingException
, modifikasi contoh 2:Yang berikut ini berhasil bagi saya:
sumber
Saya menemukan penjelasan ini dari dokumen Hibernate yang mencerahkan, karena mengandung kasus penggunaan:
Dari: http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/objectstate.html
sumber
Menjelajahi jawaban ada beberapa detail yang hilang tentang `Cascade 'dan pembuatan id. Lihat pertanyaan
Juga, perlu disebutkan bahwa Anda dapat memiliki
Cascade
anotasi terpisah untuk digabungkan dan bertahan:Cascade.MERGE
danCascade.PERSIST
yang akan diperlakukan sesuai dengan metode yang digunakan.Speknya adalah teman Anda;)
sumber
Entitas yang bertahan lama
Berbeda dengan metode penggabungan, metode bertahan ini cukup mudah dan intuitif. Skenario yang paling umum dari penggunaan metode bertahan dapat diringkas sebagai berikut:
"Sebuah instance yang baru dibuat dari kelas entitas diteruskan ke metode bertahan. Setelah metode ini kembali, entitas dikelola dan direncanakan untuk dimasukkan ke dalam database. Ini dapat terjadi pada atau sebelum transaksi dilakukan atau ketika metode flush dipanggil. Jika entitas merujuk entitas lain melalui hubungan yang ditandai dengan strategi kaskade PERSIST, prosedur ini juga berlaku untuknya. "
Spesifikasi lebih ke rincian, namun mengingatnya tidak penting karena rincian ini mencakup situasi yang kurang lebih eksotis saja.
Menggabungkan entitas
Dibandingkan dengan bertahan, deskripsi perilaku gabungan tidak begitu sederhana. Tidak ada skenario utama, seperti dalam kasus bertahan, dan seorang programmer harus mengingat semua skenario untuk menulis kode yang benar. Tampak bagi saya bahwa desainer JPA ingin memiliki beberapa metode yang perhatian utamanya akan menangani entitas terpisah (sebagai kebalikan dari metode bertahan yang berhubungan dengan entitas yang baru dibuat terutama.) Tugas utama metode penggabungan adalah untuk mentransfer negara dari suatu entitas yang tidak dikelola (diteruskan sebagai argumen) ke mitra yang dikelola dalam konteks persistensi. Namun, tugas ini membagi lebih jauh ke dalam beberapa skenario yang memperburuk kejelasan perilaku keseluruhan metode.
Alih-alih mengulangi paragraf dari spesifikasi JPA saya telah menyiapkan diagram alir yang secara skematis menggambarkan perilaku metode gabungan:
Jadi, kapan saya harus menggunakan bertahan dan kapan bergabung?
bertahan
menggabungkan
sumber
Skenario X:
Tabel: Spitter (Satu), Tabel: Spittles (Banyak) (Spittles adalah Pemilik hubungan dengan FK: spitter_id)
Skenario ini menghasilkan penghematan: The Spitter dan kedua Spittles seolah-olah dimiliki oleh Same Spitter.
Skenario Y:
Ini akan menyelamatkan Spitter, akan menyelamatkan 2 Spittles Tetapi mereka tidak akan merujuk Spitter yang sama!
sumber
Pengamatan lain:
merge()
hanya akan peduli dengan id yang dibuat secara otomatis (diuji padaIDENTITY
danSEQUENCE
) ketika catatan dengan id tersebut sudah ada di tabel Anda. Dalam hal inimerge()
akan mencoba memperbarui catatan. Namun, jika id tidak ada atau tidak cocok dengan catatan yang ada,merge()
akan sepenuhnya mengabaikannya dan meminta db untuk mengalokasikan yang baru. Ini terkadang merupakan sumber dari banyak bug. Jangan gunakanmerge()
untuk memaksa id untuk catatan baru.persist()
di sisi lain tidak akan pernah membiarkan Anda memberikan id padanya. Ini akan segera gagal. Dalam kasus saya, ini:hibernate-jpa javadoc memiliki petunjuk:
sumber
persist()
tidak akan mengeluh bahwa ia memiliki ID, itu hanya mengeluh ketika sesuatu dengan ID yang sama sudah ada dalam database.Anda mungkin datang ke sini untuk meminta nasihat tentang kapan harus menggunakan bertahan dan kapan harus menggunakan gabungan . Saya pikir itu tergantung situasinya: seberapa mungkin Anda perlu membuat catatan baru dan seberapa sulit untuk mengambil data yang ada.
Mari kita anggap Anda dapat menggunakan kunci alami / pengidentifikasi.
Data perlu dipertahankan, tetapi sesekali catatan ada dan pembaruan diperlukan. Dalam hal ini Anda bisa mencoba bertahan dan jika itu melempar EntityExistsException, Anda mencarinya dan menggabungkan data:
coba {entitasManager.persist (entitas)}
catch (pengecualian EntityExistsException) {/ * mengambil dan menggabungkan * /}
Data yang bertahan perlu diperbarui, tetapi sesekali tidak ada catatan untuk data tersebut. Dalam hal ini Anda mencarinya, dan lakukan terus jika entitas hilang:
entitas = entitasManager.find (kunci);
if (entitas == null) {entityManager.persist (entitas); }
lain {/ * gabung * /}
Jika Anda tidak memiliki kunci alami / pengidentifikasi, Anda akan memiliki waktu lebih sulit untuk mengetahui apakah entitas itu ada atau tidak, atau bagaimana cara mencarinya.
Gabungan dapat ditangani dengan dua cara, juga:
sumber
persist (entitas) harus digunakan dengan entitas yang benar-benar baru, untuk menambahkannya ke DB (jika entitas sudah ada di DB akan ada lemparan EntityExistsException).
penggabungan (entitas) harus digunakan, untuk mengembalikan entitas ke konteks persistensi jika entitas dilepaskan dan diubah.
Mungkin tetap menghasilkan pernyataan INSERT sql dan menggabungkan pernyataan UPDATE sql (tapi saya tidak yakin).
sumber