2010-01-06 2 views
131

Ho un problema con un semplice mapping @OneToMany tra un'entità padre e un'entità figlio. Tutto funziona bene, solo i record figlio non vengono cancellati quando li rimuovo dalla raccolta.JPA OneToMany non elimina child

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 cancello e bambino dal bambino Set, non vengono eliminati dal database. Ho provato ad annullare il riferimento child.parent, 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 child eliminati quando viene premuto il pulsante Save, quindi non posso eliminarli implicitamente.

risposta

222

Il comportamento di JPA è corretto (ovvero secondo la specifica): gli oggetti non vengono eliminati semplicemente perché sono stati rimossi da una raccolta OneToMany. Esistono estensioni specifiche del fornitore che lo fanno, ma il JPA nativo non provvede a questo.

In parte questo perché JPA in realtà non sa se deve eliminare qualcosa rimosso dalla raccolta. In termini di modellazione degli oggetti, questa è la differenza tra composizione e "l'aggregazione *.

In composizione, l'entità bambino non ha esistenza senza il genitore. Un esempio classico è tra House e Sala. Eliminare la casa e le camere vanno troppo.

aggregazione è un tipo più flessibile di associazione ed è caratterizzata da Course and Student. Eliminare il corso e lo studente esiste ancora (probabilmente in altri corsi).

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

io sappia:

+0

grazie della bella spiegazione. quindi è come temevo. (Ho fatto qualche ricerca/lettura prima di chiedere, volevo solo essere salvato). in qualche modo mi metto deplorando la decisione di utilizzare JPA API e nit Hibernate direttamente .. cercherò puntatore Chandra Patni e utilizzare l'ibernazione delete_orphan tipo a cascata. – bert

+0

Ho una domanda simile a riguardo. Sarebbe carino dare un'occhiata a questo post qui, per favore? http://stackoverflow.com/questions/4569857/jsf-how-to-update-the-list-after-delete-an-item-of-that-list –

+59

Con JPA 2.0, ora puoi utilizzare l'opzione orphanRemoval = true – itsadok

2

Qui a cascata, nel contesto di rimozione, significa che i bambini vengono rimossi se si rimuove il genitore. Non l'associazione. Se stai utilizzando Hibernate come provider JPA, puoi farlo usando hibernate specific cascade.

18

Come spiegato, non è possibile fare quello che voglio con APP, quindi ho impiegato l'annotazione hibernate.cascade, con questo, il relativo codice nella classe padre appare come segue:

@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 usare semplicemente "TUTTO" come questo avrebbe eliminato anche il genitore.

64

In aggiunta alla risposta di cletus, JPA 2.0, finale da dicembre 2010, viene introdotto un attributo sulle annotazioni @OneToMany. Per ulteriori dettagli, vedere questo blog entry.

Si noti che poiché la specifica è relativamente nuova, non tutti i provider JPA 1 hanno un'implementazione finale JPA 2. Ad esempio, lo Hibernate 3.5.0-Beta-2 release non supporta ancora questo attributo.

32

Si può 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 indietro di. – bert

+7

buono a sapersi, tuttavia, per quelli di noi che cercano una soluzione per questo in tempi JPA2 :) – alex440

1

Si può provare questo:

@OneToOne(cascade = CascadeType.REFRESH) 

o

@OneToMany(cascade = CascadeType.REFRESH) 
2
@Entity 
class Employee { 
    @OneToOne(orphanRemoval=true) 
    private Address address; 
} 

Vedi here.