[
http://opensource.atlassian.com/projects/hibernate/browse/HHH-2269?page=c...
]
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....
-
For more information on JIRA, see:
http://www.atlassian.com/software/jira