Hibernate lève cette exception lors de la création de SessionFactory:
org.hibernate.loader.MultipleBagFetchException: impossible de récupérer simultanément plusieurs sacs
Voici mon cas de test:
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;
}
Et ce problème? Que puis-je faire?
ÉDITER
OK, le problème que j'ai est qu'une autre entité "parent" est à l'intérieur de mon parent, mon comportement réel est le suivant:
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 n'aime pas deux collections avec FetchType.EAGER
, mais cela semble être un bug, je ne fais pas de choses inhabituelles ...
Retrait FetchType.EAGER
du Parent
ou AnotherParent
permet de résoudre le problème, mais je dois, donc vraie solution est d'utiliser au @LazyCollection(LazyCollectionOption.FALSE)
lieu de FetchType
(grâce à Bozho pour la solution).
select * from master; select * from child1 where master_id = :master_id; select * from child2 where master_id = :master_id
List<child>
avecfetchType
défini pour plus d'unList<clield>
Réponses:
Je pense qu'une version plus récente de hibernate (prenant en charge JPA 2.0) devrait gérer cela. Mais sinon, vous pouvez le contourner en annotant les champs de collection avec:
N'oubliez pas de supprimer l'
fetchType
attribut de l'@*ToMany
annotation.Mais notez que dans la plupart des cas, un
Set<Child>
est plus approprié queList<Child>
, donc à moins que vous n'ayez vraiment besoin d'unList
- allez-ySet
Mais rappelez-vous qu'avec l'utilisation d'ensembles, vous n'éliminerez pas le produit cartésien sous- jacent tel que décrit par Vlad Mihalcea dans sa réponse !
la source
fetchType
du@*ToMany
?Changez simplement de
List
type enSet
type.Mais rappelez-vous que vous n'éliminerez pas le produit cartésien sous- jacent tel que décrit par Vlad Mihalcea dans sa réponse !
la source
*ToMany
. Changer le type pourSet
résoudre mon problème aussi. Solution excellente et soignée. Cela devrait être la réponse officielle.Ajoutez une annotation @Fetch spécifique à Hibernate à votre code:
Cela devrait résoudre le problème lié au bogue Hibernate HHH-1718
la source
Set
vraiment du sens. Avoir uneOneToMany
relation unique en utilisant unSet
résultat dans les1+<# relationships>
requêtes, alors qu'en utilisant lesFetchMode.SUBSELECT
résultats dans les1+1
requêtes. En outre, l'utilisation de l'annotation dans la réponse acceptée (LazyCollectionOption.FALSE
) entraîne l'exécution de davantage de requêtes.Considérant que nous avons les entités suivantes:
Et, vous voulez récupérer certaines
Post
entités parentes avec tous lescomments
ettags
collections .Si vous utilisez plusieurs
JOIN FETCH
directives:Hibernate jettera l'infâme:
Hibernate ne permet pas d'aller chercher plus d'un sac car cela générerait un produit cartésien .
La pire "solution"
Maintenant, vous trouverez de nombreuses réponses, des articles de blog, des vidéos ou d'autres ressources vous indiquant d'utiliser un
Set
au lieu d'unList
pour vos collections.C'est un conseil terrible. Ne fais pas ça!
Utiliser
Sets
au lieu deLists
rendraMultipleBagFetchException
disparaître, mais le produit cartésien sera toujours là, ce qui est en fait encore pire, car vous découvrirez le problème de performances longtemps après avoir appliqué ce "correctif".La bonne solution
Vous pouvez faire l'astuce suivante:
Tant que vous récupérez au plus une collection en utilisant
JOIN FETCH
, tout ira bien.En utilisant plusieurs requêtes, vous éviterez le produit cartésien puisque toute autre collection mais la première est récupérée à l'aide d'une requête secondaire.
Vous pouvez faire plus
Si vous utilisez la
FetchType.EAGER
stratégie au moment de la cartographie des associations@OneToMany
ou@ManyToMany
, vous pourriez facilement vous retrouver avec unMultipleBagFetchException
.Il vaut mieux passer de
FetchType.EAGER
àFetchype.LAZY
car la récupération rapide est une idée terrible qui peut entraîner des problèmes critiques de performances des applications .Conclusion
Évitez
FetchType.EAGER
et ne passez pas deList
àSet
simplement parce que cela fera qu'Hibernate cachera leMultipleBagFetchException
dessous du tapis. Récupérez une seule collection à la fois et tout ira bien.Tant que vous le faites avec le même nombre de requêtes que vous avez de collections à initialiser, tout va bien. N'initialisez simplement pas les collections en boucle, car cela déclencherait des problèmes de requête N + 1 , qui sont également mauvais pour les performances.
la source
DISTINCT
performances sont tueuses dans cette solution. Existe-t-il un moyen de s'en débarrasserdistinct
? (a essayé de revenir à laSet<...>
place, n'a pas beaucoup aidé)PASS_DISTINCT_THROUGH
est réglé surfalse
. DISTINCT a 2 significations dans JPQL, et ici, nous en avons besoin pour dédupliquer du côté Java, pas du côté SQL. Consultez cet article pour plus de détails.hibernate.jdbc.fetch_size
(finalement je l'ai mis à 350). Par hasard, savez-vous comment optimiser les relations imbriquées? Par exemple, entité1 -> entité2 -> entité3.1, entité 3.2 (où entité3.1 / 3.2 sont des relations @OneToMany)Après avoir essayé avec chaque option décrite dans ces articles et d'autres, je suis arrivé à la conclusion que le correctif est un suivi.
Dans chaque lieu XToMany @
XXXToMany(mappedBy="parent", fetch=FetchType.EAGER)
et immédiatement aprèsCela a fonctionné pour moi
la source
@Fetch(value = FetchMode.SUBSELECT)
était suffisantPour le corriger,
Set
remplacez simplementList
votre objet imbriqué.et n'oubliez pas d'utiliser
fetch=FetchType.EAGER
ça va marcher.
Il y a encore un concept
CollectionId
dans Hibernate si vous souhaitez vous en tenir à la liste uniquement.Mais rappelez-vous que vous n'éliminerez pas le produit cartésien sous- jacent tel que décrit par Vlad Mihalcea dans sa réponse !
la source
J'ai trouvé un bon article de blog sur le comportement d'Hibernate dans ce type de mappages d'objets: http://blog.eyallupu.com/2010/06/hibernate-exception-simultaneous.html
la source
vous pouvez conserver les listes EAGER du stand dans JPA et ajouter à au moins l'une d'entre elles l'annotation JPA @OrderColumn (avec évidemment le nom d'un champ à commander). Pas besoin d'annotations d'hibernation spécifiques. Mais gardez à l'esprit qu'il pourrait créer des éléments vides dans la liste si le champ choisi n'a pas de valeurs à partir de 0
dans Enfants, vous devez ajouter le champ orderIndex
la source
Nous avons essayé Set au lieu de List et c'est un cauchemar: lorsque vous ajoutez deux nouveaux objets, equals () et hashCode () ne parviennent pas à les distinguer tous les deux! Parce qu'ils n'ont pas d'identifiant.
des outils typiques comme Eclipse génèrent ce type de code à partir des tables de base de données:
Vous pouvez également lire cet article qui explique correctement à quel point JPA / Hibernate est en désordre. Après avoir lu ceci, je pense que c'est la dernière fois que j'utilise un ORM de ma vie.
J'ai également rencontré des gars de conception pilotée par domaine qui disent essentiellement que l'ORM est une chose terrible.
la source
Lorsque vous avez des objets trop complexes avec une collection sauvage, cela ne peut pas être une bonne idée de les avoir tous avec EAGER fetchType, mieux utiliser LAZY et quand vous avez vraiment besoin de charger les collections, utilisez:
Hibernate.initialize(parent.child)
pour récupérer les données.la source
Pour moi, le problème était d'avoir des récupérations EAGER imbriquées .
Une solution consiste à définir les champs imbriqués sur LAZY et à utiliser Hibernate.initialize () pour charger le ou les champs imbriqués:
la source
À ma fin, cela s'est produit lorsque j'avais plusieurs collections avec FetchType.EAGER, comme ceci:
De plus, les collections se rejoignaient sur la même colonne.
Pour résoudre ce problème, j'ai changé l'une des collections en FetchType.LAZY car cela convenait à mon cas d'utilisation.
Bonne chance! ~ J
la source
Commenter les deux
Fetch
etLazyCollection
parfois aider à exécuter le projet.la source
Une bonne chose
@LazyCollection(LazyCollectionOption.FALSE)
est que plusieurs champs avec cette annotation peuvent coexister alors qu'ilsFetchType.EAGER
ne le peuvent pas, même dans les situations où une telle coexistence est légitime.Par exemple, un
Order
peut avoir une listeOrderGroup
(courte) ainsi qu'une listePromotions
(également courte).@LazyCollection(LazyCollectionOption.FALSE)
peut être utilisé sur les deux sans provoquerLazyInitializationException
ni l' un ni l'autreMultipleBagFetchException
.Dans mon cas,
@Fetch
cela a résolu mon problème,MultipleBacFetchException
mais ensuite les causesLazyInitializationException
, l'no Session
erreur infâme .la source
Vous pouvez utiliser une nouvelle annotation pour résoudre ce problème:
En fait, la valeur par défaut de fetch est également FetchType.LAZY.
la source