2010-07-08 14 views
7

Ho una relazione genica di base a molti come nel capitolo 21 del libro di riferimento di Hibernate.
La cascata è solo da figlio a genitore (continua a cascata solo perché non voglio cancellare il genitore se cancello un figlio).
Quando aggiungo un bambino al genitore e risparmio il bambino, ho un TransientObjectException ...Hibernate - Relazione uno a molti e orphanRemoval cascade

@Entity 
public class Parent implements Serializable { 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private Long id; 

    @OneToMany(mappedBy = "parent", orphanRemoval = true) 
    private List<Child> childs; 

    public List<Child> getChilds() { 
    return childs; 
    } 

    public void setChilds(List<Child> childs) { 
    this.childs = childs; 
    } 

    public void addChild(Child child) { 
    if (childs == null) childs = new ArrayList<Child>(); 
    if (childs.add(child)) child.setParent(this); 
    } 

    public Long getId() { 
    return id; 
    } 

    public void setId(Long id) { 
    this.id = id; 
    } 
} 

@Entity 
public class Child implements Serializable { 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private Long id; 

    @ManyToOne(optional = false) 
    @Cascade({ PERSIST, MERGE, REFRESH, SAVE_UPDATE, REPLICATE, LOCK, DETACH }) 
    private Parent parent; 

    public Parent getParent() { 
    return parent; 
    } 

    public void setParent(Parent parent) { 
    this.parent = parent; 
    } 

    public Long getId() { 
    return id; 
    } 

    public void setId(Long id) { 
    this.id = id; 
    } 
} 


@Test 
public void test() { 
    Parent parent = new Parent(); 
    Child child = new Child(); 
    parent.addChild(child); 
    genericDao.saveOrUpdate(child); 
} 

Ma d'saveOrUpdate, ho questa eccezione:

org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Child 
    at org.hibernate.engine.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:244) 
    at org.hibernate.collection.AbstractPersistentCollection.getOrphans(AbstractPersistentCollection.java:911) 
    at org.hibernate.collection.PersistentBag.getOrphans(PersistentBag.java:143) 
    at org.hibernate.engine.CollectionEntry.getOrphans(CollectionEntry.java:373) 
    at org.hibernate.engine.Cascade.deleteOrphans(Cascade.java:471) 
    at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:455) 
    at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:362) 
    at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:338) 
    at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:204) 
    at org.hibernate.engine.Cascade.cascade(Cascade.java:161) 
    at org.hibernate.event.def.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:476) 
    at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:354) 
    at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204) 
    at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:130) 
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:210) 
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:195) 
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:117) 
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93) 
    at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:677) 
    at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:669) 
    at org.hibernate.engine.CascadingAction$5.cascade(CascadingAction.java:252) 
    at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:392) 
    at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:335) 
    at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:204) 
    at org.hibernate.engine.Cascade.cascade(Cascade.java:161) 
    at org.hibernate.event.def.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:451) 
    at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:288) 
    at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204) 
    at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:130) 
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:210) 
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:195) 
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:117) 
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93) 
    at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:677) 
    at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:669) 
    at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:665) 

ho davvero don capisco perché salvare il bambino dovrebbe salvare il genitore tramite la cascata ... Qualche idea?

UPDATE 1
Il problema sembra essere correlate a "orphanRemoval", perché se io commento sul genitore:

@OneToMany(mappedBy = "parent" /*, orphanRemoval = true */) 
private List<Child> childs; 

funziona!
Salva il bambino, quindi il genitore.
Ma ho davvero bisogno che l'orfano venga eliminato tramite la cascata quando rimuovo un figlio dal genitore.

UPDATE 2
Ho creato un problema di JIRA:
http://opensource.atlassian.com/projects/hibernate/browse/HHH-5364

UPDATE 3
Sembra essere fissato :-)
http://opensource.atlassian.com/projects/hibernate/browse/HHH-2269

+0

Benvenuti a Stack Overflow! Usa il pulsante con gli zeri e quelli per formattare correttamente il tuo codice in futuro (l'ho formattato per te). –

+0

Grazie ... lo abbiamo fatto allo stesso tempo ;-) –

+0

Cosa succede se salvi il genitore prima di salvare il bambino? – Kendrick

risposta

0

Fondamentalmente si stanno violando un vincolo. La riga nel db che corrisponde al genitore non esiste, quindi non esiste una relazione di chiave esterna che il bambino possa usare per riferirsi al genitore. Aggiungi una chiamata per salvareOrUpdate sul genitore prima di fare qualcosa per il bambino.

(modifica) Ho perso il tuo commento sulla cascata prima della riformattazione. Il mio ricordo è che la cascata non funziona a monte in quel modo; avresti comunque bisogno di salvare prima il genitore.

+0

OK, ma saveOrUpdate viene eseguito in una singola transazione, quindi prima salva il figlio, quindi la cascata deve creare il genitore (e aggiornare la chiave esterna del bambino) e infine eseguire il flush su DB e solo in questo momento i vincoli vengono convalidati. . no ? –

+0

hai provato ad aggiungere "inverse = true" alla mappatura genitore? Non penserei che funzionerebbe, ma Hibernate mi sorprende. – Mikeb

+0

Non sto usando la configurazione xml ma le annotazioni. @OneToMany (mappedBy = "parent") fa lo stesso. –

Problemi correlati