





















































(Read more interesting articles on Nhibernate 2 Beginner's Guide here.)
Remember all those great relationships we created in our database to relate our tables together?. Basically, the primary types of relationships are as follows:
We won't focus on the OTO relationship because it is really uncommon. In most situations, if there is a need for a one-to-one relationship, it should probably be consolidated into the main table.
The most common type of relationship we will map is a one-to-many (OTM) and the other way—many-to-one (MTO). If you remember, these are just two different sides of the same relationship, as seen in the following screenshot:
This is a simple one-to-many (OTM) relationship where a Contact can be associated with zero to many OrderHeader records (because the relationship fields allow nulls). Notice that the Foreign Key for the relationship is stored on the "many" side, ShipToContact_Id and BillToContact_Id on the OrderHeader table. In our mapping files, we can map this relationship from both sides.
If you remember, our classes for these objects contain placeholders for each side of this relationship. On the OrderHeader side, we have a Contact object called BillToContact:
private Contact _billToContact; public Contact BillToContact { get { return _billToContact; } set { _billToContact = value; } }
On the Contact side, we have the inverse relationship mapped. From this vantage point, there could be SEVERAL OrderHeaders objects that this Contact object is associated with, so we needed a collection to map it:
private IList<OrderHeader> _billTOrderHeaders; public IList<OrderHeader> BillTOrderHeaders { get { return _billTOrderHeaders; } set { _billTOrderHeaders = value; } }
As we have mapped this collection in two separate classes, we also need to map it in two separate mapping files. Let's start with the OrderHeader side. As this is the "many" side of the one-to-many relationship, we need to use a many-to-one type to map it. Things to note here are the name and class attributes. name, again, is the property in our class that this field maps to, and class is the "other end" of the Foreign Key relationship or the Contact type in this case.
<many-to-one name="BillToContact" class="Contact"> <column name="BillToContact_Id" length="4" sql-type="int" not-null="false"/> </many-to-one>
Just like before, when we mapped our non-relational fields, the length, sql-type, and not-null attributes are optional.
Now that we have the "one" side mapped, we need to map the "many" side. In the contact mapping file, we need to create a bag element to hold all of these OrderHeaders. A bag is the NHibernate way to say that it is an unordered collection allowing duplicated items. We have a name element to reference the class property just like all of our other mapping elements and a key child element to tell NHibernate which database column this field is meant to represent.
<bag name="BillToOrderHeaders" inverse="true cascade="all-delete-orphan"> <key column="BillToContact_Id"/> <one-to-many class="BasicWebApplication.Common.DataObjects.OrderHeader, BasicWebApplication"/> </bag>
If you look at the previous XML code, you will see that the one-to-many tag looks very similar to the many-to-one tag we just created for the other side. That's because this is the inverse side of the relationship. We even tell NHibernate that the inverse relationship exists by using the inverse attribute on the bag element. The class attribute on this tag is just the name of the class that represents the other side of the relationship.
The cascade attribute tells NHibernate how to handle objects when we delete them. Another attribute we can add to the bag tag is the lazy attribute. This tells NHibernate to use "lazy loading", which means that the record won't be pulled from the database or loaded into memory until you actually use it. This is a huge performance gain because you only get data when you need it, without having to do anything. When I say "get Contact record with Id 14", NHibernate will go get the Contact record, but it won't retrieve the associated BillToOrderHeaders (OrderHeader records) until I reference Contact.BillToOrderHeaders to display or act on those objects in my code. By default, "lazy loading" is turned on, so we only need to specify this tag if we want to turn "lazy loading" off by using lazy="false".
The other relationship that is used quite often is the many-to -many (MTM) relationship. In the following example, the Contact_Phone table is used to join the Contact and Phone tables. NHibernate is smart enough to manage these MTM relationships for us, and we can "optimize out" the join table from our classes and just let NHibernate take care of it.
Just like the one-to-many relationship, we represent the phones on the Contact class with a collection of Phone objects as follows:
private IList<Phone> _phones; public IList<Phone> Phones { get { return _ phones; } set { _ phones = value; } }
Mapping the MTM is very similar to the OTM, just a little more complex. We still use a bag and we still have a key. We need to add the table attribute to the bag element to let NHibernate know which table we are really storing the relationship data in. Instead of a one-to-many and a many-to-one attribute, both sides use a many-to-many element (makes sense, it is an MTM relationship, right?). The many-to-many element structure is the same as the one-to-many element, with a class attribute and a column child element to describe the relationship.
<bag name="Phones" table="Contact_Phone" inverse="false" lazy="true" cascade="none"> <key> <column name="Contact_Id" length="4" sql-type="int" not-null="true"/> </key> <many-to-many class=" Phone"> <column name="Phone_Id" length="4" sql-type="int" not-null="true"/> </many-to-many> </bag>
From the Phone side, it looks remarkably similar, as it's just the opposite view of the same relationship:
<bag name="Contacts" table="Contact_Phone" inverse="false" lazy="true" cascade="none"> <key> <column name="Phone_Id" length="4" sql-type="int" not-null="true"/> </key> <many-to-many class=" Contact "> <column name="Contact_Id" length="4" sql-type="int" not-null="true"/> </many-to-many> </bag>
This should be enough information to get us rolling on the path to becoming NHibernate superstars! Now that we have all of the primary fields mapped, let's map the Foreign Key fields.
If you look at the following database diagram, you will see that there are two relationships that need to be mapped, BillToContact and ShipToContact (represented by BillToContact_Id and ShipToContact_Id in the following screenshot).
Let's map these two properties into our hbm.xml files.
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping namespace="Ordering.Data" assembly="Ordering.Data"> <class name="OrderHeader" table="OrderHeader "> <id name="Id"> <column name="Id"/> <generator class="hilo"/> </id> <property name="Number" type="String"/> <property name="OrderDate" type="DateTime"/> <property name="ItemQty" type="Int32"/> <property name="Total" type="Decimal"/> </class> </hibernate-mapping>
<many-to-one name="BillToContact" class="Ordering.Data.Contact, Ordering.Data"> <column name="BillToContact_Id" /> </many-to-one>
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping namespace="Ordering.Data" assembly="Ordering.Data"> <class name=" Contact " table="Contact"> <id name="Id"> <column name="Id"/> <generator class="hilo"/> </id> <property name="FirstName" type="String"/> <property name="LastName" type="String"/> <property name="Email" type="String"/> </class> </hibernate-mapping>
<bag name="BillToOrderHeaders" inverse="true" lazy="true" cascade="all-delete-orphan"> <key column="BillToContact_Id"/> <one-to-many class="OrderHeader "/> </bag>
By adding one-to-many and many-to-one child elements to the bag tag, we were able to map the relationships to the Contact object, allowing us to use dotted notation to access child properties of our objects within our code.
Like the great cartographers before us, we have the knowledge and experience to go forth and map the world!
Now that you have some experience mapping fields and Foreign Keys from the database, why not have a go at the rest of our database! Start off with the Contact-to-Phone MTM table, and map the rest of the tables to the classes we created earlier, so that we will be ready to actually connect to the database