Nov 23 2008
Many-to-many mapping in NHibernate
In systems analysis, a many-to-many relationship refers to a relationship between two entities (see also Entity-Relationship Model) A and B in which A may contain a parent row for which there are many children in B and vice versa. Because most DBMSs only support one-to-many relationships, it is necessary to implement such relationships physically via a third junction table, say, AB with two one-to-many relationships A -> AB and B -> AB. In this case the logical primary key for AB is formed from the two foreign keys (i.e. copies of the primary keys of A and B).
In web application frameworks such as CakePHP and Ruby on Rails, a many-to-many relationship between database tables in a model is usually referred to as a hasAndBelongsToMany (HABTM) relationship. - Wikipedia
Before going through many-to-many mapping in NHibernate, we need to have basic concept of some collection types used in NHibernate.
- Bag: It is a collection type in which elements can be repeated {1, 2, 1, 3, 2, 3, 4} and it is implemented as IList or IList<T>.
- Set: It is a collection type in which elements must be unique {1, 2, 3, 4} and it is implemented as ISet or Iset<T> as a part pf Iesi.Collections.dll.
- List: It is a collection of objects with integer indices {1, “Max”}, {2, “Prajwal”} , {3, “Max”} and it is also implemented as IList or IList<T>.
- Map: It is a collection of key-value pairs {{”Prajwal”, 1}, {”Max”, 2}} and it is implemented as HashTable or IDictionary<TKey, TValue>.
I am using Northwind database to illustrate many-to-many mapping.
The above table relationship between Customers, CustomerDemographics and CustomerCustomerDemo represent many-to-many relationship because CustomerCustomerDemo table has many forms of Customers as well as CustomerDemographics. I need only Customers entity and CustomerDemographics entity to match the third table CustomerCustomerDemo in NHibernate to map the relation among these tables. The two strong entities are responsible for mapping a weak entity. Mapping is straight forward.
XML Mapping file for Customers Customer.hbm.xml
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DataTransfer" namespace="DataTransfer"> <class name="DataTransfer.Customer,DataTransfer" table="`Customers`"> <id name="CustomerID" column="CustomerID" type="string" length="5"> <generator class="assigned" /> </id> <property name="CompanyName" column="CompanyName" type="string" length="40" not-null="true" /> <property name="ContactName" column="ContactName" type="string" length="30" not-null="true" /> <property name="ContactTitle" column="ContactTitle" type="string" length="30" not-null="true" /> <property name="Address" column="Address" type="string" length="60" not-null="true" /> <property name="City" column="City" type="string" length="15" not-null="true" /> <property name="Region" column="Region" type="string" length="15" not-null="true" /> <property name="PostalCode" column="PostalCode" type="string" length="10" not-null="true" /> <property name="Country" column="Country" type="string" length="15" not-null="true" /> <property name="Phone" column="Phone" type="string" length="24" not-null="true" /> <property name="Fax" column="Fax" type="string" length="24" not-null="true" /> <bag name="CustomerDemographics" generic="true" table="CustomerCustomerDemo" lazy="true"> <key column="CustomerID" /> <many-to-many column="CustomerTypeID" class="DataTransfer.CustomerDemographic,DataTransfer" /> </bag> </class> </hibernate-mapping>
<strong>XML Mapping file for CustomerDemographics CustomerDemographic.hbm.xml</strong>
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DataTransfer" namespace="DataTransfer"> <class name="DataTransfer.CustomerDemographic,DataTransfer" table="CustomerDemographics"> <id name="CustomerTypeID" column="CustomerTypeID" type="string" length="10"> <generator class="assigned" /> </id> <property name="CustomerDesc" type="string" not-null="false" /> <bag name="Customers" table="CustomerCustomerDemo" lazy="true"> <key column="CustomerTypeID" /> <many-to-many column="CustomerID" class="DataTransfer.Customer,DataTransfer" /> </bag> </class> </hibernate-mapping>
Data Access Layer Code for getting CustomerDemographics value having CustomerID
public IList GetCustomerDemographicsWithCustomerID(string customerID)
{
return _session
.CreateCriteria(typeof(CustomerDemographic))
.CreateCriteria("Customers")
.Add(Expression.Eq("CustomerID", customerID))
.List();
}
Unit Testing the code
[Test]
public void CanGetCustomerDemographicsWithCustomerID()
{
IList demographics = _provider.GetCustomerDemographicsWithCustomerID("CACTU");
Assert.GreaterThan(demographics.Count, 0);
}
Debug time vale of the object IList<CustomerDemographic>
















