-->
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.  [ 13 posts ] 
Author Message
 Post subject: Watch out for non-deep copy when Hibernate processes List
PostPosted: Thu Mar 16, 2006 4:38 am 
Newbie

Joined: Thu Mar 16, 2006 4:20 am
Posts: 3
Location: Sydney
Hi there,

here's a solution to a problem I was having that cost me a couple of days. I'm just sticking it here since it could help others in the future.

Basically, I have a class containing a List of children objects. This is a uni-directional one-to-many (parent/child) relationship. This simple relationship appears to be configured properly but hibernate was never persisting the children on a save of the parent. No inserts or updates were being generated.

The reason was down to the implementation of my setChildren method which basically looked like -
Code:
public void setFieldDefinitions(List fieldDefs) {
    fieldDefinitions.clear();
    fieldDefinitions.addAll(fieldDefs);
}


i.e. it would clear the existing List and add the newly supplied elements from the parameter List.

After going through the debugger I observed that as Hibernate processes all the properties to be persisted on an object, it takes a copy of any Collection classes. While the method doing this is called 'deepCopy' it doesn't really. It just picks up the same reference to the List belonging to my parent class.

Later, after processing, Hibernate sets the processed versions of all properties back onto the object being persisted. With my implementation of setFieldDefintions clearing the list first, this meant that the new value being set was also cleared (it's a reference to the same List). All the children were removed.

This seems a bit rude of Hibernate, but obviously the fix is easy. Re-implement setFieldDefinitions as -
Code:
public void setFieldDefinitions(List fieldDefs) {
    fieldDefinitions = fieldDefs;
}


That's all. Any comments are welcomed.

Cheers,

Paul C


Top
 Profile  
 
 Post subject: Collection property getters must return internal reference
PostPosted: Fri Mar 17, 2006 10:44 am 
Newbie

Joined: Thu Mar 16, 2006 4:20 am
Posts: 3
Location: Sydney
In a similar vein to above, here's another problem which bit me today.

Basically, after performing a query to retrieve an object and then closing the session I was receiving the error -
Quote:
org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: metadata.ActivityDefinition.fieldDefinitions


I didn't make any changes to the ActivityDefinition object retrieved from the query, so was a bit confused by this.

Here's the reason though; my getter for fieldDefinitions was implemented as follows -

Code:
public List getFieldDefinitions() {
    if (null == fieldDefinitions) {
        return Collections.EMPTY_LIST;
    }
    return Collections.unmodifiableList(fieldDefinitions);
}


If I change the implementation to just return the actual fieldDefinitions reference then there is no problem; Hibernate does not think the fieldDefintions Collection has been deleted.

So I guess this is another gotcha for newbies like myself to watch out for. Combined with my original posting, the lession is that your property getters and setters need to allow direct access to the Collection being stored by the object. You cannot try and protect this internal structure.

Cheers,

Paul C


Top
 Profile  
 
 Post subject:
PostPosted: Mon Apr 24, 2006 4:51 pm 
Beginner
Beginner

Joined: Thu Feb 23, 2006 11:22 am
Posts: 30
Paul.. I don't know what to say.. You are my Hero...

Dude.. You know people that think beyond themselves, they are what makes the world a decent place to live. I wish that there were more people like you out there.. Man.. I have all this code I wrote, schedule was tight, staying up late and everything was going great and then. This problem hit me.. Oh man.. I posted a question and did not get any replies. That was my fault, I admit that, way to long of a post, but I was desperate. I was starting to feel pretty bad.. Then I thought, what the heck, lets do one more search and BANG. Your entry came up.. Gosh man, you are on the other side of the planet and you probably heard me scream when it worked.. Thanks man.. Thanks.. People like you are a privilege to know. I just wish I really, actually, did know you, but hey your post was a real help to me and I wanted to thank you. Man I knew it was something in that section but it seemed right to me, if not unusual, but as you pointed out, it was wrong.. I should have know but.. I don't know.. but anyhow.. Nice work Paul and thanks for thinking about the rest of us.. Nice work man.. I am going to refer to your writeup in my post..
ray


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 25, 2006 1:28 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 6:10 am
Posts: 8615
Location: Neuchatel, Switzerland (Danish)
dog - you are hilarious ;) ...btw. where is my receipt for the donation ? :)

_________________
Max
Don't forget to rate


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 25, 2006 3:22 pm 
Beginner
Beginner

Joined: Thu Feb 23, 2006 11:22 am
Posts: 30
Thanks Man.. I try and have fun. Sometimes it is hard, well most of the time it is hard, real hard but I give it a good effort.. Oh yeah I am, of course, a little crazy. To much time spent in front of the computers.. The rest of you take that as a warning from an elder of the faith. All the way back when a PDP-11 and PDP-10 where hot and I did too much coding in Octal.. Yep following bit 13, the bit that recursively dereferenceed the address. In the Mill, in Maynard, MA. What we called the Blessed Realm. Digital Max, Digital.. The greatest company that ever was, Max. The greatest company that ever was, and ever will be.. God Bless Ken Olsen, an angel Max, sent from God. Do you know octal Max.. Hand assembling code.. Then toggling it into the front panel.. That kind of scrambled my brains. That is before assemblers really even existed.. The good old days.. The very best days of my lifeā€¦

I just got the paperwork from Food for the Hungry but I don't know how to get it to you.. I did, as promised make the donation for you. 50 bucks as promised.

Max if you send me an email I can email it to you.. or.. I could create an account on my ftp server at home and give you the password but then everyone else will have it. Can I email it to Customer Support and will they send it to you. I don't know how to get you that paperwork so that you can see I am not full of.. .. .. .. caca.. yeah caca..

I tried to figure out a way to embed the image into this but never got it to work...

good dog..
ray_lukas@comcast.net
send me an email.. I will send you the paperwork and prove my word.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 25, 2006 5:04 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 6:10 am
Posts: 8615
Location: Neuchatel, Switzerland (Danish)
just click at the email icon under my name and you can send it there. ok ?


assembler is fun - but soo long ago.

octal is evil ;)

_________________
Max
Don't forget to rate


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 09, 2007 5:29 pm 
Newbie

Joined: Mon Mar 06, 2006 3:17 pm
Posts: 5
I had a similar problem, but with the updates.

I was using a method like setFieldDefinitions to replace the existing list. It seems hibernate didn't like that too much. I had to create another method like your original one:

public void replaceFieldDefinitions(List fieldDefs) {
fieldDefinitions.clear();
fieldDefinitions.addAll(fieldDefs);
}

If anyone has a better solution, then let me know.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Feb 10, 2007 9:08 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 6:10 am
Posts: 8615
Location: Neuchatel, Switzerland (Danish)
there is a big difference between doing col.clear(); than doing col = new ArrayList();

one keeps track of what actually happend; the other just lost all track of what happend....

_________________
Max
Don't forget to rate


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 30, 2007 5:31 pm 
Newbie

Joined: Fri Mar 30, 2007 5:23 pm
Posts: 16
Location: New York, NY
There's actually a way around this problem, if you want to continue to protect your collections from erroneous mutation but still let Hibernate do its thing. For me, it's quite important to do that as I keep all of my associations bidirectional, and provide add() and remove() methods that always perform those operations bidirectionally; allowing direct access to my collection objects allows for the possibility of modifying a direction in only one association, thus breaking bidirectionality.

And for the fix? Simple as adding the access="field" parameter to the <set> node that maps the collection in question. This will force Hibernate to look directly at the object's field, rather than using the getter/setter functions (this will work regardless of the field's visibility). So, users of your class will only be able to access the copy of the collection using the getter/setter functions, but Hibernate can look directly at the field and get the real thing.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Nov 19, 2007 2:09 pm 
Beginner
Beginner

Joined: Fri Jul 15, 2005 12:26 pm
Posts: 37
In addition, its not always the setter. I had a setter that looked like this:

Code:
public setChildren(Set children) {
    this.children = children
}


and I was getting the same error. I fixed it by using a getter instead:

Code:
public getChildren() {
    children = Util.toNonNull(children);
    return children;
}


I did parent.getChildren().add(child) instead of parent.setChildren(children);

Just another way to shimmy...


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 28, 2007 5:54 am 
Newbie

Joined: Wed May 03, 2006 5:28 am
Posts: 2
Another way to solve the problem, if your application logic leaves you do that, is to clear the collection, than do a flush, and then fill it again.
For example, when repopulating some objects, I first clear all collections, than flush, than insert new values.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 19, 2007 6:53 am 
Newbie

Joined: Wed Dec 19, 2007 6:49 am
Posts: 8
Location: London
Similar problem,

I wanted to ensure that it was not possible to alter the given set of child objects out side of the parent, hence I was returning an unmodifyable Collection or Set, like so:

Code:
    public Set<Child> getChildren() {
        return Collections.unmodifiableSet(children);
    }


Hibernate doesn't like this and throws a org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance:

Just returning the set rather than making it unmodifyable solves the problem.

John.


Top
 Profile  
 
 Post subject: Re: Watch out for non-deep copy when Hibernate processes List
PostPosted: Thu Dec 16, 2010 5:38 am 
Newbie

Joined: Thu Dec 16, 2010 3:07 am
Posts: 5
I really have to say that I am so debt to you. Really! If it was not for you, I would have been still searching all over the internet with no hope. I have to say I was awestruck when this issue came in. I had no clue as to what to do in order to get out of the problem. I, hence was so desperate to find out the tonic for this as fast as I can because it was affecting by work as well! Thankfully, I ended up here and I got the solution! Thanks Paul!

_________________
Stereolithography


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