休眠 - 一对多的关系和孤儿删除级联
我有一个基本的一对多关系父/子像在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
基本上你违反了一个约束。数据库中与父对应的行不存在,因此不存在子对象可用于引用父对象的外键关系。在为孩子做这件事之前,在父母上添加一个saveOrUpdate调用。
(编辑)我在重新格式化之前错过了关于级联的评论。我的回忆是,级联在这种方式上不起作用;您仍然需要先保存父项。
好,但saveOrUpdate在单个事务中执行,因此它首先保存子级,然后级联应该创建父级(并更新子外键)并最终刷新到DB,并且只有在此时限制才会生效。不是? – 2010-07-08 14:03:47
您是否尝试在父映射中添加'inverse = true'?我不认为这会工作,但Hibernate让我感到惊讶。 – Mikeb 2010-07-08 14:23:04
我没有使用XML配置,但注释。 @OneToMany(mappedBy =“parent”)也是这样做的。 – 2010-07-08 15:02:53
欢迎堆栈溢出!使用带零和按钮的按钮将来可以正确格式化您的代码(我为您格式化了它)。 – 2010-07-08 13:58:35
谢谢...我们在同一时间做到了;-) – 2010-07-08 13:59:41
如果您在保存孩子之前保存父母,会发生什么情况? – Kendrick 2010-07-08 14:00:15