NHibernateComplex relationships
Originally posted at 11/18/2010
I got an interesting question today (I am teaching my NHibernate course now).
The tabular structure is similar to this:
But the desired object structure is:
That is quite different than the tabular model, but it is actually very easy to handle this with NHibernate.
Here are the mapping for the Address entity. We use the <join/> tag to have an entity that spans more than a single table:
<class name="Address" table="Addresses"> <id name="Id"> <generator class="identity"/> </id> <property name="City" /> <join table="PeopleAddresses" > <key column="AddressId"/> <property name="IsDefault"/> <property name="ValidFrom"/> <property name="ValidTo"/> </join> </class>
We then map the Person, using standard many-to-many mapping for the addresses:
<class name="Person" table="People"> <id name="Id"> <generator class="identity"/> </id> <property name="Name" /> <bag name="Addresses" table="PeopleAddresses" inverse="true"> <key column="PersonId"/> <many-to-many class="Address" column="AddressId"/> </bag> </class>
There is just one thing thing to be aware of, you can’t add new addresses via the Person.Addresses collection, because the PeopleAddresses table has more data in it than just the keys. Presumably, you are handling this in some other fashion already.
All in all, this is a pretty elegant solution.
More posts in "NHibernate" series:
- (19 Nov 2010) Complex relationships
- (27 Jun 2010) Streaming large result sets
- (25 May 2009) Why do we need to specify the query type twice?
- (20 Mar 2009) Avoid identity generator when possible
- (26 Mar 2007) Nullable DateTime Issues
- (08 Jan 2007) Fetching multiply collections in one roundtrip
Comments
Pardon my ignorance, but how "you can’t add new addresses via the Person.Addresses collection" relates to the conclusion of "this is a pretty elegant solution"? All I can think of is having another set of classes\mappings just to be able to save the person's address. That's not elegant in my books.
Victor,
You need to map the PersonId column on the PeopleAddresses, and then you can threat this as a standard Many To One.
The scenario that was brought up was were there was another way of handling that, so I didn't bother with that
The tabular model and the object structure in almost all cases will be quite different, with one modelling data and the other modelling behaviour. This is something that most ORMs don't handle as elegantly as the code above.
What about using <idbag?
Oops, it seems my comment is stripped.
What about using idbag?
If you really want flexibility given the table structure above and still want to handle inserts.
Create a sql view that fits the exact mapping structure and let the view deal with the inserts appropriately.
Can I refactorize that DB ? or it is untouchable ?
Good, old-fashioned blog entry. Interesting case.
I'm with Victor, this isn't elegant at all. It's also wrong: PersonAddresses is an objectified relationship (in Object Role Modeling / NIAM terms), and this means it's an entity by itself. Just because it defines a m:n relationship between person and address doesn't make it an entity which doesn't exist, and the limitation that you can't persist new addresses shows that.
I fail to see why PersonAddresses has to be wiped under the rug: its name is perhaps badly chosen, but it is a valid entity, that you don't want to program with it, is really ignorance: because the entity exists in the domain, you have to deal with it, and which is the reason you have to deal with it too only in 'some other fashion'.
The m:n relationship between person and addresses is readonly, over the objectified relationship. I do recall having a discussion with Fabio about this some time ago on nhusers.
Paper about this subject by prof. T. Halpin: http://www.orm.net/pdf/objectification.pdf
This challenge is an evidence of how NHibernate can deal with bad databases design but refactor is a must !.
Comment preview