Hibernacja zgłasza ten wyjątek podczas tworzenia SessionFactory:
org.hibernate.loader.MultipleBagFetchException: nie można jednocześnie pobrać wielu torebek
To mój przypadek testowy:
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;
}
Co powiesz na ten problem? Co mogę zrobić?
EDYTOWAĆ
OK, mam problem z tym, że inny „rodzic” jest wewnątrz mojego rodzica, moje prawdziwe zachowanie jest takie:
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;
}
Hibernacja nie lubi dwóch kolekcji FetchType.EAGER
, ale wydaje się, że to błąd, nie robię niezwykłych rzeczy ...
Usuwanie FetchType.EAGER
z Parent
lub AnotherParent
rozwiązuje problem, ale muszę, więc prawdziwym rozwiązaniem jest użycie @LazyCollection(LazyCollectionOption.FALSE)
zamiast FetchType
(dzięki Bozho do roztworu).
select * from master; select * from child1 where master_id = :master_id; select * from child2 where master_id = :master_id
List<child>
zefetchType
zdefiniowanym dla więcej niż jednegoList<clield>
Odpowiedzi:
Myślę, że nowsza wersja hibernacji (obsługująca JPA 2.0) powinna sobie z tym poradzić. Ale w przeciwnym razie możesz to obejść, dodając adnotacje do pól kolekcji:
Pamiętaj, aby usunąć
fetchType
atrybut z@*ToMany
adnotacji.Ale zauważ, że w większości przypadków a
Set<Child>
jest bardziej odpowiednie niżList<Child>
, więc chyba, że naprawdę potrzebujeszList
- idź doSet
Przypomnij jednak, że używając zestawów nie wyeliminujesz podkładającego się produktu kartezjańskiego, jak opisał Vlad Mihalcea w swojej odpowiedzi !
źródło
fetchType
z@*ToMany
?Wystarczy zmienić
List
typ naSet
typ.Przypomnij jednak, że nie wyeliminujesz podkładającego się produktu kartezjańskiego, jak opisał Vlad Mihalcea w swojej odpowiedzi !
źródło
*ToMany
. Zmieniłem też typ, abySet
rozwiązać mój problem. Doskonałe i schludne rozwiązanie. To powinna być oficjalna odpowiedź.Dodaj adnotację @Fetch do swojego kodu:
To powinno rozwiązać problem związany z błędem Hibernacji HHH-1718
źródło
Set
naprawdę ma sens. Posiadanie pojedynczejOneToMany
relacji przy użyciuSet
wyników w1+<# relationships>
zapytaniach, gdzie jak przy użyciuFetchMode.SUBSELECT
wyników w1+1
zapytaniach. Ponadto użycie adnotacji w zaakceptowanej odpowiedzi (LazyCollectionOption.FALSE
) powoduje wykonanie jeszcze większej liczby zapytań.Biorąc pod uwagę, że mamy następujące podmioty:
I chcesz pobrać niektóre
Post
elementy nadrzędne wraz ze wszystkimi zbioramicomments
itags
.Jeśli używasz więcej niż jednej
JOIN FETCH
dyrektywy:Hibernacja rzuci niesławny:
Hibernacja nie pozwala na pobranie więcej niż jednej torby, ponieważ wygenerowałoby to produkt kartezjański .
Najgorsze „rozwiązanie”
Teraz znajdziesz wiele odpowiedzi, postów na blogu, filmów lub innych zasobów, w których możesz użyć
Set
zamiastList
swoich kolekcji.To okropna rada. Nie rób tego!
Użycie
Sets
zamiastLists
spowoduje, żeMultipleBagFetchException
zniknie, ale produkt kartezjański nadal tam będzie, co jest nawet gorsze, ponieważ problem z wydajnością dowiesz się długo po zastosowaniu tej „poprawki”.Właściwe rozwiązanie
Możesz wykonać następującą sztuczkę:
Tak długo, jak
JOIN FETCH
pobierzesz co najwyżej jedną kolekcję , nic ci nie będzie.Używając wielu zapytań, unikniesz produktu kartezjańskiego, ponieważ jakakolwiek inna kolekcja jest pobierana przy użyciu zapytania wtórnego.
Możesz zrobić więcej
Jeśli używasz
FetchType.EAGER
strategii w czasie mapowania dla stowarzyszeń@OneToMany
lub@ManyToMany
stowarzyszeń, możesz łatwo skończyć zMultipleBagFetchException
.Lepiej jest przełączać się z
FetchType.EAGER
na,Fetchype.LAZY
ponieważ chętne pobieranie jest okropnym pomysłem, który może prowadzić do krytycznych problemów z wydajnością aplikacji .Wniosek
Unikaj
FetchType.EAGER
i nie zmieniaj opcji zList
naSet
tylko dlatego, że spowoduje to, że Hibernacja ukryjeMultipleBagFetchException
pod dywan. Pobieraj tylko jedną kolekcję na raz, a wszystko będzie dobrze.Tak długo, jak robisz to z taką samą liczbą zapytań, jak masz kolekcje do zainicjowania, nic ci nie jest. Po prostu nie inicjuj kolekcji w pętli, ponieważ spowoduje to problemy z zapytaniami N + 1 , które również mają negatywny wpływ na wydajność.
źródło
DISTINCT
w tym rozwiązaniu zabija wydajność. Czy jest sposób na pozbycie siędistinct
? (próbowała wrócićSet<...>
zamiast, nie pomogło)PASS_DISTINCT_THROUGH
jest ustawiony nafalse
. DISTINCT ma 2 znaczenia w języku JPQL, a tutaj potrzebujemy go do deduplikacji po stronie Java, a nie po stronie SQL. Sprawdź ten artykuł, aby uzyskać więcej informacji.hibernate.jdbc.fetch_size
(ostatecznie ustawiłem go na 350). Czy wiesz, jak zoptymalizować relacje zagnieżdżone? Np. Byt1 -> byt2 -> byt3.1, byt 3.2 (gdzie byt3.1 / 3.2 są relacjami @OneToMany)Po wypróbowaniu każdej opcji opisanej w tych postach i innych, doszedłem do wniosku, że poprawka jest następująca.
W każdym miejscu XToMany @
XXXToMany(mappedBy="parent", fetch=FetchType.EAGER)
i bezpośrednio po nimTo zadziałało dla mnie
źródło
@Fetch(value = FetchMode.SUBSELECT)
było wystarczająceAby rozwiązać to po prostu wziąć
Set
w miejscuList
dla zagnieżdżonego obiektu.i nie zapomnij użyć
fetch=FetchType.EAGER
to będzie działać.
Jest jeszcze jedna koncepcja
CollectionId
w Hibernacji, jeśli chcesz trzymać się tylko listy.Przypomnij jednak, że nie wyeliminujesz podkładającego się produktu kartezjańskiego, jak opisał Vlad Mihalcea w swojej odpowiedzi !
źródło
Znalazłem dobry post na blogu o zachowaniu Hibernacji w tego rodzaju mapowaniach obiektów: http://blog.eyallupu.com/2010/06/hibernate-exception-simultently.html
źródło
możesz przechowywać listy EAGER na stoisku w JPA i dodać do co najmniej jednej z nich JPA adnotacja @OrderColumn (oczywiście nazwa pola do zamówienia). Nie potrzeba specjalnych adnotacji hibernacji. Pamiętaj jednak, że może tworzyć puste elementy na liście, jeśli wybrane pole nie ma wartości zaczynających się od 0
w Dzieci należy dodać pole orderIndex
źródło
Wypróbowaliśmy Set zamiast List i jest to koszmar: kiedy dodajesz dwa nowe obiekty, równa się () i hashCode () nie rozpoznają obu! Ponieważ nie mają żadnego identyfikatora.
typowe narzędzia, takie jak Eclipse, generują ten rodzaj kodu z tabel bazy danych:
Możesz również przeczytać ten artykuł który prawidłowo wyjaśnia, jak popsuty jest JPA / Hibernacja. Po przeczytaniu tego, myślę, że po raz ostatni używam ORM w moim życiu.
Spotkałem też facetów zajmujących się projektowaniem opartym na domenie, którzy twierdzą, że ORM to straszna rzecz.
źródło
Gdy masz zbyt skomplikowane obiekty z kolekcją oszczędzającą, nie byłoby dobrym pomysłem mieć je wszystkie z EAGER fetchType, lepiej użyj LAZY, a gdy naprawdę potrzebujesz załadować kolekcje, użyj:
Hibernate.initialize(parent.child)
aby pobrać dane.źródło
Dla mnie problemem było zagnieżdżenie pobrań EAGER .
Jednym z rozwiązań jest ustawienie zagnieżdżonych pól na LAZY i użycie Hibernate.initialize () do załadowania zagnieżdżonych pól:
źródło
Na moim końcu stało się tak, gdy miałem wiele kolekcji z FetchType.EAGER, takich jak:
Ponadto kolekcje łączyły się w tej samej kolumnie.
Aby rozwiązać ten problem, zmieniłem jedną z kolekcji na FetchType.LAZY, ponieważ w moim przypadku użycia było to w porządku.
Powodzenia! ~ J
źródło
Komentowanie obu,
Fetch
aLazyCollection
czasem pomaga uruchomić projekt.źródło
Jedną dobrą rzeczą
@LazyCollection(LazyCollectionOption.FALSE)
jest to, że kilka pól z tą adnotacją może koegzystować, podczas gdyFetchType.EAGER
nie, nawet w sytuacjach, w których takie współistnienie jest uzasadnione.Na przykład,
Order
może mieć listęOrderGroup
(krótką), a także listęPromotions
(również krótką).@LazyCollection(LazyCollectionOption.FALSE)
może być stosowany na obu bez powodowaniaLazyInitializationException
żadnegoMultipleBagFetchException
.W moim przypadku
@Fetch
rozwiązałem problem,MultipleBacFetchException
ale potem powodujeLazyInitializationException
niesławnyno Session
błąd.źródło
Aby rozwiązać ten problem, możesz użyć nowej adnotacji:
W rzeczywistości domyślną wartością fetch jest również FetchType.LAZY.
źródło