-->
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.  [ 23 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Business logic layers and lazy loaded collections...
PostPosted: Thu Jan 19, 2006 11:38 pm 
Beginner
Beginner

Joined: Thu Dec 08, 2005 6:49 pm
Posts: 49
I'm a little bit confused as to the best way to handle lazy loading in my tiered application.

For example:

My UI asks for a list of blog objects. Each of these blog objects maintains a lazy collection of posts. Since the session was disposed shortly before returning the list of blogs and my UI layer doesn't know anything about NHibernate, how do I go about reattaching a blog to a new session when the UI needs a list of its posts?

Unless I'm missing something, I can't think of a time when lazy loading would actually work, at least in a databound WinForms application.

Nato


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 20, 2006 12:42 am 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
One of the jobs of your business logic layer should be to ensure that all required lazily-loaded members are loaded before returning them to the UI/Action layer. If a particular report or form page doesn't require a field, then fine, otherwise access the field or call Hibernate.initialize to inflate it.

Various ways of attaching detached objects to sessions exist, depending on what you want to do. Read the javadocs for Session.merge, Session.replicate and Session.lock.


Top
 Profile  
 
 Post subject: Read "Open Session in View" Pattern
PostPosted: Fri Jan 20, 2006 12:50 am 
Regular
Regular

Joined: Tue Jan 03, 2006 7:21 am
Posts: 85
One way ppl do it in jave is implementing "Open Session in View" Pattern. Please read the following article. You can Google for more.

http://www.jroller.com/page/cardsharp?e ... ew_pattern


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 20, 2006 5:12 am 
Regular
Regular

Joined: Mon Jul 18, 2005 4:10 am
Posts: 92
Location: Poland
In the case of tiered application, you should carefully fetch all lazy associations in the DAL tier you need in the UI tier. For CriteriaQueries use .setFetchMode("association", FetchMode.EAGER), for HQL queries use "left join fetch obj.association" and for session.load use NHibernateUtil.Initialize(obj.association).

As far I know it is the ony way if you do not want your UI tier "know" about NHibernate.

_________________
michal


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 20, 2006 1:07 pm 
Newbie

Joined: Fri Jan 20, 2006 12:47 pm
Posts: 16
Location: Ottawa, On
There isn't much in the way of a definitive solution or best practice for lazy loading in a tiered app that I have been able to find. After a few months of playing, this is how I solved the problem.

I have a base DAO object which I use to wrap NHibernate sessions, session factory, etc. In this object I have a static method for initializing a lazy collection:

public static IList reconnectSession(object obj, IList aCollection)
{
bool loaded = false;
ISession sess = null;
try
{
PersistentCollection tCollection = aCollection as PersistentCollection;

if (tCollection != null && !NHibernateUtil.IsInitialized(tCollection))
{
sess = DataAccessManager.Instance().Session;
sess.Lock(obj,LockMode.None);
NHibernateUtil.Initialize(tCollection);
loaded = true;
}
}
catch (Exception e)
{
m_Logger.Error("Error lazy loading collection for obj " + obj.GetType(),e);
}
finally
{
if (loaded)
sess.Disconnect();
}
return aCollection;
}

In my DTO's, in the getter for each lazily loaded collection, I have code similar to the following:

public IList Addresses
{
get {
return BaseDAO.reconnectSession(this,m_Addresses);
}
set {m_Addresses = value;}
}


Now, when your UI layer accesses the collection (binding to a datagrid or list, etc), the getter is called, the collection is initialized and a hydrated colection is returned. Your UI knoes nothing of the NHibernate layer.

A couple of notes about this code, In the ReconnectSession method, DataAccessManager is simply a class that maintains a static SessionFactory for me to pull sessions from.

Also, and most important, the sess.Lock() call will reload all propoerties of the object that you are locking on. So, if your are using property access within your mapping file (default) the getter for your lazy collection will be called again, resulting in anoth call to Lock(), calling the getters, etc creating an infinite loop. You must set the access on the class in your mapping file to something other than property. I am using
default-access="field.pascalcase-m-underscore" in each class declaration that contains lazily loaded collections. This means that if my <property> name in the mapping file is Name, NHibernate will expect a member variable of that property's type in the DTO named m_Name. THe different types of default-access values are listed in the NHibernate docs.

Hope this helps


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 02, 2006 1:32 pm 
Expert
Expert

Joined: Fri Oct 28, 2005 5:38 pm
Posts: 390
Location: Cedarburg, WI
Thanks stuow78, this looks like best currently available solution to me. Unfortunately, we don't use DTO's and we don't want our entities to have their collection property getters to call any ReconnectSession() methods in any service layer.

Why can't the NHibernate.IInterceptor expose two new methods, OnLazyLoading and OnLazyLoaded? That way, we could put this session reconnect/disconnect logic there, and no reference to the interceptor need exist from the entity or DTO objects. This seems to be a simple solution to the problem -- am I overlooking something?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 02, 2006 4:02 pm 
Newbie

Joined: Fri Jan 20, 2006 12:47 pm
Posts: 16
Location: Ottawa, On
Hey Nels,

that's a really good suggestion ... I haven't checked the Hibernate 3+ docs to see if the Interceptor interface has been enhanced in later versions, but if not, try posting that suggestion to the NHibernate JIRA ... you never know, they may incorporate it into a future version.

This whole lazy loading issue puzzles me a bit as I think the same issue exists with proxies. I haven't played too much with proxies, but I think they have to be re-connected to a new session if they have become transient, the same as an un-initialized collection. From what I have read, it seems that alot of people are forcing initialization of all collections and proxies on the close of a session ... which, if you're using the session per request pattern in a web app, and using the same objects across a business process, or flow of pages, to me completely defeats the purpose of lazy loading and proxies.

I love the framework, but this seems like one of the few area's that needs a bit of tweaking. Keep me posted if you create a JIRA issue, I would definitely support it.

Stu


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 02, 2006 4:47 pm 
Expert
Expert

Joined: Fri Oct 28, 2005 5:38 pm
Posts: 390
Location: Cedarburg, WI
Our domain layer can define an ILazyLoader interface, and a singleton service class with a LazyLoader property. Our domain objects can then refer to DomainService.Instance.LazyLoader. Our data access layer can then implement ILazyLoader and register it with the DomainService singleton. That will eliminate any compile-time coupling.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 02, 2006 5:00 pm 
Newbie

Joined: Fri Jan 20, 2006 12:47 pm
Posts: 16
Location: Ottawa, On
I see and ReconnectSession() will be defined by the ILazyLoader interface and implemented by your DataAccess layer. That keeps the DAL and entities nicely de-coupled! Unfortunately, I already have mine built, but this is a nice solution for the next NHibernate project!


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 03, 2006 4:52 am 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
Automatic lazy loading was not implemented in Hibernate because it introduces ad hoc transaction semantics, i.e. you will have your entity coming from one transaction, but its collection will come from a completely different transaction, possibly with its elements changed. There was a page on the Wiki somewhere explaining this.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 03, 2006 10:16 am 
Newbie

Joined: Fri Jan 20, 2006 12:47 pm
Posts: 16
Location: Ottawa, On
Hey Sergey,

I understand your reasoning surrounding automatic re-connection, keeping re-connection within a developer's control does make sense.

The suggestion here was why not add the ability for a developer to be able to implement automatic re-connection via the Interceptor interface, if they so desire? This still keeps automatic re-connection within the developer's control and in most applications, I would bet that this method would be a much cleaner and more maintainable mechanism for re-connecting lazily loaded collections and proxies.

I don't have an in-depth knowledge of the NHibernate source, but I assume that the two suggested IInterceptor methods, OnLazyLoading and OnLazyLoaded could be called from within the NHibernate framework at a point where the object and collection, or proxy which requires the lazy loading, can be passed into each method, exactly as they are in ReconnectSession() method I described in my DAO above.

You can see then how much cleaner lazy loading would be within applications such as mine, which display large amounts of data and would simply not be useable without lazy loading.

I realize that these two methods are not part of the Hibernate 3 Interceptor either and maybe that would be a deterrent to putting them into NHibernate, but I could see them being useful in both the JAVA and .Net versions of Hibernate ....

Thanks,
Stu


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 03, 2006 11:23 am 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
Well certainly if you can justify adding those methods to the Hibernate team, I will add them after they do it. But I'm sure they had lots of similar requests and had good reasons to refuse, so I doubt that your case will be different.

Personally, it looks to me that you would be better off making the relationship explicit, using a query, rather than a collection. That would be a closer match to what you are trying to do. Hibernate treats collections as value objects belonging to the containing object, and it's conceptually wrong to have one part of the object loaded in one transaction and another part in another transaction.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 03, 2006 12:15 pm 
Newbie

Joined: Fri Jan 20, 2006 12:47 pm
Posts: 16
Location: Ottawa, On
Hey Sergey,

A couple of examples of how I am using lazy loading:

I have a profile, which can have multiple addresses (primary, secondary, mailing, etc). These addresses are mapped as a lazily loaded collection. I will have around a million profiles in the live system, so when doing things like searching and displaying search results I definitely do not want each returned profile (could be hundreds of thousands) to have it's addresses hydrated. There is a requirement to display the profile address on the search results page, but results are displayed in a DataGrid and therefore only the 25 or 50 results in the grid have their colection lazily loaded, none of the x number of remaining results.

The same scenario exists for multiple collections within the profile object such as payments, event participants, profile history, positions held, etc ....

Unless I write a custom paging framework, using a query to hydrate the addresses of my displayed search results isn't really feasible .. and that really isn't an appealing solution.

For the other collections, are you saying that it would be better to not have them mapped within the profile object, but when I want to display for example, a profile's positions held that I do a separate query at that point for all position held objects relating to the profile? And then I guess the maintenance of those position held objects would be done separately from the profile? (i.e. not via the collection and cascades as is the case now).

I did look at this sort of a data maintenance and relataionship model when I first started development (and was new to NHibernate and ORM) but I though the beauty and main objective of ORM and NHibernate was to have your object tree modelled, related and available through your objects. Querying was left for retrieving top level objects and things like searching and reporting?

I dunno, there are so many patterns and suggestions out there on how to best do things, am I completely off on how I interpreted ORM in general?


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 03, 2006 9:56 pm 
Expert
Expert

Joined: Fri Oct 28, 2005 5:38 pm
Posts: 390
Location: Cedarburg, WI
Sergey, I don't understand what the big concern is about "ad-hoc transaction semantics" -- i.e. your object was retrieved in one transaction and then a collection property of that object gets retrieved in a later transaction. So what? We don't have any need for all data being processed to have come from a single point-in-time from the database. The only case I can think of where object and collection being retrieved in separate transactions would be a problem is if certain types of collection members were illegal based on some other property of the owning object, and the object and collection members externally changed by the time the collection got loaded, making some illegal combination.

Unfortunately, trying to do our own automatic lazy loading won't work if the class is marked lazy. I.e. it won't work to make some call to our own LazyLoader in non-collection property getters, because the proxy property's getter that overrides the real class's getter tries to lazy load first, and it fails because the original session is disconnected or closed. The only way we can solve this is if NHibernate exposes and calls handler methods on the Interceptor (OnLazyLoading and OnLazyLoaded).

It looks like we'll have to hack NHibernate (or Castle.DynamicProxy?) to do this in the short run, because due to the way we've divided up responsiblities in layers, there's no way one layer can know what properties and collections another layer will need to access, and we can't keep sessions open across layers (at least not between the UI and lower layers). Anyway, I thought the whole point of ORM was that you could actually use objects returned from queries without knowing that they are persistable or lazy-loaded. How can this be realized without automatic lazy-loading (or turning off all lazy loading, which would render ORM useless since nearly the whole database would get loaded on any non-trivial query ...)


Top
 Profile  
 
 Post subject:
PostPosted: Mon Feb 06, 2006 4:55 am 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
I've been thinking through this a bit on the weekend, but I can't come up with a solution because I don't fully understand the context of your applications. Can you explain how you would solve your problems using plain ADO.NET? Then I could try to come up with a NHibernate solution.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 23 posts ]  Go to page 1, 2  Next

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.