[hibernate-issues] [Hibernate-JIRA] Commented: (HHH-2269) Many-to-one cascade fails with TransientObjectException if the inverse collection is marked CascadeType.DELETE_ORPHAN

Gail Badner (JIRA) noreply at atlassian.com
Fri Jan 12 15:33:44 EST 2007


    [ http://opensource.atlassian.com/projects/hibernate/browse/HHH-2269?page=comments#action_25791 ] 

Gail Badner commented on HHH-2269:
----------------------------------

I have reproduced this issue using Hibernate mapping files. I've attached new-testcase.tgz containing the new test case with the relevant mappings and Java classes. It also contains the original test case.

Debugging showed that the exception is being thrown in AbstractPersistentCollection.getOrphans(). In this case, the new Parent entity has not been saved yet and it is in both the "old" and "new" list. While going through the "new" list, ForeignKeys.isNotTransient() incorrectly returns true when session.getPersistenceContext().isEntryFor() returns true for the unsaved Parent entity.

Commenting out the check for the entity entry in ForeignKeys.isNotTransient() moves the exception a little later when AbstractPersistentCollection.getOrphans() calls ForeignKeys.getEntityIdentifierIfNotUnsaved() for the same parent entity in the "old" list. The test passed after adding a check to ensure that ForeignKeys.getEntityIdentifierIfNotUnsaved() is only called for saved entities.

Unfortunately, these changes break the following unit tests in org.hibernate.test.orphan.OrphanTest:
testOrphanDeleteOnDelete, testOrphanDeleteAfterPersist, testOrphanDeleteAfterPersistAndFlush.

Gail Badner
SourceLabs - http://www.sourcelabs.com
Dependable Open Source Systems


> 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: Hibernate3
>         Type: Bug

>   Components: core
>     Versions: 3.2.0.cr2, 3.2.1
>  Environment: Using hibernate annotations.
>     Reporter: Edward Costello
>  Attachments: TestBidirectionalCascade.java, new-testcase.tgz
>
>
> 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:
   http://opensource.atlassian.com/projects/hibernate/secure/Administrators.jspa
-
For more information on JIRA, see:
   http://www.atlassian.com/software/jira




More information about the hibernate-issues mailing list