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:
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:
And the SQL that was generated is:
Select Party
Select Company
Select Person
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:
And the following queries:
Select Party
No, that is not a mistake, we issue two SQL queries to load all possible parties.
Select Company
Select Person
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:
And the queries:
Select Party
This is slightly tricky, basically, we get the class based on whatever we have a row in the appropriate table.
Select Company
Select Person
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:
But the querying is drastically different:
Select Party
Select Company
Select Person
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
Although these posts are geared towards your COMMERCIAL product, they are very helpful.
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.
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.
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 :)
Did ya read this?
blog.wekeroad.com/.../subsonic-to-acquire-nhibe...
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.
@ meo : check the date of this news .... :-)
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?
@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;
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.
@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!
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.
@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.
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.
Trent,
There are different optimizations and constraints that apply to each of those.
See David's comments for some of them.
Why is hilo generator "generally much more recommended anyway"? Does it has to do with optimization? What are the benefits over the identity?
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.
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.
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?
Chris,
On the face of it, I don't see why no.
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"
<id
<generator
<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
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"
Dave,
Please post the full details to nhusers
Comment preview