]
Gail Badner resolved HHH-2269.
------------------------------
Resolution: Fixed
Fixed in Branch_3_5 and trunk.
Many-to-one cascade fails with TransientObjectException if the
inverse collection is marked CascadeType.DELETE_ORPHAN
---------------------------------------------------------------------------------------------------------------------
Key: HHH-2269
URL:
http://opensource.atlassian.com/projects/hibernate/browse/HHH-2269
Project: Hibernate Core
Issue Type: Bug
Components: core
Affects Versions: 3.2.0.cr2, 3.2.1
Environment: Using hibernate annotations.
Reporter: Edward Costello
Assignee: Gail Badner
Fix For: 3.5.4, 3.6
Attachments: new-testcase.tgz, TestBidirectionalCascade.java
When both a many-to-one (child) and it's inverse one-to-many collection (on parent)
are cascading. Attempting to save a child directly and cascade the save to the parent
throws a TransientObjectException if the parent's collection is mapped
CascadeType.DELETE_ORPHAN. For Example, with the classes below:
@Entity
public class Parent {
@OneToMany(mappedBy = "parent")
@Cascade(value = {CascadeType.ALL, CascadeType.DELETE_ORPHAN})
Set<DeleteOrphanChild> deleteOrphanChildren;
}
@Entity
public class DeleteOrphanChild {
@ManyToOne
@Cascade(value = CascadeType.ALL)
Parent parent;
}
Calling session.save() with an instance of the parent will cascade the save to the child
as expected. However, calling save on an instance of the child will throw the
TransientObjectException below while attempting to cascade the save to the parent.
org.hibernate.TransientObjectException:
cascade.test.TestBidirectionalCascade$DeleteOrphanChild
at
org.hibernate.engine.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:216)
at
org.hibernate.collection.AbstractPersistentCollection.getOrphans(AbstractPersistentCollection.java:889)
at org.hibernate.collection.PersistentSet.getOrphans(PersistentSet.java:51)
at org.hibernate.engine.CollectionEntry.getOrphans(CollectionEntry.java:350)
at org.hibernate.engine.Cascade.deleteOrphans(Cascade.java:336)
at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:318)
at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:185)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:160)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:108)
at org.hibernate.engine.Cascade.cascade(Cascade.java:248)
at
org.hibernate.event.def.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:437)
at
org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:326)
at
org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:180)
at
org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:108)
at
org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:186)
at
org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:175)
at
org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:98)
at
org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:509)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:501)
at org.hibernate.engine.CascadingAction$1.cascade(CascadingAction.java:134)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:213)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:157)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:108)
at org.hibernate.engine.Cascade.cascade(Cascade.java:248)
at
org.hibernate.event.def.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:412)
at
org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:261)
at
org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:180)
at
org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:108)
at
org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:186)
at
org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:33)
at
org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:175)
at
org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:27)
at
org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
at org.hibernate.impl.SessionImpl.fireSave(SessionImpl.java:537)
at org.hibernate.impl.SessionImpl.save(SessionImpl.java:525)
at org.hibernate.impl.SessionImpl.save(SessionImpl.java:521)
If the DELETE_ORPHAN cascade is removed the save works fine. The attached test case runs
against an in memory HSQLDB database. It contains 4 tests, the second test
(testSaveChildDeleteOrphan) fails showing the above exception. The other three pass
showing that cascading from the parent works and that cascading in both directions remove
if the DELETE_ORPHAN cascade is removed.
We've only tested this with annotations, we haven't tried to reproduce it using
hibernate mapping files.
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: