NHibernate. Load Entity with 2 query and merge - nhibernate

I've got complex entity with a lot of children collection of objects, which are complex too:
public class Order : AdvancedBaseOrder, ICheckable
{
public virtual ICollection<RouteUnit> RouteUnits
public virtual ICollection<Invoice> Invoices
public virtual ICollection<Call> Calls;
public virtual ICollection<Payment> Payments;
......
}
My payment class aggregates a lot of other objects
public class Payment: ICheckable
{
public virtual A A;
public virtual B B;
public virtual C C;
public virtual D D;
......
}
I want get order with 2 queries:
load order entity without payments (FetchMode.Lazy)
load payments by order with joining its objects
combine\merger order with its payments
I don't want lazy load payments by nhib, cause I would like override fetch strategies for payment's objects.
So my question is how can i merge combine two result of queries in one aggregate
. Thanks

In this case, batch-size="25" setting could do the job for you. Read more in the documentation: 19.1.5. Using batch fetching.
batch size coulde be applied on a class or collection maping:
Payment class
<class name="Payment" batch-size="10">...</class>
Collection of Payments
<class name="Order">
<set name="Payments" batch-size="3">
...
</set>
</class>
How the batching works in a nutshell: NHibernates loads the set of all Orders. Then based on a batch-size setting (e.g. 25) creates few calls to Payments filtered by IDs of just loaded Orders:
WHERE OrderId in (#o1, #o2, #O3... #o25)
The merge will be done for you inside NHibernate session. From my experience this is most powerful mapping... Lazy & Batching.

Related

Eager loading an optional one-to-one with NHibernate

Consider the following simplified domain:
public class Movie
{
public virtual int Id { get; set; }
public virtual MovieDetail MovieDetail { get; set; }
}
public class MovieDetail
{
public virtual int Id { get; set; }
public virtual Movie Movie { get; set; }
}
A MovieDetail cannot exist without a Movie, but a Movie could exist without a MovieDetail (i.e. we have no details about it).
Our database has a separate table for Movie with columns Id, and a separate table for MovieDetail with columns Id and MovieId. There is also a foreign key from MovieDetail.MovieId to Movie.Id.
We've got this all mapped in NHibernate, but when getting a collection of Movie instances, we want a left outer join with MovieDetail. If not, we could have a N+1 problem when iterating over the Movie instances. That is the case now: there is a separate query for every call to the Movie.MovieDetail property.
I've tried one-to-one mapping, but that seems to be for the case when you have both instances. In our case, we don't always have a MovieDetail. Also, they don't share the same primary key.
I've researched formula's, but that would require me to make my MovieDetail implement IUserType, essentially putting NHibernate into my domain. I'd like to avoid that.
Maybe you could try adding a many-to-one relation in the Movie mapping to MovieDetail, it will act as a one to one mapping.
When you set the option 'not-null' to "false" it is also nullable I suppose.
I don't know if you are lazy loading or not, when this is so the MovieDetailis loaded when needed and not by a left join construction.
Shouldn't all the properties be virtual in both classes?
<many-to-one name="MovieDetail" column="Id" class="MovieDetail" not-null="false" lazy="false"/>
I'm in a bit of a hurry and I don't know if you can modify your domain / db schema but you might want to try and take a look at http://ayende.com/blog/3937/nhibernate-mapping-component.
It seems to me that a Movie can have at most one MovieDetail which might not be there. MovieDetail might have properties like Description, ReleaseDate, Actors, etc. I don't really understand why you separated these concepts. By bringing them together you would have 1 less table and 1 less FK to join on each time you want to list movies.
The component allows you to isolate your data into a separate entity while mapping to the same table as Movie.

How to ignore lazy="false" mapping?

I have following class:
public class PurchaseOrder
{
...
Store Store { get; protected set; }
User CreatedBy { get; protected set; }
User ApprovedBy { get; protected set; }
...
}
User and Store classes are mapped with lazy="false" option.
<class name="User" lazy="false">
More to say them associated to some other entities that mapped with lazy="false" option. And I am not allowed to change it. It's not "my" classes, them belongs to other domains.
My problem that every time that I fetch PurchaseOrder it produces additional queries for those classes and their dependencies leading me to SELECT N+1 problem. In 99% of cases I do not need all that information - store id and user id is enough for me.
Tried to use ...
session.CreateCreteria<PurchaseOrder>()
...
.SetFetchMode("CreatedBy", FetchMode.Lazy)
.SetFetchMode("ApprovedBy", FetchMode.Lazy)
.SetFetchMode("Store", FetchMode.Lazy)
...
.. but it does not help.
How can I force those associations to be lazy and to ignore lazy="false"?
Just to be clear. The information that stored in User and Store classes is useless for my domain. So I do not want it to be fetched at all. Also if I join users and stores in my query it will lead to chain of queries for entities that also mapped as lazy="false" (do not ask why, I can't change it). I aligned to some company's standard and must reference those classes (I do not like it but this is a rule), but I do not want to fetch their data, i want it lazy.
You can't override lazy="false".
What you can do is the exact oposite of what you did: fetch eagerly (FetchMode.Eager or FetchMode.Join, which are synonyms).

Programmatically ignore children with nHibernate

I'm just trying to get my head around nHibernate and have a query. When setting up the mappings file (with Fluent or regular .hbm.xml files) you specify relationships (bags; one-to-many, etc) and sub-types - the idea being (I believe) is that when you fetch an object it also fetches and matching data. My question is can I programmatically tell my query to ignore that relationship?
So, below, there is a Foo class with a list of Bar objects. Within the mappings file this would be a one-to-many relationship and sometimes I want to retrieve a Foo with all Bars BUT sometimes I want to just retrieve the Foo object without the Bar, for performance reasons. How can I do this?
public class Foo { public int Id { get; set; } public List<Bar> { get; set; } }
public class Bar { public int Id { get; set; }
Cheers
The relationship shouldn't be loaded automatically unless you turn off Lazy Loading or specify it to be eager loaded in the query.
Edit:
To answer your questions in the comment below.
1) It's done as part of the query. An basic example using QueryOver in NHibernate 3.0 would look something like:
var result = Session.QueryOver()
.Fetch(x => x.Category).Eager
.Where(x => x.Price > 10)
.List();
I think with ICriteria it's "SetFetchMode("Category", FetchMode.Eager)"
2) If you turn off lazy-loading on the mapping for an object, it will effectively always be eager loaded. Tho I suggest you eager load on a query-by-query basis to avoid the possibility of having a massive chain of data loaded, or loading data you don't actually need.

Nhinerbate lazy loading of reference entity

I have this scenario:
class A
{
public virtual int Id { get; set; }
public virtual B Child { get; set; }
}
class B
{
public virtual int Id { get; set; }
}
In the mapping of class A, I have a reference to class B:
map.Reference(a => a.Child).LazyLoad();
Now when I do something like:
Session.Query<TypeOfA>().Select(a => a);
Apart from the normal select * from ATable I get n selects from the BTable for each A line. Is like lazy loading is not working.
My questions are:
How to I make the lazyload work here ?
Can I bring the A entities and B entities in a single query ?
Thank you,
Lazy loading is switched on by default and should actually work. If there would be a problem, for instance if it can't generate the proxy for class B, it would complain when creating the session factory.
Are you sure that the queries for B are done by the query itself, and not be subsequent access to A?
You could optimize the access to B in two ways: fetch them together with A in a single query. (I don't know fluent, this is the xml way to configure it:)
<many-to-one fetch="join" ...>
This has some problems when used with lists and could also blow up your query a lot. It is of course not lazy loading at all.
Another, very nice and powerful optimization is batch fetching. It allows the instances to be fetched in separate queries, but fetches several of them at once.
<class name="B" batch-size="20" ...>
This would fetch 20 B's at once in one query. It is also available for lists:
<one-to-many fetch-size="20" ...>
Expanding on Stafan's suggestion to use batch-size, a quick Google search reveals that Fluent NHibernate now supports BatchSize for queries. From the docs:
ClassMap<T> BatchSize(int size)
Sets the query batch size for this entity.
Never used it myself, and the documentation is minimal (like a lot of FNH), but maybe you can find some sample code.

nHibernate Collection Count

I have the following model which I have created and mapped with nHibernate.
Using lazy loading so I don't need to get the Vehicles for the Dealer at the start.
Public class Dealer
{
public virtual string Name { get;set;}
public virtual IList<Vehicles> Vehicles { get;set;}
}
Now let's assume the Dealer has thousands of vehicles.
If I do Dealer.Vehicles.Count then NH will select and pull all the data.
What is the best way to simply get a count? Is there any way in which I can get a count with out declaring A new property dealerCount within the Dealer Class?
Also there is a feature in Hibernate which I believe will be implemented in a newer version of NH called Extra Lazy Loading. Would this solve the problem?
extra lazy loading would issue sql instead of populating the collection for certain operations such as Count or Contains. In fluent mappings its used as:
HasMany(x => x.CollectionProperty).ExtraLazyLoad();
or HBM
<one-to-many lazy="extra" ...
It's only usefull if you have large collections and need the special behavior.
Use count projection (Projections.RowCount)