325 votes

Hibernate - Une collection avec cascade="all-delete-orphan" n'était plus référencée par l'instance d'entité propriétaire

Je rencontre le problème suivant lorsque j'essaie de mettre à jour mon entité :

"A collection with cascade=”all-delete-orphan” was no longer referenced by the owning entity instance".

J'ai une entité parent et elle a une Set<...> de certaines entités enfants. Lorsque j'essaie de le mettre à jour, j'obtiens toutes les références à mettre en place dans ces collections et je les mets en place.

Le code suivant représente mon mappage :

@OneToMany(mappedBy = "parentEntity", fetch = FetchType.EAGER)
@Cascade({ CascadeType.ALL, CascadeType.DELETE_ORPHAN })
public Set<ChildEntity> getChildren() {
    return this.children;
}

J'ai essayé de nettoyer le Set<..> seulement, selon ceci : Comment résoudre "éventuellement" le problème mais ça n'a pas marché.

Si vous avez des idées, n'hésitez pas à me les communiquer.

Gracias.

3 votes

@mel3kings, le lien que vous avez fourni n'est plus actif.

0 votes

0 votes

Essayez d'utiliser des collections mutables lorsque vous supprimez des éléments. Par exemple, n'utilisez pas something.manyother.remove(other) si manyother es un List<T> . Rendre beaucoup d'autres mutables, comme ArrayList<T> et utiliser orphanDelete = true

273voto

brainimus Points 2578

Vérifiez tous les endroits où vous assignez quelque chose à sonEntities. Le lien que vous avez référencé indique clairement la création d'un nouveau HashSet mais vous pouvez avoir cette erreur à chaque fois que vous réassignez l'ensemble. Par exemple :

public void setChildren(Set<SonEntity> aSet)
{
    this.sonEntities = aSet; //This will override the set that Hibernate is tracking.
}

En général, vous ne voulez "créer" l'ensemble qu'une seule fois dans un constructeur. Chaque fois que vous voulez ajouter ou supprimer quelque chose à la liste, vous devez modifier le contenu de la liste au lieu d'assigner une nouvelle liste.

Pour ajouter des enfants :

public void addChild(SonEntity aSon)
{
    this.sonEntities.add(aSon);
}

Pour retirer les enfants :

public void removeChild(SonEntity aSon)
{
    this.sonEntities.remove(aSon);
}

10 votes

En fait, mon problème concernait les égaux et les codes de hachage de mes entités. Un code hérité peut apporter beaucoup de problèmes, n'oubliez jamais de le vérifier. Tout ce que j'ai fait est de garder la stratégie delete-orphan et de corriger les equals et hashcode.

7 votes

Je suis heureux que vous ayez résolu votre problème. equals et hashcode m'ont mordu plusieurs fois avec Hibernate. Au lieu de mettre à jour le titre de la question avec "[Résolu]", vous devriez aller de l'avant et poster votre réponse, puis la marquer comme étant la réponse acceptée.

0 votes

Merci, j'ai rencontré quelque chose de similaire et il s'est avéré que mon setter pour ce Set était vide

134voto

kmmanu Points 368

La méthode :

public void setChildren(Set<SonEntity> aSet) {
    this.sonEntities = aSet;
}

fonctionne si le parentEntity est détaché et à nouveau si on le met à jour.
Mais si l'entité n'est pas détachée de chaque contexte, (c'est-à-dire que les opérations de recherche et de mise à jour sont dans la même transaction) la méthode ci-dessous fonctionne.

public void setChildren(Set<SonEntity> aSet) {
    //this.sonEntities = aSet; //This will override the set that Hibernate is tracking.
    this.sonEntities.clear();
    if (aSet != null) {
        this.sonEntities.addAll(aSet);
    }
}

2 votes

@Skuld J'ai un problème similaire et j'ai appliqué votre solution (dans la méthode setter j'ai effacé la collection d'enfants - this.children.clear() - et j'ai ajouté les nouveaux enfants - this.children.addAll(children)). Ce changement n'a pas résolu mon problème. Je reçois toujours l'exception "A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance". Avez-vous une idée de la raison ? Merci beaucoup !

0 votes

@ovdsrn Désolé ce n'est pas ma réponse, je viens de régler le formatage de la réponse, l'auteur original (kmmanu) pourrait être en mesure de vous aider (ou vous pourriez vouloir commencer une nouvelle question si votre scénario est différent de la question originale posée ici) Bonne chance.

1 votes

Cela fonctionne bien, mais pas si bien lorsque les enfants sont contenus dans un composant hibernate imbriqué et que le composant est rendu nul dans son entité. Vous obtenez alors la même erreur. Ceci est dû au fait que le propriétaire des enfants est l'Entité racine et non le composant qui est rendu nul... A ce titre, un composant n'est jamais autorisé à devenir nul, mais doit plutôt entraîner une redirection vers une méthode destroy() dans l'enfant... En tout cas, je ne connais pas de meilleure solution... C'est une sorte de construction de collection clear() mais ensuite sur un composant par destroy()...

43voto

user2709454 Points 36

Lorsque j'ai lu à divers endroits qu'Hibernate n'aimait pas que l'on assigne à une collection, j'ai supposé que la chose la plus sûre à faire serait évidemment de la rendre finale comme ceci :

class User {
  private final Set<Role> roles = new HashSet<>();

public void setRoles(Set<Role> roles) {
  this.roles.retainAll(roles);
  this.roles.addAll(roles);
}
}

Cependant, cela ne fonctionne pas, et vous obtenez la redoutable erreur "no longer referenced", qui est en fait assez trompeuse dans ce cas.

Il s'avère qu'Hibernate appelle votre méthode setRoles ET qu'il veut que sa classe de collection spéciale soit installée ici, et n'accepte pas votre classe de collection. Cela m'a laissé perplexe pendant un LONG moment, malgré la lecture de tous les avertissements sur le fait de ne pas assigner à votre collection dans votre méthode set.

Alors j'ai changé pour ça :

public class User {
  private Set<Role> roles = null;

  public void setRoles(Set<Role> roles) {
  if (this.roles == null) {
    this.roles = roles;
  } else {
    this.roles.retainAll(roles);
   this.roles.addAll(roles);
  }
}
}

Ainsi, lors du premier appel, hibernate installe sa classe spéciale, et lors des appels suivants, vous pouvez utiliser la méthode vous-même sans tout gâcher. Si vous voulez utiliser votre classe comme un bean, vous avez probablement besoin d'un setter fonctionnel, et ceci semble au moins fonctionner.

2 votes

Pourquoi appelez-vous retainAll() ? Pourquoi pas clear() suivi de addAll() ?

1 votes

"Pourquoi appelez-vous retainAll() ? Pourquoi pas clear() suivi de addAll() ?" Bonne question, je pense que je travaillais en supposant qu'hibernate serait moins susceptible de voir cela comme une mise à jour de la base de données si vous ne supprimiez pas les éléments avant de les ajouter à nouveau. Mais je soupçonne que cela ne fonctionne pas de cette façon de toute façon.

2 votes

Vous devriez utiliser retainAll() plutôt que clear(), sinon vous risquez d'effacer les rôles si vous passez un objet set identique. Par exemple : user.setRoles(user.getRoles()) == user.roles.clear()

28voto

axcdnt Points 1313

En fait, mon problème concernait les égalités et les codes de hachage de mes entités. Un code hérité peut apporter beaucoup de problèmes, n'oubliez jamais de le vérifier. Tout ce que j'ai fait est de garder la stratégie delete-orphan et de corriger les equals et hashcode.

15 votes

S'il vous plaît, un exemple d'une méthode equals et hashCode bien faite ? parce que j'ai beaucoup de problèmes : ou je ne peux pas mettre à jour mon set ou j'obtiens une erreur StackOverflow. j'ai ouvert une question ici : stackoverflow.com/questions/24737145/ . merci

16voto

Konsumierer Points 737

J'ai eu la même erreur. Le problème pour moi était qu'après avoir sauvegardé l'entité, la collection mappée était toujours nulle et quand j'ai essayé de mettre à jour l'entité, l'exception a été levée. Ce qui m'a aidé : Sauvegarder l'entité, puis faire un rafraîchissement (la collection n'est plus nulle) et ensuite effectuer la mise à jour. Peut-être qu'initialiser la collection avec new ArrayList() ou quelque chose d'autre pourrait également aider.

2 votes

Envoyer une nouvelle ArrayList au lieu de null, a fonctionné pour moi. Merci

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X