Ayende @ Rahien

Refunds available at head office

NHibernate Mapping – Inheritance

I wanted to explore a few options regarding the way we can map inheritance using NHibernate. Here is the model that we are going to use:image

And the code that we are going to execute:

using (var session = sessionFactory.OpenSession())
using (var tx = session.BeginTransaction())
{
	session.CreateCriteria(typeof(Party)).List();
	session.CreateCriteria(typeof(Company)).List();
	session.CreateCriteria(typeof(Person)).List();
	tx.Commit();
}

From now on we are going to simply play with the mapping options to see what we can come up with. We will start with a very simple discriminator based mapping (table per hierarchy):

<class name="Party"
			 abstract="true"
			 table="Parties">
	<id name="Id">
		<generator class="identity"/>
	</id>
	<discriminator column="Discriminator"
			not-null="true"
			type="System.String"/>

	<subclass
		name="Person"
		discriminator-value="Person">
		<property name="FirstName"/>
	</subclass>

	<subclass
		name="Company"
		discriminator-value="Company">
		<property name="CompanyName"/>
	</subclass>
</class>

Which result in the following table structure:

image

And the SQL that was generated is:

Select Party

image

Select Company

image

Select Person

image

But that is just one option. Let us see what happen if we try the table per concrete class option:

<class name="Person"
	table="People">
	<id name="Id">
		<generator class="identity"/>
	</id>
	<property name="FirstName"/>
</class>

<class name="Company"
	table="Companies">
	<id name="Id">
		<generator class="identity"/>
	</id>
	<property name="CompanyName"/>
</class>

Which result in the following table structure:

image

And the following queries:

Select Party

image

image

No, that is not a mistake, we issue two SQL queries to load all possible parties.

Select Company

image

Select Person

image

The inheritance strategy is table per subclass:

<class name="Party"
		abstract="true"
		table="Parties">
	<id name="Id">
		<generator class="identity"/>
	</id>

	<joined-subclass
		table="People"
		name="Person">
		<key column="PartyId"/>
		<property name="FirstName"/>
	</joined-subclass>

	<joined-subclass
		table="Companies"
		name="Company">
		<key column="PartyId"/>
		<property name="CompanyName"/>
	</joined-subclass>
</class>

Which result in the following table structure:

image

And the queries:

Select Party

image

This is slightly tricky, basically, we get the class based on whatever we have a row in the appropriate table.

Select Company

image

Select Person

image

The final option is using unioned subclasses, which looks like this:

 

<class name="Party"
		abstract="true"
		table="Parties">
	<id name="Id">
		<generator class="hilo"/>
	</id>

	<union-subclass
		table="People"
		name="Person">
		<property name="FirstName"/>
	</union-subclass>

	<union-subclass
		table="Companies"
		name="Company">
		<property name="CompanyName"/>
	</union-subclass>
</class>

Note that it is not possible to use identity with union-subclasses, so I switched to hilo, which is generally much more recommended anyway.

The table structure is similar to what we have seen before:

image

But the querying is drastically different:

Select Party

image

Select Company

image

Select Person

image

The benefit over standard table per concrete class is that in this scenario, we can query over the entire hierarchy in a single query, rather than having to issue separate query per class.

Comments

HireDotnetexpert
04/10/2009 05:49 AM by
HireDotnetexpert

Although these posts are geared towards your COMMERCIAL product, they are very helpful.

Ayende Rahien
04/10/2009 06:00 AM by
Ayende Rahien

Hire,

a) please use a real name, not a ad.

b) I don't know if you noticed, but I didn't mention NH Prof in these, and they are talking about basic feature of NH.

pcm
04/10/2009 06:23 AM by
pcm

As communicated in the email. I am open to delete this comment if it had hurt you. I love what you are doing for the community.

I am also changing my name to acronym. And Also not using a link if that makes you happy.

Krzysztof Kozmic
04/10/2009 07:18 AM by
Krzysztof Kozmic

I didn't know about union-subclass. Interesting. There's one thing I think would make a great addition to these posts - insight. Showing the mappings and SQL in a very manual-like way is sure great but I wish you took it one step further and talked about non-obvious strengths and weaknesses of each approach.

Good to see you blogging regularly again :)

alberto
04/10/2009 11:54 AM by
alberto

I'm with Krzysztof. It's something I've missed in the series. It feels like some scenarios would help to understand when to use each approach.

Tomasz Modelski
04/10/2009 11:57 AM by
Tomasz Modelski

@ meo : check the date of this news .... :-)

huey
04/10/2009 03:31 PM by
huey

Dumb question. I might have to make a website for our vendors. So it is going to have type user and a user might be an employee or vendor. Both employee / vendor will share the same user table and have unique user id's. This seems to fit in with inheritance mapping in NH.

The thing is, in all cases I'm going to start with a user id and the first thing I'm going to want to do is figure out if the user is an employee or a vendor. So it might be something like:

User user = session.get <user(userID);

if(user.discriminator == "vendor")

{

vendor = session.get <vendor(user.ID);

}

else

{

employee = session.get <employee(user.ID);

}

I feel like I'm missing the whole point / benefit of NH inheritance mapping.

Any suggestions?

justin
04/10/2009 04:44 PM by
justin

@huey

Polymorphism is your friend.

User user = session.Get(userId);

Vendor vendor = user as Vendor;

Employee employee = user as Employee;

Lookup the "as" and "is" keywords and also remember you can do explicit casting Vendor vendor = (Vendor)user;

Dmitry
04/10/2009 05:17 PM by
Dmitry

I did not know about the union class either.

I do not think it has mentioned that you can use common base classes or interfaces to do polymorphic queries.

So commands like

session.CreateCriteria(typeof(object)).List()

or

session.CreateCriteria(typeof(IEntity)).List() (assuming it's a common interface for the entities)

are legal in NHibernate.

David
04/10/2009 05:24 PM by
David

@alberto and @Krzysztof

from my limited understanding.

table per concrete class - fastest, harder to do polymorphing, bad thing you have to update all the tables if there is a change at the parent class level.

table per hierarchy - fast, but your columns cannot must allow nulls (as multiple classes will not use them), easy for reports.

table per subclass - this is meant to be the slowest (for large projects), I have no proof, but it the best way for normalisation, clean data. So the design looks nicer.

the books i would reconmend

NHibernate in Action, this has a section on this.

Patterns of Enterprise Application Architecture

Also thank you Ayende for doing these posts.. I am finding them great for reference!

Trent Foley
04/10/2009 06:00 PM by
Trent Foley

It seems to me that 95% of the time you would want to use the first method mentioned (Table per Hierarchy). This has been the approach I have taken to date, and has been painless so far. The other 5% of the time I imagine would be due to having to map to an existing database, or you are working with a DBA who is a Normilazation Nazi.

huey
04/11/2009 05:11 AM by
huey

@justin

Thanks, I'm stupid and glossed over the fact that session.get(userID) was grabbing all of the potential data for employee and vendor so I could later cast appropriately.

Ayende Rahien
04/11/2009 08:12 AM by
Ayende Rahien

alberto & Krzysztof,

It is a matter of time and place, actually.

Those posts are part of the prep work for the NH course that I am about to give in May.

They represent about 3 hours of discussion of scenarios, alternatives and motives. Pretty hard to put into words by typing them.

And most of the info is already out there.

Ayende Rahien
04/11/2009 08:13 AM by
Ayende Rahien

Trent,

There are different optimizations and constraints that apply to each of those.

See David's comments for some of them.

sebastijanp
04/11/2009 09:33 AM by
sebastijanp

Why is hilo generator "generally much more recommended anyway"? Does it has to do with optimization? What are the benefits over the identity?

Ayende Rahien
04/11/2009 09:39 AM by
Ayende Rahien

sebastijan,

HiLo can generate a unique ID without having to go to the database.

It has all the benefits of GUIDs, but without having to deal with the GUID opaque nature.

Krzysztof Kozmic
04/11/2009 01:37 PM by
Krzysztof Kozmic

I see,

I've read in few places discussion on pros and cons of each approach. However, I think you, as someone who knows NHibernate in and out, could give a much better and in-depth explanation of the topic.

Maybe some other time then.

Chris Nicola
05/05/2009 12:07 AM by
Chris Nicola

Can the Unioned-Subclass (or any other approach) be applied to interface classes instead of abstract classes? If I have two tables which map to classes that implement the same interface can I setup something where I can get all the objects from both classes with one query on the interface class?

Ayende Rahien
05/05/2009 05:47 AM by
Ayende Rahien

Chris,

On the face of it, I don't see why no.

dave
06/05/2009 03:34 AM by
dave

Hi,

I am doing "table per subclass" with this exact same DB schema, However I also have a related table called Address which links to Party

The XML mapping file will not allow me to use both joined-subclass for my Company and Invervidual. When I try to include my address into Party via join. Is there any way around this? (p.s I am only new to nHiberbate)

for example


<class
abstract="true"

  table="Parties">

<id
<generator

<property

<join
<key
<component
<property
...... rest of mapping removed for brevity

<joined-subclass

<key

Visial stiduo compains when both joined-subclass and join table are entered I can only do one of the other?

TIA Dave

dave
06/05/2009 03:38 AM by
dave

Sorry the code should have been like this. (it seems the commect are accepting my less then tags and parsing them as HTML do I smell XSS attacks)

<class name="Party"

  abstract="true"

  table="Parties">

<id name="Id">

  <generator class="identity"/>

</id>

<property name="EntityName" column="entity_name"/>

<property name="ShortName" column="short_name"/>


<join table="logical_address">

  <key column="address_id"/>

  <component name="ContactDetails" class="ContactDetails">

    <property name="EmailAddress" column="email_address"/>

    <property name="HomePhone" column="home_phone_number"/>



  </component>

</join>



<joined-subclass  table="individual" name="Individual">

  <key column="party_key"/>
Ayende Rahien
06/05/2009 01:30 PM by
Ayende Rahien

Dave,

Please post the full details to nhusers

Comments have been closed on this topic.