Hibernate melempar pengecualian ini selama pembuatan SessionFactory:
org.hibernate.loader.MultipleBagFetchException: tidak dapat secara bersamaan mengambil beberapa tas
Ini adalah ujian saya:
Parent.java
@Entity
public Parent {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
// @IndexColumn(name="INDEX_COL") if I had this the problem solve but I retrieve more children than I have, one child is null.
private List<Child> children;
}
Child.java
@Entity
public Child {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@ManyToOne
private Parent parent;
}
Bagaimana dengan masalah ini? Apa yang dapat saya?
EDIT
OK, masalah yang saya miliki adalah entitas "induk" lain ada di dalam orangtua saya, perilaku saya yang sebenarnya adalah ini:
Parent.java
@Entity
public Parent {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@ManyToOne
private AnotherParent anotherParent;
@OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
private List<Child> children;
}
AnotherParent.java
@Entity
public AnotherParent {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
private List<AnotherChild> anotherChildren;
}
Hibernate tidak suka dengan dua koleksi FetchType.EAGER
, tapi ini sepertinya bug, saya tidak melakukan hal-hal yang tidak biasa ...
Menghapus FetchType.EAGER
dari Parent
atau AnotherParent
memecahkan masalah, tapi saya membutuhkannya, jadi solusi sebenarnya adalah dengan menggunakan @LazyCollection(LazyCollectionOption.FALSE)
alih-alih FetchType
(terima kasih kepada Bozho untuk solusinya).
select * from master; select * from child1 where master_id = :master_id; select * from child2 where master_id = :master_id
List<child>
denganfetchType
didefinisikan lebih dari satuList<clield>
Jawaban:
Saya pikir versi hibernate yang lebih baru (mendukung JPA 2.0) harus menangani ini. Tetapi jika tidak, Anda dapat mengatasinya dengan menganotasi bidang koleksi dengan:
Ingatlah untuk menghapus
fetchType
atribut dari@*ToMany
anotasi.Tetapi perhatikan bahwa dalam kebanyakan kasus a
Set<Child>
lebih tepat daripadaList<Child>
, jadi kecuali Anda benarList
- benar membutuhkanSet
Tetapi ingatkan bahwa dengan menggunakan set Anda tidak akan menghilangkan Produk Cartesian yang mendasari seperti yang dijelaskan oleh Vlad Mihalcea dalam jawabannya !
sumber
fetchType
dari@*ToMany
?Cukup ubah dari
List
tipe keSet
tipe.Tetapi ingatkan bahwa Anda tidak akan menghilangkan Produk Cartesian yang mendasari sebagaimana dijelaskan oleh Vlad Mihalcea dalam jawabannya !
sumber
*ToMany
. Mengubah tipe untukSet
menyelesaikan masalah saya juga. Solusi yang sangat baik dan rapi. Ini harus menjadi jawaban resmi.Tambahkan anotasi @Fetch khusus Hibernate ke kode Anda:
Ini harus memperbaiki masalah, terkait dengan bug Hibernate HHH-1718
sumber
Set
benar - benar masuk akal. MemilikiOneToMany
hubungan tunggal menggunakanSet
hasil dalam1+<# relationships>
kueri, di mana seperti menggunakanFetchMode.SUBSELECT
hasil dalam1+1
kueri. Selain itu, menggunakan anotasi dalam jawaban yang diterima (LazyCollectionOption.FALSE
) menyebabkan lebih banyak kueri dieksekusi.Mengingat kami memiliki entitas berikut:
Dan, Anda ingin mengambil beberapa
Post
entitas induk beserta semuacomments
dantags
koleksi.Jika Anda menggunakan lebih dari satu
JOIN FETCH
arahan:Hibernate akan melempar yang terkenal:
Hibernate tidak memungkinkan mengambil lebih dari satu tas karena itu akan menghasilkan produk Cartesian .
"Solusi" terburuk
Sekarang, Anda akan menemukan banyak jawaban, posting blog, video, atau sumber daya lain yang memberi tahu Anda untuk menggunakan
Set
alih - alihList
untuk koleksi Anda.Itu saran yang mengerikan. Jangan lakukan itu!
Menggunakan
Sets
bukannyaLists
akan membuatMultipleBagFetchException
pergi, tetapi Produk Cartesian masih akan ada, yang sebenarnya lebih buruk, karena Anda akan mengetahui masalah kinerja lama setelah Anda menerapkan "perbaikan" ini.Solusi yang tepat
Anda dapat melakukan trik berikut:
Selama Anda mengambil paling banyak satu koleksi menggunakan
JOIN FETCH
, Anda akan baik-baik saja.Dengan menggunakan beberapa kueri, Anda akan menghindari Produk Cartesian karena koleksi lain tetapi yang pertama diambil menggunakan kueri sekunder.
Masih banyak yang bisa Anda lakukan
Jika Anda menggunakan
FetchType.EAGER
strategi pada waktu pemetaan untuk@OneToMany
atau@ManyToMany
asosiasi, maka Anda dapat dengan mudah berakhir denganMultipleBagFetchException
.Anda lebih baik beralih dari
FetchType.EAGER
keFetchype.LAZY
karena bersemangat mengambil adalah ide yang buruk yang dapat menyebabkan masalah kinerja aplikasi kritis .Kesimpulan
Hindari
FetchType.EAGER
dan tidak beralih dariList
keSet
hanya karena hal itu akan membuat Hibernate menyembunyikanMultipleBagFetchException
bawah karpet. Ambil hanya satu koleksi sekaligus, dan Anda akan baik-baik saja.Selama Anda melakukannya dengan jumlah kueri yang sama seperti Anda memiliki koleksi untuk diinisialisasi, Anda baik-baik saja. Hanya saja, jangan menginisialisasi koleksi dalam satu lingkaran, karena itu akan memicu masalah permintaan N + 1 , yang juga buruk untuk kinerja.
sumber
DISTINCT
adalah pembunuh kinerja dalam solusi ini. Apakah ada cara untuk menyingkirkannyadistinct
? (Set<...>
Sebaliknya mencoba untuk kembali , tidak banyak membantu)PASS_DISTINCT_THROUGH
diatur kefalse
. DISTINCT memiliki 2 arti dalam JPQL, dan di sini, kami membutuhkannya untuk dideduplikasi di sisi Java, bukan di sisi SQL. Lihat artikel ini untuk lebih jelasnya.hibernate.jdbc.fetch_size
(akhirnya saya atur ke 350). Secara kebetulan, apakah Anda tahu cara mengoptimalkan hubungan bersarang? Misalnya entitas1 -> entitas2 -> entitas3.1, entitas 3.2 (di mana entitas3.1 / 3.2 adalah hubungan @OneToMany)Setelah mencoba dengan setiap opsi yang dijelaskan dalam posting ini dan lainnya, saya sampai pada kesimpulan bahwa perbaikannya adalah sebagai berikut.
Di setiap tempat XToMany @
XXXToMany(mappedBy="parent", fetch=FetchType.EAGER)
dan setelah menengahIni berhasil untuk saya
sumber
@Fetch(value = FetchMode.SUBSELECT)
sudah cukupUntuk memperbaikinya hanya mengambil
Set
di tempatList
untuk objek bersarang Anda.dan jangan lupa gunakan
fetch=FetchType.EAGER
ini akan bekerja.
Ada satu konsep lagi
CollectionId
di Hibernate jika Anda ingin tetap menggunakan daftar saja.Tetapi ingatkan bahwa Anda tidak akan menghilangkan Produk Cartesian yang mendasari sebagaimana dijelaskan oleh Vlad Mihalcea dalam jawabannya !
sumber
Saya menemukan posting blog yang bagus tentang perilaku Hibernate dalam pemetaan objek semacam ini: http://blog.eyallupu.com/2010/06/hibernate-exception-simultaneously.html
sumber
Anda dapat menyimpan daftar booth EAGER di JPA dan menambahkan setidaknya satu di antaranya anotasi JPA @OrderColumn (dengan jelas nama bidang yang akan dipesan). Tidak perlu penjelasan hibernasi khusus. Tetapi perlu diingat itu bisa membuat elemen kosong dalam daftar jika bidang yang dipilih tidak memiliki nilai mulai dari 0
pada Children maka Anda harus menambahkan bidang orderIndex
sumber
Kami mencoba Set bukannya Daftar dan itu adalah mimpi buruk: ketika Anda menambahkan dua objek baru, equals () dan hashCode () gagal membedakan keduanya! Karena mereka tidak memiliki id.
alat khas seperti Eclipse menghasilkan kode semacam itu dari tabel Database:
Anda juga dapat membaca artikel ini yang menjelaskan dengan benar seberapa kacau JPA / Hibernate. Setelah membaca ini, saya pikir ini adalah kali terakhir saya menggunakan ORM dalam hidup saya.
Saya juga bertemu dengan Domain Driven Design yang pada dasarnya mengatakan ORM adalah hal yang mengerikan.
sumber
Ketika Anda memiliki objek yang terlalu kompleks dengan koleksi saveral bukan ide yang baik untuk memiliki semuanya dengan EAGER fetchType, lebih baik gunakan LAZY dan ketika Anda benar-benar perlu memuat koleksi gunakan:
Hibernate.initialize(parent.child)
untuk mengambil data.sumber
Bagi saya, masalahnya adalah EAGER bersarang mengambil .
Salah satu solusinya adalah mengatur bidang bersarang ke LAZY dan menggunakan Hibernate.initialize () untuk memuat bidang bersarang:
sumber
Pada akhirnya, ini terjadi ketika saya memiliki beberapa koleksi dengan FetchType.EAGER, seperti ini:
Selain itu, koleksi bergabung di kolom yang sama.
Untuk mengatasi masalah ini, saya mengubah salah satu koleksi menjadi FetchType.LAZY karena tidak masalah untuk use case saya.
Semoga berhasil! ~ J
sumber
Mengomentari keduanya
Fetch
danLazyCollection
terkadang membantu menjalankan proyek.sumber
Satu hal yang baik tentang itu
@LazyCollection(LazyCollectionOption.FALSE)
adalah bahwa beberapa bidang dengan anotasi ini dapat hidup berdampingan sementaraFetchType.EAGER
tidak bisa, bahkan dalam situasi di mana koeksistensi seperti itu sah.Sebagai contoh, suatu
Order
mungkin memiliki daftarOrderGroup
(yang pendek) serta daftarPromotions
(juga pendek).@LazyCollection(LazyCollectionOption.FALSE)
dapat digunakan pada keduanya tanpa menyebabkanLazyInitializationException
keduanyaMultipleBagFetchException
.Dalam kasus saya
@Fetch
memang memecahkan masalah sayaMultipleBacFetchException
tetapi kemudian menyebabkanLazyInitializationException
,no Session
kesalahan terkenal .sumber
Anda dapat menggunakan anotasi baru untuk menyelesaikan ini:
Bahkan, nilai default fetch adalah FetchType.LAZY juga.
sumber