I have the following objects:
Parent
public virtual Guid Id { get; set; }
public virtual DateTime TimeStamp { get; set; }
public virtual IList<Child> Childs { get; set; }
Child
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
I use Fluent to Map One To Many as follows:
.Override<Parent>(obj =>obj.HasMany(x => x.Childs)
.Cascade.All()
.Not.Inverse()
.Not.KeyNullable()
.Not.KeyUpdate())
I need to get up to all Parent with Childs between dates order by TimeStamp.
I am trying to do it as follows (maxCapacity is int):
QueryOver<Parent>().Where(x => x.TimeStamp > from)
.And(x => x.TimeStamp < to).OrderBy(x => x.TimeStamp).Desc
.Left.JoinQueryOver<Child>(x => x.Childs)
.TransformUsing(new DistinctRootEntityResultTransformer())
.Take(maxCapacity).List();
The result is not what I expected since the Take(maxCapacity) is not on the parent result but on the total query result which includes parent and child.
How can I get the latest X already transformed Parent rows?
Thanks.
The way I would go here is:
- load the list of root entity (Parent) and
- let NHibernate load their children lazily - in separated SQL query.
To avoid 1 + N issue, we can use smart mapping feature:
19.1.5. Using batch fetching
NHibernate can make efficient use of batch fetching, that is, NHibernate can load several uninitialized proxies if one proxy is accessed (or collections. Batch fetching is an optimization of the lazy select fetching strategy. There are two ways you can tune batch fetching: on the class and the collection level.
Batch fetching for classes/entities is easier to understand. Imagine you have the following situation at runtime: You have 25 Cat instances loaded in an ISession, each Cat has a reference to its Owner, a Person. The Person class is mapped with a proxy, lazy="true". If you now iterate through all cats and call cat.Owner on each, NHibernate will by default execute 25 SELECT statements, to retrieve the proxied owners...
So, the query should be like this:
session.QueryOver<Parent>()
.Where(x => x.TimeStamp > from)
.And(x => x.TimeStamp < to).OrderBy(x => x.TimeStamp).Desc
//.Left.JoinQueryOver<Child>(x => x.Childs)
// .TransformUsing(new DistinctRootEntityResultTransformer())
.Skip(start) // paging
.Take(maxCapacity)
.List<Parent>();
And the parent mapping should be like:
<class name="Parent">
...
<bag name="Childs" batch-size="3">
...
</bag>
</class>
Please, check also these:
- How to Eager Load Associations without duplication in NHibernate?
- NHibernate QueryOver with Fetch resulting multiple sql queries and db hits
- Is this the right way to eager load child collections in NHibernate