Hibernate löst diese Ausnahme während der SessionFactory-Erstellung aus:
org.hibernate.loader.MultipleBagFetchException: Es können nicht mehrere Taschen gleichzeitig abgerufen werden
Dies ist mein Testfall:
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;
}
Wie wäre es mit diesem Problem? Was kann ich machen?
BEARBEITEN
OK, das Problem, das ich habe, ist, dass sich eine andere "übergeordnete" Entität in meinem übergeordneten Element befindet. Mein wirkliches Verhalten ist folgendes:
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 mag keine zwei Sammlungen mit FetchType.EAGER
, aber das scheint ein Fehler zu sein, ich mache keine ungewöhnlichen Dinge ...
Das Entfernen FetchType.EAGER
von Parent
oder AnotherParent
löst das Problem, aber ich brauche es, so wirkliche Lösung zu verwenden ist @LazyCollection(LazyCollectionOption.FALSE)
statt FetchType
(dank Bozho für die Lösung).
select * from master; select * from child1 where master_id = :master_id; select * from child2 where master_id = :master_id
List<child>
mitfetchType
für definierte mehr als eineList<clield>
Antworten:
Ich denke, eine neuere Version von Hibernate (unterstützt JPA 2.0) sollte dies handhaben. Andernfalls können Sie dies umgehen, indem Sie die Sammlungsfelder mit folgenden Anmerkungen versehen:
Denken Sie daran, das
fetchType
Attribut aus der@*ToMany
Anmerkung zu entfernen .Beachten Sie jedoch, dass a in den meisten Fällen
Set<Child>
besser geeignet ist alsList<Child>
, es sei denn, Sie benötigen wirklich aList
- go forSet
Aber denken Sie daran, dass Sie mit der Verwendung von Sets das zugrunde liegende kartesische Produkt nicht beseitigen können, wie es von Vlad Mihalcea in seiner Antwort beschrieben wurde !
quelle
fetchType
aus dem entfernt@*ToMany
?Wechseln Sie einfach von
List
Typ zuSet
Typ.Aber denken Sie daran, dass Sie das zugrunde liegende kartesische Produkt, wie es von Vlad Mihalcea in seiner Antwort beschrieben wurde, nicht beseitigen werden !
quelle
*ToMany
. Das Ändern des TypsSet
löste auch mein Problem. Hervorragende und saubere Lösung. Dies sollte die offizielle Antwort sein.Fügen Sie Ihrem Code eine Hibernate-spezifische @ Fetch-Annotation hinzu:
Dies sollte das Problem im Zusammenhang mit dem Hibernate-Fehler HHH-1718 beheben
quelle
Set
macht wirklich Sinn. Eine einzelneOneToMany
Beziehung unter Verwendung einesSet
Ergebnisses führt zu1+<# relationships>
Abfragen, während die Verwendung vonFetchMode.SUBSELECT
Ergebnissen zu1+1
Abfragen führt. Wenn Sie die Anmerkung in der akzeptierten Antwort (LazyCollectionOption.FALSE
) verwenden, werden noch mehr Abfragen ausgeführt.In Anbetracht dessen haben wir die folgenden Entitäten:
Und Sie möchten einige übergeordnete
Post
Entitäten zusammen mit allencomments
und abrufentags
Sammlungen abrufen.Wenn Sie mehr als eine
JOIN FETCH
Direktive verwenden:Der Winterschlaf wird das berüchtigte werfen:
Im Ruhezustand können nicht mehr als eine Tasche abgerufen werden, da dies zu einem kartesischen Produkt führen würde .
Die schlechteste "Lösung"
Jetzt finden Sie viele Antworten, Blog-Beiträge, Videos oder andere Ressourcen, in denen Sie aufgefordert werden, für Ihre Sammlungen ein
Set
anstelle einesList
zu verwenden.Das ist ein schrecklicher Rat. Tu das nicht!
Wenn Sie
Sets
anstelle von verwenden,Lists
wird dasMultipleBagFetchException
Problem behoben, aber das kartesische Produkt bleibt erhalten, was sogar noch schlimmer ist, da Sie das Leistungsproblem lange nach dem Anwenden dieses "Fixes" herausfinden werden.Die richtige Lösung
Sie können den folgenden Trick ausführen:
Solange Sie höchstens eine Sammlung mit abrufen
JOIN FETCH
, ist alles in Ordnung.Wenn Sie mehrere Abfragen verwenden, vermeiden Sie das kartesische Produkt, da jede andere Sammlung als die erste mithilfe einer sekundären Abfrage abgerufen wird.
Sie können noch mehr tun
Wenn Sie die
FetchType.EAGER
Strategie zum Mapping-Zeitpunkt für@OneToMany
oder für@ManyToMany
Assoziationen verwenden, kann dies leicht zu einem Ergebnis führenMultipleBagFetchException
.Sie sollten besser von zu wechseln
FetchType.EAGER
,Fetchype.LAZY
da eifriges Abrufen eine schreckliche Idee ist, die zu kritischen Problemen mit der Anwendungsleistung führen kann .Fazit
Vermeiden Sie
FetchType.EAGER
und wechseln Sie nicht vonList
zu,Set
nur weil der Ruhezustand dasMultipleBagFetchException
unter dem Teppich versteckt . Holen Sie sich jeweils nur eine Sammlung, und alles wird gut.Solange Sie dies mit der gleichen Anzahl von Abfragen tun, wie Sie Sammlungen initialisieren müssen, ist alles in Ordnung. Initialisieren Sie die Sammlungen nur nicht in einer Schleife, da dies N + 1-Abfrageprobleme auslöst , die auch die Leistung beeinträchtigen.
quelle
DISTINCT
jedoch Leistungskiller in dieser Lösung. Gibt es eine Möglichkeit, loszuwerdendistinct
? (versuchteSet<...>
stattdessen zurückzukehren, half nicht viel)PASS_DISTINCT_THROUGH
ist eingestellt auffalse
. DISTINCT hat in JPQL zwei Bedeutungen, und hier müssen wir es auf der Java-Seite deduplizieren, nicht auf der SQL-Seite. Weitere Informationen finden Sie in diesem Artikel .hibernate.jdbc.fetch_size
(schließlich stellte ich es auf 350 ein). Wissen Sie zufällig, wie Sie verschachtelte Beziehungen optimieren können? ZB Entität1 -> Entität2 -> Entität3.1, Entität 3.2 (wobei Entität3.1 / 3.2 @ OneToMany-Beziehungen sind)Nachdem ich es mit jeder einzelnen Option versucht hatte, die in diesen und anderen Beiträgen beschrieben wurde, kam ich zu dem Schluss, dass das Update wie folgt ist.
In jedem XToMany Ort @
XXXToMany(mappedBy="parent", fetch=FetchType.EAGER)
und dazwischenDas hat bei mir funktioniert
quelle
@Fetch(value = FetchMode.SUBSELECT)
war genugUm es zu beheben nehmen Sie einfach
Set
anstelle derList
für verschachteltes Objekt.und vergessen Sie nicht zu verwenden
fetch=FetchType.EAGER
es wird klappen.
CollectionId
In Hibernate gibt es noch ein weiteres Konzept, wenn Sie sich nur an die Liste halten möchten.Aber denken Sie daran, dass Sie das zugrunde liegende kartesische Produkt, wie es von Vlad Mihalcea in seiner Antwort beschrieben wurde, nicht beseitigen werden !
quelle
Ich habe einen guten Blog-Beitrag über das Verhalten von Hibernate in dieser Art von Objektzuordnungen gefunden: http://blog.eyallupu.com/2010/06/hibernate-exception-simultaneous.html
quelle
Sie können Stand-EAGER-Listen in JPA behalten und mindestens einer von ihnen die JPA-Anmerkung @OrderColumn hinzufügen (mit offensichtlich dem Namen eines zu bestellenden Felds). Keine spezifischen Anmerkungen zum Ruhezustand erforderlich. Beachten Sie jedoch, dass leere Elemente in der Liste erstellt werden können, wenn das ausgewählte Feld keine Werte ab 0 enthält
In Children sollten Sie dann das Feld orderIndex hinzufügen
quelle
Wir haben Set anstelle von List ausprobiert und es ist ein Albtraum: Wenn Sie zwei neue Objekte hinzufügen, können equals () und hashCode () beide nicht unterscheiden! Weil sie keinen Ausweis haben.
Typische Tools wie Eclipse generieren diese Art von Code aus Datenbanktabellen:
Sie können auch diesen Artikel lesen , der richtig erklärt, wie durcheinander JPA / Hibernate ist. Nachdem ich dies gelesen habe, denke ich, dass dies das letzte Mal ist, dass ich ORM in meinem Leben benutze.
Ich habe auch Leute von Domain Driven Design getroffen, die im Grunde sagen, ORM sei eine schreckliche Sache.
quelle
Wenn Sie zu komplexe Objekte mit mehreren Sammlungen haben, ist es möglicherweise keine gute Idee, alle Objekte mit EAGER fetchType zu verwenden. Verwenden Sie besser LAZY, und wenn Sie die Sammlungen wirklich laden müssen, verwenden Sie:,
Hibernate.initialize(parent.child)
um die Daten abzurufen.quelle
Für mich bestand das Problem darin, verschachtelte EAGER- Abrufe zu haben.
Eine Lösung besteht darin, die verschachtelten Felder auf LAZY zu setzen und Hibernate.initialize () zu verwenden , um die verschachtelten Felder zu laden :
quelle
Am Ende geschah dies, als ich mehrere Sammlungen mit FetchType.EAGER hatte, wie folgt:
Außerdem wurden die Sammlungen in derselben Spalte zusammengefasst.
Um dieses Problem zu lösen, habe ich eine der Sammlungen in FetchType.LAZY geändert, da dies für meinen Anwendungsfall in Ordnung war.
Viel Glück! ~ J.
quelle
Beides zu kommentieren
Fetch
undLazyCollection
manchmal hilft es, ein Projekt auszuführen.quelle
Eine gute Sache
@LazyCollection(LazyCollectionOption.FALSE)
ist, dass mehrere Felder mit dieser Anmerkung koexistierenFetchType.EAGER
können, während dies nicht möglich ist, selbst in Situationen, in denen eine solche Koexistenz legitim ist.Zum Beispiel kann ein
Order
eine Liste vonOrderGroup
(eine kurze) sowie eine Liste vonPromotions
(auch kurz) haben.@LazyCollection(LazyCollectionOption.FALSE)
kann , ohne dass auf beide verwendet werden ,LazyInitializationException
wederMultipleBagFetchException
.In meinem Fall
@Fetch
habe ich mein Problem gelöst,MultipleBacFetchException
aber dannLazyInitializationException
den berüchtigtenno Session
Fehler verursacht.quelle
Sie können eine neue Anmerkung verwenden, um dies zu lösen:
Tatsächlich ist der Standardwert von fetch auch FetchType.LAZY.
quelle