Saya telah menemukan situasi (yang menurut saya aneh tetapi mungkin cukup normal) di mana saya menggunakan EntityManager.getReference (LObj.getClass (), LObj.getId ()) untuk mendapatkan entitas database dan kemudian meneruskan objek yang dikembalikan ke dipertahankan di tabel lain.
Jadi pada dasarnya alirannya seperti ini:
class TFacade { createT (FObj, AObj) { T TObj = T baru (); TObj.setF (FObj); TObj.setA (AObj); ... EntityManager.persist (TObj); ... L LObj = A. getL (); FObj.setL (LObj); FFacade.editF (FObj); } } @ TransactionAttributeType.REQUIRES_NEW class FFacade { editF (FObj) { L LObj = FObj.getL (); LObj = EntityManager.getReference (LObj.getClass (), LObj.getId ()); ... EntityManager.merge (FObj); ... FLHFacade.create (FObj, LObj); } } @ TransactionAttributeType.REQUIRED class FLHFacade { createFLH (FObj, LObj) { FLH FLHObj = FLH baru (); FLHObj.setF (FObj); FLHObj.setL (LObj); .... EntityManager.persist (FLHObj); ... } }
Saya mendapatkan pengecualian berikut "java.lang.IllegalArgumentException: Entitas tidak diketahui: com.my.persistence.L $$ EnhancerByCGLIB $$ 3e7987d0"
Setelah memeriksanya beberapa saat, saya akhirnya menemukan bahwa itu karena saya menggunakan metode EntityManager.getReference () sehingga saya mendapatkan pengecualian di atas karena metode tersebut mengembalikan proxy.
Hal ini membuat saya bertanya-tanya, kapan sebaiknya menggunakan metode EntityManager.getReference () daripada metode EntityManager.find () ?
EntityManager.getReference () melontarkan EntityNotFoundException jika tidak dapat menemukan entitas yang sedang dicari yang sangat nyaman untuk dirinya sendiri. Metode EntityManager.find () hanya mengembalikan null jika tidak dapat menemukan entitas.
Berkenaan dengan batasan transaksi, bagi saya sepertinya Anda perlu menggunakan metode find () sebelum meneruskan entitas yang baru ditemukan ke transaksi baru. Jika Anda menggunakan metode getReference () maka Anda mungkin akan berakhir dalam situasi yang mirip dengan saya dengan pengecualian di atas.
sumber
Jawaban:
Saya biasanya menggunakan metode getReference ketika saya tidak perlu mengakses status database (maksud saya metode pengambil). Hanya untuk mengubah keadaan (maksud saya metode penyetel). Seperti yang Anda ketahui, getReference mengembalikan objek proxy yang menggunakan fitur canggih yang disebut pemeriksaan kotor otomatis. Misalkan berikut ini
Jika saya memanggil metode find , penyedia JPA, di belakang layar, akan memanggil
Jika saya memanggil metode getReference , penyedia JPA, di belakang layar, akan memanggil
Dan kamu tahu kenapa ???
Saat Anda memanggil getReference, Anda akan mendapatkan objek proxy. Sesuatu seperti ini (penyedia JPA menangani penerapan proxy ini)
Jadi sebelum melakukan transaksi, penyedia JPA akan melihat bendera stateChanged untuk memperbarui ATAU BUKAN entitas orang. Jika tidak ada baris yang diperbarui setelah pernyataan update, penyedia JPA akan menampilkan EntityNotFoundException sesuai dengan spesifikasi JPA.
salam,
sumber
SELECT
sebelumnyaUPDATE
, tidak peduli yang mana darifind()
/getReference()
saya gunakan. Yang lebih buruk,SELECT
melintasi hubungan NON-LAZY (mengeluarkan yang baruSELECTS
), meskipun saya hanya ingin memperbarui satu bidang dalam satu entitas.Seperti yang saya jelaskan di artikel ini , dengan asumsi Anda memiliki
Post
entitas induk dan anakPostComment
seperti yang diilustrasikan dalam diagram berikut:Jika Anda menelepon
find
ketika Anda mencoba untuk mengatur@ManyToOne
post
asosiasi:Hibernate akan menjalankan pernyataan berikut:
Query SELECT tidak berguna kali ini karena kita tidak membutuhkan entitas Post untuk diambil. Kami hanya ingin mengatur kolom Kunci Asing post_id yang mendasarinya.
Sekarang, jika Anda menggunakan
getReference
:Kali ini, Hibernate hanya akan mengeluarkan pernyataan INSERT:
Berbeda
find
dengangetReference
hanya mengembalikan proxy entitas yang hanya memiliki set pengenal. Jika Anda mengakses Proxy, pernyataan SQL terkait akan dipicu selama EntityManager masih terbuka.Namun, dalam kasus ini, kami tidak perlu mengakses proxy entitas. Kami hanya ingin menyebarkan Kunci Asing ke rekaman tabel yang mendasari sehingga memuat Proxy sudah cukup untuk kasus penggunaan ini.
Saat memuat Proxy, Anda perlu menyadari bahwa LazyInitializationException dapat muncul jika Anda mencoba mengakses referensi Proxy setelah EntityManager ditutup. Untuk detail lebih lanjut tentang penanganan
LazyInitializationException
, lihat artikel ini .sumber
hibernate.jpa.compliance.proxy
properti konfigurasi , sehingga Anda dapat memilih kepatuhan JPA atau kinerja akses data yang lebih baik.getReference
diperlukan sama sekali jika itu cukup untuk menetapkan contoh model baru dengan set PK. Apa yang saya lewatkan?Karena referensi 'dikelola', tetapi tidak terhidrasi, referensi juga memungkinkan Anda untuk menghapus entitas berdasarkan ID, tanpa perlu memuatnya ke dalam memori terlebih dahulu.
Karena Anda tidak dapat menghapus entitas yang tidak dikelola, itu konyol untuk memuat semua bidang menggunakan find (...) atau createQuery (...), hanya untuk segera menghapusnya.
sumber
EntityManager.getReference()
benar-benar merupakan metode rawan kesalahan dan hanya ada sedikit kasus di mana kode klien perlu menggunakannya.Secara pribadi, saya tidak pernah perlu menggunakannya.
EntityManager.getReference () dan EntityManager.find (): tidak ada perbedaan dalam hal overhead
Saya tidak setuju dengan jawaban yang diterima dan khususnya:
Ini bukan perilaku yang saya dapatkan dengan Hibernate 5 dan javadoc
getReference()
tidak mengatakan hal seperti itu:EntityManager.getReference()
menyimpan kueri untuk mengambil entitas dalam dua kasus:1) jika entitas disimpan dalam konteks Ketekunan, itu adalah cache tingkat pertama.
Dan perilaku ini tidak khusus untuk
EntityManager.getReference()
,EntityManager.find()
juga akan menyimpan kueri untuk mengambil entitas jika entitas disimpan dalam konteks Persistensi.Anda dapat memeriksa poin pertama dengan contoh apa pun.
Anda juga dapat mengandalkan implementasi Hibernate yang sebenarnya.
Memang,
EntityManager.getReference()
bergantung padacreateProxyIfNecessary()
metodeorg.hibernate.event.internal.DefaultLoadEventListener
kelas untuk memuat entitas.Berikut implementasinya:
Bagian yang menarik adalah:
2) Jika kita tidak secara efektif memanipulasi entitas, menggemakan javadoc yang diambil dengan malas .
Memang, untuk memastikan pemuatan yang efektif dari entitas, penerapan metode diperlukan.
Jadi, keuntungannya akan terkait dengan skenario di mana kita ingin memuat entitas tanpa perlu menggunakannya? Dalam kerangka aplikasi, kebutuhan ini sangat jarang dan selain itu
getReference()
perilakunya juga sangat menyesatkan jika Anda membaca bagian selanjutnya.Mengapa lebih menyukai EntityManager.find () daripada EntityManager.getReference ()
Dari segi overhead,
getReference()
memang tidak lebih baik dari yangfind()
dibahas pada poin sebelumnya.Jadi mengapa menggunakan yang satu atau yang lainnya?
Memanggil
getReference()
dapat mengembalikan entitas yang diambil dengan lambat.Di sini, lazy fetching tidak merujuk pada hubungan entitas tetapi entitas itu sendiri.
Artinya jika kita memanggil
getReference()
dan kemudian konteks Persistence ditutup, entitas mungkin tidak pernah dimuat dan hasilnya benar-benar tidak dapat diprediksi. Misalnya jika objek proxy berseri, Anda bisa mendapatkannull
referensi sebagai hasil berseri atau jika metode dipanggil pada objek proxy, pengecualian sepertiLazyInitializationException
dilempar.Artinya lemparan
EntityNotFoundException
itu adalah alasan utama penggunaangetReference()
untuk menangani contoh yang tidak ada dalam database karena situasi kesalahan mungkin tidak pernah dilakukan saat entitas tidak ada.EntityManager.find()
tidak memiliki ambisi untuk melemparEntityNotFoundException
jika entitas tidak ditemukan. Perilakunya sederhana dan jelas. Anda tidak akan terkejut karena selalu mengembalikan entitas yang dimuat ataunull
(jika entitas tidak ditemukan) tetapi tidak pernah entitas di bawah bentuk proxy yang mungkin tidak dimuat secara efektif.Jadi
EntityManager.find()
harus disukai di sebagian besar kasus.sumber
Saya tidak setuju dengan jawaban yang dipilih, dan seperti yang ditunjukkan davidxxx dengan benar, getReference tidak menyediakan perilaku pembaruan dinamis tanpa pemilihan. Saya mengajukan pertanyaan tentang validitas jawaban ini, lihat di sini - tidak dapat memperbarui tanpa memilih menggunakan penyetel setelah getReference () dari hibernate JPA .
Sejujurnya saya belum melihat siapa pun yang benar-benar menggunakan fungsi itu. DI MANA SAJA. Dan saya tidak mengerti mengapa itu sangat disukai.
Sekarang pertama-tama, apa pun yang Anda panggil pada objek proxy hibernasi, penyetel atau pengambil, SQL dijalankan dan objek dimuat.
Tapi kemudian saya berpikir, jadi bagaimana jika JPA getReference () proxy tidak menyediakan fungsionalitas itu. Saya bisa menulis proxy saya sendiri.
Sekarang, kita semua dapat berargumen bahwa pemilihan pada kunci primer secepat yang bisa didapat kueri dan itu bukan sesuatu yang harus dihindari. Namun bagi kita yang tidak bisa mengatasinya karena satu dan lain hal, di bawah ini adalah implementasi dari proxy tersebut. Tetapi sebelum saya Anda melihat implementasinya, lihat penggunaannya dan betapa sederhananya penggunaannya.
PEMAKAIAN
Dan ini akan mengaktifkan kueri berikut -
dan bahkan jika Anda ingin memasukkan, Anda masih bisa melakukan PersistenceService.save (new Order ("a", 2)); dan itu akan mengaktifkan sisipan sebagaimana mestinya.
PENERAPAN
Tambahkan ini ke pom.xml Anda -
Jadikan kelas ini untuk membuat proxy dinamis -
Buat antarmuka dengan semua metode -
Sekarang, buat interseptor yang memungkinkan Anda menerapkan metode ini pada proxy Anda -
Dan kelas pengecualian -
Layanan untuk menyimpan menggunakan proxy ini -
sumber