Introduction to NHibernate, pt. 4

Last time we started looking at the parts of an OO model that need translation to relational form when we looked at mapping relationships. This time we will look at another area where we need to translate: mapping inheritance hierarchies.

Mapping Inheritance

As well as associations, the other major difference between an object model and a domain model is inheritance. There are three strategies for modelling inheritance within NHibernate:

  • Table-per-class hierarchy
  • Table-per-subclass
  • Table-per-concrete class

Table-per-concrete class is the simplest conceptually but does not support polymorphic associations well. This is because an association is represented by a foreign key mapping forcing NHibernate to issue as many queries as there are tables (NHibernate does not support unions for this instance and in any case they perform badly). We’re not showing any more detail on this one for that reason.

Table-per-class hierarchy is the most performant strategy but can cause an issue where we have significantly different properties on the sub-classes as all sub-class properties must be nullable, as a row might be of any type in the hierarchy. A discriminator column is required to distinguish the concrete class needed to materialize the row. This is the most performant for polymorphic associations as only one table is queried.

Table-per-subclass removes the two issues described so far. There is a table for each subclass that can hold properties and one for the superclass. The subclass tables share the superclass tables primary key, making finding the subclass info a join operation.

For simplicity we are just going to show how to map table-per-subclass, look at the documentation for table-per-class hierarchy.

We need to add some entities that use inheritance to demonstrate how we map them:

    public class Product
    {
        public int ProductId { get; set; }
        public string Name { get; set; }
        public double Price { get; set; }
        public int Version { get; set; }
    }

    public class Book : Product
    {
        public string Author { get; set; }
        public string Genre { get; set; }
    }

    public class Album : Product
    {
        public string Artist { get; set; }
        public int NoOfTracks { get; set; }
    }

And then we map the subclasses to the class by using the <joined-subclass/> element. The <joined-subclass/> has many of the same sub-elements as <class/>. One of the most important differences is the addition of a <key/> element, which identifies the column on the superclass that this sub-class is mapped via. Note that we do not put a version column on the sub-class tables as they are versioned by the parent.

  <class name=”Product” table=”Product”>
    <id name=”ProductId” type=”Int32″ column=”ProductId” unsaved-value=”0″>
      <generator class=”identity”/>
    </id>
    <version name=”Version”/>
    <property name=”Name”/>
    <property name=”Price”/>
    <joined-subclass name=”Album” table=”Album”>
      <key column=”ProductId”/>
      <property name=”Artist”/>
      <property name=”NoOfTracks”/>
    </joined-subclass>
    <joined-subclass name=”Book” table=”Book”>
      <key column=”ProductId”/>
      <property name=”Genre”/>
    </joined-subclass>
  </class>

This allows us to use Product and its derived classes polymorphically. So if for example we have the following definition of ProductGroup:

    public class ProductGroup
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<Product> Products { get; set; }
        public int Version { get; set; }
    }

We can then map its collection as follows:

  <class name=”ProductGroup” table=”ProductGroup”>
    <id name=”Id” type=”Int32″ unsaved-value=”0″>
       <generator class=”identity”/>
    </id>
    <version name=”Version”/>
    <property name=”Name”/>
    <set inverse=”true” lazy=”false” name=”Products”>
      <key column=”Id”/>
      <one-to-many class=”Product”/>
    </set>
  </class>

We don’t show it here but the bi-directional relationship implied by inverse=true indicates that Product has the reverse mapping to ProductGroup.

Next time we will look at how NHibernate provides us support for mapping value types as well as entities, giving support for fine-grained object models.

 

About Ian Cooper

Ian Cooper has over 18 years of experience delivering Microsoft platform solutions in government, healthcare, and finance. During that time he has worked for the DTi, Reuters, Sungard, Misys and Beazley delivering everything from bespoke enterpise solutions to 'shrink-wrapped' products to thousands of customers. Ian is a passionate exponent of the benefits of OO and Agile. He is test-infected and contagious. When he is not writing C# code he is also the and founder of the London .NET user group. http://www.dnug.org.uk
This entry was posted in NHibernate, Object-Orientation, ORM. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://micaleel.wordpress.com micaleel

    Wow, this article came at the right time. Thanks