2010-07-08 176 views
7

我有一個基本的一對多關係父/子像在Hibernate參考書的第21章。
級聯僅從子級到父級(堅持僅級聯,因爲如果刪除子級,我不想刪除父級)。
當我添加一個孩子家長和我救孩子,我有一個TransientObjectException ...休眠 - 一對多的關係和孤兒刪除級聯

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

但在saveOrUpdate,我有這樣的例外:

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) 

我真不'不明白,因爲拯救孩子應該通過級聯拯救父母... 任何想法?

更新1
這個問題似乎 「orphanRemoval」 因爲如果我評論它的母公司是相關的:

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

它的工作原理!
它保存孩子,然後父母。
但我真的需要通過級聯刪除孤兒,當我從其父母中刪除一個孩子。

更新2
我創建了一個JIRA問題:
http://opensource.atlassian.com/projects/hibernate/browse/HHH-5364

更新3
這似乎是固定的:-)
http://opensource.atlassian.com/projects/hibernate/browse/HHH-2269

+0

歡迎堆棧溢出!使用帶零和按鈕的按鈕將來可以正確格式化您的代碼(我爲您格式化了它)。 – 2010-07-08 13:58:35

+0

謝謝...我們在同一時間做到了;-) – 2010-07-08 13:59:41

+0

如果您在保存孩子之前保存父母,會發生什麼情況? – Kendrick 2010-07-08 14:00:15

回答

0

基本上你違反了一個約束。數據庫中與父對應的行不存在,因此不存在子對象可用於引用父對象的外鍵關係。在爲孩子做這件事之前,在父母上添加一個saveOrUpdate調用。

(編輯)我在重新格式化之前錯過了關於級聯的評論。我的回憶是,級聯在這種方式上不起作用;您仍然需要先保存父項。

+0

好,但saveOrUpdate在單個事務中執行,因此它首先保存子級,然後級聯應該創建父級(並更新子外鍵)並最終刷新到DB,並且只有在此時限制纔會生效。不是? – 2010-07-08 14:03:47

+0

您是否嘗試在父映射中添加'inverse = true'?我不認爲這會工作,但Hibernate讓我感到驚訝。 – Mikeb 2010-07-08 14:23:04

+0

我沒有使用XML配置,但註釋。 @OneToMany(mappedBy =「parent」)也是這樣做的。 – 2010-07-08 15:02:53