NHibernate with Inheritance

After doing some initial experiments with NHibernate, I am now experimenting with inheritance to find out which of the available approaches would fit best for what I’m looking for.

Project Setup

Different from the first experiment, I’m now using only NHibernate without the Fluent NHibernate library. Also, because I’m using PostgreSQL for this experiment, I had to install the latest version of the Npgsql package.

> Install-Package NHibernate
> Install-Package Npgsql

Here is the helper class that I used to encapsulate the configuration and the session factory. I configured everything in the helper class except for the connection string.

public static class NHibernateHelper
{
    private static readonly ISessionFactory sessionFactory;

    static NHibernateHelper()
    {
        var configuration = new Configuration()
            .DataBaseIntegration(db =>
            {
                db.Dialect<PostgreSQL82Dialect>();
                db.Driver<NpgsqlDriver>();
                db.ConnectionProvider<DriverConnectionProvider>();
                db.ConnectionStringName = "NHibernateExperiments";
                db.LogSqlInConsole = true;
                db.LogFormattedSql = true;
            })
            .AddAssembly("NHibernateInheritance");

        var export = new SchemaExport(configuration);
        export.Drop(false, true);
        export.Create(false, true);

        sessionFactory = configuration.BuildSessionFactory();
    }

    public static ISession CreateSession()
    {
        return sessionFactory.OpenSession();
    }
}

Lines 14 and 15 are to use the console window to display the executed SQL. Line 17 is to define the assembly name that contains the XML mapping files. Lines 20 and 21 are to drop and recreate all the tables every time the application is executed.

Entities Class Diagram

I’m not going to paste all the source code here, instead I believe that a class diagram would be good enough. I have an abstract class for the Field and two concrete implementations for the Text and Image fields.

The Mapping Files

In this sample project, I’m using only two mapping files. One for the AssetFile class and another one for the Field mapping, which includes the abstract class and the two concrete implementations.

AssetFile.hbm.xml

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   namespace="NHibernateInheritance.Entities"
                   assembly="NHibernateInheritance">
  <class name="AssetFile" table="asset_files">
    <id name="Id">
      <column name="id" not-null="true" />
      <generator class="guid.comb" />
    </id>
    <property name="Name">
      <column name="name" length="255" not-null="true" />
    </property>
  </class>
</hibernate-mapping>

Field.hbm.xml

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   namespace="NHibernateInheritance.Entities"
                   assembly="NHibernateInheritance">
  <class name="Field" table="fields">

    <id name="Id">
      <column name="id" not-null="true" />
      <generator class="guid.comb" />
    </id>

    <discriminator column="field_type" not-null="true" />

    <property name="Name">
      <column name="name" length="255" not-null="true" />
    </property>
    <property name="Tooltip">
      <column name="tooltip" length="255" />
    </property>
    <property name="IsDeleted">
      <column name="is_deleted" not-null="true" />
    </property>

    <subclass name="TextField" discriminator-value="TEXT">
      <join table="text_fields">
        <key column="field_id" />
        <property name="Value" column="value" />
      </join>
    </subclass>

    <subclass name="ImageField" discriminator-value="IMAGE">
      <join table="image_fields">
        <key column="field_id" />
        <many-to-one name="AssetFile"
                     column="asset_file_id"
                     fetch="select" />
      </join>
    </subclass>

  </class>
</hibernate-mapping>

I’m mixing table per class hierarchy with the table per subclass approach. The discriminator is a column in the base class table that will indicate if the field is an Image or a Text. It is possible to specify the discriminator value using the attribute discriminator-value as you can see in lines 24 and 31. According to the NHibernate documentation, the discriminator is not necessary for the “table per subclass” approach, but I believe that it is very useful for when you are querying the database directly without the ORM.

Inserting and Reading Data

Here is a sample code that illustrates the insert and read operations using the inheritance approach.

object fieldId;
using (var session = NHibernateHelper.CreateSession())
{
    using (var transaction = session.BeginTransaction())
    {
        var assetId = session.Save(new AssetFile("Sample Asset"));
        var field = new ImageField("imageField")
        {
            Tooltip = "Tooltip!",
            AssetFile = session.Load<AssetFile>(assetId)
        };

        fieldId = session.Save(field);
        transaction.Commit();
    }
}

using (var session = NHibernateHelper.CreateSession())
{
    using (var transaction = session.BeginTransaction())
    {
        var field = session.Get<Field>(fieldId);
        Console.WriteLine(field.GetType().Name);
    }
}

Right now, the most important lines in this example are lines 22 and 23. You can see that I’m getting a field using the field ID, but I’m not specifying if it is and Image or Text field. The query generated by NHibernate will be smart enough to grab the correct implementation for me. In this case, the field object will be an instance of the ImageField. Here is the SELECT statement generated by it.

SELECT
    field0_.id as id1_1_0_,
    field0_.name as name3_1_0_,
    field0_.tooltip as toolti4_1_0_,
    field0_.is_deleted as is5_1_0_,
    field0_1_.value as value2_2_0_,
    field0_2_.AssetFile as AssetF2_3_0_,
    field0_.field_type as field2_1_0_
FROM

    fields field0_

left outer join
    text_fields field0_1_
        on field0_.id= field0_1_.field_id

left outer join
    image_fields field0_2_
        on field0_.id= field0_2_.field_id

WHERE
    field0_.id=:p0;
    :p0 = 82f8eb95-0d25-4176-9659-a78200c690fd[Type: Guid(0:0:0)]

The way it works is by matching the ID in the fields table with the ID in the image_fields or text_fields table. So, if it finds an entry with the same ID in the image_fields table, it means that we are dealing with ImageField and not a TextField. Pretty cool!

create table asset_files(
    id uuid not null,
    name varchar(255) not null,
    primary key(id)
)

create table fields(
    id uuid not null,
    field_type varchar(255) not null,
    name varchar(255) not null,
    tooltip varchar(255),
    is_deleted boolean not null,
    primary key(id)
)

create table text_fields(
    field_id uuid not null,
    value varchar(255),
    primary key(field_id)
)

create table image_fields(
    field_id uuid not null,
    asset_file_id uuid,
    primary key (field_id)
)

alter table text_fields
    add constraint FK8A535D0C2BD85E59
    foreign key(field_id)
    references fields

alter table image_fields
    add constraint FKDC73639A91E12594
    foreign key(field_id)
    references fields

alter table image_fields
    add constraint FKDC73639A9B63241A
    foreign key(asset_file_id)
    references asset_files

We can see from the constraints that the primary keys for both the text_fields and image_fields are actually a foreign key to the fields table.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s