Devlico.Us
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @devlicious

Billy McCafferty



When to use many-to-one(s) vs. many-to-many with NHibernate

A common question when developing with NHibernate is:  when is it appropriate to use a many-to-many relationship vs. two many-to-one relationships?  And does an intermediary domain object need to be introduced to reflect a many-to-many relational table?

As a rule, when using NHibernate, you should rarely, if ever, have to model your domain to accommodate the constraints of relational model, and vice-versa.  For example, suppose you have a Customers table and an Addresses table.  Each Customer can have multiple Addresses and each Address can be associated with multiple Customers; e.g., the same address could be associated with multiple family members in the same household.  Accordingly, you'd have a Customer_Addresses relational table in the DB to hold the many-to-many relationship entries; this table would simply have two foreign key columns:  CustomerFk and AddressFk.

At first glance, this might imply that you'd have to create a CustomerAddress object in your domain model to reflect this relationship table in the DB; that's not the case with NHibernate.  Instead, you'd simply add a many-to-many association to the Customer.hbm.xml which indicates that the CustomerAddresses table should be used as the lookup for managing this many-to-many relationship.  (See the NHibernate documentation at http://www.hibernate.org/hib_docs/nhibernate/html/collections.html for implementation details of mapping a many-to-many relationship.)  This many-to-many mapping assumes that the Customer class contains an IList<Address> property (and you could optionally have the inverse relationship from the Address class).  Consequently, the domain model maintains a clean, domain-driven design, while the DB reflects a normalized relational model.  But keep in mind that a many-to-many relationship should only be used for a true many-to-many relationship table wherein the relational table only contains foreign keys to other tables.

Now suppose, down the road, we add a bit column called IsCurrent to the Customer_Addresses table.  With this added, each row in the Customer_Addresses table is now an entity object with state, masquerading as a many-to-many relationship.  When this occurs, steps must be taken to "upgrade" this many-to-many relationship to a domain object with two many-to-one relationships to Customer and Address, accordingly:

  1. If not done so already, an identity column (or other unique identifier) needs to be added to the Customer_Addresses table; e.g., CustomerAddressId.  This is now the primary key of the table and will be the ID property of our domain object.
  2. A CustomerAddress.cs class needs to be added to the domain model having a many-to-one relationship to Customer as "public Customer Customer {...}", a many-to-one relationship to Address as "public Address Address {...}" and, of course, the "public bool IsCurrent {...}" property.
  3. CustomerAddress.hbm.xml needs to be added which reflects the same property and many-to-one relationships as indicated in step 2.  This HBM would map to Customer_Addresses table.
  4. The IList<Address> collection property within the Customer class should be modified to be IList<CustomerAddress>.
  5. Finally, the Customer.hbm.xml needs be modified to replace the existing many-to-many declaration with a one-to-many to the CustomerAddress object.
  6. (If your Address.hbm.xml contained a bi-directional reference back to IList<Customer>, you'd then have to take the same steps to replace the "other side" of the many-to-many with a one-to-many to CustomerAddress.)

With this done, the many-to-many has been removed, a domain object has been introduced, and proper associations have been established.  Many-to-many(s) are handy, but as soon as they starting doing more than just being a relationship, action should be taken to promote the relational object to the domain.

Billy McCafferty



Comments

Thomas Vochten said:

Finally someone that explained this clear to me ;-)

# July 11, 2008 6:20 PM

jack said:

thanks for the article billy. my question is bit out of the topic; i hope u will forgive me for that ;)

say i have 1 to many relation between Customer and Adreess. and address is polymorphic; i.e., Address can be HomeAddress, ShippingAddress, BillingAddress. for arguments sake lets say Customer can have multiple instances of each type of address.

since Customer has 1-to-many relation with Address, by default it would return all types of address when we say Customer.Addresses. now is it possible to execute a query from Customer which fetches only specific Addresses? please note relation is unidirectional.

sorry billy if this question is out of context.

appreciate ur contribution to the community

# July 12, 2008 12:54 AM

Cassio Tavares said:

Hi Jack,

I think you should have an enumerator (eg AddressType) representing a fields in your address table. Then you make a method in a DAO to get it by it's address type.

public Address GetAddressByType(AddressType type)

You have other ways but this seems to be one of the best for me.

# July 12, 2008 8:22 AM

Dew Drop - July 12, 2008 | Alvin Ashcraft's Morning Dew said:

Pingback from  Dew Drop - July 12, 2008 | Alvin Ashcraft's Morning Dew

# July 12, 2008 9:31 AM

jack said:

@Cassio,

in my case Address can be entity. all the addresses will be stored in a single table, hence table per class inheritance. but customer will be the aggregate root. (note: this eg. is just for arguments sake). so addresses will be accessed/updated/deleted through Customer.

now my question was is it possible to fetch lets say only ShippingAddresses only for the customer? i can filter it out in the server but was wondering if it can be done in a single query.

thanks

# July 12, 2008 9:32 AM

Billy McCafferty said:

@jack,

For the specific example you provided, I'd create a "protected IList<Address> Addresses" on the Customer and then add wrapper properties; e.g., "public IList<HomeAddress> HomeAddress" which would use LINQ to return HomeAddress objects from the Addresses collection.  IMO, for the example you're discussing, I'd more likely forego having polymorphism and simply add an AddressType enum to the Address object.  Polymorphism is typically only used if the inheritance structure has different behavior in each object; otherwise, a simple type enum should adaquately express the difference.

With that said, there are certainly other situation, when inheritance is very warranted, wherein you can use a couple of NHibernate tricks.  The "discriminator" and occassionally the "where" clause are useful for these situations:  www.hibernate.org/.../mapping.html (Setting the "polymorphism" property to explicit is usually needed in these cases as well.)

# July 13, 2008 10:06 AM

jack said:

@billy

thanks for taking time to respond.

using wrapper properties means filtering it in the application. right? so does that mean i wouldn't be able to say get Cutomers with their HomeAddresses only? as i said the relation is uni directional from Customers to Address.

thanks

# July 13, 2008 7:33 PM

Billy McCafferty said:

Ah, now I understand what you're asking.  If you want to query for one or more customers based on the address type, you'd have to do a join query.  here's an example:  devlicio.us/.../performing-join-queries-with-nhibernate-createalias.aspx   what you're looking for would require one less alias than the example shown.  let me know if that's not what you're looking for.

# July 13, 2008 8:15 PM

jack said:

i think what i am trying to achieve is not quite right semantically ;) using join queries i could filter out customers based on criteria in Address but when i do Customer.AddressList i would still get all the addresses of the customer. what i want is just specific type of Addresses not all types of Addresses.

i think i have to use different collection for each type of address and use where in the mapping. i guess that's the only way to go right?

# July 13, 2008 8:43 PM

Billy McCafferty said:

Yes...I think that would probably be the way to go for what you're trying to do.

# July 13, 2008 8:50 PM

jack said:

thanks for clearing out my doubt billy

# July 13, 2008 9:10 PM

Reflective Perspective - Chris Alcock » The Morning Brew #135 said:

Pingback from  Reflective Perspective - Chris Alcock  &raquo; The Morning Brew #135

# July 14, 2008 1:59 AM

El Guapo said:

OK, that's a good post, but here is my dilemma.

I want to add a couple columns to the Customer_Address table which are meaningful only in the relational model and not in the domain model. Let's say these are audit fields with user and timestamp info (for example). It doesn't really matter what they are.

As far as I can determine, I am still stuck in the situation where NH is forcing me to create a 3rd entity for this association, even though I don't want or need it in my domain model.

Is this true? Is there a way to hook in to manage extra columns on the association table? It seems that interceptors do not work - they are not invoked if you don't create an entity for this table.

Thanks

# July 14, 2008 9:12 AM

Billy McCafferty said:

Outside of using a trigger or default value, I'm not sure what you can do to avoid the intermediary object.

# July 14, 2008 9:17 AM

El Guapo said:

That sucks.

# July 14, 2008 6:01 PM

Billy McCafferty said:

Fair enough.

# July 14, 2008 6:32 PM

So you want to learn NHibernate? - Part 1 of 1, The Links « HSI Developer Blog said:

Pingback from  So you want to learn NHibernate? - Part 1 of 1, The Links &laquo; HSI Developer Blog

# July 31, 2008 6:27 PM

Emanuel Pasat said:

Sorry if I'm offtopic, I don't know if is stupid or not, but here is my question:

Does anybody see any problem by using a decorated Address instead of using the intermediary object (IList<DecoratedWithIsCurrentAddress> instead of a IList<CustomerAddress>)?

Basically, from the relation table I would get a decorated Address instead of the intermediary object. Does it makes sense (I'm very new to NH)? Does this hides some logical flaws?

# September 1, 2008 2:07 PM

Billy McCafferty said:

Emanuel,

If returning a decorated object meets your development needs, then that is the appropriate approach to take.

# September 1, 2008 3:48 PM

Emanuel Pasat said:

Problem is solved, I was needed it as part of a  Composite design pattern implementation. Now the relation object is behaving like the child object through decorator.

Thanks

# September 2, 2008 12:16 PM

Laura said:

Thanks a lot for the article.

# September 3, 2008 11:53 AM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add
Check out Devlicio.us!

Our Sponsors

Red-Gate!