When mapping objects to the database in NHibernate you can either use XML files or decorate your classes with mapping attributes (NHibernate.Mapping.Attributes). I've used both but I usually choose XML files simply as a matter of personal preference. Either method works well and both provide many options for defining the relationship between class and table and configuring it's function. I'll be using XML files in this tutorial but may work in some mapping attributes later in the process.
To get intellisense working with the mapping files you can copy the nhibernate-*.xsd files from the NHibernate source (NHibernate\src\NHibernate\) to C:\Program Files\Microsoft Visual Studio 8\Xml\Schemas (or wherever you installed VS). There are a few tools that can help by generating the mapping files based on an existing database schema, one that I've had the most success with is MyGeneration (try the template by lujan99).
As I go through each mapping I'll point out how certain features work but for detailed information on the available options check the official documentation here. Each of these mapping files will follow the naming convention classname.hbm.xml as this is what NHibernate expects to find when the SessionFactory is initialized (more on this in Part IV).
BlogPost mapping:
At the beginning of the file I'm setting the assembly and namespace values. They aren't required, but if they are not set you will need to fully qualify any class names used including the assembly ( i.e., NHBlog.Domain.BlogPost, NHBlog.Domain). The class element tells NHibernate what the name of the class is and which table that class maps to, in this case both are named BlogPost. The id element defines the primary key of the table and which property in the class it maps to. It's also includes the type (.net data type) and which class is generating these values. NHibernate includes many different generator classes. In our example we are using the identity function in SQL Server so the generator is set to native.
Each BlogPost will also have a collection of Comments seen here mapped using the bag element. This mapping is a bit different than normal in that the<loader query-ref="GetCommentsByBlogPostID"/> attribute specifies which query to use to load the collection of comments, in this case a stored
procedure. At the bottom of the file a
sql-query is defined to execute the stored procedure
GetCommentsByBlogPostID and pass in the value blogPostID. The
load-collection element instructs NHibernate what type of entities to expect as the result of this query.
Why am I using a stored procedure here? I don't have to, I
could load the collection like any other but doing it this way serves
two purposes; to demonstrate the use of stored procedures (a new
feature in NHibernate 1.2), and to assist in loading the comments in a
threaded conversational manner (see the requirements in Part II).
GetCommentsByBlogPost uses a feature in Sql Server 2005 (and Sql
Express 2005) called Common Table Expressions. More information can be
found here.
BlogUser mapping:
In BlogUser we have three collections mapped; BlogPosts, Comments, and BlogRoles. The first two are one-to-many relationships meaning that a single BlogUser can have posted many BlogPosts and made many Comments. The last is a many-to-many relationship meaning that a BlogUser can belong to many BlogRoles and a BlogRole can contain many BlogUsers. The table UserRole defines this relationship in the database. The mapping instructs NHibernate what type of class makes up this collection, which table to use to look up the collection members and which column holds the key for each.
BlogRole mapping:
BlogRole has a collection of BlogUsers which represents the other side of the many-to-many relationship defined in BlogUser.
Comment mapping:
A Comment has a reference to the BlogUser who made it (MadeBy), the Comment it is replying to (InReplyTo), if any, the BlogPost it is a comment on (BlogPost), and a collection of comments replying to it (Replies). Also, notice the "Depth" property doesn't allow inserting or updating of this value. That's because this value/column doesn't actually exist in the database, it's generated when loading a collection of comments. If you load an individual Comment this value will always be zero. This mapping is also using a stored procedure but in this case it will be used when loading a comment by id.
GetCommentsByBlogPostID stored procedure code:
GetCommentByID stored procedure code:
In Part IV we will configure NHibernate and begin building the supporting framework needed to use what we have done so far.