-->
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.  [ 15 posts ] 
Author Message
 Post subject: How do I reattach entities with collections?
PostPosted: Thu Jan 05, 2006 12:57 am 
Regular
Regular

Joined: Wed May 11, 2005 11:57 pm
Posts: 80
Hibernate version: 3.05

Name and version of the database you are using: MySQL 4.0.16

Let's say that I load an object, Parent, in session1. Parent has a List of Children. Let's say that there are 5 Children objects in my Parent. I do some work, which initializes the list of Children, and then close session1.

While my session is closed, another user deletes one of the 5 Children in the database, so now there are 4 in the database.

Now I want to do something with Parent again. I create session2, start a transaction, and reattach Parent using session2.lock( Parent, LockMode.UPGRADE ) (we're using pessimistic locking). The problem is that Parent still thinks it has 5 Children. This causes all sorts of problems, especially if I try to delete one of the Children, which attempts to update the index column for those Children when the transaction is committed.

What is the correct way to reattach Parent so that it's collection of Children is accurate?


Top
 Profile  
 
 Post subject: Don't use refresh()
PostPosted: Thu Jan 05, 2006 11:43 am 
Newbie

Joined: Thu Jan 05, 2006 12:42 am
Posts: 3
I've been having trouble with this too, but don't have a solution. One word of advice, stay away from Session.refresh(). It will try to refresh the related objects also, and when it gets to the deleted object, it will throw the following:

Code:
org.hibernate.UnresolvableObjectException: No row with the given identifier exists: [com.example.Subject#1033]


The only way to reliably refresh the object seems to be to open a new session and fetch a fresh copy of the object. Which is unfortunate, because then you have multiple versions of the same object floating around.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 05, 2006 12:05 pm 
Expert
Expert

Joined: Fri Aug 19, 2005 2:11 pm
Posts: 628
Location: Cincinnati
I'm not sure exactly, but have you tried session.update(parent) or perhaps session.update(parent.getChildren()) to reattach it?

_________________
Chris

If you were at work doing this voluntarily, imagine what you'd want to see to answer a question.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 05, 2006 12:20 pm 
Regular
Regular

Joined: Wed May 11, 2005 11:57 pm
Posts: 80
kochcp wrote:
I'm not sure exactly, but have you tried session.update(parent) or perhaps session.update(parent.getChildren()) to reattach it?


Nope, I just tried and no luck - session.update(parent) gives the same behavior as lock(); the collections are not updated.

session.update( parent.getChildren() ) throws an exception, it looks like you can only pass an top-level entity to update(), not a collection.


Top
 Profile  
 
 Post subject: Re: Don't use refresh()
PostPosted: Thu Jan 05, 2006 1:21 pm 
Expert
Expert

Joined: Fri Aug 19, 2005 2:11 pm
Posts: 628
Location: Cincinnati
shmert wrote:
I've been having trouble with this too, but don't have a solution. One word of advice, stay away from Session.refresh(). It will try to refresh the related objects also, and when it gets to the deleted object, it will throw the following:

Code:
org.hibernate.UnresolvableObjectException: No row with the given identifier exists: [com.example.Subject#1033]


The only way to reliably refresh the object seems to be to open a new session and fetch a fresh copy of the object. Which is unfortunate, because then you have multiple versions of the same object floating around.


you could catch this error and ignore it if it is acceptable at this point in the application.

_________________
Chris

If you were at work doing this voluntarily, imagine what you'd want to see to answer a question.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 05, 2006 1:47 pm 
Beginner
Beginner

Joined: Sat Dec 17, 2005 1:24 pm
Posts: 42
Location: Berlin, Germany
jbarnum wrote:
Nope, I just tried and no luck - session.update(parent) gives the same behavior as lock(); the collections are not updated.

session.update( parent.getChildren() ) throws an exception, it looks like you can only pass an top-level entity to update(), not a collection.


I have not yet tested that, but from the documentation (section 10.11) I read that update() should work.

What is your cascade-style for the mapping between parent and child?

The next question is, if simply reattaching the collection is the right thing to do in the first place. If your collection is modified in the database and you simply reattach it, it is not clear in my opinion, that this change should be passed to my/your object silently. If I do not care about my object beeing changed, than re-reading it is the right thing to do, I believe.

All the best,

René


Top
 Profile  
 
 Post subject: Sample Code
PostPosted: Thu Jan 05, 2006 1:54 pm 
Newbie

Joined: Thu Jan 05, 2006 12:42 am
Posts: 3
Here's some sample code I have that replicates the problem I'm having (which sounds like the same issue).

Some background: the Instructor.courses relationship is defined as an indexed <list> in the hibernate mapping file, cascade is set to "all-delete-orphan". There are more than 2 related courses for the Instructor used in the test. This means that when a course is removed which is not the last one, the index columns for the higher courses are adjusted to fill in the gaps.

Code:
   
      // load the object into memory
      Session localSession = sessionFactory.openSession();
      Transaction tx = localSession.beginTransaction();
      Instructor localInstructor = new Instructor("Joe", "Blow");
      localInstructor.getCourses().add(new Course("Math"));
      localInstructor.getCourses().add(new Course("Science"));
      localInstructor.getCourses().add(new Course("Reading"));
      localSession.save(localInstructor);
      tx.commit();
      localSession.close();

      // delete a related object (in reality, this is happening on a different machine)
      Session remoteSession = sessionFactory.openSession();
      Transaction remoteTx = remoteSession.beginTransaction();
      Instructor remoteInstructor = (Instructor) remoteSession.load(Instructor.class, localInstructor.getId(), LockMode.UPGRADE_NOWAIT);
      remoteInstructor.getCourses().remove(1);
      remoteTx.commit();
      remoteSession.close();

      // now try deleting a related object on the local parent, whose 'children' relationship is out of sync
      localSession = sessionFactory.openSession();
      tx = localSession.beginTransaction();
      localSession.lock(localInstructor, LockMode.UPGRADE_NOWAIT);
      localInstructor.getCourses().remove(0);
      tx.commit();
      localSession.close();


The final call to commit() throws the following:
Code:
org.hibernate.StaleStateException: Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1


Due to this query:
Code:
Hibernate: update Course set instructorId=?, sortOrderForinstructor=? where id=?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 05, 2006 3:14 pm 
Regular
Regular

Joined: Wed May 11, 2005 11:57 pm
Posts: 80
ReneStolp wrote:
jbarnum wrote:
Nope, I just tried and no luck - session.update(parent) gives the same behavior as lock(); the collections are not updated.

session.update( parent.getChildren() ) throws an exception, it looks like you can only pass an top-level entity to update(), not a collection.


I have not yet tested that, but from the documentation (section 10.11) I read that update() should work.

What is your cascade-style for the mapping between parent and child?

The next question is, if simply reattaching the collection is the right thing to do in the first place. If your collection is modified in the database and you simply reattach it, it is not clear in my opinion, that this change should be passed to my/your object silently. If I do not care about my object beeing changed, than re-reading it is the right thing to do, I believe.


René, thanks for your response. I believe that 10.11 in the documentation is stating that updates will write to the database for the parent and its children, if cascading is configured. That works OK for me. In my case, the problem is with reading/refreshing the children.

I have my cascading for this relationship set to "all-delete-orphan,lock". I should also point out that this is for a many-to-many relationship; I have not tested it with a one-to-many relationship, but there is no mention of that making any difference in the hibernate documentation.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 05, 2006 3:19 pm 
Regular
Regular

Joined: Wed May 11, 2005 11:57 pm
Posts: 80
ReneStolp wrote:
The next question is, if simply reattaching the collection is the right thing to do in the first place. If your collection is modified in the database and you simply reattach it, it is not clear in my opinion, that this change should be passed to my/your object silently. If I do not care about my object beeing changed, than re-reading it is the right thing to do, I believe.


Sorry, I forgot to address the second part of your statement. You might be right that there are certain cases where you don't care about refreshing the collections for an entity, but there are many important cases where you do need that information (like deleting one of the objects in an indexed List). I do think that there should be some way in Hibernate to say "make my in-memory object match the contents of the database, including collections". It doesn't seem like this is the way that the lock(), update(), or refresh() methods work; I'm hoping that there is some other API that I can call to accomplish this.


Top
 Profile  
 
 Post subject: Re: How do I reattach entities with collections?
PostPosted: Tue Jan 10, 2006 1:19 am 
Regular
Regular

Joined: Wed May 11, 2005 11:57 pm
Posts: 80
jbarnum wrote:
Now I want to do something with Parent again. I create session2, start a transaction, and reattach Parent using session2.lock( Parent, LockMode.UPGRADE ) (we're using pessimistic locking). The problem is that Parent still thinks it has 5 Children. This causes all sorts of problems, especially if I try to delete one of the Children, which attempts to update the index column for those Children when the transaction is committed.

What is the correct way to reattach Parent so that it's collection of Children is accurate?


I found part of the answer. I did not have a version attribute set for Parent. Now I do, and so now when I call session2.lock( Parent, LockMode.UPGRADE ), at least I get a StaleObjectStateException. This lets me know that the collection may be out of date. However, I'm still not sure what method to call to keep my in-memory Parent and Children objects, but refresh the collection between them. What's the best way to do this? session2.refresh( Parent ) is throwing an exception because it is trying to cascade the refresh to the missing child (I have "all-delete-orphan,lock" as my cascade style).


Top
 Profile  
 
 Post subject: Re: How do I reattach entities with collections?
PostPosted: Tue Jan 10, 2006 9:18 am 
Regular
Regular

Joined: Wed May 11, 2005 11:57 pm
Posts: 80
jbarnum wrote:
jbarnum wrote:
session2.refresh( Parent ) is throwing an exception because it is trying to cascade the refresh to the missing child (I have "all-delete-orphan,lock" as my cascade style).


OK, found the second part of the answer. session.refresh() is doing the right thing, but I was holding a reference to the original List object and accidentally using that instead of the new List created in session.refresh(). Dumb mistake.


Top
 Profile  
 
 Post subject: Re: How do I reattach entities with collections?
PostPosted: Thu Jan 12, 2006 11:50 pm 
Regular
Regular

Joined: Wed May 11, 2005 11:57 pm
Posts: 80
jbarnum wrote:
OK, found the second part of the answer. session.refresh() is doing the right thing, but I was holding a reference to the original List object and accidentally using that instead of the new List created in session.refresh(). Dumb mistake.


Actually, I'm not so sure now. I think that what actually fixed my problem is that in the process of tweaking this, I disabled the cascading refresh. When I turned that back on, it broke again, so it wasn't the old List reference that was breaking it.

It seems like cascading refresh() is currently broken - it fails if it's called on a parent where one of the children has been deleted.

Should I report this on the Jira list?


Top
 Profile  
 
 Post subject: Re: How do I reattach entities with collections?
PostPosted: Fri Jan 13, 2006 10:21 am 
Expert
Expert

Joined: Fri Aug 19, 2005 2:11 pm
Posts: 628
Location: Cincinnati
jbarnum wrote:
jbarnum wrote:
OK, found the second part of the answer. session.refresh() is doing the right thing, but I was holding a reference to the original List object and accidentally using that instead of the new List created in session.refresh(). Dumb mistake.


Actually, I'm not so sure now. I think that what actually fixed my problem is that in the process of tweaking this, I disabled the cascading refresh. When I turned that back on, it broke again, so it wasn't the old List reference that was breaking it.

It seems like cascading refresh() is currently broken - it fails if it's called on a parent where one of the children has been deleted.

Should I report this on the Jira list?


did you remove the deleted child object from the collection in the parent object?

_________________
Chris

If you were at work doing this voluntarily, imagine what you'd want to see to answer a question.


Top
 Profile  
 
 Post subject: Re: How do I reattach entities with collections?
PostPosted: Fri Jan 13, 2006 12:54 pm 
Regular
Regular

Joined: Wed May 11, 2005 11:57 pm
Posts: 80
kochcp wrote:
did you remove the deleted child object from the collection in the parent object?


No, because my application doesn't know that the child object has been deleted in the database. This is happening when I try to call refresh() on a parent object whose child has been deleted by another process/user on the database. Part of what I'm trying to accomplish by calling refresh() is to update my list of children based on the contents of the DB.


Top
 Profile  
 
 Post subject: I have same problem in 3.0.5
PostPosted: Wed Apr 26, 2006 10:02 am 
Newbie

Joined: Sat Jun 11, 2005 8:35 am
Posts: 14
I ran into the same problem. If I do the deletes via the collections in the containing object - of course there is no problem. But if you do the deletes of child objects via mass HQL delete statement - and you try to do a refresh() on the parent - you will get the unresolvable exception every time.

Only way around this problem that I found was to evict the parent from the session and then cause it to be reloaded from the db.

Not sure if the refresh() is working as intended or if this is a bug in the 3.0.5 level.

_________________
Regards,

Scott Jacobs
srj@mindspring.com


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 15 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.