NHibernate - Relational Persistence for Idiomatic .NET

NHibernate Reference Documentation

1.0.2


Table of Contents

Preface
1. Quickstart with IIS and Microsoft SQL Server
1.1. Getting started with NHibernate
1.2. First persistent class
1.3. Mapping the cat
1.4. Playing with cats
1.5. Finally
2. Architecture
2.1. Overview
3. ISessionFactory Configuration
3.1. Programmatic Configuration
3.2. Obtaining an ISessionFactory
3.3. User provided ADO.NET connection
3.4. NHibernate provided ADO.NET connection
3.5. Optional configuration properties
3.5.1. SQL Dialects
3.5.2. Outer Join Fetching
3.5.3. Custom ICacheProvider
3.5.4. Query Language Substitution
3.6. Logging
3.7. Implementing an INamingStrategy
3.8. XML Configuration File
4. Persistent Classes
4.1. A simple POCO example
4.1.1. Declare accessors and mutators for persistent fields
4.1.2. Implement a default constructor
4.1.3. Provide an identifier property (optional)
4.1.4. Prefer non-sealed classes and virtual methods (optional)
4.2. Implementing inheritance
4.3. Implementing Equals() and GetHashCode()
4.4. Lifecycle Callbacks
4.5. IValidatable callback
5. Basic O/R Mapping
5.1. Mapping declaration
5.1.1. XML Namespace
5.1.2. hibernate-mapping
5.1.3. class
5.1.4. id
5.1.4.1. generator
5.1.4.2. Hi/Lo Algorithm
5.1.4.3. UUID Hex Algorithm
5.1.4.4. UUID String Algorithm
5.1.4.5. GUID Algorithms
5.1.4.6. Identity columns and Sequences
5.1.4.7. Assigned Identifiers
5.1.5. composite-id
5.1.6. discriminator
5.1.7. version (optional)
5.1.8. timestamp (optional)
5.1.9. property
5.1.10. many-to-one
5.1.11. one-to-one
5.1.12. component, dynamic-component
5.1.13. subclass
5.1.14. joined-subclass
5.1.15. map, set, list, bag
5.1.16. import
5.2. NHibernate Types
5.2.1. Entities and values
5.2.2. Basic value types
5.2.3. Custom value types
5.2.4. Any type mappings
5.3. SQL quoted identifiers
5.4. Modular mapping files
6. Collection Mapping
6.1. Persistent Collections
6.2. Mapping a Collection
6.3. Collections of Values and Many-To-Many Associations
6.4. One-To-Many Associations
6.5. Lazy Initialization
6.6. Sorted Collections
6.7. Using an <idbag>
6.8. Bidirectional Associations
6.9. Ternary Associations
6.10. Heterogeneous Associations
6.11. Collection examples
7. Component Mapping
7.1. Dependent objects
7.2. Collections of dependent objects
7.3. Components as IDictionary indices
7.4. Components as composite identifiers
7.5. Dynamic components
8. Inheritance Mapping
8.1. The Three Strategies
8.2. Limitations
9. Manipulating Persistent Data
9.1. Creating a persistent object
9.2. Loading an object
9.3. Querying
9.3.1. Scalar queries
9.3.2. The IQuery interface
9.3.3. Filtering collections
9.3.4. Criteria queries
9.3.5. Queries in native SQL
9.4. Updating objects
9.4.1. Updating in the same ISession
9.4.2. Updating detached objects
9.4.3. Reattaching detached objects
9.5. Deleting persistent objects
9.6. Flush
9.7. Ending a Session
9.7.1. Flushing the Session
9.7.2. Committing the database transaction
9.7.3. Closing the ISession
9.8. Exception handling
9.9. Lifecyles and object graphs
9.10. Interceptors
9.11. Metadata API
10. Transactions And Concurrency
10.1. Configurations, Sessions and Factories
10.2. Threads and connections
10.3. Considering object identity
10.4. Optimistic concurrency control
10.4.1. Long session with automatic versioning
10.4.2. Many sessions with automatic versioning
10.4.3. Application version checking
10.5. Session disconnection
10.6. Pessimistic Locking
11. HQL: The Hibernate Query Language
11.1. Case Sensitivity
11.2. The from clause
11.3. Associations and joins
11.4. The select clause
11.5. Aggregate functions
11.6. Polymorphic queries
11.7. The where clause
11.8. Expressions
11.9. The order by clause
11.10. The group by clause
11.11. Subqueries
11.12. HQL examples
11.13. Tips & Tricks
12. Criteria Queries
12.1. Creating an ICriteria instance
12.2. Narrowing the result set
12.3. Ordering the results
12.4. Associations
12.5. Dynamic association fetching
12.6. Example queries
13. Native SQL Queries
13.1. Creating a SQL based IQuery
13.2. Alias and property references
13.3. Named SQL queries
14. Improving performance
14.1. Understanding Collection performance
14.1.1. Taxonomy
14.1.2. Lists, maps and sets are the most efficient collections to update
14.1.3. Bags and lists are the most efficient inverse collections
14.1.4. One shot delete
14.2. Proxies for Lazy Initialization
14.3. Using batch fetching
14.4. The Second Level Cache
14.4.1. Cache mappings
14.4.2. Strategy: read only
14.4.3. Strategy: read/write
14.4.4. Strategy: nonstrict read/write
14.5. Managing the ISession Cache
14.6. The Query Cache
15. Toolset Guide
15.1. Schema Generation
15.1.1. Customizing the schema
15.1.2. Running the tool
15.1.3. Properties
15.1.4. Using Ant
15.1.5. Incremental schema updates
15.1.6. Using Ant for incremental schema updates
15.2. Code Generation
15.2.1. The config file (optional)
15.2.2. The meta attribute
15.2.3. Basic finder generator
15.2.4. Velocity based renderer/generator
15.3. Mapping File Generation
15.3.1. Running the tool
16. Example: Parent/Child
16.1. A note about collections
16.2. Bidirectional one-to-many
16.3. Cascading lifecycle
16.4. Using cascading Update()
16.5. Conclusion
17. Example: Weblog Application
17.1. Persistent Classes
17.2. Hibernate Mappings
17.3. NHibernate Code
18. Example: Various Mappings
18.1. Employer/Employee
18.2. Author/Work
18.3. Customer/Order/Product
19. Best Practices
I. NHibernateContrib Documentation
Preface
20. NHibernate.Caches
20.1. How to use a cache?
20.2. Prevalence Cache Configuration
20.3. SysCache Configuration
21. NHibernate.Mapping.Attributes
21.1. How to use it?
21.2. Tips
21.3. Know issues and TODOs
21.4. Developer Notes
22. NHibernate.Tool.hbm2net
23. Nullables
23.1. How to use it?

Preface

Working with object-oriented software and a relational database can be cumbersome and time consuming in today's enterprise environments. NHibernate is an object/relational mapping tool for .NET environments. The term object/relational mapping (ORM) refers to the technique of mapping a data representation from an object model to a relational data model with a SQL-based schema.

NHibernate not only takes care of the mapping from .NET classes to database tables (and from .NET data types to SQL data types), but also provides data query and retrieval facilities and can significantly reduce development time otherwise spent with manual data handling in SQL and ADO.NET.

NHibernate's goal is to relieve the developer from 95 percent of common data persistence related programming tasks. NHibernate may not be the best solution for data-centric applications that only use stored-procedures to implement the business logic in the database, it is most useful with object-oriented domain models and business logic in the .NET-based middle-tier. However, NHibernate can certainly help you to remove or encapsulate vendor-specific SQL code and will help with the common task of result set translation from a tabular representation to a graph of objects.

If you are new to NHibernate and Object/Relational Mapping or even .NET Framework, please follow these steps:

  1. Read Chapter 1, Quickstart with IIS and Microsoft SQL Server for a 30 minute tutorial, using Internet Information Services (IIS) web server.

  2. Read Chapter 2, Architecture to understand the environments where NHibernate can be used.

  3. Use this reference documentation as your primary source of information. Consider reading Hibernate in Action (http://www.manning.com/bauer) if you need more help with application design or if you prefer a step-by-step tutorial. Also visit http://nhibernate.sourceforge.net/NHibernateEg/ for NHibernate tutorial with examples.

  4. FAQs are answered on the NHibernate website.

  5. Third party demos, examples and tutorials are linked on the NHibernate website.

  6. The Community Area on the NHibernate website is a good source for design patterns and various integration solutions (ASP.NET, Windows Forms).

If you have questions, use the user forum linked on the NHibernate website. We also provide a JIRA issue trackings system for bug reports and feature requests. If you are interested in the development of NHibernate, join the developer mailing list. If you are interested in translating this documentation into your language, contact us on the developer mailing list.

Chapter 1. Quickstart with IIS and Microsoft SQL Server

1.1. Getting started with NHibernate

This tutorial explains a setup of NHibernate 1.0.2 within a Microsoft environment. The tools used in this tutorial are:

  1. Microsoft Internet Information Services (IIS) - web server supporting ASP.NET.
  2. Microsoft SQL Server 2000 - the database server. This tutorial uses the desktop edition (MSDE), a free download from Microsoft. Support for other databases is only a matter of changing the NHibernate SQL dialect and driver configuration.
  3. Microsoft Visual Studio .NET 2003 - the development environment.

First, we have to create a new Web project. We use the name QuickStart, the project web virtual directory will http://localhost/QuickStart. In the project, add a reference to NHibernate.dll. Visual Studio will automatically copy the library and its dependencies to the project output directory. If you are using a database other than SQL Server, add a reference to the driver assembly to your project.

We now set up the database connection information for NHibernate. To do this, open the file Web.config automatically generated for your project and add configuration elements according to the listing below:

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

    <!-- Add this element -->
    <hibernate-configuration xmlns="urn:nhibernate-configuration-2.0">
        <session-factory>
            <property name="dialect">NHibernate.Dialect.MsSql2000Dialect</property>
            <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
            <property name="connection.connection_string">Server=(local);initial catalog=quickstart;Integrated Security=SSPI</property>
            
            <mapping assembly="QuickStart" />
        </session-factory>
    </hibernate-configuration>

    <!-- Leave the system.web section unchanged -->
    <system.web>
        ...
    </system.web>
</configuration>

The <configSections> element contains definitions of sections that follow and handlers to use to process their content. We declare the handler for the configuration section here. The <hibernate-configuration> section contains the configuration itself, telling NHibernate that we will use a Microsoft SQL Server 2000 database and connect to it through the specified connection string. The dialect is a required setting, databases differ in their interpretation of the SQL "standard". NHibernate will take care of the differences and comes bundled with dialects for several major commercial and open source databases.

An ISessionFactory is NHibernate's concept of a single datastore, multiple databases can be used by creating multiple XML configuration files and creating multiple Configuration and ISessionFactory objects in your application.

The last element of the <hibernate-configuration> section declares QuickStart as the name of an assembly containing class declarations and mapping files. The mapping files contain the metadata for the mapping of the POCO class to a database table (or multiple tables). We'll come back to mapping files soon. Let's write the POCO class first and then declare the mapping metadata for it.

1.2. First persistent class

NHibernate works best with the Plain Old CLR Objects (POCOs, sometimes called Plain Ordinary CLR Objects) programming model for persistent classes. A POCO has its data accessible through the standard .NET property mechanisms, shielding the internal representation from the publicly visible interface:

using System;

namespace QuickStart
{
    public class Cat
    {
        private string id;
        private string name;
        private char   sex;
        private float  weight;

        public Cat()
        {
        }

        public string Id
        {
            get { return id; }
            set { id = value; }
        }

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public char Sex
        {
            get { return sex; }
            set { sex = value; }
        }

        public float Weight
        {
            get { return weight; }
            set { weight = value; }
        }
    }
}

NHibernate is not restricted in its usage of property types, all .NET types and primitives (like string, char and DateTime) can be mapped, including classes from the System.Collections namespace. You can map them as values, collections of values, or associations to other entities. The Id is a special property that represents the database identifier (primary key) of that class, it is highly recommended for entities like a Cat. NHibernate can use identifiers only internally, without having to declare them on the class, but we would lose some of the flexibility in our application architecture.

No special interface has to be implemented for persistent classes nor do we have to subclass from a special root persistent class. NHibernate also doesn't use any build time processing, such as IL manipulation, it relies solely on .NET reflection and runtime class enhancement (through Castle.DynamicProxy library). So, without any dependency in the POCO class on NHibernate, we can map it to a database table.

1.3. Mapping the cat

The Cat.hbm.xml mapping file contains the metadata required for the object/relational mapping. The metadata includes declaration of persistent classes and the mapping of properties (to columns and foreign key relationships to other entities) to database tables.

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0"
    namespace="QuickStart" assembly="QuickStart">

    <class name="Cat" table="Cat">

        <!-- A 32 hex character is our surrogate key. It's automatically
            generated by NHibernate with the UUID pattern. -->
        <id name="Id">
            <column name="CatId" sql-type="char(32)" not-null="true"/>
            <generator class="uuid.hex" />
        </id>

        <!-- A cat has to have a name, but it shouldn' be too long. -->
        <property name="Name">
            <column name="Name" length="16" not-null="true" />
        </property>
        <property name="Sex" />
        <property name="Weight" />
    </class>

</hibernate-mapping>

Every persistent class should have an identifer attribute (actually, only classes representing entities, not dependent value objects, which are mapped as components of an entity). This property is used to distinguish persistent objects: Two cats are equal if catA.Id.Equals(catB.Id) is true, this concept is called database identity. NHibernate comes bundled with various identifer generators for different scenarios (including native generators for database sequences, hi/lo identifier tables, and application assigned identifiers). We use the UUID generator (only recommended for testing, as integer surrogate keys generated by the database should be prefered) and also specify the column CatId of the table Cat for the NHibernate generated identifier value (as a primary key of the table).

All other properties of Cat are mapped to the same table. In the case of the Name property, we mapped it with an explicit database column declaration. This is especially useful when the database schema is automatically generated (as SQL DDL statements) from the mapping declaration with NHibernate's SchemaExport tool. All other properties are mapped using NHibernate's default settings, which is what you need most of the time. The table Cat in the database looks like this:

 Column |     Type     | Modifiers
--------+--------------+----------------------
 CatId  | char(32)     | not null, primary key
 Name   | nvarchar(16) | not null
 Sex    | nchar(1)     |
 Weight | real         |

You should now create the database and this table manually, and later read Chapter 15, Toolset Guide if you want to automate this step with the SchemaExport tool. This tool can create a full SQL DDL, including table definition, custom column type constraints, unique constraints and indexes. If you are using SQL Server, you should also make sure the ASPNET user has permissions to use the database.

1.4. Playing with cats

We're now ready to start NHibernate's ISession. It is the persistence manager interface, we use it to store and retrieve Cats to and from the database. But first, we've to get an ISession (NHibernate's unit-of-work) from the ISessionFactory:

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

An ISessionFactory is responsible for one database and may only use one XML configuration file (Web.config or hibernate.cfg.xml). You can set other properties (and even change the mapping metadata) by accessing the Configuration before you build the ISessionFactory (it is immutable). Where do we create the ISessionFactory and how can we access it in our application?

An ISessionFactory is usually only built once, e.g. at startup inside Application_Start event handler. This also means you should not keep it in an instance variable in your ASP.NET pages, but in some other location. Furthermore, we need some kind of Singleton, so we can access the ISessionFactory easily in application code. The approach shown next solves both problems: configuration and easy access to a ISessionFactory.

We implement a NHibernateHelper helper class:

using System;
using System.Web;
using NHibernate;
using NHibernate.Cfg;

namespace QuickStart
{
    public sealed class NHibernateHelper
    {
        private const string CurrentSessionKey = "nhibernate.current_session";
        private static readonly ISessionFactory sessionFactory;

        static NHibernateHelper()
        {
            sessionFactory = new Configuration().Configure().BuildSessionFactory();
        }

        public static ISession GetCurrentSession()
        {
            HttpContext context = HttpContext.Current;
            ISession currentSession = context.Items[CurrentSessionKey] as ISession;

            if (currentSession == null)
            {
                currentSession = sessionFactory.OpenSession();
                context.Items[CurrentSessionKey] = currentSession;
            }

            return currentSession;
        }

        public static void CloseSession()
        {
            HttpContext context = HttpContext.Current;
            ISession currentSession = context.Items[CurrentSessionKey] as ISession;

            if (currentSession == null)
            {
                // No current session
                return;
            }

            currentSession.Close();
            context.Items.Remove(CurrentSessionKey);
        }

        public static void CloseSessionFactory()
        {
            if (sessionFactory != null)
            {
                sessionFactory.Close();
            }
        }
    }
}

This class does not only take care of the ISessionFactory with its static attribute, but also has code to remember the ISession for the current HTTP request.

An ISessionFactory is threadsafe, many threads can access it concurrently and request ISessions. An ISession is a non-threadsafe object that represents a single unit-of-work with the database. ISessions are opened by an ISessionFactory and are closed when all work is completed:

ISession session = NHibernateHelper.GetCurrentSession();

ITransaction tx = session.BeginTransaction();

Cat princess = new Cat();
princess.Name = "Princess";
princess.Sex = 'F';
princess.Weight = 7.4f;

session.Save(princess);
tx.Commit();

NHibernateHelper.CloseSession();

In an ISession, every database operation occurs inside a transaction that isolates the database operations (even read-only operations). We use NHibernate's ITransaction API to abstract from the underlying transaction strategy (in our case, ADO.NET transactions). Please note that the example above does not handle any exceptions.

Also note that you may call NHibernateHelper.GetCurrentSession(); as many times as you like, you will always get the current ISession of this HTTP request. You have to make sure the ISession is closed after your unit-of-work completes, either in Application_EndRequest event handler in your application class or in a HttpModule before the HTTP response is sent. The nice side effect of the latter is easy lazy initialization: the ISession is still open when the view is rendered, so NHibernate can load unitialized objects while you navigate the graph.

NHibernate has various methods that can be used to retrieve objects from the database. The most flexible way is using the Hibernate Query Language (HQL), which is an easy to learn and powerful object-oriented extension to SQL:

ITransaction tx = session.BeginTransaction();

IQuery query = session.CreateQuery("select c from Cat as c where c.Sex = :sex");
query.SetCharacter("sex", 'F');
foreach (Cat cat in query.Enumerable())
{
    Console.Out.WriteLine("Female Cat: " + cat.Name);
}

tx.Commit();

NHibernate also offers an object-oriented query by criteria API that can be used to formulate type-safe queries. NHibernate of course uses IDbCommands and parameter binding for all SQL communication with the database. You may also use NHibernate's direct SQL query feature or get a plain ADO.NET connection from an ISession in rare cases.

1.5. Finally

We only scratched the surface of NHibernate in this small tutorial. Please note that we don't include any ASP.NET specific code in our examples. You have to create an ASP.NET page yourself and insert the NHibernate code as you see fit.

Keep in mind that NHibernate, as a data access layer, is tightly integrated into your application. Usually, all other layers depend on the persistence mechanism. Make sure you understand the implications of this design.

Chapter 2. Architecture

2.1. Overview

A (very) high-level view of the NHibernate architecture:

This diagram shows NHibernate using the database and configuration data to provide persistence services (and persistent objects) to the application.

We would like to show a more detailed view of the runtime architecture. Unfortunately, NHibernate is flexible and supports several approaches. We will show the two extremes. The "lite" architecture has the application provide its own ADO.NET connections and manage its own transactions. This approach uses a minimal subset of NHibernate's APIs:

The "full cream" architecture abstracts the application away from the underlying ADO.NET APIs and lets NHibernate take care of the details.

Heres some definitions of the objects in the diagrams:

ISessionFactory (NHibernate.ISessionFactory)

A threadsafe (immutable) cache of compiled mappings for a single database. A factory for ISession and a client of IConnectionProvider. Might hold an optional (second-level) cache of data that is reusable between transactions, at a process- or cluster-level.

ISession (NHibernate.ISession)

A single-threaded, short-lived object representing a conversation between the application and the persistent store. Wraps an ADO.NET connection. Factory for ITransaction. Holds a mandatory (first-level) cache of persistent objects, used when navigating the object graph or looking up objects by identifier.

Persistent Objects and Collections

Short-lived, single threaded objects containing persistent state and business function. These might be ordinary POCOs, the only special thing about them is that they are currently associated with (exactly one) ISession. As soon as the Session is closed, they will be detached and free to use in any application layer (e.g. directly as data transfer objects to and from presentation).

Transient Objects and Collections

Instances of persistent classes that are not currently associated with a ISession. They may have been instantiated by the application and not (yet) persisted or they may have been instantiated by a closed ISession.

ITransaction (NHibernate.ITransaction)

(Optional) A single-threaded, short-lived object used by the application to specify atomic units of work. Abstracts application from underlying ADO.NET transaction. An ISession might span several ITransactions in some cases.

IConnectionProvider (NHibernate.Connection.IConnectionProvider)

(Optional) A factory for ADO.NET connections and commands. Abstracts application from the concrete vendor-specific implementations of IDbConnection and IDbCommand. Not exposed to application, but can be extended/implemented by the developer.

IDriver (NHibernate.Driver.IDriver)

(Optional) An interface encapsulating differences between ADO.NET providers, such as parameter naming conventions and supported ADO.NET features.

ITransactionFactory (NHibernate.Transaction.ITransactionFactory)

(Optional) A factory for ITransaction instances. Not exposed to the application, but can be extended/implemented by the developer.

Given a "lite" architecture, the application bypasses the ITransaction/ITransactionFactory and/or IConnectionProvider APIs to talk to ADO.NET directly.

Chapter 3. ISessionFactory Configuration

Because NHibernate is designed to operate in many different environments, there are a large number of configuration parameters. Fortunately, most have sensible default values and NHibernate is distributed with an example App.config file (found in src\NHibernate.Test) that shows the various options. You usually only have to put that file in your project and customize it.

3.1. Programmatic Configuration

An instance of NHibernate.Cfg.Configuration represents an entire set of mappings of an application's .NET types to a SQL database. The Configuration is used to build an (immutable) ISessionFactory. The mappings are compiled from various XML mapping files.

You may obtain a Configuration instance by instantiating it directly. Heres an example of setting up a datastore from mappings defined in two XML configuration files:

Configuration cfg = new Configuration()
    .AddFile("Item.hbm.xml")
    .AddFile("Bid.hbm.xml");

An alternative (sometimes better) way is to let NHibernate load a mapping file from an embedded resource:

Configuration cfg = new Configuration()
    .AddClass(typeof(NHibernate.Auction.Item))
    .AddClass(typeof(NHibernate.Auction.Bid));

Then NHibernate will look for mapping files named NHibernate.Auction.Item.hbm.xml and NHibernate.Auction.Bid.hbm.xml embedded as resources in the assembly that the types are contained in. This approach eliminates any hardcoded filenames.

Another alternative (probably the best) way is to let NHibernate load all of the mapping files contained in an Assembly:

Configuration cfg = new Configuration()
    .AddAssembly( "NHibernate.Auction" );

Then NHibernate will look through the assembly for any resources that end with .hbm.xml. This approach eliminates any hardcoded filenames and ensures the mapping files in the assembly get added.

If a tool like Visual Studio .NET or NAnt is used to build the assembly, then make sure that the .hbm.xml files are compiled into the assembly as Embedded Resources.

A Configuration also specifies various optional properties:

IDictionary props = new Hashtable();
...
Configuration cfg = new Configuration()
    .AddClass(typeof(NHibernate.Auction.Item))
    .AddClass(typeof(NHibernate.Auction.Bind))
    .SetProperties(props);

A Configuration is intended as a configuration-time object, to be discarded once an ISessionFactory is built.

3.2. Obtaining an ISessionFactory

When all mappings have been parsed by the Configuration, the application must obtain a factory for ISession instances. This factory is intended to be shared by all application threads:

ISessionFactory sessions = cfg.BuildSessionFactory();

However, NHibernate does allow your application to instantiate more than one ISessionFactory. This is useful if you are using more than one database.

3.3. User provided ADO.NET connection

An ISessionFactory may open an ISession on a user-provided ADO.NET connection. This design choice frees the application to obtain ADO.NET connections wherever it pleases:

IDbConnection conn = myApp.GetOpenConnection();
ISession session = sessions.OpenSession(conn);

// do some data access work

The application must be careful not to open two concurrent ISessions on the same ADO.NET connection!

3.4. NHibernate provided ADO.NET connection

Alternatively, you can have the ISessionFactory open connections for you. The ISessionFactory must be provided with ADO.NET connection properties in one of the following ways:

  1. Pass an instance of IDictionary mapping property names to property values to Configuration.SetProperties().

  2. Add the properties to a configuration section in the application configuration file. The section should be named nhibernate and its handler set to System.Configuration.NameValueSectionHandler.

  3. Include <property> elements in a configuration section in the application configuration file. The section should be named hibernate-configuration and its handler set to NHibernate.Cfg.ConfigurationSectionHandler. The XML namespace of the section should be set to urn:nhibernate-configuration-2.0.

  4. Include <property> elements in hibernate.cfg.xml (discussed later).

If you take this approach, opening an ISession is as simple as:

ISession session = sessions.OpenSession(); // open a new Session
// do some data access work, an ADO.NET connection will be used on demand

All NHibernate property names and semantics are defined on the class NHibernate.Cfg.Environment. We will now describe the most important settings for ADO.NET connection configuration.

NHibernate will obtain (and pool) connections using an ADO.NET data provider if you set the following properties:

Table 3.1. NHibernate ADO.NET Properties

Property namePurpose
hibernate.connection.provider_class The type of a custom IConnectionProvider.

eg. full.classname.of.ConnectionProvider if the Provider is built into NHibernate, or full.classname.of.ConnectionProvider, assembly if using an implementation of IConnectionProvider not included in NHibernate.

hibernate.connection.driver_class The type of a custom IDriver, if using DriverConnectionProvider.

full.classname.of.Driver if the Driver is built into NHibernate, or full.classname.of.Driver, assembly if using an implementation of IDriver not included in NHibernate.

This is usually not needed, most of the time the hibernate.dialect will take care of setting the IDriver using a sensible default. See the API documentation of the specific IDialect for the defaults.

hibernate.connection.connection_string Connection string to use to obtain the connection.
hibernate.connection.isolation Set the ADO.NET transaction isolation level. Check System.Data.IsolationLevel for meaningful values and the database's documentation to ensure that level is supported.

eg. Chaos, ReadCommitted, ReadUncommitted, RepeatableRead, Serializable, Unspecified

This is an example of how to specify the database connection properties inside a web.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <section name="nhibernate" type="System.Configuration.NameValueSectionHandler, System,
                    Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
    </configSections>
    
    <nhibernate>
        <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="Server=127.0.0.1; Initial Catalog=thedatabase; Integrated Security=SSPI" 
        />
        <add 
            key="hibernate.connection.isolation"
            value="ReadCommitted"
        />
        
    </nhibernate>
    
    <!-- other app specific config follows -->
</configuration>

NHibernate relies on the ADO.NET data provider implementation of connection pooling.

You may define your own plugin strategy for obtaining ADO.NET connections by implementing the interface NHibernate.Connection.IConnectionProvider. You may select a custom implementation by setting hibernate.connection.provider_class.

3.5. Optional configuration properties

There are a number of other properties that control the behaviour of NHibernate at runtime. All are optional and have reasonable default values.

System-level properties can only be set manually by setting static properties of NHibernate.Cfg.Environment class or be defined in the <nhibernate> section of the application configuration file. These properties cannot be set using Configuration.SetProperties or be defined in the <hibernate-configuration> section of the application configuration file.

Table 3.2. NHibernate Configuration Properties

Property namePurpose
hibernate.dialect The classname of a NHibernate Dialect - enables certain platform dependent features.

eg. full.classname.of.Dialect, assembly

hibernate.default_schema Qualify unqualified tablenames with the given schema/tablespace in generated SQL.

eg. SCHEMA_NAME

hibernate.use_outer_join Enables outer join fetching. Deprecated, use max_fetch_depth.

eg. true | false

hibernate.max_fetch_depth Set a maximum "depth" for the outer join fetch tree for single-ended associations (one-to-one, many-to-one). A 0 disables default outer join fetching.

eg. recommended values between 0 and 3

hibernate.use_reflection_optimizer Enables use of a runtime-generated class to set or get properties of an entity or component instead of using runtime reflection (System-level property). The use of the reflection optimizer inflicts a certain startup cost on the application but should lead to better performance in the long run. You can not set this property in hibernate.cfg.xml or <hibernate-configuration> section of the application configuration file.

eg. true | false

hibernate.cache.provider_class The classname of a custom ICacheProvider.

eg. classname.of.CacheProvider, assembly

hibernate.cache.use_minimal_puts Optimize second-level cache operation to minimize writes, at the cost of more frequent reads (useful for clustered caches).

eg. true | false

hibernate.cache.use_query_cache Enable the query cache, individual queries still have to be set cacheable.

eg. true | false

hibernate.cache.query_cache_factory The classname of a custom IQueryCacheFactory interface, defaults to the built-in StandardQueryCacheFactory.

eg. classname.of.QueryCacheFactory, assembly

hibernate.cache.region_prefix A prefix to use for second-level cache region names.

eg. prefix

hibernate.query.substitutions Mapping from tokens in NHibernate queries to SQL tokens (tokens might be function or literal names, for example).

eg. hqlLiteral=SQL_LITERAL, hqlFunction=SQLFUNC

hibernate.show_sql Write all SQL statements to console.

eg. true | false

hibernate.hbm2ddl.auto Automatically export schema DDL to the database when the ISessionFactory is created. With create-drop, the database schema will be dropped when the ISessionFactory is closed explicitly.

eg. create | create-drop

3.5.1. SQL Dialects

You should always set the hibernate.dialect property to the correct NHibernate.Dialect.Dialect subclass for your database. This is not strictly essential unless you wish to use native or sequence primary key generation or pessimistic locking (with, eg. ISession.Lock() or IQuery.SetLockMode()). However, if you specify a dialect, NHibernate will use sensible defaults for some of the other properties listed above, saving you the effort of specifying them manually.

Table 3.3. NHibernate SQL Dialects (hibernate.dialect)

RDBMSDialect
DB2NHibernate.Dialect.DB2Dialect
PostgreSQLNHibernate.Dialect.PostgreSQLDialect
MySQLNHibernate.Dialect.MySQLDialect
Oracle (any version)NHibernate.Dialect.OracleDialect
Oracle 9/10gNHibernate.Dialect.Oracle9Dialect
SybaseNHibernate.Dialect.SybaseDialect
Microsoft SQL Server 2000NHibernate.Dialect.MsSql2000Dialect
Microsoft SQL Server 7NHibernate.Dialect.MsSql7Dialect
FirebirdNHibernate.Dialect.FirebirdDialect
SQLiteNHibernate.Dialect.SQLiteDialect

Additional dialects may be available in the NHibernateContrib package (see Part I, “NHibernateContrib Documentation”). At the time of writing this package contains support for Microsoft Access (Jet) database engine.

3.5.2. Outer Join Fetching

If your database supports ANSI or Oracle style outer joins, outer join fetching might increase performance by limiting the number of round trips to and from the database (at the cost of possibly more work performed by the database itself). Outer join fetching allows a graph of objects connected by many-to-one, one-to-many or one-to-one associations to be retrieved in a single SQL SELECT.

By default, the fetched graph when loading an objects ends at leaf objects, collections, objects with proxies, or where circularities occur.

For a particular association, fetching may be enabled or disabled (and the default behaviour overridden) by setting the outer-join attribute in the XML mapping.

Outer join fetching may be disabled globally by setting the property hibernate.max_fetch_depth to 0. A setting of 1 or higher enables outer join fetching for all one-to-one and many-to-one associations, which are, also by default, set to auto outer join. However, one-to-many associations and collections are never fetched with an outer-join, unless explicitly declared for each particular association. This behavior can also be overriden at runtime with Hibernate queries.

In NHibernate 1.0, fetch attribute can be used instead of outer-join. fetch="join" is equivalent to outer-join="true", and fetch="select" corresponds to outer-join="false".

3.5.3. Custom ICacheProvider

You may integrate a process-level (or clustered) second-level cache system by implementing the interface NHibernate.Cache.ICacheProvider. You may select the custom implementation by setting hibernate.cache.provider_class.

3.5.4. Query Language Substitution

You may define new NHibernate query tokens using hibernate.query.substitutions. For example:

hibernate.query.substitutions true=1, false=0

would cause the tokens true and false to be translated to integer literals in the generated SQL.

hibernate.query.substitutions toLowercase=LOWER

would allow you to rename the SQL LOWER function.

3.6. Logging

NHibernate logs various events using Apache log4net.

You may download log4net from http://logging.apache.org/log4net/. To use log4net you will need a log4net configuration section in the application configuration file. An example of the configuration section is distributed with NHibernate in the src/NHibernate.Test project.

We strongly recommend that you familiarize yourself with NHibernate's log messages. A lot of work has been put into making the NHibernate log as detailed as possible, without making it unreadable. It is an essential troubleshooting device. Also don't forget to enable SQL logging as described above (hibernate.show_sql), it is your first step when looking for performance problems.

3.7. Implementing an INamingStrategy

The interface NHibernate.Cfg.INamingStrategy allows you to specify a "naming standard" for database objects and schema elements.

You may provide rules for automatically generating database identifiers from .NET identifiers or for processing "logical" column and table names given in the mapping file into "physical" table and column names. This feature helps reduce the verbosity of the mapping document, eliminating repetitive noise (TBL_ prefixes, for example). The default strategy used by NHibernate is quite minimal.

You may specify a different strategy by calling Configuration.SetNamingStrategy() before adding mappings:

ISessionFactory sf = new Configuration()
    .SetNamingStrategy(ImprovedNamingStrategy.Instance)
    .AddFile("Item.hbm.xml")
    .AddFile("Bid.hbm.xml")
    .BuildSessionFactory();

NHibernate.Cfg.ImprovedNamingStrategy is a built-in strategy that might be a useful starting point for some applications.

3.8. XML Configuration File

An alternative approach is to specify a full configuration in a file named hibernate.cfg.xml. This file can be used as a replacement for the <nhibernate;> or <hibernate-configuration> sections of the application configuration file.

The XML configuration file is by default expected to be in your application directory. Here is an example:

<?xml version='1.0' encoding='utf-8'?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.0">

    <!-- an ISessionFactory instance -->
    <session-factory>

        <!-- properties -->
        <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
        <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
        <property name="connection.connection_string">Server=localhost;initial catalog=nhibernate;User Id=;Password=</property>
        <property name="show_sql">false</property>
        <property name="dialect">NHibernate.Dialect.MsSql2000Dialect</property>
        <property name="use_outer_join">true</property>

        <!-- mapping files -->
        <mapping resource="NHibernate.Auction.Item.hbm.xml" assembly="NHibernate.Auction" />
        <mapping resource="NHibernate.Auction.Bid.hbm.xml" assembly="NHibernate.Auction" />

    </session-factory>

</hibernate-configuration>

Configuring NHibernate is then as simple as

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

You can pick a different XML configuration file using

ISessionFactory sf = new Configuration()
    .Configure("/path/to/config.cfg.xml")
    .BuildSessionFactory();

Chapter 4. Persistent Classes

Persistent classes are classes in an application that implement the entities of the business problem (e.g. Customer and Order in an E-commerce application). Persistent classes have, as the name implies, transient and also persistent instance stored in the database.

NHibernate works best if these classes follow some simple rules, also known as the Plain Old CLR Object (POCO) programming model.

4.1. A simple POCO example

Most .NET applications require a persistent class representing felines.

using System;
using Iesi.Collections;

namespace Eg
{
    public class Cat
    {
        private long id; // identifier
        private string name;
        private DateTime birthdate;
        private Cat mate;
        private ISet kittens
        private Color color;
        private char sex;
        private float weight;
    
        public virtual long Id
        {
            get { return id; }
            set { id = value; }
        }
    
        public virtual string Name
        {
            get { return name; }
            set { name = value; }
        }
    
        public virtual Cat Mate
        {
            get { return mate; }
            set { mate = value; }
        }
    
        public virtual DateTime Birthdate
        {
            get { return birthdate; }
            set { birthdate = value; }
        }
    
        public virtual float Weight
        {
            get { return weight; }
            set { weight = value; }
        }
    
        public virtual Color Color
        {
            get { return color; }
            set { color = value; }
        }
    
        public virtual ISet Kittens
        {
            get { return kittens; }
            set { kittens = value; }
        }
    
        // AddKitten not needed by NHibernate
        public virtual void AddKitten(Cat kitten)
        {
            kittens.Add(kitten);
        }
    
        public virtual char Sex
        {
            get { return sex; }
            set { sex = value; }
        }
    }
}

There are four main rules to follow here:

4.1.1. Declare accessors and mutators for persistent fields

Cat declares accessor methods for all its persistent fields. Many other ORM tools directly persist instance variables. We believe it is far better to decouple this implementation detail from the persistence mechanism. NHibernate persists properties, using their getter and setter methods.

Properties need not be declared public - NHibernate can persist a property with an internal, protected, protected internal or private visibility.

4.1.2. Implement a default constructor

Cat has an implicit default (no-argument) constructor. All persistent classes must have a default constructor (which may be non-public) so NHibernate can instantiate them using ConstructorInfo.Invoke(null).

4.1.3. Provide an identifier property (optional)

Cat has a property called Id. This property holds the primary key column of a database table. The property might have been called anything, and its type might have been any primitive type, string or System.DateTime. (If your legacy database table has composite keys, you can even use a user-defined class with properties of these types - see the section on composite identifiers below.)

The identifier property is optional. You can leave it off and let NHibernate keep track of object identifiers internally. However, for many applications it is still a good (and very popular) design decision.

What's more, some functionality is available only to classes which declare an identifier property:

  • Cascaded updates (see "Lifecycle Objects")

  • ISession.SaveOrUpdate()

We recommend you declare consistently-named identifier properties on persistent classes.

4.1.4. Prefer non-sealed classes and virtual methods (optional)

A central feature of NHibernate, proxies, depends upon the persistent class being non-sealed and all its methods, properties and events declared as virtual. Another possibility is for the class to implement an interface that declares all public members.

You can persist sealed classes that do not implement an interface and don't have virtual members with NHibernate, but you won't be able to use proxies - which will limit your options for performance tuning somewhat.

4.2. Implementing inheritance

A subclass must also observe the first and second rules. It inherits its identifier property from Cat.

using System;
namespace Eg
{
    public class DomesticCat : Cat
    {
        private string name;

        public virtual string Name
        {
            get { return name; }
            set { name = value; }
        }
    }
}

4.3. Implementing Equals() and GetHashCode()

You have to override the Equals() and GetHashCode() methods if you intend to mix objects of persistent classes (e.g. in an ISet).

This only applies if these objects are loaded in two different ISessions, as NHibernate only guarantees identity ( a == b , the default implementation of Equals()) inside a single ISession!

Even if both objecs a and b are the same database row (they have the same primary key value as their identifier), we can't guarantee that they are the same object instance outside of a particular ISession context.

The most obvious way is to implement Equals()/GetHashCode() by comparing the identifier value of both objects. If the value is the same, both must be the same database row, they are therefore equal (if both are added to an ISet, we will only have one element in the ISet). Unfortunately, we can't use that approach. NHibernate will only assign identifier values to objects that are persistent, a newly created instance will not have any identifier value! We recommend implementing Equals() and GetHashCode() using Business key equality.

Business key equality means that the Equals() method compares only the properties that form the business key, a key that would identify our instance in the real world (a natural candidate key):

public class Cat
{

    ...
    public override bool Equals(object other)
    {
        if (this == other) return true;
        
        Cat cat = other as Cat;
        if (cat == null) return false; // null or not a cat

        if (Name != cat.Name) return false;
        if (!Birthday.Equals(cat.Birthday)) return false;

        return true;
    }

    public override int GetHashCode()
    {
        unchecked
        {
            int result;
            result = Name.GetHashCode();
            result = 29 * result + Birthday.GetHashCode();
            return result;
        }
    }

}

Keep in mind that our candidate key (in this case a composite of name and birthday) has to be only valid for a particular comparison operation (maybe even only in a single use case). We don't need the stability criteria we usually apply to a real primary key!

4.4. Lifecycle Callbacks

Optionally, a persistent class might implement the interface ILifecycle which provides some callbacks that allow the persistent object to perform necessary initialization/cleanup after save or load and before deletion or update.

The NHibernate IInterceptor offers a less intrusive alternative, however.

public interface ILifecycle
{                                                                    (1)
        LifecycleVeto OnSave(ISession s);                            (2)
        LifecycleVeto OnUpdate(ISession s);                          (3)
        LifecycleVeto OnDelete(ISession s);                          (4)
        void OnLoad(ISession s, object id);
}
(1)

OnSave - called just before the object is saved or inserted

(2)

OnUpdate - called just before an object is updated (when the object is passed to ISession.Update())

(3)

OnDelete - called just before an object is deleted

(4)

OnLoad - called just after an object is loaded

OnSave(), OnDelete() and OnUpdate() may be used to cascade saves and deletions of dependent objects. This is an alternative to declaring cascaded operations in the mapping file. OnLoad() may be used to initialize transient properties of the object from its persistent state. It may not be used to load dependent objects since the ISession interface may not be invoked from inside this method. A further intended usage of OnLoad(), OnSave() and OnUpdate() is to store a reference to the current ISession for later use.

Note that OnUpdate() is not called every time the object's persistent state is updated. It is called only when a transient object is passed to ISession.Update().

If OnSave(), OnUpdate() or OnDelete() return LifecycleVeto.Veto, the operation is silently vetoed. If a CallbackException is thrown, the operation is vetoed and the exception is passed back to the application.

Note that OnSave() is called after an identifier is assigned to the object, except when native key generation is used.

4.5. IValidatable callback

If the persistent class needs to check invariants before its state is persisted, it may implement the following interface:

public interface IValidatable
{
        void Validate();
}

The object should throw a ValidationFailure if an invariant was violated. An instance of Validatable should not change its state from inside Validate().

Unlike the callback methods of the ILifecycle interface, Validate() might be called at unpredictable times. The application should not rely upon calls to Validate() for business functionality.

Chapter 5. Basic O/R Mapping

5.1. Mapping declaration

Object/relational mappings are defined in an XML document. The mapping document is designed to be readable and hand-editable. The mapping language is object-centric, meaning that mappings are constructed around persistent class declarations, not table declarations.

Note that, even though many NHibernate users choose to define XML mappings by hand, a number of tools exist to generate the mapping document, including NHibernate.Mapping.Attributes library and various template-based code generators (CodeSmith, MyGeneration).

Let's kick off with an example mapping:

<?xml version="1.0"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0" assembly="Eg"
    namespace="Eg">

        <class name="Cat" table="CATS" discriminator-value="C">
                <id name="Id" column="uid" type="Int64">
                        <generator class="hilo"/>
                </id>
                <discriminator column="subclass" type="Char"/>
                <property name="BirthDate" type="Date"/>
                <property name="Color" not-null="true"/>
                <property name="Sex" not-null="true" update="false"/>
                <property name="Weight"/>
                <many-to-one name="Mate" column="mate_id"/>
                <set name="Kittens">
                        <key column="mother_id"/>
                        <one-to-many class="Cat"/>
                </set>
                <subclass name="DomesticCat" discriminator-value="D">
                        <property name="Name" type="String"/>
                </subclass>
        </class>

        <class name="Dog">
                <!-- mapping for Dog could go here -->
        </class>

</hibernate-mapping>

We will now discuss the content of the mapping document. We will only describe the document elements and attributes that are used by NHibernate at runtime. The mapping document also contains some extra optional attributes and elements that affect the database schemas exported by the schema export tool. (For example the not-null attribute.)

5.1.1. XML Namespace

All XML mappings should declare the XML namespace shown. The actual schema definition may be found in the src\nhibernate-mapping-2.0.xsd file in the NHibernate distribution.

Tip: to enable IntelliSense for mapping and configuration files, copy the appropriate .xsd files to <VS.NET installation directory>\Common7\Packages\schemas\xml.

5.1.2. hibernate-mapping

This element has several optional attributes. The schema attribute specifies that tables referred to by this mapping belong to the named schema. If specified, tablenames will be qualified by the given schema name. If missing, tablenames will be unqualified. The default-cascade attribute specifies what cascade style should be assumed for properties and collections which do not specify a cascade attribute. The auto-import attribute lets us use unqualified class names in the query language, by default. The assembly and namespace attributes specify the assembly where persistent classes are located and the namespace they are declared in.

<hibernate-mapping
         schema="schemaName"                          (1)
         default-cascade="none|save-update"           (2)
         auto-import="true|false"                     (3)
         assembly="Eg"                                (4)
         namespace="Eg"                               (5)
 />
(1)

schema (optional): The name of a database schema.

(2)

default-cascade (optional - defaults to none): A default cascade style.

(3)

auto-import (optional - defaults to true): Specifies whether we can use unqualified class names (of classes in this mapping) in the query language.

(4)(5)

assembly and namespace(optional): Specify assembly and namespace to assume for unqualified class names in the mapping document.

If you are not using assembly and namespace attributes, you have to specify fully-qualified class names, including the name of the assembly that classes are declared in.

If you have two persistent classes with the same (unqualified) name, you should set auto-import="false". NHibernate will throw an exception if you attempt to assign two classes to the same "imported" name.

5.1.3. class

You may declare a persistent class using the class element:

<class
        name="ClassName"                              (1)
        table="tableName"                             (2)
        discriminator-value="discriminator_value"     (3)
        mutable="true|false"                          (4)
        schema="owner"                                (5)
        proxy="ProxyInterface"                        (6)
        dynamic-update="true|false"                   (7)
        dynamic-insert="true|false"                   (8)
        select-before-update="true|false"             (9)
        polymorphism="implicit|explicit"              (10)
        where="arbitrary sql where condition"         (11)
        persister="PersisterClass"                    (12)
        batch-size="N"                                (13)
        optimistic-lock="none|version|dirty|all"      (14)
        lazy="true|false"                             (15)
/>
(1)

name: The fully qualified .NET class name of the persistent class (or interface), including its assembly name.

(2)

table: The name of its database table.

(3)

discriminator-value (optional - defaults to the class name): A value that distiguishes individual subclasses, used for polymorphic behaviour. Acceptable values include null and not null.

(4)

mutable (optional, defaults to true): Specifies that instances of the class are (not) mutable.

(5)

schema (optional): Override the schema name specified by the root <hibernate-mapping> element.

(6)

proxy (optional): Specifies an interface to use for lazy initializing proxies. You may specify the name of the class itself.

(7)

dynamic-update (optional, defaults to false): Specifies that UPDATE SQL should be generated at runtime and contain only those columns whose values have changed.

(8)

dynamic-insert (optional, defaults to false): Specifies that INSERT SQL should be generated at runtime and contain only the columns whose values are not null.

(9)

select-before-update (optional, defaults to false): Specifies that NHibernate should never perform an SQL UPDATE unless it is certain that an object is actually modified. In certain cases (actually, only when a transient object has been associated with a new session using update()), this means that NHibernate will perform an extra SQL SELECT to determine if an UPDATE is actually required.

(10)

polymorphism (optional, defaults to implicit): Determines whether implicit or explicit query polymorphism is used.

(11)

where (optional) specify an arbitrary SQL WHERE condition to be used when retrieving objects of this class

(12)

persister (optional): Specifies a custom IClassPersister.

(13)

batch-size (optional, defaults to 1) specify a "batch size" for fetching instances of this class by identifier.

(14)

optimistic-lock (optional, defaults to version): Determines the optimistic locking strategy.

(15)

lazy (optional): Setting lazy="true" is a shortcut equalivalent to specifying the name of the class itself as the proxy interface.

It is perfectly acceptable for the named persistent class to be an interface. You would then declare implementing classes of that interface using the <subclass> element. You may persist any inner class. You should specify the class name using the standard form ie. Eg.Foo+Bar, Eg. Due to an HQL parser limitation inner classes can not be used in queries in NHibernate 1.0.

Immutable classes, mutable="false", may not be updated or deleted by the application. This allows NHibernate to make some minor performance optimizations.

The optional proxy attribute enables lazy initialization of persistent instances of the class. NHibernate will initially return proxies which implement the named interface. The actual persistent object will be loaded when a method of the proxy is invoked. See "Proxies for Lazy Initialization" below.

Implicit polymorphism means that instances of the class will be returned by a query that names any superclass or implemented interface or the class and that instances of any subclass of the class will be returned by a query that names the class itself. Explicit polymorphism means that class instances will be returned only be queries that explicitly name that class and that queries that name the class will return only instances of subclasses mapped inside this <class> declaration as a <subclass> or <joined-subclass>. For most purposes the default, polymorphism="implicit", is appropriate. Explicit polymorphism is useful when two different classes are mapped to the same table (this allows a "lightweight" class that contains a subset of the table columns).

The persister attribute lets you customize the persistence strategy used for the class. You may, for example, specify your own subclass of NHibernate.Persister.EntityPersister or you might even provide a completely new implementation of the interface NHibernate.Persister.IClassPersister that implements persistence via, for example, stored procedure calls, serialization to flat files or LDAP. See NHibernate.DomainModel.CustomPersister for a simple example (of "persistence" to a Hashtable).

Note that the dynamic-update and dynamic-insert settings are not inherited by subclasses and so may also be specified on the <subclass> or <joined-subclass> elements. These settings may increase performance in some cases, but might actually decrease performance in others. Use judiciously.

Use of select-before-update will usually decrease performance. It is very useful to prevent a database update trigger being called unnecessarily.

If you enable dynamic-update, you will have a choice of optimistic locking strategies:

  • version check the version/timestamp columns

  • all check all columns

  • dirty check the changed columns

  • none do not use optimistic locking

We very strongly recommend that you use version/timestamp columns for optimistic locking with NHibernate. This is the optimal strategy with respect to performance and is the only strategy that correctly handles modifications made outside of the session (ie. when ISession.Update() is used). Keep in mind that a version or timestamp property should never be null, no matter what unsaved-value strategy, or an instance will be detected as transient.

5.1.4. id

Mapped classes must declare the primary key column of the database table. Most classes will also have a property holding the unique identifier of an instance. The <id> element defines the mapping from that property to the primary key column.

<id
        name="PropertyName"                      (1)
        type="typename"                          (2)
        column="column_name"                     (3)
        unsaved-value="any|none|null|id_value"   (4)
        access="field|property|nosetter|ClassName(5)">

        <generator class="generatorClass"/>
</id>
(1)

name (optional): The name of the identifier property.

(2)

type (optional): A name that indicates the NHibernate type.

(3)

column (optional - defaults to the property name): The name of the primary key column.

(4)

unsaved-value (optional - defaults to a "sensible" value): An identifier property value that indicates that an instance is newly instantiated (unsaved), distinguishing it from transient instances that were saved or loaded in a previous session.

(5)

access (optional - defaults to property): The strategy NHibernate should use for accessing the property value.

If the name attribute is missing, it is assumed that the class has no identifier property.

The unsaved-value attribute is almost never needed in NHibernate 1.0.

There is an alternative <composite-id> declaration to allow access to legacy data with composite keys. We strongly discourage its use for anything else.

5.1.4.1. generator

The required <generator> child element names a .NET class used to generate unique identifiers for instances of the persistent class. If any parameters are required to configure or initialize the generator instance, they are passed using the <param> element.

<id name="Id" type="Int64" column="uid" unsaved-value="0">
        <generator class="NHibernate.Id.TableHiLoGenerator">
                <param name="table">uid_table</param>
                <param name="column">next_hi_value_column</param>
        </generator>
</id>

All generators implement the interface NHibernate.Id.IIdentifierGenerator. This is a very simple interface; some applications may choose to provide their own specialized implementations. However, NHibernate provides a range of built-in implementations. There are shortcut names for the built-in generators:

increment

generates identifiers of type Int64, Int16 or Int32 that are unique only when no other process is inserting data into the same table. Do not use in a cluster.

identity

supports identity columns in DB2, MySQL, MS SQL Server and Sybase. The identifier returned by the database is converted to the property type using Convert.ChangeType. Any integral property type is thus supported.

sequence

uses a sequence in DB2, PostgreSQL, Oracle or a generator in Firebird. The identifier returned by the database is converted to the property type using Convert.ChangeType. Any integral property type is thus supported.

hilo

uses a hi/lo algorithm to efficiently generate identifiers of type Int16, Int32 or Int64, given a table and column (by default hibernate_unique_key and next_hi respectively) as a source of hi values. The hi/lo algorithm generates identifiers that are unique only for a particular database. Do not use this generator with a user-supplied connection.

seqhilo

uses a hi/lo algorithm to efficiently generate identifiers of type Int16, Int32 or Int64, given a named database sequence.

uuid.hex

uses System.Guid and its ToString(string format) method to generate identifiers of type string. The length of the string returned depends on the configured format.

uuid.string

uses a new System.Guid to create a byte[] that is converted to a string.

guid

uses a new System.Guid as the identifier.

guid.comb

uses the algorithm to generate a new System.Guid described by Jimmy Nilsson in the article http://www.informit.com/articles/article.asp?p=25862.

native

picks identity, sequence or hilo depending upon the capabilities of the underlying database.

assigned

lets the application to assign an identifier to the object before Save() is called.

foreign

uses the identifier of another associated object. Usually used in conjunction with a <one-to-one> primary key association.

5.1.4.2. Hi/Lo Algorithm

The hilo and seqhilo generators provide two alternate implementations of the hi/lo algorithm, a favorite approach to identifier generation. The first implementation requires a "special" database table to hold the next available "hi" value. The second uses an Oracle-style sequence (where supported).

<id name="Id" type="Int64" column="cat_id">
        <generator class="hilo">
                <param name="table">hi_value</param>
                <param name="column">next_value</param>
                <param name="max_lo">100</param>
        </generator>
</id>
<id name="Id" type="Int64" column="cat_id">
        <generator class="seqhilo">
                <param name="sequence">hi_value</param>
                <param name="max_lo">100</param>
        </generator>
</id>

Unfortunately, you can't use hilo when supplying your own IDbConnection to NHibernate. NHibernate must be able to fetch the "hi" value in a new transaction.

5.1.4.3. UUID Hex Algorithm

<id name="Id" type="String" column="cat_id">
        <generator class="uuid.hex">
            <param name="format">format_value</param>
            <param name="seperator">seperator_value</param>
        </generator>
</id>

The UUID is generated by calling Guid.NewGuid().ToString(format). The valid values