Chapter 48. NHibernate QuickStart

48.1. Introduction

This QuickStart application uses the all too familiar Northwind database and uses NHibernate browse and edit customers. It It is a very simple application that directly uses the DAO layer in many use-cases, as it is doing nothing more than table maintenance, but there is also a simple service layer that handles a fullillment process. The application uses Spring's declarative transaction management features, standard NHibernate API, and Open Session In View module. See Chapter 21, Object Relational Mapping (ORM) data access for information on those features.

[Note]Note
Even though data access is performed through NHibernate API all Spring.NET provided functionality is still present when using the standard NHibernate API, as Spring transaction managment is integrated into NHibernate extension points and exception translation is provided by AOP advice.

48.2. Getting Started

The QuickStart application is located in the directory directory <spring-install-dir>\examples\Spring\Spring.Data.NHibernate.Northwind. Load the application using the VS.NET 2008 solution file Spring.Northwind.2008.sln. The application uses the SqlLite database so no additional configuration is needed. To run the application set the Web application as the project that starts and Default.aspx as the start page.

The application has several layers with each layer represented as one or more VS.NET projects.

Solution Explorer for NHibernate QuickStart Application

The data access layer consists of two projects, Spring.Northwind.Dao and Spring.Northwind.Dao.NHibernate. The former contains only the DAO (data access object) interfaces and the latter the NHibernate implementation of those interfaces. The project Spring.Northwind.Service contains a simple service that calls into multiple DAO objects in order to satisfy a fulliment process. The Web project is a ASP.NET web application and the Spring.Northwind.IntegrationTests project contains integration tests for the DAO and Service layers.

When you run the application you will see

Following the link to the customer listing pages bring up the following screen

You can click on the Name of the customer or the Orders link to view that customers orders. Selecting "BOTTM"'s orders brings us to the next page

Notice that the order 11045 has yet to be shipped. If you select 'Process Orders' this will call the Fulliment Service and the order will be processed and shipped.p

You can then go back to the customer list. If you select the name Elizabeth Lincoln, then you can edit the customer details.

48.3. Implementation

This section discussed the Spring implementation details for each layer.

48.3.1. The Data Access Layer

The interface IDao is a generic DAO layer that provides basic retrieval methods. They are located in the Spring.Northwind.Dao project.

    public interface IDao<TEntity, TId>
    {
        TEntity Get(TId id);

        IList<TEntity> GetAll();

    }

The ISupportsSave and ISupportsDeleteDao interfaces provide the rest of the CRUD functionality.

    public interface ISupportsSave<TEntity, TId>
    {
        TId Save(TEntity entity);

        void Update(TEntity entity);     
    }


    public interface ISupportsDeleteDao<TEntity>
    {
        void Delete(TEntity entity);
    }

The ICustomerDao interface combines these to manage the persistence of customer objects.

    public interface ICustomerDao : IDao<Customer, string>, ISupportsDeleteDao<Customer>, ISupportsSave<Customer, string>
    {
    }

Similar interfaces are defined to manage Order and Products in IOrderDao and IProductDao respectfully.

48.3.2. The domain objects

The POCO domain objects, Customer, Order, OrderDetail and Product are defined in the Spring.Northwind.Domain namespace within the Spring.Northwind.Dao project.

48.3.3. NHibernate based DAO implementation

The NHibernate based DAO implemenation uses the standard NHibernate APIs, retrieving the current session from the SessionFactory and using the session to retrieve or store objects to the database. An abstract base class HibernateDao is used to capture the common ISessionFactory property, provide a convenience property to access the current session, and define a GetAll Method.p

    public abstract class HibernateDao
    {
        private ISessionFactory sessionFactory;

        /// <summary>
        /// Session factory for sub-classes.
        /// </summary>
        public ISessionFactory SessionFactory
        {
            protected get { return sessionFactory; }
            set { sessionFactory = value; }
        }

        /// <summary>
        /// Get's the current active session. Will retrieve session as managed by the 
        /// Open Session In View module if enabled.
        /// </summary>
        protected ISession CurrentSession
        {
            get { return sessionFactory.GetCurrentSession(); }
        }

        protected IList<T> GetAll<T>() where T : class
        {
            ICriteria criteria = CurrentSession.CreateCriteria<T>();
            return criteria.List<T>();
        }
    }

The implementation of ICustomerDao is shown below

    [Repository]
    public class HibernateCustomerDao : HibernateDao, ICustomerDao
    {

        // Note that the transaction demaraction is here only for the case when
        // the DAO object is being used directly, i.e. not as part of a service layer
        // call.  This would be commonly only when creating an application that contains
        // no business logic and is essentially a table maintenance application.  
        // These applications are affectionaly known as 'CRUD' applications, the acronym
        // refering to Create, Retrieve, Update, And Delete and the only operations
        // performed by the application.

        // If called from a transactional service layer, typically with the transaction
        // propagation setting set to REQUIRED, then any DAO operations will use the 
        // same settings as started from the transactional layer.

        [Transaction(ReadOnly = true)]
        public Customer Get(string customerId)
        {
            return CurrentSession.Get<Customer>(customerId);
        }

        [Transaction(ReadOnly = true)]
        public IList<Customer> GetAll()
        {
            return GetAll<Customer>();
        }


        [Transaction(ReadOnly = false)]
        public string Save(Customer customer)
        {
            return (string) CurrentSession.Save(customer);
        }

        [Transaction(ReadOnly = false)]
        public void Update(Customer customer)
        {
            CurrentSession.SaveOrUpdate(customer);
        }

        [Transaction(ReadOnly = false)]
        public void Delete(Customer customer)
        {
            CurrentSession.Delete(customer);
        }
    }
[Note]Note

As mentioned in the code comments above, as this application has a distinctly CRUD based component, Spring's Transaction attribute is used to ensure that that method exeuctes as a unit of work. Often in more sophisticated applications even the basic of CRUD are handled through a service layer so as to enforce security, auditing, alterting or enforce business rules.

The Repository attribute is used to indicate that this class plays the role of a Repository or a Data Access Object. The term repository comes from modeling terminology popularized by Eric Evan's book Domain Driven Design (DDD). Those familiar with DDD will note that this implementation is very simply and does not expose higher level persistence functionality to the application, for example FindCustomersWithOpenOrders. How well the role of Repository applies to this implementation is not relevant, and we will often refer to Repository and DAO intechangable when describing the data access layer. What is relevant is that the Repository attribute serves as a marker, a place in the code that can be used to identify methods whose invocation should be intercepted so that additional behavior can be added. In Aspect-Oriented Programming terminology, the Repository attribute represents a pointcut. The behavior that we would like to add to this DAO implementation exception translation. Exception translation from the data access layer to a service layer is important as it shields the service layer from the implementation details of the data access layer. A NHibernate based DAO will throw different exceptions and a ADO.NET based implementation and so on. Spring provides a rich technology neutral data-access exception hierarchy. See Chapter 18, DAO support.

Instead of adding exception translation code in each data access method, AOP offers a simple solution. Using Spring's IObjectPostProcessor extension point, each DAO object that is managed by Spring will be automatically wrapped up in a proxy that adds the exception translation behavior. This is done by adding the following object definition to the Spring application context.

<objects>

  <!-- configure session factory -->

  <!-- Exception translation object post processor -->
  <object type="Spring.Dao.Attributes.PersistenceExceptionTranslationPostProcessor, Spring.Data"/>

  <!-- Configure transaction management strategy -->
  <!-- DAO objects go here -->


</objects>

The Spring managed DAO object definitions are shown below, referring to a SessionFactory that is created via Spring's LocalSessionFactoryObject. See the file Dao.xml for more details.

<objects xmlns="http://www.springframework.net"
         xmlns:db="http://www.springframework.net/database">

  <!-- Referenced by main application context configuration file -->
  <description>
    The Northwind object definitions for the Data Access Objects.
  </description>

  <!-- Database Configuration -->
  <db:provider id="DbProvider"
                   provider="SQLite-1.0.65"
                   connectionString="Data Source=|DataDirectory|Northwind.db;Version=3;FailIfMissing=True;"/>

  <!-- NHibernate SessionFactory configuration -->
  <object id="NHibernateSessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate21">
    <property name="DbProvider" ref="DbProvider"/>
    <property name="MappingAssemblies">
      <list>
        <value>Spring.Northwind.Dao.NHibernate</value>
      </list>
    </property>
    <property name="HibernateProperties">
      <dictionary>
        <entry key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider"/>
        <entry key="dialect" value="NHibernate.Dialect.SQLiteDialect"/>
        <entry key="connection.driver_class" value="NHibernate.Driver.SQLite20Driver"/>
      </dictionary>
    </property>

    <!-- provides integation with Spring's declarative transaction management features -->
    <property name="ExposeTransactionAwareSessionFactory" value="true" />

  </object>

  <!-- Transaction Management Strategy - local database transactions -->
  <object id="transactionManager"
        type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate21">
    <property name="DbProvider" ref="DbProvider"/>
    <property name="SessionFactory" ref="NHibernateSessionFactory"/>
  </object>

  <!-- Exception translation object post processor -->
  <object type="Spring.Dao.Attributes.PersistenceExceptionTranslationPostProcessor, Spring.Data"/>

  <!-- Data Access Objects -->
  <object id="CustomerDao" type="Spring.Northwind.Dao.NHibernate.HibernateCustomerDao, Spring.Northwind.Dao.NHibernate">
    <property name="SessionFactory" ref="NHibernateSessionFactory"/>
  </object>

  <object id="OrderDao" type="Spring.Northwind.Dao.NHibernate.HibernateOrderDao, Spring.Northwind.Dao.NHibernate">
    <property name="SessionFactory" ref="NHibernateSessionFactory"/>
  </object>


</objects>
[Note]Note

It is not required that you use Spring's [Repository] attribute. You can specify an attribute type to the PersistenceExceptionTranslationPostProcessor via the property RepositoryAttributeType to avoid coupling your DAO implementation to Spring.

48.3.4. The Service layer

The service layer is located in the Spring.Northwind.Services project. It defines a single service for the fulliment process

    public interface IFulfillmentService
    {
        void ProcessCustomer(string customerId);
    }

The implementatiion class is shown below

    public class FulfillmentService : IFulfillmentService
    {


        private IProductDao productDao;

        private ICustomerDao customerDao;

        private IOrderDao orderDao;

        private IShippingService shippingService;


       // Properties for the preceding fields omitted for brevity

        [Transaction]
        public void ProcessCustomer(string customerId)
        {
            //Find all orders for customer
            Customer customer = CustomerDao.Get(customerId);
                 
            foreach (Order order in customer.Orders)
            {
                if (order.ShippedDate.HasValue)
                {
                    log.Warn("Order with " + order.Id + " has already been shipped, skipping.");
                    continue;
                }

                //Validate Order
                Validate(order);
                log.Info("Order " + order.Id + " validated, proceeding with shipping..");

                //Ship with external shipping service
                ShippingService.ShipOrder(order);
                
                //Update shipping date
                order.ShippedDate = DateTime.Now;
                
                //Update shipment date
                OrderDao.Update(order);
               
                //Other operations...Decrease product quantity... etc
            }             
            
        }

        private void Validate(Order order)
        {            
            //no-op - throw exception on error.
        }
    }
}

What is important to note about this method is that it uses two DAO objects, CustomerDao and OrderDao as well as an additional collaborating service, IShippingService. The fact that all of the collaborating objects are interfaced based means that we can write a unit test for the business functionality of the ProcessCustomer method. Also, the use of the [Transaction] attribute will enable this business processing to proceed as a single unit-of-work. Spring's declarative transaction management features make it very easy to mix and match different DAO objects with a service method without having to worry about propagating the transaction/connection or hibernate session to each DAO object.

The Fullfillment service layer is configured to refer to its collaborating objects as shown below in the configuration file Services.xml

  <!-- Property placeholder configurer for database settings -->
  <object id="FulfillmentService" type="Spring.Northwind.Service.FulfillmentService, Spring.Northwind.Service">
    <property name="CustomerDao" ref="CustomerDao"/>
    <property name="OrderDao" ref="OrderDao"/>
    <property name="ShippingService" ref="ShippingService"/>
  </object>

  <object id="ShippingService" type="Spring.Northwind.Service.FedExShippingService, Spring.Northwind.Service"/>
    
  <tx:attribute-driven/>

48.3.5. Integration testing

Integraiton testing in addition to unit testing can be done before integrating the service and data access layer into the Web application - where automated testing is much more difficult. While coding to interfaces and using an IoC container help enable unit testing, unit tests should not have any Spring dependency. Integration tests however greatly benefit from being able to access the objects that Spring is managing in production. This way the gap between what you testi in QA and what runs is minimized, ideally with only environment specific settings being different. In addition to easily obtaining, say a transactionally aware service object from the Spring IoC container, Spring intergration testing support classes allow you to implicitly start a transaction at the start of test method and rollback at the end. The isolation guaranteed by the database means that multiple developers can run integration tests for their data access layers simultaneously and the rollback ensures that the changes made are not persisted. While in the test method, you have a consistent view of the data and can therefore exercise all the methods of your DAO object.

The project Spring.Northwind.IntegrationTests shows how this works. As a convenience, an abstract base class is created that in turn inherits from Spring's integration testing class AbstractTransactionalDbProviderSpringContextTests

    [TestFixture]
    public abstract class AbstractDaoIntegrationTests : AbstractTransactionalDbProviderSpringContextTests
    {

        protected override string[] ConfigLocations
        {
            get
            {
                return new string[]
                    {
                        "assembly://Spring.Northwind.Dao.NHibernate/Spring.Northwind.Dao/Dao.xml",
                        "assembly://Spring.Northwind.Service/Spring.Northwind.Service/Services.xml"
                    };
            }
        }
    }
[Note]Note

This unit test is NUnit based but there is similar support available for Microsoft MSTest framework.

The exact same object definition files that will be used in the production application are loaded for the integration test. To test the data access layer, you inherit from AbstractDaoIntegrationTests and expose public properties for each DAO implementation you want to test. Within each test method exercise the API of the DAO. This also tests the NHibernate mappings.

    [TestFixture]
    public class NorthwindIntegrationTests : AbstractDaoIntegrationTests
    {
        private ICustomerDao customerDao;
        private IOrderDao orderDao;

        private ISessionFactory sessionFactory;

        // These properties will be injected based on type
        public ICustomerDao CustomerDao
        {
            set { customerDao = value; }
        }

        public IOrderDao OrderDao
        {
            set { orderDao = value; }
        }

        public ISessionFactory SessionFactory
        {
            set { sessionFactory = value; }
        }

        [Test]
        public void CustomerDaoTests()
        {
            Assert.AreEqual(91, customerDao.GetAll().Count);

            Customer c = new Customer();
            c.Id = "MPOLL";           
            c.CompanyName = "Interface21";
            customerDao.Save(c);
            c = customerDao.Get("MPOLL");
            Assert.AreEqual(c.Id, "MPOLL");
            Assert.AreEqual(c.CompanyName, "Interface21");

            //Without flushing, nothing changes in the database:
            int customerCount = (int)AdoTemplate.ExecuteScalar(CommandType.Text, "select count(*) from Customers");           
            Assert.AreEqual(91, customerCount);

            //Flush the session to execute sql in the db.
            SessionFactoryUtils.GetSession(sessionFactory, true).Flush();
           
            //Now changes are visible outside the session but within the same database transaction
            customerCount = (int)AdoTemplate.ExecuteScalar(CommandType.Text, "select count(*) from Customers");
            Assert.AreEqual(92, customerCount);
            
            Assert.AreEqual(92, customerDao.GetAll().Count);

            c.CompanyName = "SpringSource";

            customerDao.Update(c);

            c = customerDao.Get("MPOLL");
            Assert.AreEqual(c.Id, "MPOLL");
            Assert.AreEqual(c.CompanyName, "SpringSource");

            customerDao.Delete(c);


            SessionFactoryUtils.GetSession(sessionFactory, true).Flush();
            customerCount = (int)AdoTemplate.ExecuteScalar(CommandType.Text, "select count(*) from Customers");
            Assert.AreEqual(92, customerCount);

            try
            {
                c = customerDao.Get("MPOLL");
                Assert.Fail("Should have thrown HibernateObjectRetrievalFailureException when finding customer with Id = MPOLL");
            }
            catch (HibernateObjectRetrievalFailureException e)
            {
                Assert.AreEqual("Customer", e.PersistentClassName);
            }

        }

        [Test]
        public void ProductDaoTests()
        {
            // ommited for brevity
        }
    }

This test uses AdoTemplate to access the database using the standard ADO.NET APIs. It is done to demonstrate that the common configuration of NHibernate is for it not to flush to the database until a commit occurs. If we did not explicitly flush, then no SQL would be sent down to the database and some potential errors would go undetected. Since the test method will rollback the transaction, we don't have to worry about 'dirtying' the database and changing its state.

48.3.6. Web Application

The Web application uses Dependency Injection on the .aspx pages so that they can access the servcies of the middle tier, for example the FullfillmentService, or in the case of simple table maintenance, the DAO objects directly.

For example the FullfillmentResult.aspx code behind is shown below

public partial class FullfillmentResult : Page
{
    private IFulfillmentService fulfillmentService;
    private ICustomerEditController customerEditController;

    public IFulfillmentService FulfillmentService
    {
        set { fulfillmentService = value; }
    }

    public ICustomerEditController CustomerEditController
    {
        set { customerEditController = value; }
    }


    protected void Page_Load(object sender, EventArgs e)
    {

      /// code omitted for brevity

      fulfillmentService.ProcessCustomer(customerEditController.CurrentCustomer.Id);

    }

    protected void customerOrders_Click(object sender, EventArgs e)
    {
        SetResult("Back");
    }

The page is configured in Spring as shown below

  <object type="FulfillmentResult.aspx">
    <property name="FulfillmentService" ref="FulfillmentService" />
    <property name="CustomerEditController" ref="CustomerEditController" />
    <property name="Results">
      <dictionary>
        <entry key="Back" value="redirect:CustomerOrders.aspx" />
      </dictionary>
    </property>
  </object>

The page is injected with a reference to the FullfillmentService and also another UI component. While Spring's ASP.NET framework supports DI for standard ASP.NET pages and user controls, you can also inherit from Spring's base page class to get added functionality. In this example the use of externalized page flow, or Result Mapping is shown. The Results property indicates the 'how', 'where' and 'what data' to bring along when moving between different web pages and associates it with a logical name "Back". This avoid hardcoding server side transfers or redirects in your code as well as other ASP.NET page references. See the chapter on Spring's ASP.NET Web Framework for more details.