Mapping inheritance

You will often have classes in your domain model that inherit from other classes. In our domain model, we have all classes representing a benefit inherited from the Benefit class. Following is a slimmed down version of the class diagram from our domain model representing this inheritance relationship:

Mapping inheritance

It is possible to map these classes to database tables as you would map any other class. But there are situations when you would want NHibernate to do more for you out of the box. One such situation involves polymorphic associations.

Polymorphic associations are associations where one of the ends is declared to be of base class type, but at runtime it holds instance of one of the derived types. For instance, the association from Employee to Benefit class in our domain model is an example of polymorphic association:

public class Employee : EntityBase
{
  public virtual ICollection<Benefit> Benefits { get; set; }
}

Here, though the collection property Benefits declared on the Employee class is of base class type, we know that, at run time, a particular instance of the Benefit class in this collection could be of any derived type. If we are building this collection in code, we can code it such that instances of right type are added to the collection. But what about NHibernate? Can NHibernate add the instances of right types when loading Benefits from database?

Fortunately, the answer to the question is yes. NHibernate is capable of dealing with polymorphic associations if we map the family of such classes using one of the inheritance mapping strategies offered by NH.

Note

You can map any inherited classes using one of the inheritance mapping strategy but this may not be needed. For example, all our domain classes inherit from the class EntityBase but there is no need to map them as such. Main reason for doing so is that these classes are never used in the context of polymorphic associations and hence there is no need for NHibernate to be aware of the fact that they are inherited from some other class. Also, we introduced this inheritance because we noticed that the Id property was repeated on all the classes. In reality, all domain classes inheriting from EntityBase do not represent family of classes in true sense.

In order for NHibernate to handle polymorphic associations, the family of inheriting classes must be mapped using one of the many inheritance mapping strategies that NHibernate offers. NHibernate supports the following three basic strategies:

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

Besides the preceding three, NHibernate supports a slight variation of "table per concrete class". This is called implicit polymorphism. We will not cover this in the chapter and readers are encouraged to explore this on their own. NHibernate also lets you mix some of these strategies if you really have to. We will shortly jump into the details of each of the preceding strategies and see how our benefit classes would be mapped using these. Before that, let's take a look at the unit test that we are going to use to verify mappings.

Unit tests to verify the inheritance mappings

In order to test that our mappings are correct, we should be able to save an instance of one of the classes inheriting from the Benefit class and be able to retrieve it successfully. Following is one such test that we will be using:

[Test]
public void MapsSkillsEnhancementAllowance()
{
  object id = 0;
  using (var transaction = session.BeginTransaction())
  {
    id = session.Save(new SkillsEnhancementAllowance
    {
      Name = "Skill Enhacement Allowance",
      Description = "Allowance for employees so that their skill enhancement trainings are paid for",
      Entitlement = 1000,
      RemainingEntitlement = 250
    });
    transaction.Commit();
  }

  session.Clear();

  using (var transaction = session.BeginTransaction())
  {
    var benefit = session.Get<Benefit>(id);

    var skillsEnhancementAllowance = benefit as SkillsEnhancementAllowance;
    Assert.That(skillsEnhancementAllowance, Is.Not.Null);

    if (skillsEnhancementAllowance != null)
    {
      Assert.That(skillsEnhancementAllowance.Name, Is.EqualTo("Skill Enhacement Allowance"));
      Assert.That(skillsEnhancementAllowance.Description, Is.EqualTo("Allowance for employees so that their skill enhancement trainings are paid for"));
      Assert.That(skillsEnhancementAllowance.Entitlement, Is.EqualTo(1000));
      Assert.That(skillsEnhancementAllowance.RemainingEntitlement, Is.EqualTo(250));
    }
    transaction.Commit();
  }
}

As you can see, we are storing an instance of the SkillsEhancementAllowance class. We then tell NHibernate to retrieve us an instance of the Benefit class using id that we received when we saved the instance of the SkillsEnahcementAllowance class. On this retrieved instance of the Benefit class, we verify three things:

  • The retrieved instance is actually of type SkillsEnhancementAllowance class
  • All properties of the Benefit class are retrieved correctly
  • All properties of the SkillsEnhancementAllowance class are retrieved correctly

Interesting thing to note here is that we told NHibernate to load the Benefit class, which is a base class, but NHibernate intelligently loaded the correct derived class instance. This is polymorphic behavior offered by NHibernate. I actually have three of such tests, one each for every class deriving from the Benefit class. But for brevity, I have only presented one here. Others are very similar to this one and you can take a look at them from the source code of the book.

Let's make these tests pass by writing some inheritance mappings.

Table per class hierarchy

When I first used NHibernate, I had a hard time remembering what each of the strategies meant. One of the sources of confusion came from my relating the word "class hierarchy" to the wrong thing. I will not go into details of what assumption I made as that would be irrelevant for this discussion, but I would like to tell you that the word "class hierarchy" refers to a family of classes that inherit from one ultimate base class. In our example, the classes Benefit, Leave, SkillsEnhancementAllowance, SeasonTicketLoan together form a class hierarchy.

If you have more than one level of inheritance, the hierarchy would cover all the classes at all the levels of inheritance. Now you may have guessed what "table per class hierarchy" offers. This strategy maps all the classes in a hierarchy to a single table in database. Following is how we can map the Benefit class hierarchy using this strategy:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain" namespace="Domain">
  <class name="Benefit">
    <id name="Id" generator="hilo" />
    <discriminator column="BenefitType" type="String" />
    <property name="Name" />
    <property name="Description" />
    <many-to-one name="Employee" class="Employee" />

    <subclass name="SkillsEnhancementAllowance" discriminator-value="SEA">
      <property name="RemainingEntitlement" />
      <property name="Entitlement" />
    </subclass>

    <subclass name="SeasonTicketLoan" discriminator-value="STL">
      <property name="Amount" />
      <property name="MonthlyInstalment" />
      <property name="StartDate" />
      <property name="EndDate" />
    </subclass>

    <subclass name="Leave" discriminator-value="LVE">
      <property name="Type" />
      <property name="AvailableEntitlement" />
      <property name="RemainingEntitlement" />
    </subclass>
  </class>
</hibernate-mapping>

Note

Though I declared all mappings in one XML file under a single class node, you can put each subclass mapping in its own XML file. NHibernate would use its own logic to weave together the mappings from multiple files. This applies to all inheritance mapping strategies.

This mapping would enable us to store data from all the Benefit classes into a table that looks as follows:

Table per class hierarchy

As you can see, this table has columns for all the properties of all four classes being mapped. Let's take a close look at the mappings.

Mapping of the base class is normal like any other class. Even mapping of derived class's attributes is also not unusual. There are two new things here as follows:

Element discriminator is used to declare a column on the database table that would be used as a discriminator. A discriminator, as the name suggests, discriminates between different rows of the table. In other words, the value of the column tells which class in the hierarchy this row belongs to. NHibernate uses this column to hydrate correct class when retrieving rows from the table. On the discriminator element, we have declared two attributes which are actually optional. Attribute column is used to declare the name of the discriminator column. Attribute type declares the type of the discriminator column. If not declared, NHibernate assumes default values for these attributes, which are class and string respectively.

Element subclass serves two purposes. One, it acts as the root element under which attributes of the derived class are mapped in the usual manner. Second, it lets you specify name of the derived class and what value in discriminator column is used to identify database record as belonging to that class.

Following are few important things to note around this strategy:

  • This strategy maps to database table that is not normalized.
  • An additional column called discriminator is required. This column does not store any business critical information but is used by NHibernate purely for the purpose of determining which class should be instantiated when a particular record from table is retrieved.
  • It is not possible to mark properties of derived classes as not-null. This is because when a record belonging to one class in inserted into the table, columns mapping to other classes have to be left null.

Table per subclass

Most problems in "table per class hierarchy" are solved by the "table per subclass" strategy. In this strategy, every class gets mapped to its own table with a caveat that we would look into soon. Let's first look at how the mapping for this strategy looks, as shown next:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain" namespace="Domain">
  <class name="Benefit">
    <id name="Id" generator="hilo" />
    <property name="Name" />
    <property name="Description" />
    <many-to-one name="Employee" class="Employee"/>

    <joined-subclass name="SkillsEnhancementAllowance">
      <key column="Id" />
      <property name="RemainingEntitlement" />
      <property name="Entitlement" />
    </joined-subclass>

    <joined-subclass name="SeasonTicketLoan">
      <key column="Id"/>
      <property name="Amount" />
      <property name="MonthlyInstalment" />
      <property name="StartDate" />
      <property name="EndDate" />
    </joined-subclass>

    <joined-subclass name="Leave">
      <key column="Id"/>
      <property name="Type" />
      <property name="AvailableEntitlement" />
      <property name="RemainingEntitlement" />
    </joined-subclass>
  </class>
</hibernate-mapping>

As you can see, there is no discriminator column here. NHibernate is able to distinguish the database records belonging to different derived classes without resorting to a discriminator column. NHibernate uses joins on correct tables in order to fetch the complete database record. That is another reason the mapping is called joined-subclass. This mapping would map to the following table schema:

Table per subclass

Here we have got one table for every class in the hierarchy. One thing to notice here is that every table has an Id column which is also the primary key on the respective tables. This primary key is shared between table for base class and table for derived class. Key points to note in this strategy are:

  • Since every class gets mapped to its own table, you can make columns on derived class not-null, unlike "table per class hierarchy".
  • NH uses joins while retrieving records from these tables. This has a slight performance overhead.

Table per concrete class

You can keep things simple and map all the four Benefit classes to their own tables without any inheritance mapping semantics. But flip side of such an approach is that you would not be able to use polymorphic associations. If you still want to map each class to its own table and be able to use polymorphic association, then "table per concrete class" is your friend. Following is how "table per concrete class mapping for our family of Benefit classes would look:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain" namespace="Domain">
  <class name="Benefit">
    <id name="Id" generator="hilo" />
    <property name="Name" />
    <property name="Description" />
    <many-to-one name="Employee" class="Employee" />

    <union-subclass name="SkillsEnhancementAllowance">
      <property name="RemainingEntitlement" />
      <property name="Entitlement" />
    </union-subclass>

    <union-subclass name="SeasonTicketLoan">
      <property name="Amount" />
      <property name="MonthlyInstalment" />
      <property name="StartDate" />
      <property name="EndDate" />
    </union-subclass>

    <union-subclass name="Leave">
      <property name="Type" />
      <property name="AvailableEntitlement" />
      <property name="RemainingEntitlement" />
    </union-subclass>
  </class>
</hibernate-mapping>

As the element union-subclass suggests, this mapping strategy runs SQL union on all the four tables in order to retrieve a full database record. The mapped database tables for this strategy would look as shown in the following image. You can see in the following diagram that NHibernate has automatically remapped properties from the base classes onto tables corresponding to derived classes:

Table per concrete class

Every class is mapped to an independent table. There is no relation between the tables. Properties from the base classes are remapped on each table for the derived class. So the tables are not in a perfectly normalized form. Use of unions may signal some drop in performance. A serious concern with this strategy is that polymorphic associations do not work reliably. They may work in some situations but not always. Another limitation of this strategy is that you cannot use identity as identifier generation strategy. This is because each table has now its own identifier column. If we use identity then we would get duplicate identifier values across the tables. If you try to load the Benefit instances using an identifier value, NHibernate would be confused as there would be multiple records matching the given identifier value. Looking at this from a different angle, it is a good thing because identity is not a recommended identifier generation strategy anyway.

Tip

The Benefit table has columns that map to the Benefit entity. These same columns are also present on the tables for the derived entities. This is redundant. We can stop having the Benefit table by making use of an attribute called abstract that can be applied to class element. A value of true means that the class can be treated as abstract from NHibernate point of view. NHibernate then generates all SQL as if there is no Benefit table present in the database.

Note that the "table per concrete class" strategy can be implemented using implicit polymorphism. This is similar to the approach we discussed at the beginning of this section, where we said that every derived class can be mapped to a database table independent of each other. In this approach you cannot use polymorphic associations so I would use this strategy only if I was unable to use any other strategy.

Choosing the right inheritance mapping strategy

Which inheritance strategy should you use? This is not an easy question to answer. More than one factor is at play and you should look at each of these factors before you decide.

If you are working with a legacy database and you cannot make changes to the existing database schema then you must choose the strategy that would let you map your domain model to the schema. Here you do not have many options but stick to one that would get the job done. Sometimes, for reasons beyond your control, you might even need to mix different strategies in order to get mappings to work. The beauty of NHibernate is that it lets you mix the strategies without you having to worry about how NHibernate figures out storing and retrieving of data from tables.

If you are working on a green field project where you are free to define your own database schema, then you can look at other factors such as preference to have a normalized vs non-normalized database schema, expected performance, requirement to set table columns as NOT NULL, and so on. While "table per class hierarchy" gives the best performance, it maps to database schema that is not normalized. You also lose the ability to mark a column from the derived class as not null. "Table per subclass" lets you mark columns as not null and also maps to tables that are normalized but its use of joins to fetch records impacts the performance slightly. I personally prefer "table per subclass" as the performance overhead is not that significant in my opinion. I like working with normalized tables. In the end, "table per concrete class" is something I would tend to avoid unless I have no other option.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.5.57