Tìm kiếm Blog này

Chủ Nhật, 22 tháng 8, 2010

NHibernate Fetching Strategies

NHibernate has various fetching strategies that you can tinker with to improve the performance when fetching object graphs from the database. I wouldn't go nuts with a lot of pre-mature optimization, but at the same time it is important to know what NHibernate is doing behind the scenes when you are selecting domain entities from the database.
 
Based on NHibernate 1.2 documentation there are 4 fetching strategies:
  • Join fetching - NHibernate retrieves the associated instance or collection in the same SELECT, using an OUTER JOIN.
  • Select fetching - a second SELECT is used to retrieve the associated entity or collection. Unless you explicitly disable lazy fetching by specifying lazy="false", this second select will only be executed when you actually access the association.
  • Subselect fetching - a second SELECT is used to retrieve the associated collections for all entities retrieved in a previous query or fetch. Unless you explicitly disable lazy fetching by specifying lazy="false", this second select will only be executed when you actually access the association.
  • Batch fetching - an optimization strategy for select fetching - NHibernate retrieves a batch of entity instances or collections in a single SELECT, by specifying a list of primary keys or foreign keys.

Fluent NHibernate Configuration

Let's put together a quick example of using NHibernate and using Fluent NHibernate for configuration. This is a trivail example of fetching a category and the products associated with it. Using Fluent NHibernate we can configure the mapping of the Category and Product Business Objects to the database tables as such:

public class CategoryMap : ClassMap<Category>
{
    public CategoryMap()
    {
        WithTable("Category");

        Id(x => x.Id);
        Map(x => x.Name).WithLengthOf(50).CanNotBeNull();
        HasMany<Product>(x => x.Products)
            .IsInverse()
            .WithKeyColumn("CategoryId")
            .AsBag();
    }
}

public class ProductMap : ClassMap<Product>
{
    public ProductMap()
    {
        WithTable("Product");

        Id(x => x.Id);
        Map(x => x.Name).WithLengthOf(50).CanNotBeNull();
        References(x => x.Category, "CategoryId");
    }
}

We are not lazy loading the collection of Products when we load the Category by choice and may want to look at using an appropriate FetchMode to improve the performance of loading the various products when we load a Category.

Improving NHibenate Performance using an Outer Join FetchMode

By default, when NHibernate fetches a Category it will select the collection of Products in the Category in a separate Select Method. This is two round-trips to the database. We can change the FetchMode to FetchMode.Join to have the Products and Categories pulled down with a single round-trip to the database. The bad thing here is that each product row returned from the database will include the Id and Name Columns from the Category Table. This is redundant. However, since this is only two small fields, the good thing is that we have avoided two round-trips to the database which in this case is a better performing situation. This may not be better performing in all situations, but the beauty is that NHibernate allows us to decide for ourselves and configure the FetchMode accordingly:

using (ISession session = configuration.BuildSessionFactory().OpenSession())
{
    ICriteria criteria = session.CreateCriteria(typeof(Category));
    criteria.SetFetchMode("Products", FetchMode.Join);
    var categories = criteria.List<Category>();
}

Above we have overriden the Fetching Strategy to a FetchMode.Join which means the Category and its Products will be pulled down in a single database round-trip.

Conclusion

If you are looking to improve the performance of NHibernate, you may want to investigate the Fetching Strategies which affect the way NHibernate fetches your domain entities and the number of round-trips to the database.

Không có nhận xét nào:

Đăng nhận xét