views:

4167

answers:

3

I'm working on a project that has a rich object model with various sets of aggregate roots.

We're using the Castle stack (Monorail through to nHibernate with ActiveRecord).

We have marked the aggregate roots as lazy [ActiveRecord(Lazy = true)] and have customized 'eager' routines on our Repository to eager fetch an object graph. We use HQL to define eager fetches from our child collection of our root,

e.g. if Account is the aggregate root (and marked lazy loaded) we'll eager fetch Account .. Order .. Product entities for a complete graph.

So no surprises so far (hopefully).

Now if in the above example, Product is also marked [ActiveRecord(Lazy = true)], this seems to stop the eager fetch directive in the HQL.

Does anyone know a way to force the eager fetch of a lazy loaded child object ??

Cheers ian

Update:

Ok here's some example hql, Using the example from 'me.yahoo.com/../1' below, we're using IMuliQuery to reslove N+1 dependencies when fetching over many-to-many relationships. We're also explicitly using many-to-many mapping classes. As a result our hql is:

from Account a 'm eager loading the graph
inner join fetch a.AccountsOrders ao 
inner join fetch ao.Order
from Account a 'm eager loading the graph
inner join fetch a.AccountAddresses aa
inner join fetch aa.Address ad
where a.ID = ?

... so this executes the 2 sql statements and returns the required minimal rowset, and we can resolve this down into a single object graph. Nice.

But... if, say, Address was marked lazy loaded (and Order wasn't), accessing Order doesn't trigger a further sql statements, yet accessing Address does, despite the fact both are eager loaded.

So why isn't the lazy loaded entity Address, above, eager fetched by the above statement?

A: 

Do an "inner join fetch" on the Account.Order.Product entity. So instead of something like this (which is what you probably already have):

"from Account a inner join fetch a.Order where a.ID = ?"

Tell it to fetch the Order.Product as well:

"from Account a inner join fetch a.Order inner join fetch a.Order.Product where a.ID = ?"
+2  A: 

Why do you want the eager behavior?

All of the Relationship attributes in ActiveRecord have a 'Lazy=' parameter to tell ActiveRecord to lazy load the related object. All except BelongsTo. BelongsTo checks if the dependent object has Lazy=true in its ActiveRecord attribute and then creates a proxy for the object instead of doing a select or join.

For Lazy loading to work all methods and properties of the class instance need to be marked as virtual. This allows ActiveRecord to construct a dynamic proxy class.

Now it may sound like a good idea to fetch the complete graph for performance but in practice its probably slower. I have 3 good reasons why:

1.) BelongsTo has a Fetch option to define how related objects are pulled. FetchEnum.Join forces AR to use a Join. FetchEnum. Select forces AR to use separate select statements for each object. Joins are slow, we see a 10x performance improvement from switching to individual selects. There is no effectual difference in the client code between Lazy=true + FetchEnum.Select and eager.

2.) NHibernate does caching. If the object is already cached in the session or in the Level 2 cache it can be loaded form there and avoid the extra work.

3.) You would miss out on any benefits of lazy loading in cases where you didn't reference part of the object graph. Again you would do more work than necessary.

Gareth Farrington
I broadly agree but disagree with the need to ever need Eager behaviour....By default of course we set our Lazy attributes as required for 'everyday' usage of our objects. However certain methods / processes are data heavy and thus we Fetch 'large graphs' of data. These Fetch roles that we have are shaped to specfic needs and are shown to be more efficient (lower freqeuency of SELECTS viewable in NHProf and SqlProfiler).So in everyday object operations, sure we rely on the mark up. However for data-heavy operations we'll Fetch from one of a candidate of roles.
ip
A: 

From "NHibernate in Action", page 225:

NHibernate currently limits you to fetching just one collection eagerly.

That might explain the second query for fetching the addresses.

Pascal Lindelauf