Wednesday, July 17, 2013

NHibernate cascade save and how many queries are produced

The simplest configuration for a cascade save in nhibernate looks following.
public class CUrlMap : ClassMap<CUrl>
{
    public CUrlMap()
    {
        Table("CommonUrl");
        Id(x => x.Id);
        Map(x => x.CommonUrl);

        HasMany(x => x.CMarketUrls)
            .Inverse()
            .Cascade.All()
            .KeyColumns.Add("CommonUrlId");
    }
}

public class CMarketUrlMap : ClassMap<CMarketUrl>
{
    public CMarketUrlMap()
    {
        Table("CommonMarketUrl");
        Id(x => x.Id);
        Map(x => x.MarketUrl);
        Map(x => x.MarketId);

        References(x => x.CUrl).Column("CommonUrlId");
    }
}

One CUrl has many CMarketUrls. The goal is to create/update/delete everything in lowest possible number of steps. Unfortunately it is not really beautiful from a code and SQL point of view.
The simple code to save CUrl
using (var trans = _repository.BeginTransaction())
{
    _repository.SaveOrUpdate(curl);
    trans.Commit();
}

The first thing is that CommonUrlId column (a foreign key) in SQL must allowed nulls. It is because what Nhibernate does during creation of CUrl is it first creates it and then it creates all CMarketUrl that it keeps in a collection (CMarketUrls). It means that total number of queries is:
  • 1 x insert to CUrl
  • K x insert to CMarketUrls (where K is number of CMarketUrl that are kept in CMarketUrls)

But we are not done yet, the code listed above will save everything but it will not assign Id from a freshly created CUrl to also freshly created CUrlMaps. Programmer needs to map this Id manually during creation. It complicates code, and now it looks:
using (var trans = _repository.BeginTransaction())
{
    _repository.SaveOrUpdate(curl);
                    
    foreach (var cMarketUrl in curl.CMarketUrls)
    {
        cMarketUrl.CUrl = curl;
        _repository.SaveOrUpdate(cMarketUrl);
    }
    trans.Commit();
}

What nhibernate will do is after creation of new records it will update CMarketUrls and set to them newly created Id for CUrl.

It all means that each time when we create a new CUrl with some CMarketUrls the total number of queries is following:
  • 1 x insert to CUrl
  • K x insert to CMarketUrls (where K is number of CMarketUrl that are kept in CMarketUrls)
  • 1 x batch update (in this one batch all CMarketUrls are updated, CUrl is set to them)

As always there is plenty of place to improve nhibernate.

No comments: