Got it! :) But I am now faced with a new wrinkle. It seems sensible to me to build the "cleanup" methods right into the mapped dataobjects. I.e., in my Biker object, I provide the method:
Code:
public void cleanup() {
for ( Iterator i = gangs.iterator() ; i.hasNext() ; ) {
Gang g = (Gang)i.next();
g.getBikers().remove(this);
}
}
I've made this method part of an interface "HouseKeeper", as my app may deal w/ mapped objects of different types, and I wish to be able to call it like so:
Code:
Object o = methodForLoadingObjectsOfVariousTypes(); // returns a Biker object in this case
HouseKeeper hk = (HouseKeeper)o;
hk.cleanup(); // first removes this biker from any gang's biker collections
sess.delete(o); // then delete this biker
tx.commit();
This produces the exception:
Code:
Exception in thread "main" org.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations): [com.kqed.simplecms.test.list.Biker#33]
at org.hibernate.impl.SessionImpl.forceFlush(SessionImpl.java:1014)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:165)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:94)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:507)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:499)
at org.hibernate.engine.CascadingAction$5.cascade(CascadingAction.java:218)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:268)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:216)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:296)
at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:242)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:219)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
at org.hibernate.engine.Cascade.cascade(Cascade.java:130)
at org.hibernate.event.def.AbstractFlushingEventListener.cascadeOnFlush(AbstractFlushingEventListener.java:131)
at org.hibernate.event.def.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:122)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:65)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
at com.kqed.simplecms.test.list.TestList.deleteById(TestList.java:126)
at com.kqed.simplecms.test.list.TestList.main(TestList.java:24)
The funny thing is that this worked fine before I encapsulated the code into the biker object. That is, when I did the cleanup "inline", like so:
Code:
for (Iterator i = biker.getGangs().iterator() ; i.hasNext() ; ) {
Gang gang = (Gang)i.next();
gang.getBikers().remove(biker);
}
session.delete(biker);
It worked fine. Upon closer inspection with a debugger, I saw that in my calling code, my biker object reported itself to be:
Biker$$EnhancerByCGLIB$$2e8dd29a
So when I used this object when calling:
Code:
gang.getBikers().remove(biker);
follwed by:
Code:
session.delete(biker);
it works. However when I call:
Code:
biker.cleanup();
and step through that method internal to biker, I see that the
this variable reports itself as a plain old Biker class (not the CGLIB generated class), so that when I call:
Code:
gang.getBikers().remove(this);
and later, after returning from the cleanup() method, call:
Code:
biker.cleanup();
... the results are bad, so that when I call tx.commit(), I get that exception.
I guess I'm expecting java to behave like java here, but the introduction of the CGLIB-generated objects is causing the PersistentSet.remove() method to not work as expected.
I would greatly appreciate any recommendations on how to handle this correctly!
Thanks!
Ken