Ryan LanciauxNew Media Mercenary

Using NHibernate (years after I should have been)

April 30, 2008 by ryan

As mentioned in my last post, I've been starting to use NHibernate in some of my more recent projects. I checked it out years ago and I completely hated it (maybe becuase I was a newer developer -- not totally sure). More recently, however, I've realized some of the benefits of Domain Driven Design and thought its about time to give Hibernate another shot. I'm admittedly pretty new to Hibernate so any feedback would be appreciated!

Classes 

    public class ProductGroup

    {

        public virtual string ProductGroupID { get; set; }

        public virtual string Title{ get; set;}

        public virtual IList<SimpleProduct> Products { get; set; }

    } 

    public class SimpleProduct

    {

        private IList<SimpleProduct> _relatedProducts;

 

        public virtual string ID { get; set; }

        public virtual string Title { get; set; }

        public virtual string ImagePath { get; set; }

        public virtual string Description { get; set; }

        public virtual IList<SimpleProduct> RelatedProducts

        {

            get{ return _relatedProducts; }

            private set { _relatedProducts = value;  }

        }

 

        public virtual void AddRelatedProduct(SimpleProduct product)

        {

            if (_relatedProducts == null)

                _relatedProducts = new List<SimpleProduct>();

            _relatedProducts.Add(product);

        }

 

    }

We're going to start out with some very contrived classes (normally would start with tests but this is just for the hibernate concepts)... These classes should be pretty straight forward; they consist only of some basic properties and list methods.

Data Tables 

Next, we need to create our tables to hold the data coming from the Classes -- I've created a product table / product group table and a lookup table for both (there are many-to-many references in both SimpleProduct and ProductGroup). 

CREATE TABLE [dbo].[SimpleProducts](
    [ProductID] [char](32) NOT NULL,
    [Title] [nvarchar](50) NOT NULL,
    [ImagePath] [nvarchar](300) NULL,
    [Description] [nvarchar](500) NULL
)
   
CREATE TABLE [dbo].[RelatedProductsLookup](
    [ProductID] [char](32) NOT NULL,
    [RelatedProductID] [char](32) NOT NULL
)

CREATE TABLE [dbo].[ProductsProductGroupsLookup](
    [ProductGroupID] [char](32) NULL,
    [ProductID] [char](32) NULL
)

CREATE TABLE [dbo].[ProductGroups](
    [ProductGroupID] [char](32) NOT NULL,
    [Title] [nvarchar](50) NULL
)    

 You will want to set the Primary Key of the SimpleProduct and Product Groups table to be the ID. 

Hibernate Mappings

Next we're on to the Hibernate mapping files. These files link the columns in the database to the fields/properties in the domain classes. Please check the project documentation for more detailed information on the Hibernate Schemas.

<?xml version="1.0" encoding="utf-8" ?>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"

                  namespace="ProductModel" assembly="ProductModel" default-lazy="false">

  <class name="ProductGroup" table="ProductGroups">

    <id name="ProductGroupID">

      <column name="ProductGroupID" sql-type="char(32)" not-null="true" />

      <generator class="uuid.hex" />

    </id>   

    <property name="Title">

      <column name="Title" length="50" not-null="true" />

    </property>   

    <bag name="Products"  table="ProductsProductGroupsLookup" lazy="false">

      <key column="ProductGroupID" />

      <many-to-many class="SimpleProduct" column="ProductID" />     

    </bag>       

  </class

</hibernate-mapping>

Inside the hibernate mapping tag, we have an element called class where we're defining the relationship between the class and the table in the database. From there we're defining the properties -- Most of this should be pretty straight forward but there are a couple things I would like to focus on. 

    <id name="ProductGroupID">

      <column name="ProductGroupID" sql-type="char(32)" not-null="true" />

      <generator class="uuid.hex" />

    </id>   

 This node is defining the unique ID for the class -- the ID is being created for each item and looks a little something like this: 46abbefc08d14b49a5d15c8a4dd69ff2

    <bag name="Products"  table="ProductsProductGroupsLookup" lazy="false">

      <key column="ProductGroupID" />

      <many-to-many class="SimpleProduct" column="ProductID" />     

    </bag>       

  </class

Here we're defining a many-to-many relationship with Products -- this is bumping up against the lookup table we defined earlier to get all SimpleProducts associated with this class. currently the bag is defining a many-to-many relationship. We're going to assume that a product can exist under any number of product groups -- for instance if we have a video game system it could be under both the Home Entertainment and Electronics product group. See the project documentation for other types of relationships (one-to-many, etc).

SimpleProduct Mapping:

<?xml version="1.0" encoding="utf-8" ?>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"

                  namespace="ProductModel" assembly="ProductModel" default-lazy="false">

  <class name="SimpleProduct" table="SimpleProducts">

    <id name="ID">

      <column name="ProductID" sql-type="char(32)" not-null="true" />

      <generator class="uuid.hex" />

    </id>   

    <property name="Title">

      <column name="Title" length="50" not-null="true" />

    </property>   

    <property name="ImagePath">

      <column name="ImagePath" length="300" not-null="false" />

    </property>   

    <property name="Description">

      <column name="Description" length="500" not-null="false" />

    </property>       

    <bag name="RelatedProducts"  table="RelatedProductsLookup" lazy="false">

      <key column="ProductID" />

      <many-to-many class="SimpleProduct" column="RelatedProductID" />     

    </bag>       

  </class

</hibernate-mapping>
 

 

Accessing The Data

Now that we've defined all of our mappings its time to create some methods to access our data.

    public class ProductGroupRepository

    {

        private ISession _session;

 

        public ProductGroupRepository(ISession session)

        {

            _session = session;

        }

 

        public ProductGroup GetByTitle(string Title)

        {

                return _session.CreateCriteria(typeof(ProductGroup))

                    .Add(Expression.Eq("Title", Title))

                    .UniqueResult<ProductGroup>();

        }

 

        public IList<ProductGroup> List()

        {

                return _session.CreateCriteria(typeof(ProductGroup))

                    .List<ProductGroup>();

 

        }

    }

 

The ISession is the Hibernate object to use when accessing the data -- from what it looks like, these should pretty much line up with a unit of work (More on that here).

In the other methods of the class, we first need to define what type of object we're looking for. In case it's not obvious, we're specifying that in the CreateCriteria section. In the List() method, we're returning a list (of ProductGroups) -- there are no filters or other criteria defined for this operation. In the GetByTitle() method, however,  we're stating that the Title for the product must match the Parameter 'Title'.

Demo Application

I've created a quick demo application using MVC and NHibernate. As I said, I'm still very new to hibernate so there's a lot I'm trying to learn (for instance where the ISession should initially be created and closed in an MVC application). Please let me know of any way that this could be better -- generally I'm writing this to help solidfy my thoughts on the technology, help others (hopefully) and also improve from others feedback. 

If you have tortoise or some other svn client, you can grab the code from http://svn2.assembla.com/svn/NHibernateTest/ -- You will need to change the database connection to whatever your db is. Again I would like to reiterate, it's demo code -- not even close to anything that I would ever use in production but worked out okay for testing. 

kick it on DotNetKicks.com

 

Disclaimer: Use this code at your own risk. 

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList



Related posts

Comments

May 1. 2008 09:15

Chase Saunders

tried LINQ?

Chase Saunders

May 1. 2008 09:36

Igor

...or SubSonic.

Igor

May 1. 2008 09:45

Ryan Lanciaux

Yes I use both of them Smile SubSonic, especially, is one of my favorite tools of all time. That being said, Linq to SQL and Subsonic have a different mindset in regard to accessing data. Ayende has a pretty good comparison of SubSonic and NHibernate here www.ayende.com/.../...ubSonic-amp-NHibernate.aspx.

I'm not moving away from Subsonic or Linq-to-SQL but which methodology I'm going to use really depends on the project.

Ryan Lanciaux

May 1. 2008 10:08

Seth Petry-Johnson

@Ryan: Your link to Ayende's blog post isn't working (at least in firefox) due to the trailing period. Here's a repost:
www.ayende.com/.../On-SubSonic-amp-NHibernate.aspx

Seth Petry-Johnson

May 1. 2008 10:11

Ryan Lanciaux

@Seth: Oh you're right -- thank you for re-posting that Smile

Ryan Lanciaux

May 1. 2008 11:07

Onur Gumus

("Linq" != "Linq to SQL" && "Linq to SQL" != ORM) period

Onur Gumus

May 1. 2008 11:11

Ryan Lanciaux

@Onur I agree -- I assumed that Chase was talking about Linq to SQL. Its a way to access data with objects but imo not an ORM. I think Entity Framework might be more of an ORM but I haven't had much time to look into that yet. msdn.microsoft.com/en-us/library/bb399572.aspx

Ryan Lanciaux

May 2. 2008 17:40

que0x

you should consider Castle's ActiveRecord, it's based on Nhibernate but more time saving .
Side Note : NHibernate is totally different than SubSonic. my experience with subsonic was great at the beginning then i realized than I'm creating the database to fit subsonic generated code ! that's a disaster actually and ends up with a messed up database design. also HQL is a lot better than SubSonic queries .

que0x

June 6. 2008 10:18

Joe

I still have not gotten on the nHibernate or LINQ bandwagons.
Definately not a requirement for DDD as they are independent things.

When I adopt a new technique or "Microsoft feature" I clarify that it is easing some pain I have. Right now I have no data translation pains. So I played with nHibernate for two days, linked up some tables, then shrugged my shoulders.

My two cents,
Good luck,
-Joe

Joe

Add comment


(Will show your Gravatar icon)  

  Country flag

[b][/b] - [i][/i] - [u][/u]- [quote][/quote]



Live preview

July 23. 2008 23:10





© 2008 Ryan Lanciaux :: powered by BlogEngine.NET