Chapter 2. Hello NHibernate! – NHibernate in Action

Chapter 2. Hello NHibernate!

This chapter covers

  • NHibernate in action with a “Hello World” application
  • How to architecture an NHibernate application
  • Writing and mapping a simple entity
  • Configuring NHibernate
  • Implementing primitive CRUD operations

It’s good to understand the need for object/relational mapping in .NET applications, but you’re probably eager to see NHibernate in action. We start by showing you a simple example that demonstrates some of its power.

As you’re probably aware, it’s traditional for a programming book to start with a “Hello World” example. In this chapter, we follow that tradition by introducing NHibernate with a relatively simple “Hello World” program. But printing a message to a console window won’t be enough to really demonstrate NHibernate. Instead, your program will store newly created objects in the database, update them, and perform queries to retrieve them from the database.

This chapter forms the basis for the subsequent chapters. In addition to the canonical “Hello World” example, we introduce the core NHibernate APIs and explain how to configure NHibernate in various runtime environments, such as ASP.NET applications and standalone WinForms applications.

2.1. “Hello World” with NHibernate

NHibernate applications define persistent classes that are mapped to database tables. Our “Hello World” example consists of one class and one mapping file. Let’s see what a simple persistent class looks like, how the mapping is specified, and some of the things you can do with instances of the persistent class using NHibernate.

2.1.1. Installing NHibernate

Before you can start coding the “Hello World” application, you must first install NHibernate. You then need to create a new Visual Studio solution to contain the sample application.

NHibernate 1.2.1GA can be downloaded via http://www.nhforge.org. Click the “download” tab and locate “NHibernate Core.” From there you can find and download the NHibernate 1.2.1.GA.msi file.

Although the book is written for NHibernate 1.2.1GA, we’re aware that many people are using NHibernate 2.0 Beta. We’ve therefore ensured our first tutorial applies to both of these versions of NHibernate.

Once you’ve downloaded and installed NHibernate, you’re ready to create a new solution and start using it.

2.1.2. Create a new Visual Studio project

For the example application, you should create a new blank project with Visual Studio. This is a simple application, so the easiest thing to create is a C# Console Application. Name your project HelloNHibernate. Note that you can also use NHibernate with VB.NET projects, but in this book, we’ve chosen to use C# examples.

The application will need to use the NHibernate library, so the next step is to reference it in your new project. To do this, follow these steps:

  1. Right-click the project and select Add Reference.
  2. Click the Browse tab and navigate to the folder where NHibernate is installed. By default, NHibernate resides in the C:\Program Files\NHibernate\bin\net2.0\ folder.
  3. From the list of assemblies, select NHibernate.dll. Click OK to add this reference to your solution. By default, the Console Application should have added a file called Program.cs to your solution. Locate this file and open it. Note that, in console applications, this will be the first thing that is run when you execute the program.
  4. Reference the NHibernate library at the top of the Program.cs file with the using NHibernate, using System.Reflection and NHibernate.Cfg statements, as follows:
using System;
using System.Reflection;
using NHibernate;
using NHibernate.Cfg;

namespace HelloNHibernate
{
public class Program
{
static void Main()
{
}
}
}

Now that your solution is set up, you’re ready to start writing your first NHibernate application.

2.1.3. Creating the Employee class

The objective of the sample application is to store an Employee record in a database and to later retrieve it for display. The application needs a simple persistent class, Employee, which represents a person who is employed by a company.

In Visual Studio, add a new class file to your application and name it Employee.cs when prompted. Then enter the code from listing 2.1 for the Employee entity.

Listing 2.1. Employee.cs: A simple persistent class
namespace HelloNHibernate
{
class Employee
{
public int id;
public string name;
public Employee manager;

public string SayHello()
{
return string.Format(
"'Hello World!', said {0}.", name);
}
}
}

The Employee class has three fields: the identifier, the name of the employee, and a reference to the employee’s manager. The identifier field allows the application to access the database identity—the primary key value—of a persistent object. If two instances of Employee have the same identifier value, they represent the same row in the database. We’ve chosen int for the type of the identifier field, but this isn’t a requirement. NHibernate allows virtually anything for the identifier type, as you’ll see later.

Note that you use public fields here rather than properties. This is purely to make the sample code shorter; it isn’t always considered good practice.

Instances of the Employee class may be managed (made persistent) by NHibernate, but they don’t have to be. Because the Employee object doesn’t implement any NHibernate-specific classes or interfaces, you can use it like any other .NET class:

Employee fred = new Employee();
fred.name = "Fred Bloggs";
Console.WriteLine( fred.SayHello() );

This code fragment does exactly what you’ve come to expect from “Hello World” applications: it prints “Hello World, said Fred Bloggs” to the console. It may look like we’re trying to be cute; in fact, we’re demonstrating an important feature that distinguishes NHibernate from some other persistence solutions. The persistent class can be used with or without NHibernate—no special requirements are needed. Of course, you came here to see NHibernate, so let’s first set up the database and then demonstrate using NHibernate to save a new Employee to it.

2.1.4. Setting up the database

You need to have a database set up so that NHibernate has somewhere to save entities. Setting up a database for this program should only take a minute. NHibernate can work with many databases, but for this example you’ll use Microsoft SQL Server 2000 or 2005.

Your first step is to open Microsoft SQL Server Management Studio, connect to your database server, and open a new query window. Type the following in the SQL window to quickly create a new database:

CREATE DATABASE HelloNHibernate
GO

Run this SQL to create the database. The next step is to switch to that database and create a table to hold your Employee data. To do so, delete the previous SQL and replace it with the following

USE HelloNHibernate
GO
CREATE TABLE Employee (
id int identity primary key,
name varchar(50),
manager int )
GO

Run this code: you’ve created a place to store your Employee entities. You’re now ready to see NHibernate in action!

Note that, in chapter 9, we show you how to use NHibernate to automatically create the tables your application needs using just the information in the mapping files. There’s some more SQL you won’t need to write by hand!

2.1.5. Creating an Employee and saving to the database

The code required to create an Employee and save it to the database is shown in listing 2.2. It comprises two functions: CreateEmployeeAndSaveToDatabase and OpenSession. You can type these functions into your Program.cs file below the static void Main() function in the Program class.

Listing 2.2. Creating and saving an Employee
static void CreateEmployeeAndSaveToDatabase()
{
Employee tobin = new Employee();
tobin.name = "Tobin Harris";

using (ISession session = OpenSession())
{
using( ITransaction transaction = session.BeginTransaction() )
{
session.Save(tobin);
transaction.Commit();
}
Console.WriteLine("Saved Tobin to the database");
}
}

static ISession OpenSession()
{
if(factory == null)
{
Configuration c = new Configuration();
c.AddAssembly(Assembly.GetCallingAssembly());
factory = c.BuildSessionFactory();
}
return factory.OpenSession();
}

static ISessionFactory factory;

The CreateEmployeeAndSaveToDatabase function calls the NHibernate Session and Transaction interfaces. (We’ll get to that OpenSession() call soon.) You’re not ready to run the code just yet; but to give you an idea of what would happen, running the CreateEmployeeAndSaveToDatabase function would result in NHibernate executing some SQL behind the scenes:

insert into Employees (name, manager)
values ('Tobin Harris', null)

Hold on—the Id column isn’t being initialized here. You didn’t set the id field of message anywhere, so how can you expect it to get a value? The id property is special: it’s an identifier property—it holds a unique value generated by the database. This generated value is assigned to the Employee instance by NHibernate during the call to the Save() method.

We don’t discuss the OpenSession function in depth here, but essentially it configures NHibernate and returns a session object that you can use to save, load, and search objects in your database (and much more!). Don’t use this OpenSession function in your production projects; you’ll learn more economical approaches throughout this book.

You now have the Employee class defined, and have added an Employee table to your database. We’ve also added some code to create an Employee instance and save it to the database using NHibernate. To complete the program, you will next add some code that will load the Employee from the database, and print our “Hello World” message. Let’s add that code now.

2.1.6. Loading an Employee from the database

Let’s start by adding code that can retrieve all Employees from the database in alphabetical order. Type the code in listing 2.3 below the previous OpenSession() function.

Listing 2.3. Retrieving Employees
static void LoadEmployeesFromDatabase()
{
using (ISession session = OpenSession())
{
IQuery query = session.CreateQuery(
"from Employee as emp order by emp.name asc");

IList<Employee> foundEmployees = query.List<Employee>();

Console.WriteLine("\n{0} employees found:";,
foundEmployees.Count);

foreach( Employee employee in foundEmployees )
Console.WriteLine(employee.SayHello());
}
}

The literal string "from Employee as emp order by emp.name asc" is an NHibernate query, expressed in NHibernate’s own object-oriented Hibernate Query Language (HQL). This query is internally translated into the following SQL when query.List() is called:

select e.id, e.name, e.manager
from Employee e
order by e.name asc

If you’ve never used an ORM tool like NHibernate before, you were probably expecting to see the SQL statements somewhere in the code or metadata. They aren’t there. All SQL is generated at runtime (at startup, where possible).

So far, you’ve defined the Employee entity, set up the database, and written code to create a new Employee and later retrieve it from the Employee table. NHibernate has barely entered the picture yet. Next, you’ll write some XML to tell NHibernate about the Employee entity and how you want Employees saved in the database.

2.1.7. Creating a mapping file

In order for NHibernate to do its magic in any of the code so far, it first needs more information about how the Employee class should be made persistent. This information is usually provided in an XML mapping document. The mapping document defines, among other things, how properties of the Employee class map to columns of the Employees table. Let’s look at the mapping document in listing 2.4.

Listing 2.4. Simple Hibernate XML mapping
<?xml version="1.0"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
auto-import="true">
<class name="HelloNHibernate.Employee, HelloNHibernate" lazy="false">
<id name="id" access="field">
<generator class="native" />
</id>
<property name="name" access="field" column="name"/>
<many-to-one access="field" name="manager" column="manager"
cascade="all"/>
</class>
</hibernate-mapping>

To add this mapping document to your solution, do the following:

  1. Right-click your HelloNHibernate project in the Solution Explorer, and select Add > New Item.
  2. Select the XML document type, and name it Employee.hbm.xml.
  3. Click OK.
  4. Now highlight the XML file in Solution Explorer and look for the Build Action property in the Properties pane.
  5. Change it from “Content” to “Embedded Resource.” This is an important step that you shouldn’t miss, because it allows NHibernate to easily find the mapping information.
  6. Now copy the XML in listing 2.4 into your Employee.hbm.xml file.

The mapping document you’ve just created tells NHibernate that the Employee class is to be persisted to the Employees table, that the id field maps to a column named id, that the name field maps to a column named name, and that the manager property is an association with many-to-one multiplicity that maps to a column named ManagerId. (Don’t worry about the other details for now.)

As you can see, the XML document isn’t difficult to understand. You can easily write and maintain it by hand. In chapter 3, we discuss a way to generate the XML file from comments embedded in the source code. Whichever method you choose, NHibernate has enough information to completely generate all the SQL statements needed to insert, update, delete, and retrieve instances of the Employee class. You no longer need to write these SQL statements by hand.


Note

NHibernate has sensible defaults that minimize typing and a mature document type definition that can be used for auto-completion or validation in editors, including Visual Studio. You can even automatically generate metadata with various tools.


While we’re on the subject of XML, now is a good time to show you how to configure NHibernate.

2.1.8. Configuring your application

If you’ve created .NET applications that use DataSets or DataReaders to connect to a database, you may be familiar with the concept of storing a ConnectionString in your web.config or app.config file. Configuring NHibernate is similar; you add some connection information to the config file. Follow these steps:

1.  

Right-click your HelloNHibernate project in the Solution Explorer, and select Add > New Item.

2.  

Select Application Configuration File from the options. Click OK to add an app.config file to the project.

3.  

Copy the following XML into your file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="nhibernate"
type="System.Configuration.NameValueSectionHandler,
System, Version=1.0.3300.0,Culture=neutral,
PublicKeyToken=b77a5c561934e089"
/>
</configSections>
<nhibernate>
<add key="hibernate.show_sql"
value="false" />
<add key="hibernate.connection.provider"
value="NHibernate.Connection.DriverConnectionProvider" />
<add key="hibernate.dialect"
value="NHibernate.Dialect.MsSql2000Dialect" />
<add key="hibernate.connection.driver_class"
value="NHibernate.Driver.SqlClientDriver" />
<add key="hibernate.connection.connection_string"
value="Data Source=127.0.0.1;
Database=HelloNHibernate;Integrated Security=SSPI;" />
</nhibernate>
</configuration>

That’s quite a lot of XML! But remember, NHibernate is very flexible and can be configured in many ways. Note that you may need to change the hibernate.connection. connection_string key at the bottom of the XML to connect to the database server on your development computer.

Also note that this configuration is for NHibernate 1.2.1GA. If you’re using NHibernate 2.0 or later, then copy the following XML instead:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="hibernate-configuration"
type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" />
</configSections>

<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">
NHibernate.Connection.DriverConnectionProvider
</property>
<property name="connection.driver_class">
NHibernate.Driver.SqlClientDriver
</property>
<property name="connection.connection_string">
Server=(local);database=HelloNHibernate;Integrated Security=SSPI;
</property>
<property name="dialect">
NHibernate.Dialect.MsSql2000Dialect
</property>
<property name="show_sql">
false
</property>
</session-factory>
</hibernate-configuration>
</configuration>

2.1.9. Updating an Employee

You’ve added code for saving and loading Employees. Before we run our application, let’s finish off by adding one more function to demonstrate how NHibernate can update existing entities. You’ll write some code to update the first Employee and, while you’re at it, create a new Employee to be the manager of the first, as shown in listing 2.5. Again, type this code below the other functions in Program.cs.

Listing 2.5. Updating an Employee
static void UpdateTobinAndAssignPierreHenriAsManager()
{
using (ISession session = OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
IQuery q = session.CreateQuery(
"from Employee where name = 'Tobin Harris'");

Employee tobin = q.List<Employee>()[0];
tobin.name = "Tobin David Harris";

Employee pierreHenri = new Employee();
pierreHenri.name = "Pierre Henri Kuate";

tobin.manager = pierreHenri;
transaction.Commit();

Console.WriteLine("Updated Tobin and added Pierre Henri");
}
}
}

Behind the scenes, NHibernate runs four SQL statements inside the same transaction:

select e.id, e.name, e.manager
from Employee e
where e.id = 1

insert into Employees (name, manager)
values ('Pierre Henri Kuate', null)

declare @newId int
select @newId = scope_identity()

update Employees
set name = 'Tobin David Harris', manager = @newId
where id = 1

Notice how NHibernate detects the modification to the name and manager properties of the first Employee (Tobin) and automatically updates the database. You’re taking advantage of an NHibernate feature called automatic dirty checking: this feature saves you the effort of explicitly asking NHibernate to update the database when we modify the state of an object. Similarly, you can see that the new Employee (Pierre Henri) was saved when it was associated with the first Employee. This feature is called cascading save: it saves you the effort of explicitly making the new object persistent by calling Save(), as long as it’s reachable by an already persistent object (Tobin). Also, notice that the ordering of the SQL statements isn’t the same as the order in which you set fields of the object. NHibernate uses a sophisticated algorithm to determine an efficient ordering that avoids database foreign-key-constraint violations but is still sufficiently predictable to the user. This feature is called transactional write-behind.

2.1.10. Running the program

Before finally running the example, you need to write some code to run all these functions in the right order. Modify your Program.cs Main method to look like this:

static void Main()
{
CreateEmployeeAndSaveToDatabase();
UpdateTobinAndAssignPierreHenriAsManager();
LoadEmployeesFromDatabase();

Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}

If you run “Hello World,” it prints

Saved Tobin to the database
Updated Tobin and added Pierre Henri

2 employees found:
'Hello World!', said Pierre Henri Kuate.
'Hello World!', said Tobin David Harris.
Press any key to exit...

This is as far as we take the “Hello World” application. Now that you have some code under your belt, we take a step back and present an overview of NHibernate’s main APIs.

2.2. Understanding the architecture

The programming interfaces are the first thing you have to learn about NHibernate in order to use it in the persistence layer of your application. A major objective of API design is to keep the interfaces between software components as narrow as possible. But in practice, ORM APIs aren’t especially small. Don’t worry; you don’t have to understand all the NHibernate interfaces at once.

Figure 2.1 illustrates the roles of the most important NHibernate interfaces in the business and persistence layers. We show the business layer above the persistence layer because the business layer acts as a client of the persistence layer in a traditionally layered application. Note that some simple applications may not cleanly separate business logic from persistence logic; that’s OK—it simplifies the diagram.

Figure 2.1. High-level overview of the NHibernate API in a layered architecture

The NHibernate interfaces shown in figure 2.1 may be approximately classified as follows:

  • Interfaces called by applications to perform basic CRUD and querying operations (Create, Retrieve, Update, and Delete). These interfaces are the main point of dependency of application business/control logic on NHibernate. They include ISession, ITransaction, IQuery, and ICriteria.
  • Interfaces called by application infrastructure code to configure NHibernate, most importantly the Configuration class.
  • Callback interfaces that allow the application to react to events occurring inside NHibernate, such as IInterceptor, ILifecycle, and IValidatable.
  • Interfaces that allow extension of NHibernate’s powerful mapping functionality, such as IUserType, ICompositeUserType, and IIdentifierGenerator. These interfaces are implemented by application infrastructure code (if necessary).

NHibernate makes use of existing .NET APIs, including ADO.NET and its ITransaction API. ADO.NET provides a rudimentary level of abstraction of functionality common to relational databases, letting NHibernate support almost any database with an ADO.NET driver.

In this section, we don’t cover the detailed semantics of NHibernate API methods, just the role of each of the primary interfaces. We progressively discuss API methods in the next chapters. You can find a complete and succinct description of these interfaces in NHibernate’s reference documentation. Let’s take a brief look at each interface in turn.

2.2.1. The core interfaces

The five core interfaces described in this section are used in just about every NHibernate application. Using these interfaces, you can store and retrieve persistent objects and control transactions.

ISession Interface

The ISession interface is the primary interface used by NHibernate applications. It exposes NHibernate’s methods for finding, saving, updating, and deleting objects. An instance of ISession is lightweight and is inexpensive to create and destroy. This is important because your application will need to create and destroy sessions all the time, perhaps on every ASP.NET page request. NHibernate sessions are not thread safe and should by design be used by only one thread at a time. This is discussed in further details in future chapters.

The NHibernate notion of a session is something between connection and transaction. It may be easier to think of a session as a cache or collection of loaded objects relating to a single unit of work. NHibernate can detect changes to the objects in this unit of work. We sometimes call the ISession a persistence manager because it’s also the interface for persistence-related operations such as storing and retrieving objects. Note that an NHibernate session has nothing to do with an ASP.NET session. When we use the word session in this book, we mean the NHibernate session.

We describe the ISession interface in detail in section 4.2.

ISessionfactory Interface

The application obtains ISession instances from an ISessionFactory. Compared to the ISession interface, this object is much less exciting.

The ISessionFactory certainly isn’t lightweight! It’s intended to be shared among many application threads. There is typically a single instance of ISessionFactory for the whole application—created during application initialization, for example. But if your application accesses multiple databases using NHibernate, you’ll need a SessionFactory for each database.

The SessionFactory caches generated SQL statements and other mapping metadata that NHibernate uses at runtime. It can also hold cached data that has been read in one unit of work and which may be reused in a future unit of work or session. This is possible if you configure class and collection mappings to use the second-level cache.

Configuration Interface

The Configuration object is used to configure NHibernate. The application uses a Configuration instance to specify the location of mapping documents and to set NHibernate-specific properties before creating the ISessionFactory.

Even though the Configuration interface plays a relatively small part in the total scope of an NHibernate application, it’s the first object you’ll meet when you begin using NHibernate. Section 2.2 covers the issue of configuring NHibernate in some detail.

ITransaction Interface

The ITransaction interface is shown in figure 2.1, next to the ISession interface. The ITransaction interface is an optional API. NHibernate applications may choose not to use this interface, instead managing transactions in their own infrastructure code. An NHibernate ITransaction abstracts application code from the underlying transaction implementation—which might be an ADO.NET transaction or any kind of manual transaction—allowing the application to control transaction boundaries via a consistent API. This helps to keep NHibernate applications portable between different kinds of execution environments and containers.

We use the NHibernate ITransaction API throughout this book. Transactions and the ITransaction interface are explained in chapter 5.

IQuery and ICriteria Interfaces

The IQuery interface gives you powerful ways to perform queries against the database while also controlling how the query is executed. It’s the basic interface used for fetching data using NHibernate. Queries are written in HQL or in your database’s native SQL dialect. An IQuery instance is lightweight and can’t be used outside the ISession that created it. It’s used to bind query parameters, limit the number of results returned by the query, and execute the query.

The ICriteria interface is similar; it lets you create and execute object-oriented criteria queries.

We describe the features of the IQuery interface in chapter 7, where you’ll learn how to use it in your applications. Now that we’ve introduced you to the main APIs needed to write real-world NHibernate applications, the next section introduces some more advanced features. After that, we dive into how NHibernate is configured and how you can set up logging to view what NHibernate is doing behind the scenes (a great way of seeing NHibernate in action, if you’ll excuse the pun!).

2.2.2. Callback interfaces

Callback interfaces allow the application to receive a notification when something interesting happens to an object—for example, when an object is loaded, saved, or deleted. NHibernate applications don’t need to implement these callbacks, but they’re useful for implementing certain kinds of generic functionality, such as creating audit records.

The ILifecycle and IValidatable interfaces let a persistent object react to events relating to its own persistence lifecycle. The persistence lifecycle is encompassed by an object’s CRUD operations (when it’s created, retrieved, updated, or deleted).


Note

The original Hibernate team was heavily influenced by other ORM solutions that have similar callback interfaces. Later, they realized that having the persistent classes implement Hibernate-specific interfaces probably isn’t a good idea, because doing so pollutes our persistent classes with nonportable code. Because these interfaces are deprecated, we don’t discuss them in this book.


The IInterceptor interface was introduced to let the application process callbacks without forcing the persistent classes to implement NHibernate-specific APIs. Implementations of the IInterceptor interface are passed to the persistent instances as parameters. We discuss an example in chapter 8.

2.2.3. Types

A fundamental and powerful element of the architecture is NHibernate’s notion of a Type. An NHibernate Type object maps a .NET type to a database column type (the type may span multiple columns). All persistent properties of persistent classes, including associations, have a corresponding NHibernate type. This design makes NHibernate extremely flexible and extensible because each RDBMS has a different set of mapping to .NET types.

NHibernate includes a rich range of built-in types, covering all .NET primitives and many CLR classes, including types for System.DateTime, System.Enum, byte[], and Serializable classes.

Even better, NHibernate supports user-defined custom types. The interfaces IUserType, ICompositeUserType and IParameterizedType are provided to let you create your own types. You can also use IUserCollectionType to create your own collection types. You can use this feature to handle commonly used application classes such as Address, Name, and MonetaryAmount conveniently and elegantly. Custom types are considered a central feature of NHibernate, and you’re encouraged to put them to new and creative uses!

We explain NHibernate types and user-defined types in section 6.1. We now go on to list some of the lower-level interfaces. You may not need to use or understand all of them, but knowing they exist may give you extra flexibility when it comes to designing your applications.

2.2.4. Extension interfaces

Much of the functionality that NHibernate provides is configurable, allowing you to choose between certain built-in strategies. When the built-in strategies are insufficient, NHibernate will usually let you plug in your own custom implementation by implementing an interface. Extension points include the following:

  • Primary-key generation (IIdentifierGenerator interface)
  • SQL dialect support (Dialect abstract class)
  • Caching strategies (ICache and ICacheProvider interfaces)
  • ADO.NET connection management (IConnectionProvider interface)
  • Transaction management (ITransactionFactory and ITransaction interfaces)
  • ORM strategies (IClassPersister interface hierarchy)
  • Property-access strategies (IPropertyAccessor interface)
  • Proxy creation (IProxyFactory interface)

NHibernate ships with at least one implementation of each of the listed interfaces, so you don’t usually need to start from scratch if you wish to extend the built-in functionality. The source code is available for you to use as an example for your own implementation.

You should now have an awareness of the various APIs and interfaces that NHibernate provides. Luckily, you won’t need them all. For simple applications, you may need only the Configuration and ISession interfaces, as shown in the “Hello World” example. But before you can begin to use NHibernate in your applications, you must have some understanding of how NHibernate is configured. That is what we discuss next.

2.3. Basic configuration

NHibernate can be configured to run in almost any .NET application and development environment. Generally, NHibernate is used in two- and three-tiered client/ server applications, with NHibernate deployed only on the server. The client application is usually a web browser, but Windows client applications aren’t uncommon. Although we concentrate on multitiered web applications in this book, we cover Windows applications when needed.

The first thing you must do is start NHibernate. In practice, doing so is easy: you create an ISessionFactory instance from a Configuration instance.

2.3.1. Creating a SessionFactory

To create an ISessionFactory instance, you first create a single instance of Configuration during application initialization and use it to set the database access and mapping information. Once configured, the Configuration instance is used to create the SessionFactory. After the SessionFactory is created, you can discard the Configuration class.

In the previous examples, we used a MySessionFactory static property to create ISession instances. Here is its implementation:

The location of the mapping file, Employee.hbm.xml, is relative to the application’s current directory. In this example, you also use an XML file to set all other configuration options (which may have been set earlier by application code or in the application configuration file).


Method chaining

Method chaining is a programming style supported by many NHibernate interfaces (they’re also called fluent interfaces). This style is more popular in Smalltalk than in .NET and is considered by some people to be less readable and more difficult to debug than the more accepted .NET style, but it’s convenient in most cases.

Most .NET developers declare setter or adder methods to be of type void, meaning they return no value. In Smalltalk, which has no void type, setter and adder methods usually return the receiving object. This would let you rewrite the previous code example as follows:

ISessionFactory sessionFactory = new Configuration()
.Configure()
.AddXmlFile("Employee.hbm.xml")
.BuildSessionFactory();

Notice that you don’t need to declare a local variable for the Configuration.

We use this style in some code examples; but if you don’t like it, you don’t need to use it. If you do use this coding style, it’s better to write each method invocation on a different line. Otherwise, it may be difficult to step through the code in your debugger.


By convention, NHibernate XML mapping files are named with the .hbm.xml extension. Another convention is to have one mapping file per class, rather than have all your mappings listed in one file (which is possible but considered bad style). The “Hello World” example had only one persistent class. But let’s assume you have multiple persistent classes, with an XML mapping file for each. Where should you put these mapping files?

Working with Mapping Files

The NHibernate documentation recommends that the mapping file for each persistent class be placed in the same directory as that class file. For instance, the mapping file for the Employee class would be placed in a file named Employee.hbm.xml in the same directory as the file Employee.cs. If you had another persistent class, it would be defined in its own mapping file. We suggest that you follow this practice and that you load multiple mapping files by calling AddXmlFile().

It’s even possible to embed XML mapping files inside .NET assemblies. You have to tell the compiler that each of these files is an embedded resource; most IDEs allow you to specify this option. Then you can use the AddClass() method, passing the class’s type as the parameter:

ISessionFactory sessionFactory = new Configuration()
.Configure()
.AddClass( typeof(Model.Item) )
.AddClass( typeof(Model.User) )
.AddClass( typeof(Model.Bid) )
.BuildSessionFactory();

The AddClass() method assumes that the name of the mapping file ends with the .hbm.xml extension and is embedded in the same assembly as the mapped class file.

If you want to add all mapped classes (with .NET attributes) in an assembly, you can use an overload of the method HbmSerializer.Serialize(); or, if you want to add all mapping files embedded in an assembly, you can use the method AddAssembly():

ISessionFactory sessionFactory = new Configuration()
.Configure()
.AddInputStream( // .NET Attributes
HbmSerializer.Default.Serialize(typeof(Model.Item).Assembly) )
.AddAssembly( typeof(Model.Item).Assembly ) // XML
.BuildSessionFactory();

Note that it’s error-prone to use assemblies’ names (like "NHibernate.Auction"). That’s why you use one class’s type to directly retrieve the assembly containing the embedded mapping files.


Why does NHibernate say it doesn’t know your class?

A common issue when starting to use NHibernate is making sure all your mappings are sent to NHibernate; if you miss one, you’ll get an exception. When building the session factory, it will be a MappingException with a comment containing ... refers to an unmapped class: YourClass. When executing a query, it will be a QueryException with a comment like possibly an invalid or unmapped class name was used in the query.

To solve this issue, the first step is to set log4net to the INFO level (you’ll learn how to do that in section 3.3.2). Then read the log to make sure NHibernate read your mappings; you should find a message like Mapping class: Namespace.YourClass -> YourClass. If it isn’t the case, then check your initialization code to make sure you included the mappings.

If you use AddAssembly(), make sure the hbm.xml files are embedded in your assembly.

On the other hand, you may get a DuplicateMappingException if you add a mapping many times. For example, avoid adding both the XML and the attributes-based mapping.


Multiple Databases and Session Factories

We’ve demonstrated the creation of a single SessionFactory, which is all that most applications need. If you need another ISessionFactory instance—in the case of multiple databases, for example—repeat the process. Each SessionFactory is then available for one database and ready to produce ISession instances to work with that particular database and a set of class mappings. Once you have your SessionFactory, you can go on to create sessions, and start loading and saving objects.

Configuration Techniques

Of course, there is more to configuring NHibernate than pointing to mapping documents. You also need to specify how database connections are to be obtained, along with various other settings that affect the behavior of NHibernate at runtime. The multitude of configuration properties may appear overwhelming (a complete list appears in the NHibernate documentation), but don’t worry; most define reasonable default values, and only a handful are commonly required.

To specify configuration options, you may use any of the following techniques:

  • Pass an instance of System.Collections.IDictionary to Configuration.SetProperties(), or use Configuration.SetProperty() for each property (or manipulate the collection Configuration.Properties directly).
  • Set all properties in application configuration file (App.config or Web.config).
  • Include <property> elements in an XML file called hibernate.cfg.xml in the current directory.

The first option is rarely used except for quick testing and prototypes, but most applications need a fixed configuration file. Both the application configuration file and the hibernate.cfg.xml file provide the same function: to configure NHibernate. Which file you choose to use depends on your syntax preference. hibernate.cfg.xml is the filename chosen by convention. You can use any filename (such as NHibernate.config, because .config files are automatically protected by ASP.NET when deployed) and provide this filename to the Configure() method. It’s even possible to mix both options and have different settings for development and deployment.

A rarely used alternative option is to let the application provide an ADO.NET IDbConnection when it opens an NHibernate ISession from the SessionFactory (for example, by calling sessionFactory.OpenSession(myConnection)). Using this option means you don’t have to specify any database-connection properties (the other properties are still required). We don’t recommend this approach for new applications that can be configured to use the environment’s database-connection infrastructure.

Of all the configuration options, database-connection settings are the most important because, without them, NHibernate won’t know how to correctly talk to the database.

2.3.2. Configuring the ADO.NET database access

Most of the time, the application is responsible for obtaining ADO.NET connections. NHibernate is part of the application, so it’s responsible for getting these connections. You tell NHibernate how to get (or create new) ADO.NET connections.

Figure 2.2 shows how .NET applications interact with ADO.NET. Without NHibernate, the application code usually receives an ADO.NET connection from the connection pool (which is configured transparently) and uses it to execute SQL statements.

Figure 2.2. Direct access to ADO.NET connections

With NHibernate, the picture changes: NHibernate acts as a client of ADO.NET and its connection pool, as shown in figure 2.3. The application code uses the NHibernate ISession and IQuery APIs for persistence operations and only has to manage database transactions, ideally using the NHibernate ITransaction API.

Figure 2.3. NHibernate managing database access

Configuring NHibernate Using hibernate.cfg.xml

Listing 2.6 uses a file named hibernate.cfg.xml to configure NHibernate to access a Microsoft SQL Server 2000 database.

Listing 2.6. Using hibernate.cfg.xml to configure NHibernate
<?xml version="1.0" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">
NHibernate.Connection.DriverConnectionProvider
</property>
<property name="dialect">
NHibernate.Dialect.MsSql2000Dialect
</property>
<property name="connection.driver_class">
NHibernate.Driver.SqlClientDriver
</property>
<property name="connection.connection_string">
Data Source=(local); Initial Catalog=nhibernate;
Integrated Security=SSPI
</property>
</session-factory>
</hibernate-configuration>

This code’s lines specify the following information:

  • connection.provider specifies the name of the .NET class implementing the IConnectionProvider interface; here, we use the default one.
  • dialect specifies the name of the .NET class implementing the database Dialect. Dialects are how NHIbernate can take advantage of database-specific features. Despite the ANSI standardization effort, SQL is implemented differently by various databases vendors. You must specify a Dialect. NHibernate includes built-in support for most popular SQL databases, and new dialects may be defined easily.
  • connection.driver_class specifies the name of the .NET class implementing the ADO.NET Driver. Note when using the partial name of a driver that is in the global assembly cache (GAC), you have to add a <qualifyAssembly> element in the application configuration file to specify its fully qualified name so that NHibernate can successfully load it.
  • connection.connection_string specifies a standard ADO.NET connection string, used to create a database connection.

Note that these names (except the ConnectionString) should be fully qualified type names; they aren’t here because they’re implemented in the NHibernate.dll library, which is where the .NET framework looks for non–fully qualified types when NHibernate tries to load them.

Starting NHibernate

How do you start NHibernate with these properties? You declared the properties in a file named hibernate.cfg.xml, so you need only place this file in the application’s directory. It’s automatically detected and read when you create a Configuration object and call its Configure() method.

Let’s summarize the configuration steps you’ve learned so far (this is a good time to download and install NHibernate):

  1. If your database’s ADO.NET data provider isn’t yet installed, download and install it; it’s usually available from the database vendor website. If you’re using SQL Server, then you can skip this step.
  2. Add log4net.dll as reference to your project. This is optional but recommended.
  3. Decide which database-access properties NHibernate will need.
  4. Let the Configuration know about these properties by placing them in a hibernate.cfg.xml file in the current directory.
  5. Create an instance of Configuration in your application, call the Configure() method; load the mapped classes (with .NET attributes) using HbmSerializer. Default.Serialize() and AddInputStream(); and load the XML mapping files using either AddAssembly(), AddClass(), or AddXmlFile(). Build an ISessionFactory instance from the Configuration by calling BuildSessionFactory().
  6. Remember to close the instance of ISessionFactory (using MySessionFactory.Close()) when you’re done using NHibernate. Most of the time, you’ll do it while closing your application.

There are a few more steps when you use COM+ Enterprise Services; you’ll learn more about them in chapter 6. Don’t worry; NHibernate code can be easily integrated into COM+ with only a few additions.

You should now have a running NHibernate system. Create and compile a persistent class (the initial Employee, for example), add references to NHibernate.dll, log4net, and NHibernate.Mapping.Attributes in your project, put a hibernate.cfg.xml file in the application current directory, and build an ISessionFactory instance.

The next section covers advanced NHibernate configuration options. Some of them are recommended, such as logging executed SQL statements for debugging, and using the convenient XML configuration file instead of plain properties. But if you wish, you may safely skip this section and come back later once you’ve read more about persistent classes in chapter 3.

2.4. Advanced configuration settings

When you finally have an NHibernate application running, it’s well worth getting to know all the NHibernate configuration parameters. These parameters let you optimize the runtime behavior of NHibernate, especially by tuning the ADO.NET interaction (for example, using ADO.NET batch updates).

We don’t bore you with these details now; the best source of information about configuration options is the NHibernate reference documentation. In the previous section, we showed you the options you need to get started.

But there is one parameter we must emphasize at this point. You’ll need it continually when you develop software with NHibernate. Setting the property show_sql to the value true enables logging of all generated SQL to the console. You’ll use it for troubleshooting, performance tuning, and just to see what’s going on. It pays to be aware of what your ORM layer is doing—that’s why ORM doesn’t hide SQL from developers.

So far, we’ve assumed that you specify configuration parameters using a hibernate.cfg.xml file or programmatically using the collection Configuration.Properties. You may also specify these parameters using the application configuration file (web.config, app.config, and so on).

2.4.1. Using the application configuration file

You can use the application configuration file to fully configure an ISessionFactory instance (as demonstrated in listings 2.7 and 2.8). The application configuration file can contain either configuration parameters using an <nhibernate> section, or the same content as hibernate.cfg.xml file (using a <hibernate-configuration> section). Many users prefer to centralize the configuration of NHibernate this way instead of adding parameters to the Configuration in application code.

Listing 2.7. App.config configuration file using <nhibernate>

The NHibernate section is declared as a series of key/value entries. The key is the name of the property to set . You’ll learn about log4net in the next section.

It’s recommended that you use a <hibernate-configuration> section, as shown in listing 2.8.

Listing 2.8. App.config configuration file using <hibernate-configuration>

Now, declares a <hibernate-configuration>, as in hibernate.cfg.xml, which is based on the schema nhibernate-configuration.xsd. The value is inside the <property> tag .

This way is far more elegant and powerful, because you can also specify assemblies/ mapping documents. And you can configure an IDE like Visual Studio to provide IntelliSense inside the <hibernate-configuration> section: copy the configuration schema file (nhibernate-configuration.xsd) in the subdirectory \Common7\Packages\schemas\ xml\ of the Visual Studio installation directory. You can also configure the mapping schema file (nhibernate-mapping.xsd) to have IntelliSense when editing mapping files. You can find these files in NHibernate’s source code.

Note that you can use a <connectionStrings> configuration-file element to define a connection string and then give its name to NHibernate using the hibernate. connection.connection_string_name property.

Now you can initialize NHibernate as follows:

ISessionFactory sessionFactory = new Configuration()
.Configure()
.BuildSessionFactory();

Wait—how does NHibernate know where the configuration file is located?

When Configure() is called, NHibernate first searches for the information in the application configuration file and then in a file named hibernate.cfg.xml in the current directory. If you wish to use a different filename or have NHibernate look in a subdirectory, you must pass a path to the Configure() method:

ISessionFactory sessionFactory = new Configuration()
.Configure("NHibernate.config")
.BuildSessionFactory();

Using an XML configuration file is more comfortable than using a programmatic configuration. The fact that you can have the class-mapping files externalized from the application’s source (even if it’s only in a startup helper class) is a major benefit of this approach. You can, for example, use different sets of mapping files (and different configuration options) depending on your database and environment (development or production), and switch them programmatically.

If you have both an application configuration file and hibernate.cfg.xml in the current directory, the application configuration file’s settings are used.


Note

You can give the ISessionFactory a name. This name is specified as an attribute like this: <session-factory name="MySessionFactory">. NHibernate uses this name to identify the instance after creation. You can use the static method NHibernate.Impl.SessionFactoryObjectFactory.GetNamedInstance() to retrieve it. This feature may be useful when you’re sharing a SessionFactory between loosely coupled components. But it’s seldom used because, most of the time, it’s better to hide NHibernate behind the persistence layer.


Now that you have a functional NHibernate application, you’ll start encountering runtime errors. To ease the debugging process, you need to log NHibernate operations.

2.4.2. Logging

NHibernate (and many other ORM implementations) defers the execution of SQL statements. An INSERT statement isn’t usually executed when the application calls ISession.Save(); an UPDATE isn’t immediately issued when the application calls Item.AddBid(). Instead, the SQL statements are generally issued at the end of a transaction. This behavior is called write-behind, as we mentioned earlier.

This fact is evidence that tracing and debugging ORM code is sometimes nontrivial. In theory, it’s possible for the application to treat NHibernate as a black box and ignore this behavior. The NHibernate application can’t detect this write-behind (at least, not without resorting to direct ADO.NET calls).

But when you find yourself troubleshooting a difficult problem, you need to be able to see exactly what’s going on inside NHibernate. Because NHibernate is open source, you can easily step into the NHibernate code. Occasionally, doing so helps a great deal. But especially in the face of write-behind behavior, debugging NHibernate can quickly get you lost. You can use logging to obtain a view of NHibernate’s internals.

We’ve mentioned the show_sql configuration parameter, which is usually the first port of call when troubleshooting. Sometimes the SQL alone is insufficient; in that case, you must dig a little deeper.

NHibernate logs all interesting events using the open source library log4net. To see any output from log4net, you need to add some information in your application configuration file. The example in listing 2.9 directs all log messages to the console.

Listing 2.9. Basic configuration of log4net
<?xml version="1.0" ?>
<configuration>
<configSections>
<section
name="log4net"
type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"
/>
</configSections>

<log4net>
<appender name="ConsoleAppender"
type="log4net.Appender.ConsoleAppender, log4net">
<layout type="log4net.Layout.PatternLayout, log4net">
<param name="ConversionPattern" value="%m" />
</layout>
</appender>
<root>
<priority value="WARN" />
<appender-ref ref="ConsoleAppender" />
</root>
</log4net>
</configuration>

You can easily merge this file with listing 2.8. With this configuration, you won’t see many log messages at runtime.

Replacing the priority value WARN with INFO or DEBUG reveals the inner workings of NHibernate. Make sure you don’t do this in a production environment—writing the log will be much slower than the actual database access. We don’t give more details about log4net configuration here; feel free to read its documentation.

In this section, we talked about database-access configuration. This configuration is useless if NHibernate doesn’t know how to manipulate your entities. The next chapter covers NHibernate mapping.

2.5. Summary

In this chapter, we took a high-level look at NHibernate and its architecture after running a simple “Hello World” example. You also saw how to configure NHibernate in various environments and with various techniques.

The Configuration and SessionFactory interfaces are the entry points to NHibernate for applications running in both WinForms and ASP.NET environments. Hibernate can be integrated into almost every .NET environment, be it a console application, an ASP.NET application, or a fully managed three-tiered client/server application. The most important elements of an NHibernate configuration are the database resources (connection configuration), the transaction strategies, and, of course, the XML-based mapping metadata.

NHibernate’s configuration interfaces have been designed to cover as many usage scenarios as possible while still being easy to understand. Usually, a few modifications to your .config file and one line of code are enough to get NHibernate up and running.

None of this is much use without some persistent classes and their XML mapping documents. The next chapter is dedicated to writing and mapping persistent classes. You’ll soon be able to store and retrieve persistent objects in a real application with a nontrivial object/relational mapping.