Ayende @ Rahien

Hi!
My name is Ayende Rahien
Founder of Hibernating Rhinos LTD and RavenDB.
You can reach me by phone or email:

ayende@ayende.com

+972 52-548-6969

, @ Q c

Posts: 5,949 | Comments: 44,548

filter by tags archive

NHibernate – Mapping a single domain model to multiple physical data models


A while ago I sat down and talked with a colleague about the Entity Framework, he raved about how important the separation of the logical model from the physical one is. I don’t really buy into that, but that is  beside the point.

Last week, on the Progressive.NET NHibernate workshops, I setup, quite accidently, to create a single domain model and map it to several physical data models. I promised to share the code, and I think that this form is as good as any.

Let us start from the following domain model:

image

I am going to present three things for each physical data model manifestation. The mapping, the database schema and the result of the following query:

s.CreateQuery("from Owner owner where owner.Name = 'ayende'")
    .List<Owner>();

We will start with the classic, table per class, which looks like this:

<class name="Company">
    <id column="Id"  type="System.Int32">
        <generator class="hilo"/>
    </id>
    <property name="Name"/>
    <property name="CompanyRegistrationId"/>
    <set name="Horses" table="CompanyHorses">
        <key column="Company"/>
        <many-to-many class="Horse" column="Horse"/>
    </set>
</class>

<class name="Individual">
    <id column="Id"  type="System.Int32">
        <generator class="hilo"/>
    </id>
    <property name="Name"/>
    <property name="Email"/>
    <set name="Horses" table="HorsesBelongToIndividuals">
        <key column="Company"/>
        <many-to-many class="Horse" column="Horse"/>
    </set>
</class>

<class name="Consortium">
    <id column="Id"  type="System.Int32">
        <generator class="hilo"/>
    </id>
    <property name="Name"/>
    <set name="Horses" table="HorsesBelongToIndividuals">
        <key column="Company"/>
        <many-to-many class="Horse" column="Horse"/>
    </set>
    <set name="Owners" table="ConsortiumOwners">
        <key column="Consortium"/>
        <many-to-any id-type="System.Int32" meta-type="System.String">
            <meta-value class="Individual" value="Individual"/>
            <meta-value class="Company" value="Company"/>
            <meta-value class="Consortium" value="Consortium"/>
            <column name="OwnerType"/>
            <column name="OwnerId"/>
        </many-to-any>
    </set>
</class>

The database schema is:

image

You might want to pay some attention to the association between Consortium and its owner, using <many-to-any/>.

Trying to execute the aforementioned query will give us an error, NHibernate is not aware of any persistent class called Owner and HQL queries are not polymorphic over unknown types. Criteria API, however, are. And we can execute the following query successfully:

s.CreateCriteria<Owner>()
    .Add(Restrictions.Eq("Name", "ayende"))
    .List<Owner>();

Which result in:

image

image

image

Let us look at another scenario, which would keep the same data model, but let NHibernate know about the inheritance association between the classes. NHibernate call this type of association union subclasses. Here are the mapping:

<class name="Owner" abstract="true">
    <id column="Id"  type="System.Int32">
        <generator class="hilo"/>
    </id>
    <set name="Horses" table="OwnerHorses">
        <key column="Owner"/>
        <many-to-many class="Horse" column="Horse"/>
    </set>
    <property name="Name"/>
    <union-subclass name="Company">
        <property name="CompanyRegistrationId"/>
    </union-subclass>

    <union-subclass  name="Individual">
        <property name="Email"/>
    </union-subclass>

    <union-subclass name="Consortium">
        <set name="Owners" table="ConsortiumOwners">
            <key column="Consortium"/>
            <many-to-many class="Owner" column="Owner"/>
        </set>
    </union-subclass>
</class>

You might notice how simple the mapping looks like. For that matter, look at how we could simplify the mapping between horses and owners.

image

The resulting query is… interesting:

image

And now let us move to the more classic inheritance schemes, first, let us look at table per hierarchy:

<class name="Owner" abstract="true">
    <id column="Id"  type="System.Int32">
        <generator class="hilo"/>
    </id>
    <discriminator column="Type" type="System.String"/>
    <set name="Horses" table="OwnerHorses">
        <key column="Owner"/>
        <many-to-many class="Horse" column="Horse"/>
    </set>
    <property name="Name"/>
    <subclass discriminator-value="Company" name="Company">
        <property name="CompanyRegistrationId"/>
    </subclass>

    <subclass discriminator-value="Individual" name="Individual">
        <property name="Email"/>
    </subclass>

    <subclass discriminator-value="Consortium" name="Consortium">
        <set name="Owners" table="ConsortiumOwners">
            <key column="Consortium"/>
            <many-to-many class="Owner" column="Owner"/>
        </set>
    </subclass>
</class>

With the following schema:

image

In this case, Owner.Type is the discriminator for the Owner hierarchy. And the query that is generated is:

image

And finally, we have table per subclass, which we use the following mapping:

<class name="Owner" abstract="true">
    <id column="Id"  type="System.Int32">
        <generator class="hilo"/>
    </id>
    <set name="Horses" table="OwnerHorses">
        <key column="Owner"/>
        <many-to-many class="Horse" column="Horse"/>
    </set>
    <property name="Name"/>
    <joined-subclass name="Company">
        <key column="Id"/>
        <property name="CompanyRegistrationId"/>
    </joined-subclass>

    <joined-subclass name="Individual">
        <key column="Id"/>
        <property name="Email"/>
    </joined-subclass>

    <joined-subclass name="Consortium">
        <key column="Id"/>
        <set name="Owners" table="ConsortiumOwners">
            <key column="Consortium"/>
            <many-to-many class="Owner" column="Owner"/>
        </set>
    </joined-subclass>
</class>

This gives us this schema:

image

Using this approach, we query them using:

image

I would like to point out that this is just a sample of the type of things that we can do with NHibernate, and that I took the easy path doing so. There are actually more complex inheritance option available (discriminator with joined classes, for example) and it is pretty easy to change things even further.

And, of course, it is entirely possible to take things in the other direction, and have a single physical data model that can map to several domain models.

Have fun…


Comments

Peter Morris

Discriminator with joined classes sounds like an interesting (hint) example ;-)

Colin Jack

(discriminator with joined classes, for example)

Yeah in general the variety of mappings you've shown are a very poweful aspect of NHibernate, and I liked the discriminator with joined classes approach when we used it too. Good stuff.

Jason

This is a great post about mapping options. Thank you for a post on (what i consider) advanced features of NH.

Omer Mor

On a side note:

You're known as a guy who hate magic strings (wasn't that what made you write RhinoMocks?).

So why don't you use Linq to NHibernate in your samples?

Ayende Rahien

Omer,

Because L2NH isn't able to express all the richness that I have here.

Ayende Rahien

Omer,

I love the idea, but I am currently teaching, and I want to teach people from the basic up

Omer Mor

Oh - sorry. I guess I was completely out of context here.

Michel Grootjans

Thanks for the transcription of your session Oren. I couldn't quite figure out how to reproduce your work at SkillsMatter.

Ayende Rahien

Peter, just try implementing that, it isn't hard

Comment preview

Comments have been closed on this topic.

FUTURE POSTS

No future posts left, oh my!

RECENT SERIES

  1. The RavenDB Comic Strip (3):
    28 May 2015 - Part III – High availability & sleeping soundly
  2. Special Offer (2):
    27 May 2015 - 29% discount for all our products
  3. RavenDB Sharding (3):
    22 May 2015 - Adding a new shard to an existing cluster, splitting the shard
  4. Challenge (45):
    28 Apr 2015 - What is the meaning of this change?
  5. Interview question (2):
    30 Mar 2015 - fix the index
View all series

RECENT COMMENTS

Syndication

Main feed Feed Stats
Comments feed   Comments Feed Stats