Sospensione: una raccolta con cascade = "all-delete-orphan" non è più stata referenziata dall'istanza dell'entità proprietaria


225

Sto riscontrando il seguente problema quando provo ad aggiornare la mia entità:

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

Ho un'entità padre e ha una Set<...>di alcune entità figlio. Quando provo ad aggiornarlo, ottengo tutti i riferimenti da impostare su queste raccolte e impostarlo.

Il seguente codice rappresenta la mia mappatura:

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

Ho provato a pulire solo il Set <..>, secondo questo: Come "possibile" risolvere il problema ma non ha funzionato.

Se hai qualche idea, per favore fatemelo sapere.

Grazie!


1
@ mel3kings, il link che hai fornito non è più attivo.
Opale,


prova a usare raccolte mutabili quando rimuovi elementi. Ad esempio, non usare something.manyother.remove(other)if manyotherè a List<T>. Rendi ArrayList<T>orphanDelete = true
molti altri mutevoli

Risposte:


220

Controlla tutti i luoghi in cui stai assegnando qualcosa a sonEntities. Il collegamento a cui fai riferimento indica chiaramente la creazione di un nuovo HashSet ma puoi avere questo errore ogni volta che riassegni il set. Per esempio:

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

Di solito si desidera "nuovo" l'insieme una sola volta in un costruttore. Ogni volta che si desidera aggiungere o eliminare qualcosa all'elenco, è necessario modificare il contenuto dell'elenco anziché assegnare un nuovo elenco.

Per aggiungere bambini:

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

Per rimuovere i bambini:

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

8
In realtà, il mio problema riguardava la parità e l'hashcode delle mie entità. Un codice legacy può comportare molti problemi, non dimenticare mai di verificarlo. Tutto quello che ho fatto è stato solo mantenere la strategia di eliminazione degli orfani e correggere uguali e hashcode.
axcdnt,

6
Sono contento che tu abbia risolto il tuo problema. uguale e hashcode mi hanno morso alcune volte con Hibernate. Invece di aggiornare il titolo della domanda con "[Risolto]" dovresti andare avanti e pubblicare la tua risposta, quindi contrassegnarla come risposta accettata.
brainimus,

Grazie, mi sono imbattuto in qualcosa di simile e ho scoperto che il mio setter per quel Set era vuoto ..
Siddhartha,

sì, anche su liste vuote viene visualizzato questo errore. non me lo aspetto perché non ci sono orfani (l'elenco era vuoto).
Tibi,

4
Di solito si desidera "nuovo" l'insieme una sola volta in un costruttore. Ogni volta che si desidera aggiungere o eliminare qualcosa all'elenco, è necessario modificare il contenuto dell'elenco anziché assegnare un nuovo elenco. Most imp
Nikhil Sahu,

109

Il metodo:

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

funziona se parentEntityè staccato e di nuovo se lo aggiorniamo.
Ma se l'entità non è distaccata dal contesto (per es. Le operazioni di ricerca e aggiornamento sono nella stessa transazione), il metodo seguente funziona.

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

1
@Se dovessi avere un problema simile e ho applicato la tua soluzione (nel metodo setter deseleziono la raccolta children - this.children.clear () - e ho aggiunto i nuovi figli - this.children.addAll (children)). Questa modifica non ha risolto il mio problema. Ricevo ancora l'eccezione "Una raccolta con cascade =" all-delete-orphan "non era più indicata dall'istanza dell'entità proprietaria". Hai un'idea del perché? Grazie mille!
Ovdsrn,

@ovdsrn Spiacente, questa non è la mia risposta, ho appena risolto il problema con la formattazione della risposta, l'autore originale (kmmanu) potrebbe essere in grado di aiutarti (o alt potresti voler iniziare una nuova domanda se il tuo scenario è diverso da la domanda originale è stata posta qui) Buona fortuna
Skuld il

1
Funziona bene, ma non così bene quando i figli sono contenuti in un componente ibernato nidificato e il componente viene reso nullo nella sua Entità. Quindi ricevi lo stesso errore. Questo perché il proprietario dei figli è l'Entità radice e non il componente reso nullo ... In tal modo, un componente non può mai diventare nullo, ma piuttosto dovrebbe comportare un metodo forward ( bambino ... Almeno, non conosco una soluzione migliore ... È una specie di costruzione clear () della raccolta ma poi su un componente attraverso destroy () ...
edbras

Vedi anche questo post per maggiori dettagli di cui sopra: stackoverflow.com/questions/4770262/...
edbras

7
usa this.sonEntities.retainAll (aSet) su sonEntities.clear () perché se aSet == this.sonEntities (cioè lo stesso oggetto) cancellerai il set prima di aggiungerlo!
Martin,

33

Quando ho letto in vari posti che il letargo non ti piaceva assegnare a una raccolta, ho pensato che la cosa più sicura da fare sarebbe ovviamente renderla definitiva in questo modo:

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

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

Tuttavia, questo non funziona e si ottiene l'errore temuto "non più referenziato", che in questo caso è piuttosto fuorviante.

Si scopre che l'ibernazione chiama il tuo metodo setRoles E vuole che la sua classe di raccolta speciale sia installata qui e non accetti la tua classe di raccolta. Questo mi ha fatto rimanere a lungo per molto, nonostante abbia letto tutti gli avvertimenti sul non assegnare alla tua collezione nel tuo metodo set.

Quindi sono passato a questo:

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

In modo che alla prima chiamata, hibernate installi la sua classe speciale e nelle chiamate successive puoi usare tu stesso il metodo senza distruggere tutto. Se vuoi usare la tua classe come bean, probabilmente hai bisogno di un setter funzionante, e questo almeno sembra funzionare.


2
Perché stai chiamando retainAll ()? Perché non cancellare () seguito da addAll ()?
Edbre

1
"Perché stai chiamando retainAll ()? Perché non clear () seguito da addAll ()?" Bella domanda, penso che stavo lavorando supponendo che l'ibernazione avrebbe meno probabilità di vederlo come un aggiornamento del database se non avessi rimosso gli elementi prima di aggiungerli di nuovo. Ma sospetto che comunque non funzioni in questo modo.
xpusostomos,

2
Dovresti usare retainAll () su clear () altrimenti potresti cancellare i ruoli se ti capita di passare l'oggetto set identico. Ad esempio: user.setRoles (user.getRoles ()) == user.roles.clear ()
Martin

2
Quando si imposta orphanRemoval = true e si crea un record in cui questa raccolta è nulla, viene visualizzato anche questo errore. Quindi: a ha oneToMany b con orphanremoval = true. Quando si crea A dove B = null, questo problema è attivato. La tua soluzione per inizializzare e renderla definitiva sembra la migliore.
Lawrence,

Grazie! Stavo inizializzando la mia lista come List<String> list = new ArrayList<>();. Modificandolo per List<String> list = null;risolvere il problema :)
Radicale

19

In realtà, il mio problema riguardava la parità e l'hashcode delle mie entità. Un codice legacy può comportare molti problemi, non dimenticare mai di verificarlo. Tutto quello che ho fatto è stato solo mantenere la strategia di eliminazione degli orfani e correggere uguali e hashcode.


9
per favore, un esempio di uguali e metodi hashCode ben fatti? perché ho molti problemi: o non riesco ad aggiornare il mio set o ricevo un errore StackOverflow. ho aperto una domanda qui: stackoverflow.com/questions/24737145/… . grazie
SaganTheBest

11

Ho avuto lo stesso errore. Il problema per me era che dopo aver salvato l'entità la raccolta mappata era ancora nulla e quando si cercava di aggiornare l'entità veniva generata l'eccezione. Cosa mi ha aiutato: salvare l'entità, quindi eseguire un aggiornamento (la raccolta non è più nulla) e quindi eseguire l'aggiornamento. Forse l'inizializzazione della raccolta con il nuovo ArrayList () o qualcosa potrebbe anche aiutare.


L'invio di una nuova ArrayList invece di null, ha funzionato per me. Grazie
rpajaziti

4

HA UN TIPO DI RELAZIONE:


Non provare a creare un'istanza della raccolta quando viene dichiarata in hasMany, basta aggiungere e rimuovere oggetti.

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

USARE IL TIPO DI RELAZIONE:


Ma la raccolta potrebbe essere nulla solo quando viene dichiarata come proprietà (relazione di utilizzo) e non è inizializzata nella dichiarazione.

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

4

Ho usato l'approccio @ user2709454 con un piccolo miglioramento.

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

3

Ho avuto questo problema durante il tentativo di utilizzo TreeSet. Ho fatto l'inizializzazione oneToManycon TreeSetcui funziona

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

Ma questo porterà all'errore descritto questionsopra. Quindi sembra hibernatesupportato SortedSete se si cambia la riga sopra in

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

funziona come per magia :) maggiori informazioni su hibernate SortedSetpossono essere qui


Forse, è meglio usare Collections.emptySet () per usare un elenco vuoto singleton
ryzhman,

3

L'unica volta che ottengo questo errore è quando provo a passare NULL nel setter per la raccolta. Per evitare ciò, i miei setter si presentano così:

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

3

Mi sono imbattuto in questo durante l'aggiornamento di un'entità con una richiesta di post JSON. L'errore si è verificato quando ho aggiornato l'entità senza dati sui figli, anche se non ce n'erano. Aggiunta

"children": [],

al corpo della richiesta risolto il problema.


1

Un'altra causa potrebbe essere l'uso di lombok.

@Builder- fa risparmiare Collections.emptyList()anche se dici.myCollection(new ArrayList());

@Singular- ignora le impostazioni predefinite a livello di classe e lascia il campo nullanche se il campo della classe è stato dichiarato comemyCollection = new ArrayList()

I miei 2 centesimi, ho appena trascorso 2 ore con lo stesso :)



1

Sto utilizzando Spring Boot e ho riscontrato questo problema con una raccolta, nonostante non lo sovrascrissi direttamente, perché sto dichiarando un campo aggiuntivo per la stessa raccolta con un serializzatore e un deserializzatore personalizzati al fine di fornire una rappresentazione più intuitiva del dati:

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

Sembra che anche se non sto sovrascrivendo la collezione da solo , la deserializzazione lo fa sotto il cofano, innescando comunque questo problema. La soluzione era quella di cambiare il setter associato al deserializzatore in modo da cancellare l'elenco e aggiungere tutto, anziché sovrascriverlo:

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

1

Ho avuto lo stesso problema, ma era quando il set era nullo. Trova solo nella raccolta Imposta in elenco. Puoi provare all'annotazione in ibernazione @LazyCollection (LazyCollectionOption.FALSE) istallata dell'annotazione JPA fetch = FetchType.EAGER.

La mia soluzione: questa è la mia configurazione e funziona benissimo

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

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

Ho riscontrato lo stesso errore durante l'aggiunta di un oggetto figlio all'elenco esistente di oggetti figlio.

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

Ciò che ha risolto il mio problema sta cambiando in:

child = childService.saveOrUpdate(child);

Ora il bambino è rinato anche con altri dettagli e ha funzionato bene.


0

Aggiungendo la mia stupida risposta. Stiamo utilizzando Spring Data Rest. Questa era la nostra relazione piuttosto standard. Il modello è stato usato altrove.

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

Con la relazione che abbiamo creato, era sempre inteso che i bambini sarebbero stati aggiunti attraverso il loro repository. Non avevo ancora aggiunto il repository. Il test di integrazione che abbiamo svolto stava attraversando un ciclo di vita completo dell'entità tramite chiamate REST in modo che le transazioni si chiudessero tra le richieste. Nessun repository per il bambino significava che JSON aveva i bambini come parte della struttura principale anziché all'interno _embedded. Gli aggiornamenti al genitore causerebbero quindi problemi.


0

La seguente soluzione ha funzionato per me

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

Invece di assegnare una nuova raccolta

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

Sostituisci tutti gli elementi con

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

0

stai attento con

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

Anche questo metodo interrompe il letargo.



0

Il mio era completamente diverso con Spring Boot! Per me non era dovuto all'impostazione della proprietà della raccolta.

Nei miei test stavo cercando di creare un'entità e stavo ottenendo questo errore per un'altra raccolta che non era stata utilizzata!

Dopo aver provato così tanto ho appena aggiunto un @Transactionalmetodo di prova e l'ho risolto. Non capisco però il motivo.


0

Questo è in contrasto con le risposte precedenti, ho avuto esattamente lo stesso errore: "Una raccolta con cascade =" all-delete-orphan "non era più referenziata ...." quando la mia funzione setter appariva così:

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

E poi è scomparso quando l'ho cambiato alla versione semplice:

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

(versioni di ibernazione - provato sia il 5.4.10 che il 4.3.11. Ho trascorso diversi giorni a provare ogni sorta di soluzione prima di tornare al semplice incarico nel setter. Ora confuso sul perché).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.