Ruhezustand - Eine Sammlung mit cascade = "all-delete-orphan" wurde von der Instanz der besitzenden Entität nicht mehr referenziert

225

Beim Versuch, meine Entität zu aktualisieren, tritt das folgende Problem auf:

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

Ich habe eine übergeordnete Entität und es hat eine Set<...>von einigen untergeordneten Entitäten. Wenn ich versuche, es zu aktualisieren, erhalte ich alle Referenzen, die auf diese Sammlungen gesetzt werden sollen, und setze es.

Der folgende Code repräsentiert meine Zuordnung:

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

Ich habe nur versucht, das Set <..> zu reinigen, wie folgt: Wie "möglich" das Problem zu lösen, aber es hat nicht funktioniert.

Wenn Sie Ideen haben, lassen Sie es mich bitte wissen.

Vielen Dank!

axcdnt
quelle
1
@ mel3kings, der von Ihnen angegebene Link ist nicht mehr aktiv.
Opal
Versuchen Sie, beim Entfernen von Elementen veränderbare Sammlungen zu verwenden. Verwenden Sie zum Beispiel nicht something.manyother.remove(other)if manyotheris a List<T>. Machen Sie viele andere veränderlich, wie ArrayList<T>und verwenden SieorphanDelete = true
nurettin

Antworten:

220

Überprüfen Sie alle Stellen, an denen Sie sonEntities etwas zuweisen. Der Link, auf den Sie verwiesen haben, weist deutlich darauf hin, dass ein neues HashSet erstellt wurde. Dieser Fehler kann jedoch jederzeit auftreten, wenn Sie den Satz neu zuweisen. Beispielsweise:

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

Normalerweise möchten Sie die Menge nur einmal in einem Konstruktor "neu" machen. Jedes Mal, wenn Sie der Liste etwas hinzufügen oder löschen möchten, müssen Sie den Inhalt der Liste ändern, anstatt eine neue Liste zuzuweisen.

So fügen Sie Kinder hinzu:

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

So entfernen Sie Kinder:

public void removeChild(SonEntity aSon)
{
    this.sonEntities.remove(aSon);
}
brainimus
quelle
8
Eigentlich ging es bei meinem Problem um Gleichheit und Hashcode meiner Entitäten. Ein Legacy-Code kann viele Probleme mit sich bringen. Vergessen Sie niemals, ihn zu überprüfen. Alles, was ich getan habe, war, die Strategie zum Löschen und Verwaisen beizubehalten und Gleichheit und Hashcode zu korrigieren.
Axcdnt
6
Ich bin froh, dass Sie Ihr Problem gelöst haben. Gleichheit und Hashcode haben mich ein paar Mal mit Hibernate gebissen. Anstatt den Titel der Frage mit "[Gelöst]" zu aktualisieren, sollten Sie Ihre Antwort veröffentlichen und sie dann als akzeptierte Antwort markieren.
Brainimus
Danke, ich bin auf etwas Ähnliches gestoßen und habe festgestellt, dass mein Setter für dieses Set leer war.
Siddhartha
Ja, auch bei leeren Listen wird dieser Fehler angezeigt. Ich würde das nicht erwarten, weil es keine Waisen gibt (die Liste war leer).
Tibi
4
Normalerweise möchten Sie die Menge nur einmal in einem Konstruktor "neu" machen. Jedes Mal, wenn Sie der Liste etwas hinzufügen oder löschen möchten, müssen Sie den Inhalt der Liste ändern, anstatt eine neue Liste zuzuweisen. Die meisten Kobolde
Nikhil Sahu
109

Die Methode:

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

funktioniert, wenn das getrennt parentEntityist und erneut, wenn wir es aktualisieren.
Wenn die Entität jedoch nicht pro Kontext getrennt ist (dh Such- und Aktualisierungsvorgänge befinden sich in derselben Transaktion), funktioniert die folgende Methode.

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);
    }
}
Manu
quelle
1
@Skuld Ich habe ein ähnliches Problem und habe Ihre Lösung angewendet (in der Setter-Methode lösche ich die Kinder-Sammlung - this.children.clear () - und füge die neuen Kinder hinzu - this.children.addAll (Kinder)). Diese Änderung hat mein Problem nicht behoben. Ich erhalte immer noch die Ausnahme "Eine Sammlung mit cascade =" all-delete-orphan "wurde von der Instanz der Eigentümerentität nicht mehr referenziert. Hast du eine Idee warum? Vielen Dank!
Ovdsrn
@ovdsrn Entschuldigung, dies ist nicht meine Antwort. Ich habe nur die Formatierung der Antwort sortiert. Der ursprüngliche Autor (kmmanu) kann Ihnen möglicherweise helfen (oder Sie möchten möglicherweise eine neue Frage starten, wenn sich Ihr Szenario von dem unterscheidet die ursprüngliche Frage hier gestellt) Viel Glück
Skuld
1
Dies funktioniert gut, ist aber nicht so gut, wenn die untergeordneten Elemente in einer verschachtelten Ruhezustandskomponente enthalten sind und die Komponente in ihrer Entität auf Null gesetzt wird. Dann erhalten Sie den gleichen Fehler. Dies liegt daran, dass der Eigentümer der untergeordneten Elemente die Stammentität ist und nicht die Komponente, die auf null gesetzt wird ... Eine solche Komponente darf niemals null werden, sondern sollte zu einer Weiterleitung an eine destroy () -Methode in der führen Kind ... Zumindest kenne ich keine bessere Lösung ... Es ist eine Art Sammlung clear () Konstruktion, aber dann auf einer Komponente durch destroy () ...
edbras
Siehe auch diesen Beitrag für weitere Details von oben: stackoverflow.com/questions/4770262/…
edbras
7
Verwenden Sie this.sonEntities.retainAll (aSet) über sonEntities.clear (), denn wenn aSet == this.sonEntities (dh dasselbe Objekt), löschen Sie die Menge, bevor Sie sie hinzufügen!
Martin
33

Als ich an verschiedenen Stellen las, an denen Sie im Winterschlaf nicht gerne einer Sammlung zugeordnet wurden, ging ich davon aus, dass es am sichersten wäre, sie wie folgt endgültig zu machen:

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

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

Dies funktioniert jedoch nicht und Sie erhalten den gefürchteten Fehler "nicht mehr referenziert", der in diesem Fall tatsächlich ziemlich irreführend ist.

Es stellt sich heraus, dass der Ruhezustand Ihre setRoles-Methode aufruft UND die spezielle Auflistungsklasse hier installiert werden soll und Ihre Auflistungsklasse nicht akzeptiert. Dies hat mich für eine LANGE Zeit verblüfft, obwohl ich alle Warnungen gelesen habe, dass ich Ihrer Sammlung in Ihrer festgelegten Methode nicht zugewiesen habe.

Also habe ich folgendes geändert:

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);
  }
}
}

Damit Hibernate beim ersten Aufruf seine spezielle Klasse installiert und bei nachfolgenden Aufrufen die Methode selbst verwenden kann, ohne alles zu zerstören. Wenn Sie Ihre Klasse als Bean verwenden möchten, benötigen Sie wahrscheinlich einen funktionierenden Setter, und dies scheint zumindest zu funktionieren.

xpusostomos
quelle
2
Warum rufst du RetainAll () auf? Warum nicht clear () gefolgt von addAll ()?
Edbras
1
"Warum rufst du RetainAll () auf? Warum nicht clear () gefolgt von addAll ()?" Gute Frage, ich glaube, ich habe unter der Annahme gearbeitet, dass der Ruhezustand dies weniger wahrscheinlich als Datenbankaktualisierung ansieht, wenn Sie die Elemente nicht entfernen, bevor Sie sie erneut hinzufügen. Aber ich vermute, dass es sowieso nicht so funktioniert.
Xpusostomos
2
Sie sollten RetainAll () über clear () verwenden, da Sie sonst die Rollen möglicherweise löschen, wenn Sie das identische Set-Objekt übergeben. Zum Beispiel: user.setRoles (user.getRoles ()) == user.roles.clear ()
Martin
2
Wenn Sie orphanRemoval = true setzen und einen Datensatz erstellen, in dem diese Sammlungen null sind, wird auch dieser Fehler angezeigt. Also: a hat oneToMany b mit orphanremoval = true. Wenn Sie A mit B = null erstellen, wird dieses Problem ausgelöst. Ihre Lösung, um sie zu initialisieren und endgültig zu machen, scheint die beste zu sein.
Lawrence
Danke dir! Ich habe meine Liste als initialisiert List<String> list = new ArrayList<>();. Ändern Sie es, um List<String> list = null;das Problem zu beheben :)
Radical
19

Eigentlich ging es bei meinem Problem um Gleichheit und Hashcode meiner Entitäten. Ein Legacy-Code kann viele Probleme mit sich bringen. Vergessen Sie niemals, ihn zu überprüfen. Alles, was ich getan habe, war, die Strategie zum Löschen und Verwaisen beizubehalten und Gleichheits- und Hashcode zu korrigieren.

axcdnt
quelle
9
Bitte ein Beispiel für eine gut gemachte Equals- und HashCode-Methode. weil ich viele Probleme habe: oder ich kann mein Set nicht aktualisieren oder ich bekomme einen StackOverflow-Fehler. Ich habe hier eine Frage geöffnet: stackoverflow.com/questions/24737145/… . danke
SaganTheBest
11

Ich hatte den gleichen Fehler. Das Problem für mich war, dass nach dem Speichern der Entität die zugeordnete Sammlung immer noch null war und beim Versuch, die Entität zu aktualisieren, die Ausnahme ausgelöst wurde. Was mir geholfen hat: Speichern Sie die Entität, führen Sie eine Aktualisierung durch (Sammlung ist nicht mehr null) und führen Sie dann die Aktualisierung durch. Vielleicht hilft auch das Initialisieren der Sammlung mit neuem ArrayList () oder etwas anderem.

Konsumierer
quelle
Das Senden einer neuen ArrayList anstelle von null hat bei mir funktioniert. Danke
rpajaziti
4

HAT BEZIEHUNGSTYP:


Versuchen Sie nicht, die Sammlung zu instanziieren, wenn sie in deklariert ist. Fügen Sie hasManyeinfach Objekte hinzu und entfernen Sie sie.

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

VERWENDUNGSBEZIEHUNGSTYP:


Die Auflistung kann jedoch nur dann null sein, wenn sie als Eigenschaft deklariert ist (Verwendungsbeziehung) und in der Deklaration nicht initialisiert wird.

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

Ich habe den @ user2709454-Ansatz mit kleinen Verbesserungen verwendet.

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);
            }
        }
    }
}
luwojtaszek
quelle
3

Ich hatte dieses Problem beim Versuch zu verwenden TreeSet. Ich habe initialisiert, oneToManymit TreeSetwelchen Arbeiten

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

Dies führt jedoch zu dem questionoben beschriebenen Fehler . So scheint es, dass hibernateunterstützt SortedSetund wenn man nur die obige Zeile auf ändert

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

es funktioniert wie Magie :) Weitere Informationen hibernate SortedSetfinden Sie hier

Eiche
quelle
Vielleicht ist es besser, Collections.emptySet () zu verwenden, um eine leere Singleton-Liste zu verwenden
Ryzhman
3

Ich erhalte diesen Fehler nur, wenn ich versuche, NULL an den Setter für die Sammlung zu übergeben. Um dies zu verhindern, sehen meine Setter folgendermaßen aus:

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

Ich bin darauf gestoßen, als ich eine Entität mit einer JSON-Post-Anfrage aktualisiert habe. Der Fehler trat auf, als ich die Entität ohne Daten zu den untergeordneten Elementen aktualisierte, auch wenn keine vorhanden waren. Hinzufügen

"children": [],

an die Anfrage Stelle löste das Problem.

co ting
quelle
1

Eine andere Ursache kann die Verwendung von Lombok sein.

@Builder- verursacht zu sparen, Collections.emptyList()auch wenn Sie sagen.myCollection(new ArrayList());

@Singular- ignoriert die Standardeinstellungen auf Klassenebene und verlässt das Feld, nullauch wenn das Klassenfeld als deklariert wurdemyCollection = new ArrayList()

Meine 2 Cent, habe gerade 2 Stunden mit dem gleichen verbracht :)

Jan Zyka
quelle
1

Ich verwende Spring Boot und hatte dieses Problem mit einer Sammlung, obwohl ich es nicht direkt überschrieben habe, da ich ein zusätzliches Feld für dieselbe Sammlung mit einem benutzerdefinierten Serializer und Deserializer deklariere , um eine frontendfreundlichere Darstellung der Sammlung zu ermöglichen Daten:

  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;
  }

Es scheint , dass , obwohl ich nicht die Sammlung zu überschreiben mich , die Deserialisierung es unter der Haube hat, die Auslösung dieses Problems alle gleich. Die Lösung bestand darin, den dem Deserializer zugeordneten Setter so zu ändern, dass die Liste gelöscht und alles hinzugefügt wird, anstatt sie zu überschreiben:

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

Ich hatte das gleiche Problem, aber es war, als der Satz null war. Nur in der Set-Sammlung in List Arbeit finden. Sie können versuchen, die Annotation @LazyCollection (LazyCollectionOption.FALSE) für den Ruhezustand zu aktivieren, die von der JPA-Annotation fetch = FetchType.EAGER installiert wurde.

Meine Lösung: Dies ist meine Konfiguration und funktioniert einwandfrei

@OneToMany(mappedBy = "format", cascade = CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
private Set<Barcode> barcodes;

@OneToMany(mappedBy = "format", cascade = CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
private List<FormatAdditional> additionals;
Dario Gaston
quelle
1
@OneToMany(mappedBy = 'parent', cascade= CascadeType.ALL, orphanRemoval = true)
List<Child> children = new ArrayList<>();

Der gleiche Fehler trat auf, als ich der vorhandenen Liste der untergeordneten Objekte ein untergeordnetes Objekt hinzufügte.

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

Was mein Problem gelöst hat, ändert sich zu:

child = childService.saveOrUpdate(child);

Jetzt belebt sich das Kind auch mit anderen Details und es hat gut funktioniert.

Neha Gupta
quelle
0

Meine dumme Antwort hinzufügen. Wir verwenden Spring Data Rest. Dies war unsere ziemlich normale Beziehung. Das Muster wurde an anderer Stelle verwendet.

//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

Mit der Beziehung, die wir aufgebaut haben, war es immer beabsichtigt, dass die Kinder durch ihr eigenes Repo hinzugefügt werden. Ich hatte das Repo noch nicht hinzugefügt. Der Integrationstest, den wir hatten, durchlief über REST-Aufrufe einen vollständigen Lebenszyklus der Entität, sodass die Transaktionen zwischen den Anforderungen geschlossen wurden. Kein Repo für das Kind bedeutete, dass der json die Kinder als Teil der Hauptstruktur anstelle von in hatte _embedded. Aktualisierungen des übergeordneten Elements würden dann Probleme verursachen.

Snekse
quelle
0

Die folgende Lösung hat bei mir funktioniert

//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;
priyanka_rao
quelle
0

Anstatt eine neue Sammlung zuzuweisen

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

Ersetzen Sie alle Elemente durch

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

sei vorsichtig mit

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

Auch diese Methode unterbricht den Ruhezustand.

Diógenes Ricardo
quelle
0

Meins war mit Spring Boot ganz anders! Für mich lag es nicht daran, die Sammlungseigenschaft festzulegen.

In meinen Tests habe ich versucht, eine Entität zu erstellen, und diesen Fehler für eine andere Sammlung erhalten, die nicht verwendet wurde!

Nachdem ich so viel versucht hatte, fügte ich einfach eine @TransactionalTestmethode hinzu und sie löste sie. Aber nicht den Grund.

madz
quelle
0

Dies steht im Gegensatz zu den vorherigen Antworten, ich hatte genau den gleichen Fehler: "Eine Sammlung mit cascade =" all-delete-orphan "wurde nicht mehr referenziert ...", als meine Setter-Funktion so aussah:

public void setTaxCalculationRules(Set<TaxCalculationRule> taxCalculationRules_) {
    if( this.taxCalculationRules == null ) {
        this.taxCalculationRules = taxCalculationRules_;
    } else {
        this.taxCalculationRules.retainAll(taxCalculationRules_);
        this.taxCalculationRules.addAll(taxCalculationRules_);
    }
}

Und dann verschwand es, als ich es auf die einfache Version änderte:

public void setTaxCalculationRules(Set<TaxCalculationRule> taxCalculationRules_) {
    this.taxCalculationRules = taxCalculationRules_;
}

(Versionen im Ruhezustand - haben sowohl 5.4.10 als auch 4.3.11 ausprobiert. Wir haben mehrere Tage damit verbracht, alle möglichen Lösungen auszuprobieren, bevor wir zu der einfachen Zuordnung im Setter zurückgekehrt sind. Wir sind jetzt verwirrt, warum dies so ist.)

Ligett
quelle