Chapter 3. Writing and mapping classes – NHibernate in Action

Chapter 3. Writing and mapping classes

This chapter covers

  • POCO basics for rich domain models
  • The concept of object identity and its mapping
  • Mapping class inheritance
  • Association and collection mappings

The “Hello World” example in chapter 2 gave a gentle introduction to NHibernate; but we need a more thorough example to demonstrate the needs of real-world applications with complex data models. For the rest of the book, we explore NHibernate using a more sophisticated example application—an online auction system.

We start our discussion of the application by introducing a programming model for persistent classes.

First, you’ll learn how to identify the business objects (or entities) of a problem domain. You’ll create a conceptual model of these entities and their attributes, called a domain model. You’ll implement this domain model in C# by creating a persistent class for each entity, and we’ll spend some time exploring what these .NET classes should look like.

You’ll then define mapping metadata to tell NHibernate how these classes and their properties relate to database tables and columns. We covered the basis of this step in chapter 2. In this chapter, we give an in-depth presentation of the mapping techniques for fine-grained classes, object identity, inheritance, and associations. This chapter therefore provides the beginnings of a solution to the first generic problems of ORM listed in section 1.3.1. For example, how do you map fine-grained objects to simple tables? Or how do you map inheritance hierarchies to tables?

We start by introducing the example application.

3.1. The CaveatEmptor application

The CaveatEmptor online auction application demonstrates ORM techniques and NHibernate functionality; you can download the source code for the entire working application from the website http://caveatemptor.hibernate.org/. The application will have a console-based user interface. We don’t pay much attention to the user interface; we concentrate on the data-access code. In chapter 8, we discuss the changes that would be necessary if you were to perform all business logic and data access from a separate business tier. And in chapter 10, we discuss many solutions to common issues that arise when integrating NHibernate in Windows and web applications.

But let’s start at the beginning. In order to understand the design issues involved in ORM, let’s pretend the CaveatEmptor application doesn’t yet exist and that you’re building it from scratch. Your first task is analysis.

3.1.1. Analyzing the business domain

A software development effort begins with analysis of the problem domain (assuming that no legacy code or legacy database already exists).

At this stage, you, with the help of problem domain experts, identify the main entities that are relevant to the software system. Entities are usually notions understood by users of the system: Payment, Customer, Order, Item, Bid, and so forth. Some entities may be abstractions of less concrete things the user thinks about (for example, PricingAlgorithm), but even these are usually understandable to the user. All these entities are found in the conceptual view of the business, which we sometimes call a business model.

Developers of object-oriented software analyze the business model and create an object model, still at the conceptual level (no C# code). This object model may be as simple as a mental image existing only in the mind of the developer, or it may be as elaborate as a UML class diagram (as in figure 3.1) created by a Computer-Aided Software Engineering (CASE) tool like Microsoft Visio, Sparx Systems Enterprise Architect, or UMLet.

Figure 3.1. A class diagram of a typical online auction object model

This simple model contains entities that you’re bound to find in any typical auction system: Category, Item, and User. The entities and their relationships (and perhaps their attributes) are all represented by this model of the problem domain. We call this kind of model—an object-oriented model of entities from the problem domain, encompassing only those entities that are of interest to the user—a domain model. It’s an abstract view of the real world. We’ll refer to this model when you implement your persistent .NET classes.

Let’s examine the outcome of the analysis of the the CaveatEmptor application’s problem domain.

3.1.2. The CaveatEmptor domain model

The CaveatEmptor site auctions many different kinds of items, from electronic equipment to airline tickets. Auctions proceed according to the “English auction” model: users continue to place bids on an item until the bid period for that item expires, and the highest bidder wins.

In any store, goods are categorized by type and grouped with similar goods into sections and onto shelves. Your auction catalog requires some kind of hierarchy of item categories. A buyer may browse these categories or arbitrarily search by category and item attributes. Lists of items appear in the category browser and search-result screens. Selecting an item from a list takes the buyer to an item-detail view.

An auction consists of a sequence of bids. One particular bid is the winning bid. User details include name, login, address, email address, and billing information.

A web of trust is an essential feature of an online auction site. The web of trust allows users to build a reputation for trustworthiness (or untrustworthiness). Buyers may create comments about sellers (and vice versa), and the comments are visible to all other users.

A high-level overview of the domain model is shown in figure 3.2. Let’s briefly discuss some interesting features of this model.

Figure 3.2. Persistent classes of the CaveatEmptor object model and their relationships

Each item may be auctioned only once, so you don’t need to make Item distinct from the Auction entities. Instead, you have a single auction item entity named Item. Bid is associated directly with Item. Users can write Comments about other users only in the context of an auction; hence the association between Item and Comment. The Address information of a User is modeled as a separate class, even though the User may have only one Address. You do let the user have multiple BillingDetails. The various billing strategies are represented as subclasses of an abstract class (allowing future extension).

A Category may be nested inside another Category. This is expressed by a recursive association from the Category entity to itself. Note that a single Category may have multiple child categories, but at most one parent category. Each Item belongs to at least one Category.

The entities in a domain model should encapsulate state and behavior. For example, the User entity should define the name and address of a customer and the logic required to calculate the shipping costs for items (to this particular customer). This domain model is a rich object model, with complex associations, interactions, and inheritance relationships. An interesting and detailed discussion of object-oriented techniques for working with domain models can be found in Patterns of Enterprise Application Architecture [Fowler 2003] or Domain-Driven Design [Evans 2004].

In this book, we don’t have much to say about business rules or the behavior of the domain model. This isn’t because we consider them unimportant concerns; rather, they’re mostly orthogonal to the problem of persistence. It’s the state of your entities that is persistent. So we concentrate our discussion on how to best represent state in your domain model, not on how to represent behavior. For example, in this book, we aren’t interested in how tax for sold items is calculated or how the system might approve a new user account. We’re more interested in how the relationship between users and the items they sell is represented and made persistent.

Now that you have a domain model, the next step is to implement it in C#. Let’s look at some of the things you need to consider.


Can you use ORM without a domain model?

We stress that object persistence with full ORM is most suitable for applications based on a rich domain model. If your application doesn’t implement complex business rules or complex interactions between entities (or if you have few entities), you may not need a domain model. Many simple and some not-so-simple problems are perfectly suited to table-oriented solutions, where the application is designed around the database data model instead of around an object-oriented domain model, often with logic executed in the database (stored procedures). But the more complex and expressive your domain model, the more you’ll benefit from using NHibernate; it shines when dealing with the full complexity of object/relational persistence.


3.2. Implementing the domain model

Several issues typically must be addressed when you implement a domain model. For instance, how do you separate the business concerns from the cross-cutting concerns (such as transactions and even persistence)? What kind of persistence is needed: automated or transparent? Do you have to use a specific programming model to achieve this? In this section, we examine these types of issues and how to address them in a typical NHibernate application.

Let’s start with an issue that any implementation must deal with: the separation of concerns. The domain-model implementation is usually a central, organizing component; it’s reused heavily whenever you implement new application functionality. For this reason, you should be prepared to go to some lengths to ensure that concerns other than business aspects don’t leak into the domain model implementation.

3.2.1. Addressing leakage of concerns

The domain-model implementation is such an important piece of code that it shouldn’t depend on other .NET APIs. For example, code in the domain model shouldn’t perform input/output operations or call the database via the ADO.NET API. This allows you to reuse the domain model implementation virtually anywhere. Most important, it makes it easy to unit-test the domain model (in NUnit, for example) outside of any application server or other managed environment.

We say that the domain model should be “concerned” only with modeling the business domain. But there are other concerns, such as persistence, transaction management, and authorization. You shouldn’t put code that addresses these cross-cutting concerns in the classes that implement the domain model. When these concerns start to appear in the domain model classes, we call this an example of leakage of concerns.

The DataSet doesn’t address this problem. It can’t be regarded as a domain model mainly because it isn’t designed to include business rules.

Much discussion has gone into the topic of persistence, and both NHibernate and DataSets take care of that concern. But NHibernate offers something that DataSets don’t: transparent persistence.

3.2.2. Transparent and automated persistence

A DataSet allows you to extract the changes performed on it in order to persist them. NHibernate provides a different feature, which is sophisticated and powerful: it can automatically persist your changes in a way that is transparent to your domain model.

We use transparent to mean a complete separation of concerns between the persistent classes of the domain model and the persistence logic itself, where the persistent classes are unaware of—and have no dependency on—the persistence mechanism.

The Item class, for example, won’t have any code-level dependency to any NHibernate API. Furthermore:

  • NHibernate doesn’t require that any special base classes or interfaces be inherited or implemented by persistent classes. Nor are any special classes used to implement properties or associations. Thus, transparent persistence improves code readability, as you’ll soon see.
  • Persistent classes may be reused outside the context of persistence—in unit tests or in the user interface (UI) tier, for example. Testability is a basic requirement for applications with rich domain models.
  • In a system with transparent persistence, objects aren’t aware of the underlying data store; they need not even be aware that they’re being persisted or retrieved. Persistence concerns are externalized to a generic persistence manager interface—in the case of NHibernate, the ISession and IQuery interfaces.

Transparent persistence fosters a degree of portability; without special interfaces, the persistent classes are decoupled from any particular persistence solution. Your business logic is fully reusable in any other application context. You could easily change to another transparent persistence mechanism.

By this definition of transparent persistence, certain non-automated persistence layers are transparent (for example, the DAO pattern) because they decouple the persistence-related code with abstract programming interfaces. Only plain .NET classes without dependencies are exposed to the business logic. Conversely, some automated persistence layers (like many ORM solutions) are non-transparent, because they require special interfaces or intrusive programming models.

We regard transparency as required. Transparent persistence should be one of the primary goals of any ORM solution. But no automated persistence solution is completely transparent: every automated persistence layer, including NHibernate, imposes some requirements on the persistent classes. For example, NHibernate requires that collection-valued properties be typed to an interface such as IList or IDictionary (or their .NET 2.0 generic versions) and not to an actual implementation such as ArrayList (this is a good practice anyway). (We discuss the reasons for this requirement in appendix B, “Going forward.”)

You now know why the persistence mechanism should have minimal impact on how you implement a domain model and that transparent and automated persistence are required. DataSet isn’t suitable here, so what kind of programming model should you use? Do you need a special programming model at all? In theory, no; in practice, you should adopt a disciplined, consistent programming model that is well accepted by the .NET community. Let’s discuss this programming model and see how it works with NHibernate.

3.2.3. Writing POCOs

Developers have found DataSets to be unnatural for representing business objects in many situations. The opposite of a heavy model like DataSet is the Plain Old CLR Object (POCO). It’s a back-to-basics approach that essentially consists of using unbound classes in the business layer.[1]

1 The term POCO was derived from the Java term Plain Old Java Object (POJO). It’s sometimes written Plain Ordinary Java Objects. The term was coined in 2002 by Martin Fowler, Rebecca Parsons, and Josh Mackenzie. As an alternative to POCO, it’s also common to use the term PONO, which stands for Plain Old .NET Object.

When you’re using NHibernate, entities are implemented as POCOs. The few requirements that NHibernate imposes on your entities are also best practices for the POCO programming model. Most POCOs are NHibernate-compatible without any changes. The programming model we introduce is a non-intrusive mix of POCO best practices and NHibernate requirements. A POCO declares business methods, which define behavior, and properties, which represent state. Some properties represent associations to other POCOs.

Listing 3.1 shows a simple POCO class; it’s an implementation of the User entity of the example domain model.

Listing 3.1. POCO implementation of the User class

NHibernate doesn’t require persistent classes to be serializable (as this class is ). But serializability is commonly needed, mainly when you’re using .NET remoting.

NHibernate requires a default parameterless constructor for every persistent class . The constructor may be non-public, but it should be at least protected if runtime-generated proxies will be used for performance optimization (see chapter 4). Note that .NET automatically adds a public parameterless constructor to classes if you haven’t written one in the code.

The properties of the POCO implement the attributes of your business entities . For example, the User’s name Username provides access to the private userName instance variable (the same is true for Address). NHibernate doesn’t require that properties be declared public; it can easily use private ones too. Some properties do something more sophisticated than simple instance variables access (validation, for example), but trivial properties are common. Of course, if you’re using C# 3.0, you can take advantage of the auto-implemented properties for these simple cases.

This POCO also defines a business method that calculates the cost of shipping an item to a particular user (we left out the implementation of this method).

Now that you understand the value of using POCO persistent classes as the programming model, let’s see how you handle the associations between those classes.

3.2.4. Implementing POCO associations

You use properties to express associations between POCO classes, and you use accessor methods to navigate the object graph at runtime. Let’s consider the associations defined by the Category class. The first association is shown in figure 3.3.

Figure 3.3. Diagram of the Category class with an association

As with all our diagrams, we left out the association-related attributes (parentCategory and childCategories) because they would clutter the illustration. These attributes and the methods that manipulate their values are called scaffolding code.

Let’s implement the scaffolding code for the one-to-many self-association of Category:

public class Category : ISerializable {
private string name;
private Category parentCategory;
private ISet childCategories = new HashedSet();
public Category() { }
//...
}

Note that you could use .NET 2.0 generics here by writing ISet<Category> childCategories. No other change would be required (even in the mapping).

To allow bidirectional navigation of the association, you require two attributes. The parentCategory attribute implements the single-valued end of the association and is declared to be of type Category. The many-valued end, implemented by the childCategories attribute, must be of collection type. Here you use an ISet and initialize the instance variable to a new instance of HashedSet.

NHibernate requires interfaces for collection-typed attributes. You must, for example, use ISet rather than HashedSet. At runtime, NHibernate wraps the collection instance with an instance of one of NHibernate’s own classes. (This special class isn’t visible to the application code.) It’s good practice to program to collection interfaces rather than concrete implementations, so this restriction shouldn’t bother you.

You now have some private instance variables but no public interface to allow access from business code or property management by NHibernate. Let’s add some properties to the Category class:


Used external library: Iesi.Collections

Java has a kind of collection called Set, which lets you store items without duplication (that is, you can’t add the same object many times). But .NET doesn’t provide an equivalent to this collection. NHibernate uses a library called Iesi.Collections, which includes the interface ISet and many implementations (like HashedSet). Their behavior is similar to that of the IList, so you should be able to use them easily. We frequently use Sets because their semantic fits with the requirement of our classes.


public string Name {
get { return name; }
set { name = value; }
}
public ISet ChildCategories {
get { return childCategories; }
set { childCategories = value; }
}
public Category ParentCategory {
get { return parentCategory; }
set { parentCategory = value; }
}

Again, these properties need to be declared public only if they’re part of the external interface of the persistent class, the public interface used by the application logic.

The basic procedure for adding a child Category to a parent Category looks like this:

Category aParent = new Category();
Category aChild = new Category();
aChild.ParentCategory = aParent;
aParent.ChildCategories.Add(aChild);

Whenever an association is created between a parent Category and a child Category, two actions are required:

  • The parentCategory of the child must be set, effectively breaking the association between the child and its old parent (there can be only one parent for any child).
  • The child must be added to the childCategories collection of the new parent Category.

Managed relationships in NHibernate

NHibernate doesn’t “manage” persistent associations. If you want to manipulate an association, you must write exactly the same code you would write without NHibernate. If an association is bidirectional, both sides of the relationship must be considered. Anyway, this is required if you want to use your objects without NHibernate (for testing or with the UI).


If you ever have problems understanding the behavior of associations in NHibernate, ask yourself, “What would I do without NHibernate?” NHibernate doesn’t change the usual .NET semantics.

It’s a good idea to add a convenience method to the Category class that groups these operations, allowing reuse and helping ensure correctness:

public void AddChildCategory(Category childCategory) {
if (childCategory.ParentCategory != null)
childCategory.ParentCategory.ChildCategories
.Remove(childCategory);
childCategory.ParentCategory = this;
childCategories.Add(childCategory);
}

The AddChildCategory() method not only reduces the lines of code when dealing with Category objects, but also enforces the cardinality of the association. Errors that arise from leaving out one of the two required actions are avoided. This kind of grouping of operations should always be provided for associations, if possible.

Because you’d like the AddChildCategory() to be the only externally visible mutator method for the child categories, you make the ChildCategories property private; you may add more methods to access to ChildCategories if required. NHibernate doesn’t care if properties are private or public, so you can focus on good API design.

A different kind of relationship exists between Category and the Item: a bidirectional many-to-many association (see figure 3.4).

Figure 3.4. Category and the associated Item

In the case of a many-to-many association, both sides are implemented with collection-valued attributes. Let’s add the new attributes and methods to access the Item class to the Category class, as shown in listing 3.2.

Listing 3.2. Category-to-Item scaffolding code
public class Category {
//...
private ISet items = new HashedSet();
//...
public ISet Items {
get { return items; }
set { items = value; }
}
}

The code for the Item class (the other end of the many-to-many association) is similar to the code for the Category class. You add the collection attribute, the standard properties, and a method that simplifies relationship management (you can also add this to the Category class; see listing 3.3).

Listing 3.3. Item-to-Category scaffolding code
public class Item {
private string name;
private string description;
//...
private ISet categories = new HashedSet();
//...
public ISet Categories() {
get { return categories; }
set { categories = value; }
}
public void AddCategory(Category category) {
category.Items.Add(this);
categories.Add(category);
}
}

The AddCategory() method of the Item class is similar to the AddChildCategory() convenience method of the Category class. It’s used by a client to manipulate the relationship between Item and a Category. For the sake of readability, we don’t show convenience methods in future code samples and assume you’ll add them according to your own taste.

You should now understand how to create classes to form your domain model; these classes can be persisted by NHibernate. Also, you should be able to create associations between these classes, using convenience methods where necessary to improve the domain model. The next step is to further enrich the domain model by adding business logic. We start by looking at how you can you can add logic to your properties.

3.2.5. Adding logic to properties

One of the reasons we like to use properties is that they provide encapsulation: you can change a property’s hidden internal implementation without any changes to the public interface. This lets you abstract a class’s internal data structure—the instance variables—from the design of the database.

For example, if your database stores a username as a single NAME column, but your User class has firstname and lastname properties, you can add the following persistent name property to your class:

public class User {
private string firstname;
private string lastname;
//...
public string Name {
get { return firstname + ' ' + lastname; }
set {
string[] names = value.Split(' ');
firstname = names[0];
lastname = names[1];
}
)
//...
}

Later, you’ll see that an NHibernate custom type is probably a better way to handle many of these kinds of situations. But it helps to have several options.

Properties can also perform validation. For instance, in the following example, the FirstName property’s setter verifies that the name is capitalized:

public class User {
private string firstname;
//...
public string Firstname {
get { return firstname; }
set {
if ( !StringUtil.IsCapitalizedName(firstname) )
throw new InvalidNameException(value);
firstname = value;
)
//...
}

NHibernate will later use your properties to populate the state of an object when loading the object from the database. Sometimes you’d prefer that this validation not occur when NHibernate is initializing a newly loaded object. In that case, it may make sense to tell NHibernate to directly access the instance variables (you’ll see later that you can do so by mapping the property with access="field" in NHibernate metadata), forcing NHibernate to bypass the property and access the instance variable directly.

Another issue to consider is dirty checking. NHibernate automatically detects objectstate changes in order to synchronize the updated state with the database. It’s usually safe to return a different object from the get accessor to the object passed by NHibernate to the set accessor. NHibernate compares the objects by value—not by object identity—to determine whether the property’s persistent state needs to be updated. For example, the following get accessor won’t result in unnecessary SQL UPDATEs:

public string Firstname {
get { return new string(firstname); }
}

But there is one important exception. Collections are compared by identity!

For a property mapped as a persistent collection, you should return exactly the same collection instance from the get accessor as NHibernate passed to the set accessor. If you don’t, NHibernate updates the database, even if no update is necessary, every time the session synchronizes state held in memory with the database. This kind of code should almost always be avoided in properties:

public IList Names {
get { return new ArrayList(names); }
set { names = new string[value.Count]; value.CopyTo(names, 0); }
}

NHibernate doesn’t unnecessarily restrict the POCO programming model. You’re free to implement whatever logic you need in properties (as long as you keep the same collection instance in both get and set accessors). Note that collections shouldn’t have a setter at all.

If absolutely necessary, you can tell NHibernate to use a different access strategy to read and set the state of a property (for example, direct instance-field access), as you’ll see later. This kind of transparency guarantees an independent and reusable domain model implementation.

At this point, you’ve defined a number of classes for your domain model and set up some associations between them. You’ve also added convenience methods to make working with the model easier, and added some business logic. Your next goal is to be able to load and save objects in the domain model to and from a relational database. We now need to look at setting up the necessary pieces to let NHibernate perist objects—or, more specifically, object/relational mapping.

3.3. Defining the mapping metadata

ORM tools require a metadata format for the application to specify the mapping between classes and tables, properties and columns, associations and foreign keys, .NET types and SQL types. This information is called the object/relational mapping metadata. It defines the transformation between the different data type systems and relationship representations.

It’s our job as developers to define and maintain this metadata. We can do this two different ways: attributes and XML mapping files. In this section, you’ll learn how to write mapping using these two approaches, and we’ll compare them so you can decide which one to use. Let’s start with the mapping you’re familiar with: using .NET XML files.

3.3.1. Mapping using XML

NHibernate provides a mapping format based on the popular XML. Mapping documents written in and with XML are lightweight, are human readable, are easily handeditable, are easily manipulated by version-control systems and text editors, and may be customized at deployment time (or even at runtime, with programmatic XML generation).

But is XML-based metadata a viable approach? A certain backlash against the overuse of XML can be seen in the developer community. Every framework and service seems to require its own XML descriptors.

In our view, there are three main reasons for this backlash:

  • Many existing metadata formats weren’t designed to be readable and easy to edit by hand. A major cause of pain is the lack of sensible defaults for attribute and element values, requiring significantly more typing than should be necessary.
  • Metadata-based solutions were often used inappropriately. Metadata isn’t by nature more flexible or maintainable than plain C# code.
  • Good XML editors, especially in IDEs, aren’t as common as good .NET coding environments. Worst, and most easily fixable, an XML Schema Definition (XSD) often isn’t provided, preventing auto-completion and validation. Also problematic are XSDs that are too generic, where every declaration is wrapped in a generic extension of a meta element (like the key/value approach).

There is no getting around the need for text-based metadata in ORM. But NHibernate was designed with full awareness of the typical metadata problems. The metadata format is extremely readable and defines useful default values. When some values are missing, NHibernate uses reflection on the mapped class to help determine the defaults. NHibernate comes with a documented and complete XSD. Finally, IDE support for XML has improved lately, and modern IDEs provide dynamic XML validation and even an auto-complete feature. If that’s not enough for you, in chapter 9 we demonstrate some tools you can use to generate NHibernate XML mappings.

Let’s look at the way you can use XML metadata in NHibernate. We introduced the mapping of the Category class in a previous section; now we provide more details about the structure of its XML mapping document shown in listing 3.4.

Listing 3.4. NHibernate XML mapping of the Category class

As you can see, an XML mapping document can be divided into many parts:

  • Mappings are declared inside a <hibernate-mapping> element . You can include as many class mappings as you like, along with certain other special declarations that we mention later in the book.
  • The NHibernate mapping XSD is declared to provide syntactic validation of the XML , and many XML editors use it for auto-completion. But it isn’t recommended that you use the online copy of this file, for performance reasons.
  • The Category class (in the assembly CaveatEmptor.Model) is mapped to the table of the same name (Category) . Every row in this table represents one instance of type Category.
  • We haven’t discussed the concept of object identity much. This complex topic is covered in section 3.5. To understand this mapping, it’s sufficient to know that every record in the Category table will have a primary key value that matches the object identity of the instance in memory. The <id> mapping element is used to define the details of object identity .
  • The Name property is mapped to a database column of the same name (Name) . NHibernate will use .NET reflection to discover the type of this property and deduce how to map it to the SQL column, assuming they have compatible types. Note that it’s possible to explicitly specify the mapping data type that NHibernate should use. We take a close look at these types in section 7.1.
  • You use an association to link a Category to another. Here, it’s a many-to-one association . In the database, the Category table contains a ParentCategory column that is a foreign key to another row in the same table. Association mappings are more complex, so we return to them in section 4.6.

Although it’s possible to declare mappings for multiple classes in one mapping file by using multiple <class> elements, the recommended practice (and the practice expected by some NHibernate tools) is to use one mapping file per persistent class. The convention is to give the file the same name as the mapped class, appending an hbm suffix: for example, Category.hbm.xml.

Sometimes you may want to use .NET attributes rather than XML files to define your mappings; next, we briefly explain how to do this. After that, we look more closely at the nature of the class and property mappings described in this section.

3.3.2. Attribute-oriented programming

One way to define the mapping metadata is to use .NET attributes. Since its first release,.NET has provided support for class/member attributes. In chapter 2, we introduced the NHibernate.Mapping.Attributes library, which uses attributes directly embedded in the .NET source code to provide all the information NHibernate needs to map classes. All you have to do is to mark up the .NET source code of your persistent classes with custom .NET attributes, as shown in listing 3.5.

Listing 3.5. Mapping with NHibernate.Mapping.Attributes
using NHibernate.Mapping.Attributes;

[Class(Lazy=false)]
public class Category {
//...
[Id(Name="Id")]
[Generator(1, Class="native")]
public long Id {
//...
}
//...
[Property]
public string Name {
//...
}
//...
}

It’s easy to use this mapping with NHibernate:

cfg.AddInputStream(
NHibernate.Mapping.Attributes.HbmSerializer.Default.Serialize(
typeof(Category) ) );

Here, NHibernate.Mapping.Attributes generates an XML stream from the mapping in the Category class, and this stream is sent to the NHibernate configuration. You can also write this mapping information in external XML documents.


XML mapping or .NET attributes?

We’ve introduced mapping using XML mapping files and using NHibernate.Mapping.Attributes. Although you can use both at the same time, it’s more common (and homogenous) to use only one technique. Your choice is based on the way you develop your application. You can read more details about development processes in chapter 8.

For now, you have already realized that .NET attributes are much more convenient and reduce the lines of metadata significantly. They’re also type-safe, support auto-completion in your IDE as you type (like any other C# type), and make refactoring of classes and properties easier. NHibernate.Mapping.Attributes is usually used when starting a new project. Arguably, attribute mappings are less configurable at deployment time. But nothing is stopping you from hand-editing the generated XML before deployment, so this probably isn’t a significant objection.

On the other hand, XML mapping documents are external; this means that they can evolve independently of your domain model; they’re also easier to manipulate for complex mapping and they can contain some useful information (not directly related to the mapping of the classes). It’s common to use XML mapping files when the classes already exist and aren’t under our control.

Note that, in few cases, it’s better to write XML mapping; for example, when dealing with a highly customized component or collection mapping. In these cases, you can use the attribute [RawXml] to insert this XML in your attribute mapping.


You should now have grasped the basic idea of how both XML mappings and attribute mappings work. Next, we look more closely at the types of mappings in more detail, starting with property and class mappings.

3.4. Basic property and class mappings

In this section, you’ll learn a number of features and tips that will help you write better mappings. NHibernate can “guess” some information to make your mappings shorter. It’s also possible to configure it to access your entities in a specific way.

Let’s start with a deeper review of the mapping of simple properties.

3.4.1. Property mapping overview

A typical NHibernate property mapping defines a property name, a database column name, and the name of an NHibernate type. It maps a .NET property to a table column. The basic declaration provides many variations and optional settings; for example, it’s often possible to omit the type name. For example, if Description is a property of (.NET) type String, NHibernate uses the NHibernate type String by default (we discuss the NHibernate type system in chapter 7). NHibernate uses reflection to determine the.NET type of the property. Thus, the following mappings are equivalent, as long as they’re on the property Description:

[Property(Name="Description", Column="DESCRIPTION", Type="String")]
[Property(Column="DESCRIPTION")]
public string Description { ... }

These mapping can be written using XML. The following mappings are equivalent:

<property name="Description" column="DESCRIPTION" type="String"/>
<property name="Description" column="DESCRIPTION"/>

As you already know, you can omit the column name if it’s the same as the property name, ignoring case. (This is one of the sensible defaults we mentioned earlier.)

In some cases, you may need to tell NHibernate more about the database column than just its name. For this, you can use the <column> element instead of the column attribute. The <column> element provides more flexibility; it has more optional attributes and may appear more than once. The following two property mappings are equivalent:

[Property]
[Column(1, Name="DESCRIPTION")]
public string Description { ... }

Using XML, you can write

<property name="Description" type="String">
<column name="DESCRIPTION"/>
</property>

Because .NET attributes aren’t ordered, you sometimes need to specify their position. Here, [Column] comes after [Property], so its position is 1; the position of [Property] is 0 (the default value).

NHibernate.Mapping.Attributes mimics XML mapping, so if you can write one, you can deduce how to write the other. The main difference is that you don’t need to specify names with attribute mappings because NHibernate.Mapping.Attributes can guess them based on where the attribute mappings are in the code. The exception is [Id]; you must specify the identifier’s name when it has one, because it’s optional.

The <property> element (and especially the <column> element) also defines certain attributes that apply mainly to automatic database-schema generation. If you aren’t using the hbm2ddl tool (see section 10.1.1) to automatically generate the database schema, you can safely omit these. But it’s still preferable to include at least the not-null attribute, because NHibernate can then report illegal null property values without going to the database:

<property name="InitialPrice" column="INITIAL_PRICE" not-null="true"/>

Detection of illegal null values is mainly useful for providing sensible exceptions at development time. It isn’t intended for true data validation, which is outside the scope of NHibernate.

Some properties don’t map to a column. In particular, a derived property takes its value from a SQL expression.

3.4.2. Using derived properties

The value of a derived property is calculated at runtime by evaluating an expression. You define the expression using the formula attribute. For example, for a Shopping-Cart class, you might map a TotalIncludingTax property. Because it’s a formula, there is no column to store that value in the database:

<property name="TotalIncludingTax"
formula="TOTAL + TAX_RATE * TOTAL"
type="Double"/>

The given SQL formula is evaluated every time the entity is retrieved from the database. So the database does the calculation rather than the .NET object. The property doesn’t have a column attribute (or sub-element) and never appears in a SQL INSERT or UPDATE, only in SELECTs. Formulas may refer to columns of the database table, call SQL functions, and include SQL subselects.

This example, mapping a derived property of Item, uses a correlated subselect to calculate the average amount of all bids for an item:

<property
name="AverageBidAmount"
formula="( select AVG(b.AMOUNT) from BID b
where b.ITEM_ID = ITEM_ID )"
type="Double"/>

Notice that unqualified column names (in this case, those not preceded by b). refer to table columns of the class to which the derived property belongs.

As we mentioned earlier, NHibernate doesn’t require properties on entities if you define a new property-access strategy. The next section explains the various strategies and when you should use them in your mapping.

3.4.3. Property access strategies

The access attribute allows you to specify how NHibernate should access values of the entity. The default strategy, property, uses the property accessors—the getters and setters you declare in your classes. In your mapping XML file, mapping a class property getter and setter to a column is simple:

<property name="Description"/>

When NHibernate loads or saves an object, it always uses the defined getter and setter to access the data in the object.

In our “Hello World” example in chapter 2, you used the field access strategy in the XML mapping file for the Employee entity. The field strategy is useful when you haven’t defined property getters and setters for your classes. Behind the scenes, it uses reflection to access the instance class field directly. For example, the following property mapping doesn’t require a getter/setter pair in the class because it’s using the field access strategy:

<property name="name" access="field"/>

The field access strategy can be useful at times, but access through property getters and setters is considered best practice by the NHibernate community; they give you an extra level of abstraction between the .NET domain model and the data model beyond that provided by NHibernate. Properties are also more flexible than fields; for example, property definitions may be overridden by persistent subclasses.

NHibernate gives you additional flexibility when working with properties. For example, what if your property setters contain business logic? Often, you only want this logic to be executed when your client code sets the property, not during load time. If a class is mapped using a property setter, NHibernate runs the code as it loads the object. Thankfully, there are ways to deal with this situation. NHibernate provides a special access strategy called the nosetter.* strategy. Using this in your mapping tells NHibernate to use the property getter when reading data from the object, but to use the underlying field while writing data to it.

If you need even more flexibility than this, you can learn about other access strategies available in the NHibernate reference documentation online. As a sample, if you want NHibernate not to use the getter if you use the standard way of naming fields in C#—camelcase prefixed by an underscore (such as _firstName)—you can map it like this, using NHibernate.Mapping.Attributes:

private string _firstName;

[Property(Access="field.camelcase-underscore")]
public string FirstName {
get { return _firstName; }
}

The equivalent XML mapping is

<property name="FirstName" access="field.camelcase-underscore"/>

The nice side effect of this example is that, when writing NHibernate HQL queries, you use the more readable property name rather than ugly field names. Behind the scenes, NHibernate knows to bypass the property and instead use the field when loading and saving objects. Because you’re using a field, the property is effectively ignored—it doesn’t even have to exist in the code! As a useful extra, if you want to do this for all properties on a class, you can specify this access strategy at the class level by using <hibernate-mapping default-access="..."> or the property HbmSerializer.HbmDefaultAccess when using NHibernate.Mapping.Attributes.

If you still need more flexibility, you can define your own customized property-access strategy by implementing the interface NHibernate.Property.IPropertyAccessor; you name the class implementing your custom strategy in the access attribute (using its fully qualified name). With NHibernate.Mapping.Attributes, you have the alternative of using

[Property(AccessType=typeof(MyPropertyAccessor))]

This facility (adding Type at the end of an element to provide a .NET type instead of a string) is available in many other places.

So far, you’ve learned how to build the classes for your domain model and how to define mapping metadata to tell NHibernate how to persist these classes and their members. NHibernate gives you a wealth of features and flexibility, but essentially we’re talking about straightforward ORM capabilities. Next, we look at some under-the-hood aspects of NHibernate, including the ability to disable its optimizer to assist with debugging, the ability to enforce that objects are immutable by preventing NHibernate from inserting and updating them, and a few other handy tricks that will help you tackle thorny scenarios.

3.4.4. Taking advantage of the reflection optimizer

We mentioned that NHibernate can use reflection to get and set properties of an entity at runtime. Reflection can be slow, so NHibernate goes a step further and uses an optimizer to speed up this process. The optimizer is enabled by default and goes to work as you create your session factories. Because of this, you suffer a small startup cost, but it’s usually worth it.

Depending on the version of .NET you’re using, NHibernate takes different approaches to optimizing reflection.

Under .NET 1.1, NHibernate uses a CodeDom provider. This provider generates special classes at runtime that know about your business entities and that can access them without using reflection. A small caveat is that it only works for public properties; you must use the default property-access strategy in your mapping files to get optimal results. Another restriction is that quoted SQL identifiers (section 3.4.6) aren’t supported.

NHibernate 1.2 introduces another provider, which only works (and is used by default) under .NET 2.0. This provider injects dynamic methods into your business entities at startup, and it’s more powerful because it isn’t restricted to public properties.

It’s rarely necessary, but you can disable this reflection optimizer by updating your configuration file:

<property name="hibernate.use_reflection_optimizer">false</property>

or, at runtime, using

Environment.UseReflectionOptimizer = false;

This may be helpful when debugging your application, because runtime-generated classes are harder to trace. You must set this property before instantiating the Configuration. You can’t use the <hibernate-configuration> section in your config file (hibernate.cfg.xml, web.config, and so on) because this is read after the Configuration object is created (during the call to your Configuration object’s Configure() method).

You can select the CodeDom provider using

<property name="hibernate.bytecode.provider">codedom</property>

The value codedom can be replaced by null (to disable the optimizer) or lcg (on .NET 2.0 or later only). Note that codedom may not work properly with generic types.

You must set this property in the <nhibernate> section of your application configuration file. At runtime, before building the session factory, you can set the property Environment.BytecodeProvider to the value returned by the static method Environment.BuildBytecodeProvider() or to an instance of your own provider that implements the interface NHibernate.Bytecode.IBytecodeProvider.

The next interesting capability we look at is controlling database inserts and updates for classes and their members. This level of control is useful when you want to create immutable objects, or when you want to disable updates on a per-property basis.

3.4.5. Controlling insertion and updates

You can control whether properties that map to columns appear in the INSERT statement by using the insert attribute, and whether they appear in the UPDATE statement by using the update attribute.

The following property is never written to the database:

<property name="Name"
column="NAME"
type="String"
insert="false"
update="false"/>

The entity’s Name property is immutable; it can be read from the database but not modified in any way. If the complete class is immutable, set the mutable="false" in the class mapping. (If you’re unfamiliar with immutable classes, they’re basically classes that you’ve decided should never be updated after they’ve been created. An example might be a financial transaction record.)

In addition, the dynamic-insert and dynamic-update attributes tell NHibernate whether to include unmodified property values during SQL INSERTs and UPDATEs:

<class name="NHibernate.Auction.Model.User, NHibernate.Auction"
dynamic-insert="true"
dynamic-update="true">
...
</class>

These are both class-level settings that are off by default; when NHibernate generates INSERT and UPDATE SQL for an object, it does so for all properties on the object regardless of whether they’ve changed since the object was loaded. Enabling either of these settings causes NHibernate to generate SQL at runtime instead of using the SQL cached at startup time. The performance and memory cost of doing this is usually small. Furthermore, leaving out columns in an insert (and especially in an update) can occasionally improve performance if your tables define many/large columns.

3.4.6. Using quoted SQL identifiers

By default, NHibernate doesn’t quote table and column names in the generated SQL. This makes the SQL slightly more readable and also lets you take advantage of the fact that most SQL databases are case insensitive when comparing unquoted identifiers. From time to time, especially in legacy databases, you’ll encounter identifiers with strange characters or whitespace, or you may wish to force case sensitivity.

If you quote a table or column name with backticks in the mapping document, NHibernate always quotes this identifier in the generated SQL. The following property declaration forces NHibernate to generate SQL with the quoted column name "Item Description". NHibernate also knows that Microsoft SQL Server needs the variation [Item Description] and that MySQL requires 'Item Description':

<property name="Description"
column="'Item Description'"/>

There is no way, apart from quoting all table and column names in backticks, to force NHibernate to use quoted identifiers everywhere.

NHibernate gives you further control when mapping between your domain model and the database schema, by also letting you control naming conventions. We discuss this next.

3.4.7. Naming conventions

Development teams must often follow strict conventions for table and column names in their databases. NHibernate provides a feature that lets you enforce naming standards automatically.

Suppose that all table names in CaveatEmptor should follow the pattern CE_<table name>. One solution is to manually specify a table attribute on all <class> and collection elements in your mapping files. This approach is time consuming and easily forgotten. Instead, you can implement NHibernate’s INamingStrategy interface, as in listing 3.6.

Listing 3.6. INamingStrategy implementation
public class CENamingStrategy : INamingStrategy {
public string ClassToTableName(string className) {
return TableName(
StringHelper.Unqualify(className).ToUpper() );
}
public string PropertyToColumnName(string propertyName) {
return propertyName.ToUpper ();
}
public string TableName(string tableName) {
return "CE_" + tableName;
}
public string ColumnName(string columnName) {
return columnName;
}
public string PropertyToTableName(string className,
string propertyName) {
return ClassToTableName(className) + '_' +
PropertyToColumnName(propertyName);
}
}

The ClassToTableName() method is called only if a <class> mapping doesn’t specify an explicit table name. The PropertyToColumnName() method is called if a property has no explicit column name. The TableName() and ColumnName() methods are called when an explicit name is declared.

If you enable CENamingStrategy, this class mapping declaration

<class name="BankAccount">

results in CE_BANKACCOUNT as the name of the table. The ClassToTableName() method is called with the fully qualified class name as the argument.

But if you specify a table name

<class name="BankAccount" table="BANK_ACCOUNT">

then CE_BANK_ACCOUNT is the name of the table. In this case, BANK_ACCOUNT is passed to the TableName() method.

The best feature of INamingStrategy is the potential for dynamic behavior. To activate a specific naming strategy, you can pass an instance to the NHibernate Configuration at runtime:

Configuration cfg = new Configuration();
cfg.NamingStrategy = new CENamingStrategy();
ISessionFactory sessionFactory =
cfg.configure().BuildSessionFactory();

This lets you have multiple ISessionFactory instances based on the same mapping documents, each using a different INamingStrategy. This is extremely useful in a multiclient installation where unique table names (but the same data model) are required for each client.

But a better way to handle this kind of requirement is to use the concept of a SQL schema (a kind of namespace).

3.4.8. SQL schemas

SQL schemas are a feature available in many databases, including SQL Server 2005 and MySQL. They let you organize your database objects into meaningful groups. For example, the AdventureWorks sample database that comes with Microsoft SQL Server 2005 defines five schemas: Human Resources, Person, Production, Purchasing, and Sales. All these schemas live in a single database, and each has its own tables, views and other database objects.

Many databases are designed with only one schema. You can specify a default schema using the hibernate.default_schema configuration option; doing so offers some small performance benefits.

Alternatively, if your database is like AdventureWorks and has many schemas, you can specify the schema for a particular mapping document or even a particular class or collection mapping:

<hibernate-mapping>
<class
name="NHibernateInAction.HelloWorld.Message,
NHibernateInAction.HelloWorld"
schema="HelloWorld">
...
</class>
</hibernate-mapping>

You can even declare a schema for the whole document:

<hibernate-mapping
schema="HelloWorld">
..
</hibernate-mapping>

Next, we discuss another useful thing you can do with the <hibernate-mapping> element: specify a default namespace for your classes, to reduce duplication.

3.4.9. Declaring class names

In this chapter, we introduced the CaveatEmptor application. All the persistent classes of the application are declared in the namespace NHibernate.Auction.Model and are compiled in the NHibernate.Auction assembly. It would become tedious to specify this fully qualified name every time you name a class in your mapping documents.

Let’s reconsider the mapping for the User class (the file User.hbm.xml):

<?xml version="1.0"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xsi:schemaLocation="urn:nhibernate-mapping-2.2
http://nhibernate.sourceforge.net/schemas/nhibernate-mapping.xsd">
<class
name="NHibernate.Auction.Model.User, NHibernate.Auction">
...
</class>
</hibernate-mapping>

You don’t want to repeat the fully qualified name whenever this or any other class is named in an association, subclass, or component mapping. Instead, you can specify a namespace and an assembly:

<hibernate-mapping
namespace="NHibernate.Auction.Model"
assembly="NHibernate.Auction">
<class
name="User">
...
</class>
</hibernate-mapping>

Now all unqualified class names that appear in this mapping document will be prefixed with the declared package name. We assume this setting in all mapping examples in this book. But this setting is mostly useless when using attribute mapping, because you can specify the .NET type, and NHibernate.Mapping.Attributes will write its fully qualified name.

You can also use the methods SetDefaultNamespace() and SetDefaultAssembly() of the Configuration class to achieve the same result for the entire application.


Note

We’ll no longer write the XSD information, because it clutters the examples.


Both approaches we’ve described—XML and .NET attributes—assume that all mapping information is known at deployment time. Suppose that some information isn’t known before the application starts. Can you programmatically manipulate the mapping metadata at runtime?

3.4.10. Manipulating metadata at runtime

It’s sometimes useful for an application to browse, manipulate, or build new mappings at runtime. (You can safely skip this section and come back to it later when you need to.) .NET provides XML APIs that allow direct runtime manipulation of XML documents: you can create or manipulate an XML document at runtime before feeding it to the Configuration object.

But NHibernate also exposes a configuration-time metamodel. The metamodel contains all the information declared in your XML mapping documents. Direct programmatic manipulation of this metamodel is sometimes useful, especially for applications that allow for extension by user-written code.

For example, the following code adds a new Motto property to the User class mapping:

A PersistentClass object represents the metamodel for a single persistent class; we retrieve it from the Configuration. Column, SimpleValue, and Property are all classes of the NHibernate metamodel and are available in the namespace NHibernate.Mapping; the class StringType is in the namespace NHibernate.Type. Keep in mind that adding a property to an existing persistent class mapping as shown here is easy, but programmatically creating a new mapping for a previously unmapped class is quite a bit more involved.

Once an ISessionFactory is created, its mappings are immutable. The ISessionFactory uses a different metamodel internally than the one used at configuration time. There is no way to get back to the original Configuration from the ISessionFactory or ISession. But the application may read the ISessionFactory’s metamodel by calling GetClassMetadata() or GetCollectionMetadata(). Here’s an example:

User user = ...;
ClassMetadata meta = sessionFactory.GetClassMetadata(typeof(User));
string[] metaPropertyNames = meta.GetPropertyNames();
object[] propertyValues = meta.GetPropertyValues(user);

This code snippet retrieves the names of persistent properties of the User class and the values of those properties for a particular instance. This helps you write generic code. For example, you might use this feature to label UI components or improve log output.

Now let’s turn to a special mapping element you’ve seen in most of the previous examples: the identifier property mapping. We begin by discussing the notion of object identity.

3.5. Understanding object identity

It’s vital to understand the difference between object identity and object equality before we discuss terms like database identity and how NHibernate manages identity. You need these concepts if you want to finish mapping the CaveatEmptor persistent classes and their associations with NHibernate.

3.5.1. Identity versus equality

.NET developers understand the difference between .NET object identity and equality. Object identity, object.ReferenceEquals(), is a notion defined by the CLR environment. Two object references are identical if they point to the same memory location.

On the other hand, object equality is a notion defined by classes that implement the Equals() method (or the operator ==), sometimes also referred to as equivalence. Equivalence means that two different (non-identical) objects have the same value. Two different instances of string are equal if they represent the same sequence of characters, even though they each have their own location in the memory space of the virtual machine. (We admit that this isn’t entirely true for strings, but you get the idea.)

Persistence complicates this picture. With object/relational persistence, a persistent object is an in-memory representation of a particular row of a database table. Along with .NET identity (memory location) and object equality, we pick up database identity (location in the persistent data store). We now have three methods for identifying objects:

  • Object identity— Objects are identical if they occupy the same memory location. This can be checked by using object.ReferenceEquals().
  • Object equality— Objects are equal if they have the same value, as defined by the Equals(object o) method. Classes that don’t explicitly override this method inherit the implementation defined by System.Object, which compares object identity.
  • Database identity— Objects stored in a relational database are identical if they represent the same row or, equivalently, share the same table and primary key value.

You need to understand how database identity relates to object identity in NHibernate.

3.5.2. Database identity with NHibernate

NHibernate exposes database identity to the application in two ways:

  • The value of the identifier property of a persistent instance
  • The value returned by ISession.GetIdentifier(object o)

The identifier property is special: its value is the primary-key value of the database row represented by the persistent instance. We don’t usually show the identifier property in the domain model—it’s a persistence-related concern, not part of the business problem. In our examples, the identifier property is always named id. If myCategory is an instance of Category, calling myCategory.Id returns the primary key value of the row represented by myCategory in the database.

Should you make the property for the identifier private scope or public? Well, database identifiers are often used by the application as a convenient handle to a particular instance, even outside the persistence layer. For example, web applications often display the results of a search screen to the user as a list of summary information. When the user selects a particular element, the application may need to retrieve the selected object. It’s common to use a lookup by identifier for this purpose—you’ve probably already used identifiers this way, even in applications using direct ADO.NET. It’s usually appropriate to fully expose the database identity with a public identifier property.

On the other hand, we usually don’t implement a set accessor for the identifier (in this case, NHibernate uses .NET reflection to modify the identifier field). And we also usually let NHibernate generate the identifier value. The exceptions to this rule are classes with natural keys, where the value of the identifier is assigned by the application before the object is made persistent, instead of being generated by NHibernate. (We discuss natural keys in the next section.) NHibernate doesn’t let you change the identifier value of a persistent instance after it’s first assigned. Remember, part of the definition of a primary key is that its value should never change. Let’s implement an identifier property for the Category class and map it with .NET attributes:

[Class(Table="CATEGORY")]
public class Category {
private long id;
//...
[Id(Name="Id", Column="CATEGORY_ID", Access="nosetter.camelcase")]
[Generator(1, Class="native")]
public long Id {
get { return this.id; }
}
//...
}

The property type depends on the primary key type of the CATEGORY table and the NHibernate mapping type. This information is determined by the <id> element in the mapping document. Here is the XML mapping:

<class name="Category" table="CATEGORY">
<id name="Id" column="CATEGORY_ID" access="nosetter.camelcase">
<generator class="native"/>
</id>
...
</class>

The identifier property is mapped to the primary-key column CATEGORY_ID of the CATEGORY table. The NHibernate type for this property is long, which maps to a BIGINT column type in most databases and which has also been chosen to match the type of the identity value produced by the native identifier generator. (We discuss identifier-generation strategies in the next section.) The access strategy used here—access="nosetter.camelcase"—tells NHibernate that there is no set accessor and that it should use the camelCase transformation to deduce the name of the identity field using the property name. If possible, NHibernate will use a reflection optimizer to avoid reflection costs (explained later in this chapter).

In addition to operations for testing .NET object identity (object.ReferenceEquals(a,b)) and object equality (a.Equals(b)), you may now use a.Id==b.Id to test database identity.

An alternative approach to handling database identity is to not implement any identifier property, and let NHibernate manage database identity internally. In this case, you omit the name attribute in the mapping declaration:

<id column="CATEGORY_ID">
<generator class="native"/>
</id>

NHibernate will now manage the identifier values internally. You may obtain the identifier value of a persistent instance as follows:

long catId = (long) session.GetIdentifier(category);

This technique has a serious drawback: you can no longer use NHibernate to manipulate detached objects effectively (see section 4.1.5). You should always use identifier properties in NHibernate. If you don’t like them being visible to the rest of your application, make the property protected or private.

Using database identifiers in NHibernate is easy. Choosing a good primary key (and key-generation strategy) can be more difficult. We discuss these issues next.

3.5.3. Choosing primary keys

You have to tell NHibernate about your preferred strategy for generating a primary key. But first, let’s define primary key.

The candidate key is a column or set of columns that uniquely identifies a specific row of the table. A candidate key must satisfy the following properties:

  • The value or values are never null.
  • Each row has a unique value or values.
  • The value or values of a particular row never change.

For a given table, several columns or combinations of columns may satisfy these properties. If a table has only one identifying attribute, it’s by definition the primary key. If there are multiple candidate keys, you need to choose between them (candidate keys not chosen as the primary key should be declared as unique keys in the database). If there are no unique columns or unique combinations of columns, and hence no candidate keys, then the table is by definition not a relation as defined by the relational model (it permits duplicate rows), and you should rethink your data model.

Many legacy SQL data models use natural primary keys. A natural key is a key with business meaning: an attribute or combination of attributes that is unique by virtue of its business semantics. Examples of natural keys might be a U.S. Social Security Number or an Australian Tax File Number. Distinguishing natural keys is simple: if a candidate-key attribute has meaning outside the database context, it’s a natural key, whether or not it’s automatically generated.

Experience has shown that natural keys almost always cause problems in the long run. A good primary key must be unique, constant, and required (never null or unknown). Few entity attributes satisfy these requirements, and some that do aren’t efficiently indexable by SQL databases. In addition, you should make absolutely certain that a candidate-key definition could never change throughout the lifetime of the database before promoting it to a primary key. Changing the definition of a primary key and all foreign keys that refer to it is a frustrating task.

For these reasons, we strongly recommend that new applications use synthetic identifiers (also called surrogate keys). Surrogate keys have no business meaning—they’re unique values generated by the database or application. You can use a number of well-known approaches to generate surrogate keys.

NHibernate has several built-in identifier-generation strategies. We list the most useful options in table 3.1.

Table 3.1. NHibernate’s built-in identifier generator modules

Generator name

Description

native

Picks other identity generators like identity, sequence, or hilo depending on the capabilities of the underlying database.

identity

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

sequence

Uses a sequence in DB2, PostgreSQL, Oracle, SAP DB, McKoi, and Firebird. The identifier returned by the database is converted to the property type using Convert.ChangeType. Any integral property type is thus supported.

increment

At NHibernate startup, reads the table’s maximum primary-key column value and increments the value by one each time a new row is inserted. The generated identifier can be of any integral type. This generator is especially efficient if the single-server NHibernate application has exclusive access to the database but shouldn’t be used in any other scenario (like in clusters).

hilo

Generates identifiers that are unique only for a particular database. A high/low algorithm is an efficient way to generate identifiers of any integral type, given a table and column (by default hibernate_unique_key and next_hi, respectively) as a source of hi values. See (Ambler 2002) for more information about the high/low approach to unique identifiers. Don’t use this generator with a user-supplied connection.

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. This generation strategy isn’t popular, because CHAR primary keys consume more database space than numeric keys and are marginally slower.

guid

Used when the identifier’s type is Guid. The identifier must have Guid.Empty as default value. When saving the entity, this generator assigns it a new value using Guid.NewGuid().

guid.comb

Similar to guid, but uses another algorithm that makes it almost as fast as when using integers (especially when saving in a SQL Server database). The generated values are ordered; you can use a part of these values as reference numbers, for example.

You aren’t limited to these built-in strategies; you can learn about others by reading NHibernate’s reference documentation. You may also create your own identifier generator by implementing NHibernate’s IIdentifierGenerator interface. It’s even possible to mix identifier generators for persistent classes in a single domain model, but for nonlegacy data we recommend using the same generator for all classes.

The special assigned identifier generator strategy is most useful for entities with natural primary keys. This strategy lets the application assign identifier values by setting the identifier property before making the object persistent by calling Save(). This strategy has some serious disadvantages when you’re working with detached objects and transitive persistence (both of these concepts are discussed in the next chapter). Don’t use assigned identifiers if you can avoid them; it’s much easier to use a surrogate primary key generated by one of the strategies listed in table 3.1.

For legacy data, the picture is more complicated. In this case, you’re often stuck with natural keys and especially composite keys (natural keys composed of multiple table columns). Because composite identifiers can be more difficult to work with, we only discuss them in the context of section 9.2, “Legacy schemas.”

The next step is to add identifier properties to the classes of the CaveatEmptor application. Do all persistent classes have their own database identity? To answer this question, we must explore the distinction between entities and value types in NHibernate. These concepts are required for fine-grained object modeling.

3.6. Fine-grained object models

A major objective of the NHibernate project is support for fine-grained object models, which we isolated as the most important requirement for a rich domain model. It’s one reason we’ve chosen POCOs.

In crude terms, fine-grained means “more classes than tables.” For example, a user may have both a billing address and a home address. In the database, you may have a single USER table with the columns BILLING_STREET, BILLING_CITY, and BILLING_ZIPCODE along with HOME_STREET, HOME_CITY, and HOME_ZIPCODE. There are good reasons to use this somewhat denormalized relational model (performance, for one).

In your object model, you can use the same approach, representing the two addresses as six string-valued properties of the User class. But it’s much better to model this using an Address class, where User has the billingAddress and homeAddress properties.

This object model achieves improved cohesion and greater code reuse and is more understandable. In the past, many ORM solutions haven’t provided good support for this kind of mapping.

NHibernate emphasizes the usefulness of fine-grained classes for implementing type-safety and behavior. For example, many people would model an email address as a string-valued property of User. We suggest that a more sophisticated approach is to define an EmailAddress class that can add higher-level semantics and behavior. For example, it may provide a SendEmail() method.

3.6.1. Entity and value types

This leads us to a distinction of central importance in ORM. In .NET, all classes are of equal standing: all objects have their own identity and lifecycle, and all class instances are passed by reference. Only primitive types are passed by value.

We advocate a design in which there are more persistent classes than tables. One row represents multiple objects. Because database identity is implemented by primary-key value, some persistent objects won’t have their own identity. In effect, the persistence mechanism implements pass-by-value semantics for some classes. One of the objects represented in the row has its own identity, and others depend on that.

NHibernate makes the following essential distinction:

  • An object of entity type has its own database identity (primary-key value). An object reference to an entity is persisted as a reference in the database (a foreign-key value). An entity has its own lifecycle; it may exist independently of any other entity.
  • An object of value type has no database identity; it belongs to an entity, and its persistent state is embedded in the table row of the owning entity (except in the case of collections, which are also considered value types, as you’ll see in chapter 6). Value types don’t have identifiers or identifier properties. The lifespan of a value-type instance is bounded by the lifespan of the owning entity.

The most obvious value types are simple objects like Strings and Integers. NHibernate also lets you treat a user-defined class as a value type, as you’ll see next. (We also come back to this important concept in section 6.1.)

3.6.2. Using components

So far, the classes of the object model have all been entity classes with their own lifecycle and identity. But the User class has a special kind of association with the Address class, as shown in figure 3.5.

Figure 3.5. Relationships between User and Address using composition

In object-modeling terms, this association is a kind of aggregation—a “part of” relationship. Aggregation is a strong form of association: it has additional semantics with regard to the lifecycle of objects. In this case, you have an even stronger form, composition, where the lifecycle of the part is dependent on the lifecycle of the whole.

Object modeling experts and UML designers will claim that there is no difference between this composition and other weaker styles of association when it comes to the .NET implementation. But in the context of ORM, there is a big difference: a composed class is often a candidate value type.

You now map Address as a value type and User as an entity. Does this affect the implementation of your POCO classes?

.NET has no concept of composition—a class or attribute can’t be marked as a component or composition. The only difference is the object identifier: a component has no identity; hence the persistent component class requires no identifier property or identifier mapping. The composition between User and Address is a metadata-level notion; you only have to tell NHibernate that the Address is a value type in the mapping document.

NHibernate uses the term component for a user-defined class that is persisted to the same table as the owning entity, as shown in listing 3.7. (The use of the word component here has nothing to do with the architecture-level concept, as in software component.)

Listing 3.7. Mapping the User class with a component Address

You declare the persistent attributes of Address inside the <component> element . The property of the User class is named HomeAddress. You reuse the same component class to map another property of this type to the same table .

Figure 3.6 shows how the attributes of the Address class are persisted to the same table as the User entity.

Figure 3.6. Table attributes of User with Address component

Components may be harder to map with NHibernate.Mapping.Attributes. When you’re using a component in many classes with the identical mapping, it’s easy to do (far easier than with XML mapping):

[Component]
public class Address {
[Property(NotNull=true)]
public string Street { ... }
[Property(NotNull=true)]
public string City { ... }
[Property(NotNull=true)]
public short Zipcode { ... }
}
[Class]
class User {
//...
[ComponentProperty]
public Address HomeAddress { ... }
}
[Class]
class House {
//...
[ComponentProperty]
public Address Location { ... }
}

But the User class has two addresses, each mapped to different columns. There are many ways to map them (see appendix B). Here is one solution:

[Class]
class User {
//...
[Component(Name="HomeAddress", ClassType=typeof(Address))]
protected class HomeAddressMapping {
[Property(Column="HOME_STREET", NotNull=true)]
public string Street { ... }
[Property(Column="HOME_CITY", NotNull=true)]
public string City { ... }
[Property(Column="HOME_ZIPCODE", NotNull=true)]
public short Zipcode { ... }
}
public Address HomeAddress { ... }
[Component(Name="BillingAddress", ClassType=typeof(Address))]
protected class BillingAddressMapping {
[Property(Column="BILLING_STREET", NotNull=true)]
public string Street { ... }
[Property(Column="BILLING_CITY", NotNull=true)]
public string City { ... }
[Property(Column="BILLING_ZIPCODE", NotNull=true)]
public short Zipcode { ... }
}
public Address BillingAddress { ... }
}

We simulate the hierarchy of the XML mapping using the classes HomeAddressMapping and BillingAddressMapping, whose sole purpose is to provide the mapping. Note that NHibernate.Mapping.Attributes will automatically pick them because they belong to the User class. This solution isn’t elegant. You won’t have to deal with this kind of mapping often.

Whenever you think that XML mapping would be easier to use than attributes, you can use the [RawXml] attribute to integrate this XML inside your attributes. This is probably the case here; you can include the XML mapping of the components in listing 3.7 like this:

[Class]
class User {
//...
[RawXml( After=typeof(ComponentAttribute), Content=@"
<component name=""HomeAddress"">
...
</component>" )]
public Address HomeAddress { ... }
//...
}

The [RawXml] attribute has two properties. After tells which kind of mapping the XML should be inserted after; most of the time, it’s the type of the attribute defined in the XML. This property is optional, in which case the XML is inserted on the top of the mapping. The second property is Content; it’s the string containing the XML to include.

Notice that in this example, you model the composition association as unidirectional. You can’t navigate from Address to User. NHibernate supports both unidirectional and bidirectional compositions, but unidirectional composition is far more common. Here’s an example of a bidirectional mapping:

<component
name="HomeAddress"
class="Address">
<parent name="Owner"/>
<property name="Street" type="String" column="HOME_STREET"/>
<property name="City" type="String" column="HOME_CITY"/>
<property name="Zipcode" type="short" column="HOME_ZIPCODE"/>
</component>

The <parent> element maps a property of type User to the owning entity; in this example, the property is named Owner. You then call Address.Owner to navigate in the other direction.

An NHibernate component may own other components and even associations to other entities. This flexibility is the foundation of NHibernate’s support for fine-grained object models. (We discuss various component mappings in chapter 6.)

But classes mapped as components have two important limitations:

  • Shared references aren’t possible— The component Address doesn’t have its own database identity (primary key), and so a particular Address object can’t be referred to by any object other than the containing instance of User.
  • There is no elegant way to represent a null reference to an Address— In lieu of an elegant approach, NHibernate represents null components as null values in all mapped columns of the component. This means that if you store a component object with all null property values, NHibernate will return a null component when the owning entity object is retrieved from the database.

Finally, it’s also possible to make a component immutable:

<component ... insert="false", update="false" />

When you make entities or components immutable, they may not be updated or deleted. This allows NHibernate to make minor performance optimizations. More important, immutable objects are much simpler to deal with: they can be shared and copied, and you’re safe in the knowledge that they can’t be changed.

Support for fine-grained classes is just one ingredient of a rich domain model. We now look at creating and mapping associations between the domin model classes.

3.7. Introducing associations

Managing the associations between classes and the relationships between tables is the soul of ORM. Most of the difficult problems involved in implementing an ORM solution relate to association management.

The NHibernate association model is extremely rich but isn’t without pitfalls, especially for new users. In this section, we don’t try to cover all the possible combinations; we examine certain cases that are extremely common. We return to the subject of association mappings in chapter 7 for a more complete treatment.

But first, we need to explain something up front.

3.7.1. Unidirectional associations

When you’re using (typed) DataSets, associations are represented as in a database. To link two entities, you have to set the foreign key in one entity to the primary key of the other. There isn’t the notion of collection; so you can’t add an entity to a collection and get the association created.

Transparent POCO-oriented persistence implementations such as NHibernate provide support for collections. But it’s important to understand that NHibernate associations are all inherently unidirectional. As far as NHibernate is concerned, the association from Bid to Item is a different association than the association from Item to Bid. This means that bid.Item=item and item.Bids.Add(bid) are two unrelated operations.

To some people, this seems strange; to others, it feels natural. After all, associations at the language level are always unidirectional—and NHibernate claims to implement persistence for plain .NET objects. We’ll merely observe that this decision was made because NHibernate objects aren’t bound to any context. In NHibernate applications, the behavior of a nonpersistent instance is the same as the behavior of a persistent instance.

Because associations are so important, we need precise language for classifying them.

3.7.2. Multiplicity

In describing and classifying associations, you’ll almost always use the multiplicity association. Look at figure 3.7.

Figure 3.7. Relationship between Item and Bid

The multiplicity consists of two bits of information:

  • Can there be more than one Bid for a particular Item?
  • Can there be more than one Item for a particular Bid?

After glancing at the object model, you can conclude that the association from Bid to Item is a many-to-one association. Recalling that associations are directional, you can also call the inverse association from Item to Bid a one-to-many association. (There are two more possibilities: many-to-many and one-to-one; we get back to them in chapter 6.)

In the context of object persistence, we aren’t interested in whether “many” means “two” or “maximum of five” or “unrestricted.”

3.7.3. The simplest possible association

The association from Bid to Item is an example of the simplest possible kind of association in ORM. The object reference returned by bid.Item is easily mapped to a foreign key column in the BID table. First, here’s the C# class implementation of Bid mapped using .NET attributes:

[Class(Table="BID")]
public class Bid {
...
private Item item;
[ManyToOne(Column="ITEM_ID", NotNull=true)]
public Item Item {
get { return item; }
set { item = value; }
}
...
}

Next, here’s the corresponding NHibernate mapping for this association:

<class
name="Bid"
table="BID">
...
<many-to-one
name="Item"
column="ITEM_ID"
class="Item"
not-null="true" />
</class>

This mapping is called a unidirectional many-to-one association. The column ITEM_ID in the BID table is a foreign key to the primary key of the ITEM table.

You explicitly specify the Item class, which the association refers to. This specification is usually optional, because NHibernate can determine this using reflection.

You specify the not-null attribute because you can’t have a bid without an item. The not-null attribute doesn’t affect the runtime behavior of NHibernate in this case; it exists mainly to control automatic data definition language (DDL) generation (see chapter 10).

In some legacy databases, a many-to-one association may point to a nonexistent entity. The property not-found lets you define how NHibernate should react to this situation:

<many-to-one ... not-found="ignore|exception" />

Using not-found="exception" (the default value), NHibernate throws an exception. And not-found="ignore" makes NHibernate ignore this association (leaving it null).

3.7.4. Making the association bidirectional

So far so good. But you also need to be able to easily fetch all the bids for a particular item. You need a bidirectional association here, so you have to add scaffolding code to the Item class:

public class Item {
//...
private ISet bids = new HashedSet();
public ISet Bids {
get { return bids; }
set { bids = value; }
}
public void AddBid(Bid bid) {
bid.Item = this;
bids.Add(bid);
}
//...
}

You can think of the code in AddBid() (a convenience method) as implementing a strong bidirectional association in the object model.

A basic mapping for this one-to-many association would look like this:

[Set]
[Key(1, Column="ITEM_ID")]
[OneToMany(2, ClassType=typeof(Bid))]
public ISet Bids { ... }

Here is the equivalent XML wrapped in its class mapping:

<class
name="Item"
table="ITEM">
...
<set name="Bids">
<key column="ITEM_ID"/>
<one-to-many class="Bid"/>
</set>
</class>

The column mapping defined by the <key> element is a foreign-key column of the associated BID table. Notice that you specify the same foreign-key column in this collection mapping that you specified in the mapping for the many-to-one association. The table structure for this association mapping is shown in figure 3.8.

Figure 3.8. Table relationships and keys for a one-to-many/many-to-one mapping

Now you have two different unidirectional associations mapped to the same foreign key, which poses a problem. At runtime, there are two different in-memory representations of the same foreign key value: the item property of Bid and an element of the bids collection held by an Item. Suppose your application modifies the association by, for example, adding a bid to an item in this fragment of the AddBid() method:

bid.Item = this;
bids.Add(bid);

This code is fine, but in this situation, NHibernate detects two different changes to the in-memory persistent instances. From the point of view of the database, just one value must be updated to reflect these changes: the ITEM_ID column of the BID table. NHibernate doesn’t transparently detect the fact that the two changes refer to the same database column, because at this point you’ve done nothing to indicate that this is a bidirectional association.

You need one more thing in your association mapping to tell NHibernate to treat this as a bidirectional association. The inverse attribute tells NHibernate that the collection is a mirror image of the many-to-one association on the other side:

<class
name="Item"
table="ITEM">
...
<set
name="bids"
inverse="true">
<key column="ITEM_ID"/>
<one-to-many class="Bid"/>
</set>
</class>

Without the inverse attribute, NHibernate would try to execute two different SQL statements, both updating the same foreign-key column, when you manipulate the association between the two instances. By specifying inverse="true", you explicitly tell NHibernate which end of the association it should synchronize with the database. In this example, you tell NHibernate that it should propagate changes made at the Bid end of the association to the database, ignoring changes made only to the bids collection. If you only call item.Bids.Add(bid), no changes are made persistent. This is consistent with the behavior in .NET without NHibernate: if an association is bidirectional, you have to create the link on two sides, not just one.

You now have a working bidirectional many-to-one association (which could also be called a bidirectional one-to-many association, of course).

Cascading Saves and Deletes

One final piece is missing. We explore the notion of transitive persistence in much greater detail in the next chapter. For now, we introduce the concepts of cascading save and cascading delete, which you need in order to finish mapping this association.

When you instantiate a new Bid and add it to an Item, the bid should become persistent immediately. You want to avoid the need to explicitly make a Bid persistent by calling Save() on the ISession interface.

You make one final tweak to the mapping document to enable cascading save:

<class
name="Item"
table="ITEM">
...
<set
name="Bids"
inverse="true"
cascade="save-update">
<key column="ITEM_ID"/>
<one-to-many class="Bid"/>
</set>
</class>

The cascade attribute tells NHibernate to make any new Bid instance persistent (that is, save it in the database) if the Bid is referenced by a persistent Item.

The cascade attribute is directional: it applies to only one end of the association. You could also specify cascade="save-update" for the many-to-one association declared in the mapping for Bid, but doing so would make no sense in this case because Bids are created after Items.

Are you finished? Not quite. You still need to define the lifecycle for both entities in your association.

3.7.5. A parent/child relationship

With the previous mapping, the association between Bid and Item is fairly loose. You’d use this mapping in a real system if both entities had their own lifecycle and were created and removed in unrelated business processes. Certain associations are much stronger than this; some entities are bound together so that their lifecycles aren’t truly independent. In the example, it seems reasonable that deletion of an item implies deletion of all bids for the item. A particular bid instance references only one item instance for its entire lifetime. In this case, cascading both saves and deletions makes sense.

If you enable cascading delete, the association between Item and Bid is called a parent/child relationship. In a parent/child relationship, the parent entity is responsible for the lifecycle of its associated child entities. This is the same semantic as a composition (using NHibernate components), but in this case only entities are involved; Bid isn’t a value type. The advantage of using a parent/child relationship is that the child may be loaded individually or referenced directly by another entity. A bid, for example, may be loaded and manipulated without retrieving the owning item. It may be stored without storing the owning item at the same time. Furthermore, you reference the same Bid instance in a second property of Item, the single SuccessfulBid (see figure 3.2). Objects of value type can’t be shared.

To remodel the Item to Bid association as a parent/child relationship, the only change you need to make is to the cascade attribute:

<class
name="Item"
table="ITEM">
...
<set
name="Bids"
inverse="true"
cascade="all-delete-orphan">
<key column="ITEM_ID"/>
<one-to-many class="Bid"/>
</set>
</class>

You use cascade="all-delete-orphan" to indicate the following:

  • Any newly instantiated Bid becomes persistent if the Bid is referenced by a persistent Item (as is also the case with cascade="save-update"). Any persistent Bid should be deleted if it’s referenced by an Item when the item is deleted.
  • Any persistent Bid should be deleted if it’s removed from the bids collection of a persistent Item. (NHibernate will assume that it was only referenced by this item and consider it an orphan.)

You achieve the following with this mapping: a Bid is removed from the database if it’s removed from the collection of Bids of the Item (or it’s removed if the Item is removed).

The cascading of operations to associated entities is NHibernate’s implementation of transitive persistence. We look more closely at this concept in section 4.3.

We’ve covered only a tiny subset of the association options available in NHibernate. But you already have enough knowledge to be able to build entire applications. The remaining options are either rare or variations of the associations we’ve described.

We recommend keeping your association mappings simple and using NHibernate queries for more complex tasks.

So far we’ve covered how to map classes, components and associations. We now look at an essential capability of NHibernate—mapping inheritance hierarchies.

3.8. Mapping class inheritance

A simple strategy for mapping classes to database tables might be “one table for every class.” This approach sounds simple, and it works well until you encounter inheritance. We end the chapter by exploring this somewhat advanced topic; if you’re interested primarily in basic NHibernate usage, feel free to skip to chapter 4.

Inheritance is the most visible feature of the structural mismatch between the object-oriented and relational worlds. Object-oriented systems model both “is a” and “has a” relationships. SQL-based models provide only “has a” relationships between entities.

You can use three different approaches to represent an inheritance hierarchy. These were catalogued by Scott Ambler (Ambler 2002) in his widely read paper “Mapping Objects to Relational Databases”:

  • Table per concrete class— Discard polymorphism and inheritance relationships from the relational model.
  • Table per class hierarchy— Enable polymorphism by denormalizing the relational model and using a type-discriminator column to hold type information.
  • Table per subclass— Represent “is a” (inheritance) relationships as “has a” (foreign key) relationships.

This section takes a top-down approach; it assumes that you’re starting with a domain model and trying to derive a new SQL schema. But the mapping strategies described are just as relevant if you’re working bottom up, starting with existing database tables.

3.8.1. Table per concrete class

Suppose you stick with the simplest approach: you can use exactly one table for each (non-abstract) class. All properties of a class, including inherited properties, can be mapped to columns of this table, as shown in figure 3.9.

Figure 3.9. Mapping table per concrete class

The main problem with this approach is that it doesn’t support polymorphic associations well. In the database, associations are usually represented as foreign-key relationships. In figure 3.9, if the subclasses are all mapped to different tables, a polymorphic association to their base class (abstract BillingDetails in this example) can’t be represented as a simple foreign-key relationship. This would be problematic in the domain model, because BillingDetails is associated with User; both tables would need a foreign-key reference to the USER table.

Polymorphic queries (queries that return objects of all classes that match the interface of the queried class) are also problematic. A query against the base class must be executed as several SQL SELECTs, one for each concrete subclass. You might be able to use a SQL UNION to improve performance by avoiding multiple round trips to the database. But unions are somewhat nonportable and otherwise difficult to work with. NHibernate doesn’t support the use of unions at the time of writing and will always use multiple SQL queries. For a query against the BillingDetails class (for example, restricting to a certain date of creation), NHibernate would use the following SQL:

select CREDIT_CARD_ID, OWNER, NUMBER, CREATED, TYPE, ...
from CREDIT_CARD
where CREATED = ?
select BANK_ACCOUNT_ID, OWNER, NUMBER, CREATED, BANK_NAME, ...
from BANK_ACCOUNT
where CREATED = ?

Notice that a separate query is needed for each concrete subclass:

On the other hand, queries against the concrete classes are trivial and perform well:

select CREDIT_CARD_ID, TYPE, EXP_MONTH, EXP_YEAR
from CREDIT_CARD where CREATED = ?

Note that here, and in other places in this book, we show SQL that is conceptually identical to the SQL executed by NHibernate. The actual SQL may look superficially different.

A further conceptual problem with this mapping strategy is that several different columns of different tables share the same semantics. This makes schema evolution more complex. For example, a change to a base class property type results in changes to multiple columns. It also makes it much more difficult to implement database-integrity constraints that apply to all subclasses.

This mapping strategy doesn’t require any special NHibernate mapping declaration: you create a new <class> declaration for each concrete class, specifying a different table attribute for each. We recommend this approach (only) for the top level of your class hierarchy, where polymorphism isn’t usually required.

3.8.2. Table per class hierarchy

Alternatively, an entire class hierarchy can be mapped to a single table. This table includes columns for all properties of all classes in the hierarchy. The concrete subclass represented by a particular row is identified by the value of a type discriminator column. This approach is shown in figure 3.10.

Figure 3.10. Table-per-class hierarchy mapping

This mapping strategy is a winner in terms of both performance and simplicity. It’s the best-performing way to represent polymorphism—both polymorphic and nonpolymorphic queries perform well—and it’s easy to implement by hand. Ad hoc reporting is possible without complex joins or unions, and schema evolution is straightforward.

There is one major problem: columns for properties declared by subclasses must be declared to be nullable. If your subclasses each define several non-nullable properties, the loss of NOT NULL constraints can be a serious problem from the point of view of data integrity.

In NHibernate, you use the <subclass> element to indicate a table-per-class hierarchy mapping, as in listing 3.8.

Listing 3.8. NHibernate <subclass> mapping

The root class BillingDetails of the inheritance hierarchy is mapped to the table BILLING_DETAILS.

You have to use a special column to distinguish between persistent classes: the discriminator. This isn’t a property of the persistent class; it’s used internally by NHibernate. The column name is BILLING_DETAILS_TYPE, and the values are strings—in this case, "CC" (credit card) or "BA" (bank account). NHibernate automatically sets and retrieves the discriminator values.

Properties of the base class are mapped as always, with a <property> element.

Every subclass has its own <subclass> element. Properties of a subclass are mapped to columns in the BILLING_DETAILS table. Remember that not-null constraints aren’t allowed, because a CreditCard instance won’t have a BankSwift property, and the BANK_ACCOUNT_BANK_SWIFT field must be null for that row.

The <subclass> element can in turn contain other <subclass> elements, until the whole hierarchy is mapped to the table. A <subclass> element can’t contain a <joined-subclass> element. (The <joined-subclass> element is used in the specification of the third mapping option: one table per subclass. This option is discussed in the next section.) The mapping strategy can’t be switched any more at this point.

Here are the classes mapped using NHibernate.Mapping.Attributes:

[Class(Table="BILLING_DETAILS", DiscriminatorValue="BD")]
public class BillingDetails {
[Id(Name="Id", Column="BILLING_DETAILS_ID")]
[Generator(1, Class="native")]
[Discriminator(2, Column="BILLING_DETAILS_TYPE"
TypeType=typeof(string))]
public long Id { ... }
[Property(Column="OWNER")]
public string Name { ... }
[Subclass(DiscriminatorValue="CC")]
public class CreditCard : BillingDetails {
[Property(Column="CREDIT_CARD_TYPE")]
public CreditCardType Type { ... }
}
//...
}

Remember that when you want to specify a class in the mapping, you can add "Type" to the element’s name; for the attribute [Discriminator], you use TypeType. Note that this attribute can be written before any property because it isn’t linked to any field/property of the class (if there is more than this attribute on the property, make sure it comes after the [Id] and before the other attributes).

NHibernate will use the following SQL when querying the BillingDetails class:

select BILLING_DETAILS_ID, BILLING_DETAILS_TYPE,
OWNER, ..., CREDIT_CARD_TYPE,
from BILLING_DETAILS
where CREATED = ?

To query the CreditCard subclass, NHibernate uses a condition on the discriminator:

select BILLING_DETAILS_ID,
CREDIT_CARD_TYPE, CREDIT_CARD_EXP_MONTH, ...
from BILLING_DETAILS
where BILLING_DETAILS_TYPE='CC' and CREATED = ?

How could it be any simpler than that?

Instead of having a discriminator field, it’s possible to use an arbitrary SQL formula. For example:

<discriminator type="String"
formula="case when CREDIT_CARD_TYPE is null then 'BD' else 'CC' end"
/>

Here, you use the column CREDIT_CARD_TYPE to evaluate the type.

Now, let’s discover the alternative to a table-per-class-hierarchy.

3.8.3. Table per subclass

The third option is to represent inheritance relationships as relational foreign-key associations. Every subclass that declares persistent properties—including abstract classes and even interfaces—has its own table.

Unlike the strategy that uses a table per concrete class, the table here contains columns only for each non-inherited property (each property declared by the subclass) along with a primary key that is also a foreign key of the base class table. This approach is shown in figure 3.11.

Figure 3.11. Table-per-subclass mapping

If an instance of the CreditCard subclass is made persistent, the values of properties declared by the BillingDetails base class are persisted to a new row of the BILLING_ DETAILS table. Only the values of properties declared by the subclass are persisted to the new row of the CREDIT_CARD table. The two rows are linked together by their shared primary-key value. Later, you can retrieve the subclass instance from the database by joining the subclass table with the base class table.

The primary advantage of this strategy is that the relational model is completely normalized. Schema evolution and integrity-constraint definition are straightforward. A polymorphic association to a particular subclass may be represented as a foreign key pointing to the table of that subclass.

In NHibernate, you use the <joined-subclass> element to indicate a table-per-subclass mapping (see listing 3.9).

Listing 3.9. NHibernate <joined-subclass> mapping

Again, the root class BillingDetails is mapped to the BILLING_DETAILS table. Note that no discriminator is required with this strategy.

The new <joined-subclass> element is used to map a subclass to a new table (in this example, CREDIT_CARD). All properties declared in the joined subclass are mapped to this table. Note that we intentionally left out the mapping example for BankAccount, which is similar to CreditCard.

A primary key is required for the CREDIT_CARD table; it also has a foreign-key constraint to the primary key of the BILLING_DETAILS table. A CreditCard object lookup will require a join of both tables.

A <joined-subclass> element may contain other <joined-subclass> elements but not a <subclass> element. NHibernate doesn’t support mixing of these two mapping strategies.

NHibernate will use an outer join when querying the BillingDetails class:

select BD.BILLING_DETAILS_ID, BD.OWNER, BD.NUMER, BD.CREATED,
CC.TYPE, ..., BA.BANK_SWIFT, ...
case
when CC.CREDIT_CARD_ID is not null then 1
when BA.BANK_ACCOUNT_ID is not null then 2
when BD.BILLING_DETAILS_ID is not null then 0
end as TYPE
from BILLING_DETAILS BD
left join CREDIT_CARD CC on
BD.BILLING_DETAILS_ID = CC.CREDIT_CARD_ID
left join BANK_ACCOUNT BA on
BD.BILLING_DETAILS_ID = BA.BANK_ACCOUNT_ID
where BD.CREATED = ?

The SQL case statement uses the existence (or nonexistence) of rows in the subclass tables CREDIT_CARD and BANK_ACCOUNT to determine the concrete subclass for a particular row of the BILLING_DETAILS table.

To narrow the query to the subclass, NHibernate uses an inner join instead:

select BD.BILLING_DETAILS_ID, BD.OWNER, BD.CREATED, CC.TYPE, ...
from CREDIT_CARD CC
inner join BILLING_DETAILS BD on
BD.BILLING_DETAILS_ID = CC.CREDIT_CARD_ID
where CC.CREATED = ?

As you can see, this mapping strategy is more difficult to implement by hand—even ad hoc reporting will be more complex. This is an important consideration if you plan to mix NHibernate code with handwritten SQL/ADO.NET. (For ad hoc reporting, database views provide a way to offset the complexity of the table-per-subclass strategy. A view may be used to transform the table-per-subclass model into the much simpler table-per-hierarchy model.)

Even though this mapping strategy is deceptively simple, our experience is that performance may be unacceptable for complex class hierarchies. Queries always require either a join across many tables or many sequential reads. The problem should be recast as how to choose an appropriate combination of mapping strategies for an application’s class hierarchies. A typical domain model design has a mix of interfaces and abstract classes.

3.8.4. Choosing a strategy

You can apply all mapping strategies to abstract classes and interfaces. Interfaces may have no state but may contain property declarations, so they can be treated like abstract classes. You can map an interface using <class>, <subclass>, or <joined-subclass>; and you can map any declared or inherited property using <property>. NHibernate won’t try to instantiate an abstract class, even if you query or load it.

Here are some rules of thumb:

  • If you don’t require polymorphic associations or queries, lean toward the table-per-concrete-class strategy. If you require polymorphic associations (an association to a base class, hence to all classes in the hierarchy with dynamic resolution of the concrete class at runtime) or queries, and subclasses declare relatively few properties (particularly if the main difference between subclasses is in their behavior), lean toward the table-per-class-hierarchy model.
  • If you require polymorphic associations or queries, and subclasses declare many properties (subclasses differ mainly by the data they hold), lean toward the table-per-subclass approach.

By default, choose table-per-class-hierarchy for simple problems. For more complex cases (or when you’re overruled by a data modeler insisting upon the importance of nullability constraints), you should consider the table-per-subclass strategy. But at that point, ask yourself whether it might be better to remodel inheritance as delegation in the object model. Complex inheritance is often best avoided for all sorts of reasons unrelated to persistence or ORM. NHibernate acts as a buffer between the object and relational models, but that doesn’t mean you can completely ignore persistence concerns when designing your object model.

Note that you may also use <subclass> and <joined-subclass> mapping elements in a separate mapping file (as a top-level element, instead of <class>). You then have to declare the class that is extended (for example, <subclass name="CreditCard" extends="BillingDetails">), and the base-class mapping must be loaded before the subclass mapping file. This technique allows you to extend a class hierarchy without modifying the mapping file of the base class. Using NHibernate.Mapping.Attributes, you can move the implementation of CreditCard to another file and map it like this:

[Subclass(ExtendsType=typeof(BillingDetails), DiscriminatorValue="CC")]
public class CreditCard : BillingDetails {
//...
}

3.9. Summary

In this chapter, we focused on the structural aspect of the object/relational paradigm mismatch and discussed the first four generic ORM problems. We explored the programming model for persistent classes and the NHibernate ORM metadata for fine-grained classes, object identity, inheritance, and associations.

You now understand that persistent classes in a domain model should be free of cross-cutting concerns such as transactions and security. Even persistence-related concerns shouldn’t leak into the domain model. We no longer entertain the use of restrictive programming models such as DataSets for our domain model. Instead, we use transparent persistence, together with the unrestrictive POCO programming model—which is really a set of best practices for the creation of properly encapsulated .NET types.

You also learned about the important differences between entities and value-typed objects in NHibernate. Entities have their own identity and lifecycle, whereas value-typed objects are dependent on an entity and are persisted with by-value semantics. NHibernate allows fine-grained object models with fewer tables than persistent classes.

Finally, we introduced the three well-known inheritance-mapping strategies in NHibernate. We also covered associations and collections mapping; and you implemented and mapped your first parent/child association between persistent classes, using database foreign key fields and the cascading of operations.

With this understanding, you can experiment with NHibernate and handle most common mapping scenarios, and perhaps some of the thornier ones too. As you become familiar with creating domain models and persisting them with NHibernate, you may face other architectural challenges. We next investigate the dynamic aspects of the object/relational mismatch, including a much deeper study of the cascaded operations we introduced and the lifecycle of persistent objects.