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.  [ 14 posts ] 
Author Message
 Post subject: 2 Queries returning same object but != in code
PostPosted: Wed Feb 13, 2008 10:39 pm 
Senior
Senior

Joined: Sat Sep 03, 2005 12:54 am
Posts: 139
Hi,

We have are just testing an upgrade of our application from 1.0.4 to 1.2.1 and are experiencing a worrying problem.

We are executing 2 separate HQL queries against the same session instance and both of the queries return the same domain object (ID's are the same).

However, when we compare the objects using object1 == object2 we get the following results:

1.0.4 = true
1.2.1 = false

Are we missing something in the upgrade path from 1.0.4 or could this be a bug with 1.2.1? Obviously, my concern is that 2 separate instances of the same object in memory is a major issue. What happens when we the session is persisted to the DB, i.e. which in-memory object is in the session and therefore going to be updated in the DB?

Thanks,

Jason


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 13, 2008 11:04 pm 
Regular
Regular

Joined: Tue Dec 25, 2007 3:41 pm
Posts: 57
Location: Argentina
Could you provide us a testcase at Jira ?

Thanks and best regards

_________________
Dario Quintana


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 13, 2008 11:14 pm 
Senior
Senior

Joined: Sat Sep 03, 2005 12:54 am
Posts: 139
Just followed the links from the hibernate site to access the NH JIRA but get a page not found error with the following URL:

http://jira.nhibernate.org/secure/Dashboard.jspa

What is the correct address for the JIRA?

PS. Just did a full comparison test with 1.0.4, 1.2.0 and 1.2.1 and 1.0.4 is the only one that returns true for the equality test.

Am I right in assuming that 2 separate HQL queries executed against the same session should always return the same object instance?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 13, 2008 11:35 pm 
Regular
Regular

Joined: Tue Dec 25, 2007 3:41 pm
Posts: 57
Location: Argentina
Wait, did you override the Equals and GetHashCode correctly ?

_________________
Dario Quintana


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 13, 2008 11:49 pm 
Senior
Senior

Joined: Sat Sep 03, 2005 12:54 am
Posts: 139
We haven't overridden those methods at all in our objects.

I was under the impression that NH used the ID of your objects to ensure the uniqueness of an object within a session context...is this incorrect!?

This is what we were seeing under 1.0.4 but this seems to be different under 1.2.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 14, 2008 1:50 am 
Senior
Senior

Joined: Sat Sep 03, 2005 12:54 am
Posts: 139
OK...we have tracked down the cause of this problem...it is due to the change in default lazy loading behaviour in 1.2.

We have the following object relationship:

User 1 -- * CreditCard

In one particular use case, the following sequence of NH interactions is occuring:

1. CreditCard is loaded via HQL
2. Parent User of that CreditCard is loaded via HQL (not via navigation of a property on CreditCard)
3. Same User is loaded again via another HQL query

The result is that the User loaded in #2 is not equal to the User loaded in #3 when tested via ==. When the variables are inspected in debug, you can see that one of them is an auto-generated proxy type (due to lazy loading being on by default for that relationship) and one of them is the normal type as defined in our domain. This causes an inequality in the object instances.

We flipped the default-lazy="false" for all of our mapping files and the problem goes away. This is OK for us because we are migrating from 1.0.4 and we have explicity enabled lazy loading in places where we want it enabled.

However, on the surface, this doesn't seem to be operating correctly to me, i.e. getting back 2 different instances of the same object via HQL should not happen within the same session context.

Also, can someone please explain why lazy loading (by default) is good for many-to-one relationships? Doesn't it make sense for related objects to be loaded at the same time in a single SQL call rather than potentially requiring multiple hits to the DB as properties are accessed?

I can understand why lazy loading (by default) is obvious for collections but not for many-to-ones.

Thanks,

Jason


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 14, 2008 2:07 am 
Regular
Regular

Joined: Tue Dec 25, 2007 3:41 pm
Posts: 57
Location: Argentina
Quote:
However, on the surface, this doesn't seem to be operating correctly to me, i.e. getting back 2 different instances of the same object via HQL should not happen within the same session context.


The object identity isn't equals to same object (instance). The concept is a more deep.

If you need 2 persistent object be obj1.Equals(obj2) == true, you must override the Equals and GetHashCode to keed the CLR identity

_________________
Dario Quintana


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 14, 2008 2:47 am 
Senior
Senior

Joined: Sat Sep 03, 2005 12:54 am
Posts: 139
Shouldn't the session be smart enough to know if the object (via its identity) exists in its internal memory cache though and return this instance rather than a new instance?

Turn off lazy loading and this is exactly what happens...multiple HQL queries return the same *instance* of an object if the ID is the same. Turn on lazy loading though for many-to-one relationships and you can get back different instances because one is a proxy and the other is fully loaded.


Top
 Profile  
 
 Post subject: 2 Queries returning same object but != in code
PostPosted: Thu Feb 14, 2008 6:34 am 
Senior
Senior

Joined: Thu Jun 21, 2007 8:03 am
Posts: 127
Location: UK
Hi Jason,

I think NHibernate does return the same object instance from the session (course, I could be wrong). Are you by any chance using the 'this' reference to compare objects?

See discussions in:
http://forum.hibernate.org/viewtopic.php?t=982610
http://forum.hibernate.org/viewtopic.php?t=981912

Regards,
Richard


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 14, 2008 6:51 pm 
Senior
Senior

Joined: Sat Sep 03, 2005 12:54 am
Posts: 139
Hi Richard,

Thanks a lot for your pointers...I think you have identified the cause of the problem.

I am getting back the same object because I can see that the IDs are the same but one is proxied and one is not.

Essentially, we get back an instance of User via HQL and then call a method on that instance to perform validation. This validation then checks for uniqueness by executing another HQL and performing a comparison.

Pseudo code below:

User user = [get via Username]
user.Validate()

public void Validate()
{
User user = [get via Username]
if (user != null && user != this)
{
// Error
}
}


So, we are using "this" within the Validate method...is that the cause of our problems? Seems like a major gotcha...is this highlighted in the docs anywhere? The migration doc does have a warning about default lazy loading although it would be nice if it was more explicit about this pitfall.

Jason


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 14, 2008 8:06 pm 
Newbie

Joined: Wed Nov 23, 2005 11:16 pm
Posts: 3
Hi Jason,

This is an issue with Proxying by Interface rather than by Subclassing - Interface based proxies are always sensitive to this problem. It is not unique to NHibernate.

/Mats


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 14, 2008 8:29 pm 
Senior
Senior

Joined: Sat Sep 03, 2005 12:54 am
Posts: 139
Hi Mats,

Long time no speak (NPersist days).

Does this mean that it is never safe to compare object instances using "==" when lazy loading is involved?

So:

Quick fix = disable lazy loading
Harder fix = compare object IDs rather than instances

Does that sound about right?

Jason


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 15, 2008 4:28 am 
Expert
Expert

Joined: Tue Aug 23, 2005 5:52 am
Posts: 335
You need to watch out because comparing instance equality can get you into trouble on several fronts.

NHibernate *does* go to some effort to return the same instance within the same session, however once you are comparing entities from different sessions you'll be comparing different instances.

As a result using == is not really a great idea and you really *should* override Equals and GetHashCode if you want to compare entities. There are some pitfalls to this, however, if you use database assigned identifiers but these problems can be worked around.

There's a good Hibernate article that explains the reasoning and some possible solutions here: http://www.hibernate.org/109.html


Cheers,

Symon.

_________________
Symon Rottem
http://blog.symbiotic-development.com


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 15, 2008 8:47 pm 
Newbie

Joined: Wed Nov 23, 2005 11:16 pm
Posts: 3
Hi Jason,

Good to talk to you again, indeed too long! :-)

Since NHibernate, as far as I know, correctly enforces object uniqueness (has an Identity Map) and since the proxy will not store its own copy of the target's state (all calls are forwarded to the target, which holds the single copy of the state) then working around the lack of instance equality by implementing an identity based comparison should be safe.

The issue with interface based proxies, assuming you have an Identity Map, is not that it is unsafe to use identity based comparisons (because it isn't). The problem is rather that it limits what the mapper can safely be allowed to do. Specifically, features such as partially loaded objects and dirty tracking by interception (and, yes, inverse relationship synchronization) become unsafe, since a method in a partially loaded object that returns a direct, unproxied reference to "this" gives the caller a version of the object where the lazy loading on the still unloaded properties and the dirty tracking wouldn't work. But since NH doesn't allow partially loaded objects (afaik) and doesn't use interception based dirty tracking, you should be safe.

However, I should point out that considering the scenario of partially loaded objects etc in this context is is of course academic, because supporting these features also requires _all_ objects to be proxied - meaning you couldn't just "new" up objects yourself (you'd rely on an object factory in the session for creating new objects). Since NH, again afaik, explicitly wants to support that you use the "new" keyword to create new objects these limitations are implied from other tradeoff desicions than interface based proxies and so are not really relevant to a discussion about NH (except as an explanation of why it is indeed prudent to be alert to the dangers of interface based proxies, but also to see why the worries do not really apply to NHibernate).

The conclusion would be that interface based proxies are fine in a mapper that doesn't want to rely on object factories anyway (nor on other, perhaps even more exotic, AOP techniques like bytecode manipulation) and so in this case whether you'll be safe working around instance equality with identity equality comparisons depends completely on the implementation of the Identity Map in the Mapper...whereas for a mapper that sacrifices the new keyword to be able to support stuff like partially loaded objects and interception based dirty tracking, it would actually be a pretty bad idea to use interface based proxies since leaking references to unproxied targets becomes a real problem for such a mapper. In such a case, subclass based proxies are, imo, the way to go.

Hope this made some sense :-)

/Mats


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