web-dev-qa-db-fra.com

Hibernate - Une collection avec cascade = "all-delete-Orphan" n'a plus été référencée par l'instance de l'entité propriétaire

J'ai 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é mère et elle a un Set<...> de certaines entités enfants. Lorsque j'essaie de le mettre à jour, toutes les références sont définies sur cette collection et définies.

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 "possible" résoudre le problème mais ça n'a pas marché.

Si vous avez des idées, s'il vous plaît faites le moi savoir.

Merci!

164
axcdnt

En fait, mon problème concernait les égaux et le hashcode de mes entités. Un code hérité peut poser de nombreux problèmes, n'oubliez jamais de le vérifier. Tout ce que j'ai fait était de garder la stratégie delete-Orphan et de corriger les égaux et le hashcode.

14
axcdnt

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

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

Habituellement, vous voulez seulement "renouveler" l'ensemble une fois dans un constructeur. Chaque fois que vous souhaitez ajouter ou supprimer quelque chose à la liste, vous devez modifier le contenu de la liste au lieu de lui affecter une nouvelle liste.

Pour ajouter des enfants:

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

Pour enlever les enfants:

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

La méthode:

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

fonctionne si la parentEntity est détachée et à nouveau si nous la mettons à jour.
Mais si l’entité n’est pas détachée par contexte (c’est-à-dire que les opérations de recherche et de mise à jour se font 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);
    }
}
83
Manu

Lorsque je lisais à divers endroits que hiberner n'aimait pas que vous assigniez une collection, je pensais que la chose la plus sûre à faire serait évidemment de la rendre définitive 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 l'erreur redoutée "non référencée", ce qui est en réalité assez trompeur dans ce cas.

Il se trouve que Hibernate appelle votre méthode setRoles ET 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 de ne pas attribuer à votre collection dans votre méthode set.

Alors j'ai changé pour ceci:

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 détruire. Si vous voulez utiliser votre classe comme un haricot, vous avez probablement besoin d'un setter qui fonctionne, et cela semble au moins fonctionner.

21
user2709454

J'ai eu la même erreur. Le problème pour moi était que, après avoir sauvegardé l'entité, la collection mappée était toujours nulle et que, lors de la tentative de mise à jour de l'entité, l'exception était levée. Ce qui m’a aidé: enregistrer l’entité, puis actualiser (la collection n’est plus nulle), puis effectuer la mise à jour. Peut-être que l’initialisation de la collection avec le nouvel ArrayList () ou quelque chose qui pourrait l’aider peut aussi aider.

9
Konsumierer

A UN TYPE DE RELATION:


N'essayez pas d'instancier la collection lorsqu'elle est déclarée dans hasMany, ajoutez simplement et supprimez des objets. 

class Parent {
    static hasMany = [childs:Child]
}

TYPE DE RELATION D'UTILISATION:


Mais la collection ne peut être nulle que si elle est déclarée en tant que propriété (relation d'utilisation) et n'est pas initialisée dans la déclaration.

class Parent {
    List<Child> childs = []
}
4
IgniteCoders

J'ai eu ce problème en essayant d'utiliser TreeSet. J'ai initialisé oneToMany avec TreeSet qui fonctionne

@OneToMany(mappedBy = "question", fetch = FetchType.EAGER, cascade = { CascadeType.ALL }, orphanRemoval=true)
@OrderBy("id")
private Set<WizardAnswer> answers = new TreeSet<WizardAnswer>();

Mais cela apportera l'erreur décrite à la question ci-dessus. Il semble donc que hibernate supporte SortedSet et si l’on change simplement la ligne ci-dessus en

@OneToMany(mappedBy = "question", fetch = FetchType.EAGER, cascade = { CascadeType.ALL }, orphanRemoval=true)
@OrderBy("id")
private SortedSet<WizardAnswer> answers;

cela fonctionne comme par magie:) plus d’informations sur hibernate SortedSet peuvent être ici

3
oak

Le seul cas où j'obtiens cette erreur est lorsque j'essaie de transmettre NULL au créateur de la collection. Pour éviter cela, mes setters ressemblent à ceci:

public void setSubmittedForms(Set<SubmittedFormEntity> submittedForms) {
    if(submittedForms == null) {
        this.submittedForms.clear();
    }
    else {
        this.submittedForms = submittedForms;
    }
}
2
Arlo Guthrie

J'ai utilisé l'approche @ user2709454 avec une petite amélioration.

public class User {
    private Set<Role> roles;

    public void setRoles(Set<Role> roles) {
        if (this.roles == null) {
            this.roles = roles;
        } else if(this.roles != roles) { // not the same instance, in other case we can get ConcurrentModificationException from hibernate AbstractPersistentCollection
            this.roles.clear();
            if(roles != null){
                this.roles.addAll(roles);
            }
        }
    }
}
2
luwojtaszek

Une autre cause peut être l'utilisation de lombok.

@Builder - permet de sauvegarder Collections.emptyList() même si vous dites .myCollection(new ArrayList());

@Singular - ignore les valeurs par défaut au niveau de la classe et quitte le champ null même si le champ de classe a été déclaré comme myCollection = new ArrayList()

Mes 2 centimes, je viens de passer 2 heures avec le même :)

0
Jan Zyka
@OneToMany(mappedBy = 'parent', cascade= CascadeType.ALL, orphanRemoval = true)
List<Child> children = new ArrayList<>()

J'ai rencontré la même erreur lorsque j'ajoutais un objet enfant à la liste existante d'objets enfants.

childService.saveOrUpdate(child);
parent.addToChildren(child);
parentService.saveOrUpdate(parent);

Ce qui a résolu mon problème est: child = childService.saveOrUpdate (child);

Maintenant, l'enfant est ravivé avec d'autres détails et tout a bien fonctionné.

0
Neha Gupta

J'obtenais A collection with cascade=”all-delete-Orphan” was no longer referenced by the owning entity instance lorsque je réglais parent.setChildren(new ArrayList<>()). Lorsque j'ai changé pour parent.getChildren().clear(), le problème a été résolu.

Recherchez des informations supplémentaires: HibernateException - Une collection avec cascade = "all-delete-Orphan" n’était plus référencée par l’instance d’entité propriétaire .

0
Justas

Ajout de ma réponse stupide. Nous utilisons Spring Data Rest. C'était notre belle relation standard. Le motif a été utilisé ailleurs. 

//Parent class
@OneToMany(mappedBy = 'parent', 
           cascade= CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
List<Child> children = new LinkedList<>()


//Child class
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = 'ParentID', updatable = false)
@JsonBackReference
Parent parent

Avec la relation que nous avons créée, il a toujours été prévu que les enfants soient ajoutés via leur propre référant. Je n'avais pas encore ajouté le repo. Le test d'intégration que nous avons eu traversait un cycle de vie complet de l'entité via des appels REST afin que les transactions se ferment entre les demandes. Pas de repo pour l’enfant signifiait que le json avait les enfants dans la structure principale au lieu de _embedded. Les mises à jour du parent causeraient alors des problèmes.

0
Snekse

La solution suivante a fonctionné pour moi

//Parent class
@OneToMany(mappedBy = 'parent', 
           cascade= CascadeType.ALL, orphanRemoval = true)
@OrderBy(value="ordinal ASC")
List<Child> children = new ArrayList<>()

//Updated setter of children 
public void setChildren(List<Children> children) {
    this.children.addAll(children);
    for (Children child: children)
        child.setParent(this);
}


//Child class
@ManyToOne
@JoinColumn(name="Parent_ID")
private Parent parent;
0
priyanka_rao

Cela pourrait être causé par hibernate-enhance-maven-plugin. Lorsque j'ai activé la propriété enableLazyInitialization, cette exception a commencé à se produire sur ma collection paresseuse. J'utilise Hibernate 5.2.17.Final.

Notez ces deux problèmes d'hibernation:

0
Michał Stochmal

attention à 

BeanUtils.copyProperties(newInsum, insumOld,"code");

Cette méthode aussi briser l'hibernation.

0
Diógenes Ricardo

Je me suis heurté à cela lors de la mise à jour d'une entité avec une demande de publication JSON. L'erreur s'est produite lorsque j'ai mis à jour l'entité sans données sur les enfants, même s'il n'y en avait pas. Ajouter

"children": [],

au corps de la demande a résolu le problème.

0
co ting

Au lieu d'attribuer une nouvelle collection

public void setChildren(Set<ChildEntity> children) {
    this.children = children;
}

Remplacer tous les éléments avec

public void setChildren(Set<ChildEntity> children) {
    Collections.replaceAll(this.children,children);
}
0
Marcin Szymczak

Le mien était complètement différent avec Spring Boot! Pour moi, ce n'était pas dû à la définition de la propriété de la collection.

Lors de mes tests, j'essayais de créer une entité et j'obtenais cette erreur pour une autre collection inutilisée!

Après tant d'essais, j'ai simplement ajouté un @Transactional à la méthode de test et cela l'a résolu. Ne pas la raison cependant.

0
madz

J'utilise Spring Boot et je rencontre ce problème avec une collection, même si je ne l'écrase pas directement, car je déclare un champ supplémentaire pour la même collection avec un sérialiseur personnalisé et un désérialiseur afin de fournir une interface plus frontale Représentation conviviale des données:

  public List<Attribute> getAttributes() {
    return attributes;
  }

  public void setAttributes(List<Attribute> attributes) {
    this.attributes = attributes;
  }

  @JsonSerialize(using = AttributeSerializer.class)
  public List<Attribute> getAttributesList() {
    return attributes;
  }

  @JsonDeserialize(using = AttributeDeserializer.class)
  public void setAttributesList(List<Attribute> attributes) {
    this.attributes = attributes;
  }

Il semble que même si je n'écrase pas la collection moi-même, la désérialisation le fait sous le capot, ce qui déclenche tout de même ce problème. La solution consistait à changer le séparateur associé au désérialiseur afin qu'il efface la liste et ajoute tout au lieu de l'écraser:

  @JsonDeserialize(using = AttributeDeserializer.class)
  public void setAttributesList(List<Attribute> attributes) {
    this.attributes.clear();
    this.attributes.addAll(attributes);
  }
0
Sofia Paixão