2010-07-08 13 views
7

Mam podstawowy do wielu relacji rodzic/dziecko, jak w rozdziale 21 książki odniesienia Hibernate.
Kaskada jest tylko z potomka do rodzica (kaskada trwa tylko dlatego, że nie chcę usunąć rodzica, jeśli usunę dziecko).
Kiedy dodać dziecka do rodzica i zapisać dziecko, mam TransientObjectException ...Hibernate - Jeden do wielu relacji i sierociniec kaskady Reemoval

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

Ale na saveOrUpdate, mam ten wyjątek:

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) 

naprawdę don nie rozumiem, ponieważ uratowanie Dziecka powinno uratować Rodzica przez kaskadę ... Jakieś pomysły?

UPDATE 1
Problem wydaje się być związane z "orphanRemoval" bo gdybym skomentować go na dominującej:

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

To działa!
Zapisuje dziecko, a następnie rodzica.
Ale naprawdę potrzebuję, aby sierota została usunięta przez kaskadę, kiedy usuwam dziecko z jego rodzica.

UPDATE 2
Utworzyłem JIRA problem:
http://opensource.atlassian.com/projects/hibernate/browse/HHH-5364

UPDATE 3
Wydaje się być ustalona :-)
http://opensource.atlassian.com/projects/hibernate/browse/HHH-2269

+0

Zapraszamy do przepełnienie stosu! Użyj przycisku zer i jedynek, aby poprawnie sformatować kod w przyszłości (sformatowałem go dla ciebie). –

+0

Dzięki ... zrobiliśmy to w tym samym czasie ;-) –

+0

Co się stanie, jeśli uratujesz rodzica przed zapisaniem dziecka? – Kendrick

Odpowiedz

0

Zasadniczo są naruszono ograniczenie. Wiersz w db odpowiadający rodzicowi nie istnieje, więc nie istnieje żadna relacja klucza obcego, którą dziecko może wykorzystać do odnoszenia się do rodzica. Dodaj wywołanie saveOrUpdate na rodzica przed zrobieniem tego dla dziecka.

(edytuj) Brakowało mi komentarza na temat kaskady przed ponownym formatowaniem. Przypominam sobie, że kaskada nie działa w ten sposób w górę; nadal musisz najpierw zapisać rodzica.

+0

OK, ale saveOrUpdate wykonane w pojedynczej transakcji, więc najpierw zapisać dziecko, następnie kaskada powinna utworzyć rodzica (i zaktualizować klucz obcego dziecka) i wreszcie wypłukać do DB i tylko w tym momencie ograniczenia są sprawdzane .. nie? –

+0

Czy próbowałeś dodać "odwrotnie = true" do mapowania nadrzędnego? Nie sądzę, że to zadziała, ale Hibernate mnie zaskakuje. – Mikeb

+0

Nie używam konfiguracji xml, ale adnotacje. @OneToMany (mappedBy = "parent") robi to samo. –

Powiązane problemy