web-dev-qa-db-fra.com

Hibernate ne peut pas chercher simultanément plusieurs sacs

Hibernate lève cette exception lors de la création de SessionFactory:

org.hibernate.loader.MultipleBagFetchException: impossible d'extraire simultanément plusieurs sacs

Ceci est 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;

}

Que diriez-vous de ce problème? Que puis-je faire?


MODIFIER

OK, le problème que j'ai, c'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 AntoherParent anotherParent;

 @OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
 private List<Child> children;

}

AnotherParent.Java

@Entity
public AntoherParent {

 @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 des choses inhabituelles ...

Supprimer FetchType.EAGER de Parent ou AnotherParent résout le problème, mais j'en ai besoin, donc la vraie solution consiste à utiliser @LazyCollection(LazyCollectionOption.FALSE) au lieu de FetchType (merci à Bozho pour la solution).

378
blow

Je pense qu'une version plus récente d'Hibernate (prenant en charge JPA 2.0) devrait gérer cela. Mais sinon, vous pouvez le contourner en annotant les champs de collection avec:

@LazyCollection(LazyCollectionOption.FALSE)

N'oubliez pas de supprimer l'attribut fetchType de l'annotation @*ToMany.

Mais notez que dans la plupart des cas, un Set<Child> est plus approprié que List<Child>, donc à moins que vous ayez vraiment besoin d'une List - optez pour Set

454
Bozho

Changez simplement de type List à type Set.

252
Ahmad Zyoud

Vous pouvez également ajouter une annotation @Fetch spécifique à Hibernate à votre code:

@OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
@Fetch(value = FetchMode.SUBSELECT)
private List<Child> childs;

Cela devrait résoudre le problème lié au bogue Hibernate HHH-1718

100
DaveRlz

Après avoir essayé avec chaque option décrite dans ce billet et d’autres, j’en suis arrivée à la conclusion que le correctif est un suivant.

Dans chaque XToMany place @XXXToMany(mappedBy="parent", fetch=FetchType.EAGER) Et de manière intermédiaire après

@Fetch(value = FetchMode.SUBSELECT)

Cela a fonctionné pour moi

22
Javier

Pour résoudre ce problème, prenez simplement Set à la place de List pour votre objet imbriqué.

@OneToMany
Set<Your_object> objectList;

et n'oubliez pas d'utiliser fetch=FetchType.EAGER

ça va marcher.

Il existe un autre concept CollectionId dans Hibernate si vous souhaitez vous en tenir à la liste uniquement.

17
Prateek Singh

J'ai trouvé un bon article sur le comportement d'Hibernate dans ce type de mappages d'objets: http://blog.eyallupu.com/2010/06/hibernate-exception-simultaneous.html

11
Christian Müller

vous pouvez conserver les listes de stands EAGER 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 que cela pourrait créer des éléments vides dans la liste si le champ choisi n'a pas de valeur à partir de 0

 [...]
 @OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
 @OrderColumn(name="orderIndex")
 private List<Child> children;
 [...]

dans Enfants, vous devez ajouter le champ orderIndex 

5
Massimo

La raison pour laquelle vous obtenez cette exception est que Hibernate finirait par créer un produit cartésien qui serait mauvais pour la performance.

Maintenant, bien que vous puissiez "résoudre" le problème en utilisant Set au lieu de List, vous ne devriez pas le faire, car le produit cartésien sera toujours présenté dans les instructions SQL sous-jacentes.

Vous feriez mieux de passer de FetchType.EAGER à Fetchype.LAZY car l'extraction assidue est une idée terrible qui peut entraîner des problèmes critiques de performances des applications .

Si vous avez besoin d'extraire les entités enfants dans une hiérarchie à plusieurs niveaux, sélectionnez plutôt de l'enfant le plus à l'intérieur jusqu'aux parents, comme expliqué dans cet article .

3
Vlad Mihalcea

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! Parce qu'ils n'ont pas d'identifiant.

des outils typiques comme Eclipse génèrent ce type de code à partir de tables de base de données:

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((id == null) ? 0 : id.hashCode());
    return result;
}

Vous pouvez également lire cet article qui explique correctement l’attractivité de JPA/Hibernate. Après avoir lu ceci, je pense que c'est la dernière fois que j'utilise un ORM dans ma vie.

J'ai aussi rencontré des spécialistes de Domain Driven Design qui disent que l'ORM est une chose terrible.

1
Mike Dudley

Lorsque vous avez des objets trop complexes avec une collection saveral, il ne serait pas judicieux de tous les avoir avec EAGER fetchType. Il vaut mieux utiliser LAZY et lorsque vous devez vraiment charger les collections, utilisez: Hibernate.initialize(parent.child) pour récupérer les données.

1
Felipe Cadena

Pour moi, le problème était d’avoir imbriquéD&EACUTE;SIREUX.

Une solution consiste à définir les champs imbriqués surLAZYet à utiliser Hibernate.initialize () pour charger les champs imbriqués:

x = session.get(ClassName.class, id);
Hibernate.initialize(x.getNestedField());
0
butter_prune