Saya memiliki kasus penggunaan yang disebut sebagai berikut:
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public UserControl getUserControlById(Integer id){
return this.userControlRepository.getOne(id);
}
Amati Propagation yang@Transactional
dimiliki.REQUIRES_NEW dan repositori menggunakan getOne . Ketika saya menjalankan aplikasi, saya menerima pesan kesalahan berikut:
Exception in thread "main" org.hibernate.LazyInitializationException:
could not initialize proxy - no Session
...
Tetapi jika saya mengubah getOne(id)
dengan findOne(id)
semua bekerja dengan baik.
BTW, tepat sebelum use case memanggil metode getUserControlById , sudah disebut metode insertUserControl
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public UserControl insertUserControl(UserControl userControl) {
return this.userControlRepository.save(userControl);
}
Kedua metode adalah Propagation.REQUIRES_NEW karena saya melakukan kontrol audit yang sederhana .
Saya menggunakan getOne
metode ini karena didefinisikan dalam antarmuka JpaRepository dan antarmuka Repositori saya memanjang dari sana, saya bekerja dengan JPA tentu saja.
The JpaRepository antarmuka meluas dari CrudRepository . The findOne(id)
metode didefinisikan dalam CrudRepository
.
Pertanyaan saya adalah:
- Mengapa gagal
getOne(id)
metode ini? - Kapan saya harus menggunakan
getOne(id)
metode ini?
Saya bekerja dengan repositori lain dan semua menggunakan getOne(id)
metode dan semua berfungsi dengan baik, hanya ketika saya menggunakan Propagasi . REQUIRES_NEW gagal.
Menurut dengan getOne API:
Mengembalikan referensi ke entitas dengan pengidentifikasi yang diberikan.
Menurut dengan findOne API:
Mengambil entitas dengan idnya.
3) Kapan saya harus menggunakan findOne(id)
metode ini?
4) Metode apa yang direkomendasikan untuk digunakan?
Terima kasih sebelumnya.
sumber
Jawaban:
TL; DR
T findOne(ID id)
(nama di API lama) /Optional<T> findById(ID id)
(nama di API baru) bergantung padaEntityManager.find()
yang melakukan pemuatan entitas yang bersemangat .T getOne(ID id)
bergantung padaEntityManager.getReference()
yang melakukan pemuatan entitas malas . Jadi untuk memastikan pemuatan entitas yang efektif, diperlukan metode untuk itu.findOne()/findById()
benar-benar lebih jelas dan mudah digunakan daripadagetOne()
.Jadi dalam sangat sebagian besar kasus, mendukung
findOne()/findById()
lebihgetOne()
.Perubahan API
Setidaknya dari
2.0
versi,Spring-Data-Jpa
dimodifikasifindOne()
.Sebelumnya, itu didefinisikan dalam
CrudRepository
antarmuka sebagai:Sekarang,
findOne()
metode tunggal yang akan Anda temukanCrudRepository
adalah yang didefinisikan dalamQueryByExampleExecutor
antarmuka sebagai:Yang akhirnya diimplementasikan oleh
SimpleJpaRepository
, implementasi standarCrudRepository
antarmuka.Metode ini adalah kueri dengan pencarian contoh dan Anda tidak ingin itu sebagai pengganti.
Faktanya, metode dengan perilaku yang sama masih ada di API baru tetapi nama metode telah berubah.
Itu diubah namanya dari
findOne()
menjadifindById()
diCrudRepository
antarmuka:Sekarang mengembalikan sebuah
Optional
. Yang tidak begitu buruk untuk dicegahNullPointerException
.Jadi, pilihan sebenarnya sekarang antara
Optional<T> findById(ID id)
danT getOne(ID id)
.Dua metode berbeda yang mengandalkan dua metode pengambilan EntityManager JPA yang berbeda
1)
Optional<T> findById(ID id)
Javadoc menyatakan bahwa:Ketika kita melihat implementasi, kita dapat melihat bahwa itu bergantung
EntityManager.find()
untuk melakukan pengambilan:Dan di sini
em.find()
adalah sebuahEntityManager
metode dinyatakan sebagai:Status javadoc-nya:
Jadi, mengambil entitas yang dimuat tampaknya diharapkan.
2) Sementara
T getOne(ID id)
javadoc menyatakan (penekanan adalah milikku):Faktanya, terminologi rujukan benar-benar papan dan JPA API tidak menentukan
getOne()
metode apa pun .Jadi hal terbaik yang harus dilakukan untuk memahami apa yang dilakukan pembungkus Spring adalah dengan melihat implementasinya:
Berikut
em.getReference()
adalahEntityManager
metode yang dinyatakan sebagai:Dan untungnya,
EntityManager
javadoc mendefinisikan dengan lebih baik niatnya (penekanan adalah milikku):Jadi, memohon
getOne()
dapat mengembalikan entitas yang diambil malas.Di sini, pengambilan malas tidak merujuk pada hubungan entitas tetapi entitas itu sendiri.
Ini berarti bahwa jika kita memanggil
getOne()
dan kemudian konteks Persistence ditutup, entitas mungkin tidak pernah dimuat dan hasilnya benar-benar tidak dapat diprediksi.Misalnya jika objek proxy serial, Anda bisa mendapatkan
null
referensi sebagai hasil serial atau jika metode dipanggil pada objek proxy, pengecualian sepertiLazyInitializationException
dilemparkan.Jadi dalam situasi seperti ini, lemparan
EntityNotFoundException
itu adalah alasan utama yang digunakangetOne()
untuk menangani sebuah instance yang tidak ada dalam database karena situasi kesalahan mungkin tidak pernah dilakukan ketika entitas tidak ada.Bagaimanapun, untuk memastikan pemuatannya, Anda harus memanipulasi entitas saat sesi dibuka. Anda dapat melakukannya dengan menggunakan metode apa pun pada entitas.
Atau penggunaan alternatif yang lebih baik
findById(ID id)
daripada.Mengapa API begitu tidak jelas?
Untuk menyelesaikan, dua pertanyaan untuk pengembang Spring-Data-JPA:
mengapa tidak memiliki dokumentasi yang lebih jelas
getOne()
? Entitas pemuatan malas sebenarnya bukan detail.mengapa Anda perlu memperkenalkan
getOne()
untuk membungkusEM.getReference()
?Mengapa tidak hanya menempel metode dibungkus:
getReference()
? Metode EM ini sangat khusus saatgetOne()
menyampaikan pemrosesan yang sangat sederhana.sumber
getOne()
menggunakan lazy loading, dan melemparEntityNotFoundException
jika tidak ada barang yang ditemukan.findById()
segera memuat, dan mengembalikan nol jika tidak ditemukan. Karena ada beberapa situasi yang tidak dapat diprediksi dengan getOne (), disarankan menggunakan findById () sebagai gantinya.Perbedaan mendasar adalah bahwa
getOne
malas dimuat danfindOne
tidak.Perhatikan contoh berikut:
sumber
1. Mengapa metode getOne (id) gagal?
Lihat bagian ini dalam dokumen . Anda menimpa transaksi yang sudah ada di tempat mungkin menyebabkan masalah. Namun, tanpa info lebih lanjut, yang ini sulit dijawab.
2. Kapan saya harus menggunakan metode getOne (id)?
Tanpa menggali ke dalam internal Spring Data JPA, perbedaannya tampaknya pada mekanisme yang digunakan untuk mengambil entitas.
Jika Anda melihat javadoc untuk
getOne(ID)
di bawah Lihat Juga :tampaknya metode ini hanya mendelegasikan ke implementasi manajer entitas JPA.
Namun, dokumen untuk
findOne(ID)
tidak menyebutkan ini.Petunjuknya juga atas nama repositori.
JpaRepository
khusus untuk JPA dan karenanya dapat mendelegasikan panggilan ke manajer entitas jika diperlukan.CrudRepository
adalah agnostik dari teknologi ketekunan yang digunakan. Lihat di sini . Ini digunakan sebagai antarmuka penanda untuk beberapa teknologi persistensi seperti JPA, Neo4J dll.Jadi sebenarnya tidak ada 'perbedaan' dalam dua metode untuk kasus penggunaan Anda, hanya saja
findOne(ID)
lebih umum daripada yang lebih khususgetOne(ID)
. Yang mana yang Anda gunakan terserah Anda dan proyek Anda, tetapi saya pribadi akan tetap menggunakannyafindOne(ID)
karena membuat kode Anda kurang spesifik implementasi dan membuka pintu untuk pindah ke hal-hal seperti MongoDB dll di masa depan tanpa terlalu banyak refactoring :)sumber
there's not really a 'difference' in the two methods
sini, karena memang ada perbedaan besar dalam bagaimana entitas diambil dan apa yang Anda harapkan metode untuk kembali. Jawaban lebih jauh ke bawah oleh @davidxxx menyoroti ini dengan sangat baik, dan saya pikir semua orang yang menggunakan Spring Data JPA harus mengetahui hal ini. Kalau tidak, itu bisa menyebabkan sakit kepala yang cukup.The
getOne
metode kembali hanya referensi dari DB (lazy loading). Jadi pada dasarnya Anda berada di luar transaksi (Transactional
Anda telah menyatakan dalam kelas layanan tidak dipertimbangkan), dan kesalahan terjadi.sumber
Saya benar-benar merasa sangat sulit dari jawaban di atas. Dari perspektif debugging saya hampir menghabiskan 8 jam untuk mengetahui kesalahan konyol.
Saya telah menguji pegas + hibernate + dozer + proyek Mysql. Agar jelas.
Saya memiliki entitas Pengguna, Entitas Buku. Anda melakukan perhitungan pemetaan.
Apakah Banyak Buku terikat dengan Satu pengguna. Tetapi dalam UserServiceImpl saya mencoba menemukannya dengan getOne (userId);
Hasil sisanya adalah
}
Kode di atas tidak mengambil buku yang dibaca oleh pengguna, katakan saja.
BookList selalu nol karena getOne (ID). Setelah mengubah ke findOne (ID). Hasilnya adalah
}
sumber
sementara spring.jpa.open-in-view benar, saya tidak punya masalah dengan getOne tapi setelah pengaturannya menjadi false, saya mendapat LazyInitializationException. Kemudian masalah diselesaikan dengan mengganti dengan findById.
Meskipun ada solusi lain tanpa mengganti metode getOne, dan itu adalah meletakkan @Transactional pada metode yang memanggil repository.getOne (id). Dengan cara ini transaksi akan ada dan sesi tidak akan ditutup dalam metode Anda dan saat menggunakan entitas tidak akan ada LazyInitializationException.
sumber
Saya memiliki masalah yang sama memahami mengapa JpaRespository.getOne (id) tidak berfungsi dan melempar kesalahan.
Saya pergi dan berganti ke JpaRespository.findById (id) yang mengharuskan Anda mengembalikan Opsional.
Ini mungkin komentar pertama saya di StackOverflow.
sumber