-->
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.  [ 9 posts ] 
Author Message
 Post subject: Lazy initialization returns CProxy if obj was loaded before
PostPosted: Thu Jul 05, 2007 7:29 am 
Newbie

Joined: Mon Jun 04, 2007 6:15 pm
Posts: 9
Hi All.

I have a problem. If my instances was loaded previously via some relation by the NHibernate session and I get to a lazy collection where the same instance is referenced to, after initializating my collection contains the proxy object instead of the originally created object.

As far as I can figure out I only have 1 Session. Since I do not close the session and I only request 1 session ever from the sessionfactory. From the reference manual:

Quote:
This only applies if these objects are loaded in two different ISessions, as NHibernate only guarantees identity
( a == b , the default implementation of Equals()) inside a single ISession!


I tried to overide GetHashCode and Equals in my objects, I use a hash of a Guid on the object which is not the key . As stated in the manual.

Why am I always getting the CProxy type object if it was loaded before? Is this the normal behaviour ? Is it possible to make NHibernate always return the entity instance.

There is a similar problem discussion on
http://forum.hibernate.org/viewtopic.php?t=972826&highlight=cproxytype

The reason I do not want to work with the proxy object is that the software I wrote does not like it. I would much rather try and solve the problem at the loading part than try to work around it.

I am using NHibernate 1.2.0.GA with Attribute mappings.
Also my Proxy is not interface type but sits on the class itself.

Thanks in advance.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jul 05, 2007 9:08 am 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
You can use ISession.Get, it will never return a proxy.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jul 05, 2007 11:19 am 
Regular
Regular

Joined: Wed Apr 25, 2007 4:18 am
Posts: 51
Location: Belarus, Gomel
Hi sergey!

But what will be in assosiated objects?
So if I have class ObjA that have property ObjB RefToB - what will be created for ObjB after ISession.Get(typeof(ObjA), idA)? And moreover - As documentation state "If the instance, or a proxy for the instance, is already associated with the session, return that instance or proxy." so if ObjA was loaded (indirectly) before this call - I'll get proxy...

Maybe the only reliable way is to use queries with well defined fetching strategy - and in no case allow "automatic" assosiated instances creation?

_________________
WBR, Igor


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jul 05, 2007 1:51 pm 
Newbie

Joined: Mon Jun 04, 2007 6:15 pm
Posts: 9
Hi sergey / IgorK

Sergey, I see you are quite the expert (lots of credits!!!) Hopefully you can help Igork and myself. (IgorK. not that I think you need help)


I could use Session.Get , but how do I use that to initialise sets / lists automatically when i need it to happen? I currently use a function as follows in my own 'session'.

Code:
        public virtual void LazyInitCollection(System.Collections.IEnumerable Collection, Framework.Objects.FrameworkObject parent)
        {
            this.Session.Lock(parent, NHibernate.LockMode.None);
            //iterate over the collection in order for lazy loading to work

            //if (!NHibernate.NHibernateUtil.IsInitialized(Collection))
            //{
            //    NHibernate.NHibernateUtil.Initialize(Collection);
            //}
            foreach (Framework.Objects.FrameworkObject obj in Collection)
            {               
                obj.SetSession(this);
                obj.SetRoot(parent.GetRoot());
            }                                             
        }


As can be seen there i must first Lock onto the parent. I do not know why I must lock because I do not close the ISession anywhere.

My frameworkobject is an abstract interface implementation to perform common functions like GetUUID() etc.. It is not a persistable class.

Now it is there that if I could never get a CProxyType object in my collection I will be very happy.
I would be ever more extatic if the object is the exact same instance as the previously loaded one.

Thanks.
Sergey I see you are from the Hibernate team. I must congratulate you and your people with an excellent product. You people did alot of amazing stuff!


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 06, 2007 9:14 am 
Regular
Regular

Joined: Wed Apr 25, 2007 4:18 am
Posts: 51
Location: Belarus, Gomel
Hi tman!

I'm afraid you can't overcome existing behaviour - if session cache contain proxy for some object it will always return it - proxy is perfectly valid "entity" from NHibernate point of view. If you can remove proxy from session cache
Code:
ISession.Evict(proxy)
- then you will get "real object" even with simple
Code:
NHibernateUtil.Initialize(somePersistentCollection)
call.
But evicting proxy is dangerous thing - if you still have some reference to this proxy, it may fail upon access (as it will be disconnected from session), and, what may be more complex problem, you may have 2 different objects that represent single "database row" - evicted proxy and newly created "real object". NHibernate can't do any magic and can't "replace" all references to this evil proxy with references to good real object.

As of sergey's statement, I'm afraid it is a bit incorrect:

Code:
// Master class have IList<Detail> Details property,
// Detail class have backlink Master Master property.
// session is newly created and so is empty (clear) one
Detail detail = session.Load<Detail>(1);
// detail will be proxy here
Detail detail1 = session.Get<Detail>(1);
// detail1 will be proxy! and it will be the same object as detail
// I mean references will be equal - so there will be just one underlying object.
Detail detail2 = session.Get<Detail>(2);
// here we get "real object" - as Detail with id=2 wasn't in session cache.
Master master = session.Get<Master>(1);
// now master.details is uninitialized collection
NHibernateUtil.Initialize(master.Details);
// collection will be created and filled - and it will contain "real objects"
// for all the deails except one with id=1.
// Moreover detail with id=2 will be the same object as previously requested detail2

_________________
WBR, Igor


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 10, 2007 10:18 pm 
Newbie

Joined: Thu Jun 02, 2005 10:58 pm
Posts: 5
Hi All,

I also have a similar situation however I use HQL to load an object tree.

As we can only eagerly load one collection per HQL query, I have to perform a number of HQL queries to eagerly load the collections that I need.

As the object I'm building up is in the first level Hibernate cache, each HQL query builds up the object tree in the cache. I can then pass this object back from my Data Access Layer and it is fantastic.

The only problem is when an object is indirectly loaded by an earlier HQL query and it is stored in the Hibernate cache as a proxy. Any subsequent HQL query returns a proxy even when I specify a "left join fetch".

The only way I can get around this is to ensure that I anticipate these situations and re-order the HQL to eagerly load the objects in earlier HQL queries.

This solution is not ideal from a maintainability point of view.

Apart from doing evicts or immediate loads, do you guys know of any other solutions to this?

Thanks for your help.

Regards,
Gaj


Top
 Profile  
 
 Post subject: Seems currently there is no quick solution
PostPosted: Wed Jul 11, 2007 4:15 am 
Newbie

Joined: Mon Jun 04, 2007 6:15 pm
Posts: 9
Hi All

It seems there is no quick solution currently for the single ones of us who coded our projects in such a way that this is a problem for us.

The only way seems to be to try and make your code proxy friendly or find some other workaround (IgorK). This is quite sad. I would really love to see an option to never receive a proxy in NHibernate. Well it still workes great for me I just have to work around the problem.

Where would I look in the NHiberNate source to even try to implement such an option?

I've been scratching around in Loader.cs but could not yet figure out exactly at which point one can insert such functionality.

IgorK thanks for all your comments so-far. I'll give you a credit.


Top
 Profile  
 
 Post subject: Seems currently there is no quick solution
PostPosted: Wed Jul 11, 2007 5:07 am 
Newbie

Joined: Mon Jun 04, 2007 6:15 pm
Posts: 9
Hi All

It seems there is no quick solution currently for the single ones of us who coded our projects in such a way that this is a problem for us.

The only way seems to be to try and make your code proxy friendly or find some other workaround (IgorK). This is quite sad. I would really love to see an option to never receive a proxy in NHibernate. Well it still workes great for me I just have to work around the problem.

Where would I look in the NHiberNate source to even try to implement such an option?

I've been scratching around in Loader.cs but could not yet figure out exactly at which point one can insert such functionality.

IgorK thanks for all your comments so-far. I'll give you a credit.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 11, 2007 6:10 am 
Beginner
Beginner

Joined: Tue Jan 02, 2007 5:53 pm
Posts: 42
Location: Bergen, Norway
Hi!

I'm no expert here, but I think I would start looking in NHibernateUtil and the method Initialize(), and navigate from there to other classes and methods used to fetch data from DB or identity map.

I think that the method is currently working correctly and that it should return the proxy if it exists in the identity map. With that being said I also think that there should be another method to easily detect and initialize proxies in n-depth equal to your default fetch level. This means that the object returned from the identity map should be checked to see if it is a proxy as well, potentially evicted and reloaded.

From an architectural point of view, I believe the easiest thing to do would be to test if you can change the default fetch plan defined in your mappings. Try using lazy only on collections and modify default fetch level. This might help some, but of course not totally remove the problem. It might also help to have well defined points in your architecture where you open and close sessions, and of course a nice way to transparently handle the LazyInitializeException for the few cases where you are unable to predict the data you needed to fetch and want to load it automatically on demand.

_________________
Cheers,
Steinar.


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