JPA OneToMany non elimina figlio


158

Ho un problema con una semplice @OneToManymappatura tra un'entità padre e un'entità figlio. Tutto funziona bene, solo i record figlio non vengono eliminati quando li rimuovo dalla raccolta.

Il genitore:

@Entity
public class Parent {
    @Id
    @Column(name = "ID")
    private Long id;

    @OneToMany(cascade = {CascadeType.ALL}, mappedBy = "parent")
    private Set<Child> childs = new HashSet<Child>();

 ...
}

Il bambino:

@Entity
public class Child {
    @Id
    @Column(name = "ID")
    private Long id;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name="PARENTID", nullable = false)
    private Parent parent;

  ...
}

Se ora elimino e figlio dal set figlio, non viene eliminato dal database. Ho provato a annullare il child.parentriferimento, ma non ha funzionato neanche.

Le entità vengono utilizzate in un'applicazione Web, l'eliminazione avviene come parte di una richiesta Ajax. Non ho un elenco di elementi eliminati quando si preme il pulsante Salva, quindi non posso eliminarli implicitamente.

Risposte:


253

Il comportamento di JPA è corretto (significato secondo la specifica ): gli oggetti non vengono eliminati semplicemente perché li hai rimossi da una raccolta OneToMany. Esistono estensioni specifiche del fornitore che lo fanno, ma JPA nativo non lo soddisfa.

In parte ciò è dovuto al fatto che JPA in realtà non sa se dovrebbe eliminare qualcosa rimosso dalla raccolta. In termini di modellazione di oggetti, questa è la differenza tra composizione e "aggregazione *.

Nella composizione , l'entità figlio non ha esistenza senza il genitore. Un classico esempio è tra House e Room. Elimina anche la casa e le camere.

L'aggregazione è un tipo di associazione più flessibile ed è caratterizzata da Corso e Studente. Elimina il corso e lo studente esiste ancora (probabilmente in altri corsi).

Quindi è necessario utilizzare estensioni specifiche del fornitore per forzare questo comportamento (se disponibile) o eliminare esplicitamente il figlio E rimuoverlo dalla raccolta del genitore.

Sono a conoscenza di:


grazie della bella spiegazione. quindi è come temevo. (Ho fatto qualche ricerca / lettura prima di chiedere, volevo solo essere salvato). in qualche modo comincio a pentirmi della decisione di utilizzare direttamente l'API JPA e nit Hibernate .. Proverò il puntatore Chandra Patni e userò il tipo a cascata hibernate delete_orphan.
Bert

Ho una specie di domanda simile a riguardo. Sarebbe gentile dare un'occhiata a questo post qui, per favore? stackoverflow.com/questions/4569857/…
Thang Pham

78
Con JPA 2.0, ora puoi utilizzare l'opzione orphanRemoval = true
aggiunto il

2
Ottima spiegazione iniziale e buoni consigli su OrphanRemoval. Non avevo idea che JPA non tenesse conto di questo tipo di rimozione. Le sfumature tra ciò che so di Hibernate e ciò che effettivamente fa l'APP possono essere esasperanti.
sma,

Ben spiegato spiegando le differenze tra composizione e aggregazione!
Felipe Leão,

73

Oltre alla risposta di Cletus, JPA 2.0 , definitivo da dicembre 2010, introduce un orphanRemovalattributo sulle @OneToManyannotazioni. Per maggiori dettagli vedi questo post di blog .

Si noti che poiché le specifiche sono relativamente nuove, non tutti i provider JPA 1 hanno un'implementazione finale di JPA 2. Ad esempio, la versione Hibernate 3.5.0-Beta-2 non supporta ancora questo attributo.


post di blog: il collegamento è interrotto.
Steffi,

1
E la magia della macchina del ritorno ci salva: web.archive.org/web/20120225040254/http://javablog.co.uk/2009/…
Louis Jacomet

43

Puoi provare questo:

@OneToOne(orphanRemoval=true) or @OneToMany(orphanRemoval=true).


2
Grazie. Ma la domanda è tornata dall'APP 1 volte. E questa opzione non era disponibile a.
Bert

9
buono a sapersi, comunque, per quelli di noi che cercano una soluzione per questo nei tempi dell'APP2 :)
alex440

20

Come spiegato, non è possibile fare ciò che voglio con JPA, quindi ho usato l'annotazione hibernate.cascade, con questo, il codice pertinente nella classe Parent ora assomiglia a questo:

@OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH}, mappedBy = "parent")
@Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE,
            org.hibernate.annotations.CascadeType.DELETE,
            org.hibernate.annotations.CascadeType.MERGE,
            org.hibernate.annotations.CascadeType.PERSIST,
            org.hibernate.annotations.CascadeType.DELETE_ORPHAN})
private Set<Child> childs = new HashSet<Child>();

Non potrei semplicemente usare 'ALL' in quanto ciò avrebbe eliminato anche il genitore.


4

Qui cascata, nel contesto di rimuovere, significa che i figli vengono rimossi se si rimuove il genitore. Non l'associazione. Se si utilizza Hibernate come provider JPA, è possibile farlo utilizzando la specifica cascata Hibernate .


4

Puoi provare questo:

@OneToOne(cascade = CascadeType.REFRESH) 

o

@OneToMany(cascade = CascadeType.REFRESH)

3
@Entity 
class Employee {
     @OneToOne(orphanRemoval=true)
     private Address address;
}

Vedi qui .

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.