tags:

views:

553

answers:

3

I have a problem trying the following with nHibernate:

  • Eager loading.
  • Use selects (here is my problem, I can only do this using joins)
  • Filtering children.

Let's use an example where we have the RichPerson class that can have multiple: cars, houses, motorbikes and companies. All mapped to different entities with different tables.

If I try the following:

session.CreateCriteria(typeof (RichPerson))
    .Add(Expression.Eq("Id", someId))
    .CreateCriteria("Cars")
    .Add(Expression.Eq("Brand","Ferrari")

nHibernate will make an inner join in order to retrieve the Cars. If RichPerson some Ferrari cars, the join will send duplicated data.

Even with a .SetFetchMode("Cars",FetchMode.Select) nHibernate does a join.

I guess the join is because I ask: the rich person with id X and with Ferrari cars. Thus, the join is needed. But I just want to preload (somehow) the Ferrari cars. And use them as guy.Cars.

Uf! I don't know if I've expressed myself correctly. Anyway, thanks for your time reading this.

A: 

You have to do a join to test the criteria in a related table. There's no way around that. What you want to do is also pre-load the data in the related table, instantiating those objects immediately.

session.CreateCriteria(typeof (RichPerson))
    .Add(Expression.Eq("Id", someId))
    .SetFetchMode("Cars", FetchMode.Join)
    .CreateCriteria("Cars")
    .Add(Expression.Eq("Brand","Ferrari");

This can also be done with HQL like:

"SELECT p FROM RichPerson p inner join fetch p.cars c where p.id=:id and c.brand=:brand"
geofflane
That's the reason nHibernate does a join. But in fact no join is needed because the only criteria in the child it's the foreign key. For "Cars" the criteria is "RichPersonId". What I'm trying to achieve is to make nHibernate use the select instead of the join. (Thanks for the post though).
graffic
A: 

Are you're saying that the mapping for RichPerson has a Cars property with the FK of Brand, right? If so, that's a <many-to-one> than you're fine, you can do that and just skip the use of CreateCriteria and you'll be fine, since the Brand field is in the RichPerson table.

But if it is a <one-to-many>, which is what it looks like to me, than yes, you have to do the join since the Brand field is not stored in the RichPerson table, but in the Cars table, which is referenced back to the RichPerson.Id field that is in the Cars.RichPersonId (or whatever you call it) field.

If this doesn't make sense, post the mapping files and we can confirm the correct way for you.

Mufasa
It is <one-to-many>. Cars.RichPersonId column references the RichPerson.Id.
graffic
In that case than your original query that creates the join is the best query you can make in this case. And that's fine--as long as it's indexed properly, this is a completely normal and appropriate use of an RDBMS and is still properly normalized.
Mufasa
A: 

I just want to preload (some how) the Ferrari cars. And use them as guy.Cars.

It sounds like you have a lazy initialization problem here.

Here's an interesting post about it:

http://nhforge.org/wikis/howtonh/lazy-loading-eager-loading.aspx

In short, it looks like your query is fine. But immediately after your query (while the session is still open), try adding:

NHibernateUtil.Initialize(guy.Cars);

That will force the initialization of your Cars entity, so you can immediately use it.

Doug
Bingo :) That's what I'm doing.
graffic