NHibernate one-to-one

time to read 38 min | 7539 words

I should start by saying that <one-to-one> is not a recommended approach, but the question came up in the Castle mailing list, and I set to investigate. There are scenarios where this is the only choice, but in general, prefer to avoid it. Here are our entities:

[ActiveRecord("Users")]

publicclassUser : ActiveRecordBase<User>

{

       privateBlog blog;

       privateint id;

       privatestring name;

 

       [PrimaryKey]

       publicvirtualint Id

       {

              get { return id; }

              set { id = value; }

       }

 

       [Property]

       public virtual string Name

       {

              get { return name; }

              set { name = value; }

       }

 

       [OneToOne]

       publicvirtualBlog Blog

       {

              get { return blog; }

              set { blog = value; }

       }

}

 

[ActiveRecord]

public class Blog : ActiveRecordBase<Blog>

{

       private int id;

       private User user;

       private string name;

 

       [PrimaryKey]

       public virtual int Id

       {

              get { return id; }

              set { id = value; }

       }

 

       [BelongsTo("`User`")]

       public virtual User User

       {

              get { return user; }

              set { user = value; }

       }

 

       [Property]

       public virtual string Name

       {

              get { return name; }

              set { name = value; }

       }

}

Now, let us see what happens when I try to run this code:

using(new SessionScope())

{

       User user = User.Find(u.Id);

       Console.WriteLine(user.Name);

}

For this, NHibernate will generate the following query:

declare @p0 int;

set @p0 = '1';

SELECT user0_.Id as Id1_2_, blog1_.Id as Id0_0_,

     blog1_.Name as Name0_0_, blog1_.[User] as User3_0_0_,

     user2_.Id as Id1_1_

FROM Users user0_ left outer join

     Blog blog1_ on user0_.Id=blog1_.Id

     left outer join Users user2_

     on blog1_.[User]=user2_.Id WHERE user0_.Id=@p0;

The double left outer join had me scratching my head for a while, until I figured out how NHibernate was thinking about it. The first left outer join is to find the related blog, and the second is to find the blog's user. Not reasonable, I agree, but that is the way that one-to-one works.

Now, let us see what happens if we specify that both classes are lazy? Well, now NHibernate generate this SQL:

declare @p0 int;

set @p0 = '1';

SELECT user0_.Id as Id1_1_, user0_.Name as Name1_1_,

     blog1_.Id as Id0_0_, blog1_.Name as Name0_0_,

     blog1_.[User] as User3_0_0_

FROM Users user0_

     left outer join Blog blog1_ on user0_.Id=blog1_.Id

WHERE user0_.Id=@p0;

But wait, didn't we specify that both classes should be lazy? It removed one left outer join, but kept the second one, why is it doing that?

Well, let us give a moment's thought to the way NHibernate see things, shall we? We told it that User has a one to one assoication with Blog, this means that when it loads a User, it has to populate all the properties of the user, but the Blog's column is not kept on the Users table, but on the "Blog" table. What this means is that in order to find the id of the blog entity, NHibernate must query the Blog table as well. At that point, it is more efficent to just grab all the data from the table rather than just the id.

In other words, one-to-one cannot be lazily loaded, which is one of the reasons why it is recommended to use two many-to-one instead.