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).
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
Changez simplement de type List
à type Set
.
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
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
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.
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
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
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 .
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.
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.
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());