Search code examples
linqnhibernatehqleager-loadingqueryover

HQL and Session.Query ignores eager fetching defined in mapping


I have a problem with NHibernate not using my mappings configuration for eager loading a collection when I get something using HQL or Linq (Session.Query). Session.Get and Session.QueryOver is working like expected.

I'm using NHibernate 3.2. Here's the mapping of a collection in my Product mapping.

<bag name="OrderItems" inverse="true" cascade="none" lazy="false" fetch="join">
  <key column="order_id" />
  <one-to-many class="OrderItem" />
</bag>

and from the other side the mapping looks like this:

<many-to-one name="Product" class="Product" column="product_id" not-null="true" />

I have 4 Tests, 2 are successfull and 2 are not. They use Session.SessionFactory.Statistics to keep track of CollectionFetchCount (was OrderItems selected in 1 joined query or in a separate). The intent is to have OrderItems selected and loaded when selecting the product as OrderItems are almost always accessed as well.

LastCreated is a simple reference to the last product inserted into the DB.

[Test] /* Success */
public void Accessing_Collection_Using_Session_Get_Results_In_1_Select()
{

    // Get by Id
    var product = Session.Get<Product>(LastCreated.Id);
    var count = product.OrderItems.Count;
    Assert.AreEqual(0,statistics.CollectionFetchCount,"Product collectionfetchcount using Get");
}


[Test] /* Success */
public void Accessing_Collection_Using_Session_QueryOver_Results_In_1_Select()
{

    // Get by Id
    var product = Session.QueryOver<Product>().SingleOrDefault();
    var count = product.OrderItems.Count;
    Assert.AreEqual(0, statistics.CollectionFetchCount, "Product collectionfetchcount using QueryOver");
}
[Test] /* Fail */
public void Accessing_Collection_Using_Session_Query_Results_In_1_Select()
{

    // Get by IQueryable and Linq
    var product = Session.Query<Product>().Single(x => x.Id == LastCreated.Id);

    var count = product.OrderItems.Count;
    Assert.AreEqual(0, statistics.CollectionFetchCount, "Product collectionfetchcount using Linq");
}

[Test] /* Fail */
public void Accessing_Collection_Using_HQL_Results_In_1_Select()
{

    // Get by IQueryable and Linq
    var product = Session.CreateQuery("from Product where Id = :id")
        .SetParameter("id",LastCreated.Id)
        .UniqueResult<Product>();

    var count = product.OrderItems.Count;
    Assert.AreEqual(0, statistics.CollectionFetchCount, "Product collectionfetchcount using HQL");
}

Is this intended behaviour or am I doing something wrong?


Solution

  • HQL queries will not respect a fetch="join" set in mapping. This is because they are freeform queries, making it impossible for NH to guess how to transform them to add the join.

    Linq is implemented as a wrapper for HQL, QueryOver is a wrapper for Criteria; that's why you see the different behaviors.

    If you need eager loads in Linq/HQL, you will have to make them explicit in the query (using join fetch and Fetch()/FetchMany()