-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 
Author Message
 Post subject: Adopting a child to new parent
PostPosted: Tue May 01, 2007 10:06 am 
Newbie

Joined: Tue May 01, 2007 9:11 am
Posts: 4
Location: Rochester, NY
Hi,

I have a parent-child (one to many) bidirectional relationship, with inverse="true" and cascade="all-delete-orphan". What I'm trying to do is take an existing child (Document) and move it from one parent to a new parent (Batch), then delete the old parent.

Hibernate thinks I want the child documents deleted, and says "org.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations) [Document#0];"

I'm assuming this is because of my cascade constraint... See, I _want_ the children deleted when a parent is deleted, or if the child is removed from the parent and has no parent (orphan)... but not if I have moved the children to a different parent (would that be adopted?) :) I tried moving the children first before deleting any of the old parent objects, and I tried flushing in between but it still seems to barf when I flush after deleting the old parents.

Hibernate version: 3.2.2ga

Mapping documents:
Code:
  <class name="Batch" table="batches">
    <!--ID, version, properties omitted -->
    <set name="children" inverse="true" cascade="all-delete-orphan">
      <key column="batch_id" />
      <one-to-many class="Document"/>
    </set>
  </class>

  <class name="Document" table="documents">
    <!--ID, version, properties omitted -->
    <many-to-one name="parent" column="batch_id" class="Batch" />   
  </class>


Pseudocode:
Code:
get all existing batches
for each batch:
  get all documents,
  start adding them to a new batch: document.setParent(newBatch); newBatch.addChild(doc);
  hibernate.save(newBatch);
  if that batch fills up, create a new batch and repeat
hibernate.flush()
for each old batch:
  oldBatch.getChildren().clear();
  oldBatch.setparent(null); //this will delete the old batch
hibernate.flush() //exception


I realize I could remove the cascade and fix it, but I'm trying to take advantage of the "natural object model" as much as possible. Any suggestions?
Thanks in advance for your help!


Top
 Profile  
 
 Post subject:
PostPosted: Mon May 07, 2007 2:49 pm 
Newbie

Joined: Tue May 01, 2007 9:11 am
Posts: 4
Location: Rochester, NY
I hate to reply to my own topic, but really -- am I the only person who has tried to do this? Am I missing something obvious?

I think it boils down to this:

I have a one-to-many relationship with cascade="all-delete-orphan".
I want to move a child from one parent to a new parent.
Then I want to delete the old parent. Hibernate thinks I'm trying to re-save a deleted child.

Any help is appreciated. Thanks.


Top
 Profile  
 
 Post subject:
PostPosted: Mon May 07, 2007 11:37 pm 
Senior
Senior

Joined: Sat Aug 19, 2006 6:31 pm
Posts: 139
Yes. You are exactly right.

So I think you are going to have to disassociate the documents from the old parent.

I saw in your pseudocode that you do the following two things:

Code:
oldBatch.getChildren().clear();
oldBatch.setparent(null); //this will delete the old batch


I'd think you'd need to have something like document.setParent(null). The owner of the relationship (the one that has the foreign key) needs to remove the relationship first. Or is the second line above supposed to be document?

_________________
Don't forget to rate the reply if it helps..:)

Budyanto


Top
 Profile  
 
 Post subject: The Answer!!
PostPosted: Wed May 09, 2007 4:07 pm 
Newbie

Joined: Tue May 01, 2007 9:11 am
Posts: 4
Location: Rochester, NY
So I did find an answer, although it is a bit weird...

So first off, there is no problem moving a child from one parent to the other except in the case where I delete the old parent. I came up with this solution:
Code:
Batch b = new Batch();
b.addChildren( new Document(), new Document() );
hibernate.save(b);
hibernate.flush(); //documents and batch are persisted

Batch b2 = new Batch();
for( Document d : b.getChildren() ) {
   //d.setParent( null );
   d.setParent( b2 );
   b2.addChildren( d );
}
hibernate.save( b2 );
hibernate.flush();

hibernate.refresh( b );      //ideal solution
//b.setChildren( null );     //THIS WORKS TOO!
//b.getChildren().clear();   //this doesn't work; hibernate thinks the children should be deleted.
assertEquals( 0, b.getChildren().size() );

hibernate.delete( b );
hibernate.flush();      //ObjectDeletedException if children are still referenced by b's PersistentSet

b2 = (Batch)hibernate.get( Batch.class, 1L );
assertEquals( 2, b2.getChildren().size() );


Hibernate was trying to cascade the delete to the child documents even though they were moved to a new Batch (b2). This is because the children Set instance (PersistentSet) hangs on to references of child instances that were removed.

So a slightly hack-ish way to avoid this is to call b.setChildren(null) so the PersistentSet is no longer referenced, and Hibernate cannot find references to the child objects. This is not a great idea (setChildren() is package-protected anyway) but it seems to work.

If I do a refresh() on the old batch before deleting it, Hibernate realizes the child documents have been moved, and does not attempt to delete them. I think this is the correct operation.

What's odd, however, is that Hibernate doesn't recognize that the still-referenced children of "b" are stale and do not actually belong there.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.