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:
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:
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:
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.
The resulting query is… interesting:
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:
In this case, Owner.Type is the discriminator for the Owner hierarchy. And the query that is generated is:
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:
Using this approach, we query them using:
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
Discriminator with joined classes sounds like an interesting (hint) 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.
This is a great post about mapping options. Thank you for a post on (what i consider) advanced features of NH.
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?
Omer,
Because L2NH isn't able to express all the richness that I have here.
What about NH Lambda Extensions ( http://code.google.com/p/nhlambdaextensions/) ?
And FluentNHibernate.Contrib ( www.lostechies.com/.../...nh-contrib-is-alive.aspx) ?
I'm asking because I hate magic strings and I'm curios about the state of current string-less NH solutions, and your take on the subject. Do you think those projects have a future?
Omer,
I love the idea, but I am currently teaching, and I want to teach people from the basic up
Oh - sorry. I guess I was completely out of context here.
Thanks for the transcription of your session Oren. I couldn't quite figure out how to reproduce your work at SkillsMatter.
Peter, just try implementing that, it isn't hard
Comment preview