Chapter 6. Advanced mapping concepts – NHibernate in Action

Chapter 6. Advanced mapping concepts

This chapter covers

  • The NHibernate type system
  • Custom mapping types
  • Collection mappings
  • One-to-one and many-to-many associations

In chapter 3, we introduced the most important ORM features provided by NHibernate, including basic class and property mappings, inheritance mappings, component mappings, and one-to-many association mappings. We now extend these topics by turning to the more exotic collection and association mappings that allow you to handle trickier use cases. It’s worth noting that these more exotic mappings should only be used with careful consideration; it’s possible to implement any domain model using simpler component mappings (one-to-many associations and one-to-one associations). Throughout this chapter, we advise you about when you may or may not want to use the advanced features as they’re discussed.

Some of these features require you to have a more in-depth understanding of NHibernate’s type system, particularly the distinction between entity and value types. That is where we begin.

6.1. Understanding the NHibernate type system

We first distinguished between entity and value types back in section 3.6.1. In order to give you a better understanding of the NHibernate type system, we elaborate a little more.

Entities are the coarse-grained classes in a system. You usually define the features of a system in terms of the entities involved: “The user places a bid for an item” is a typical feature definition that mentions three entities—user, bid, and item. In contrast, value types are the much more fine-grained classes in a system, such as strings, numbers, dates, and monetary amounts. These fine-grained classes can be used in many places and serve many purposes; the value-type string can store email address, user-names, and many other things. Strings are simple value types, but it’s possible (but less common) to create value types that are more complex. For example, a value type like an address can contain several fields.

How do you differentiate between value types and entities? From a more formal standpoint, we can say an entity is any class whose instances have their own persistent identity, and a value type is a class whose instances don’t. The entity instances may therefore be in any of the three persistent lifecycle states: transient, detached, or persistent. But we don’t consider these lifecycle states to apply to the simpler value-type instances. Furthermore, because entities have their own lifecycle, the Save() and Delete() methods of the NHibernate ISession interface apply to them, but never to value-type instances. To illustrate, consider figure 6.1.

Figure 6.1. An order entity with a TotalAmount value type

TotalAmount is an instance of value type Money. Because value types are completely bound to their owning entities, TotalAmount is saved only when the Order is saved.

6.1.1. Associations and value types

As we said, not all value types are simple. It’s possible for value types to also define associations. For example, the Money value type may have a property called Currency that is an association to a Currency entity, as shown in figure 6.2

Figure 6.2. The Money value type with an association to a Currency entity

If your value types have associations, they must always point to entities. The reason is that, if those associations could point from entities to value types, a value type could potentially belong to several entities, which isn’t desirable. This is one of the great things about value types; if you update a value-type instance, you know that it affects only the entity that owns it. For example, changing the TotalAmount of one Order can’t accidentally affect others.

So far, we’ve talked about value types and entities from an object-oriented perspective. To build a more complete picture, we now look at how the relational model sees value types and entities, and how NHibernate bridges the gap.

6.1.2. Bridging from objects to database

You may be aware that a database architect sees the world of value types and entities slightly differently from this object-oriented view of things. In the database, tables represent the entities, and columns represent the values. Even join tables and lookup tables are entities.

If all tables represent entities in the database, does that mean you have to map all tables to entities in your .NET domain model? What about those value types you want in your model? NHibernate provides constructs for dealing with this. For example, a many-to-many association mapping hides the intermediate association table from the application, so you don’t end up with an unwanted entity in your domain model. Similarly, a collection of value-typed strings behaves like a value type from the point of view of the .NET domain model even though it’s mapped to its own table in the database.

These features have their uses and can often simplify your C# code. But over time, we’ve become suspicious of them; these “hidden” entities often end up needing exposure in applications as business requirements evolve. The many-to-many association table, for example, often has columns added as the application matures, so the relationship itself becomes an entity. You may not go far wrong if you make every database-level entity be exposed to the application as an entity class. For example, we’d be inclined to model the many-to-many association as two one-to-many associations to an intervening entity class. We’ll leave the final decision to you and return to the topic of many-to-many entity associations later in this chapter.

6.1.3. Mapping types

So far, we’ve discussed the differences between value types and entities as seen from the object-oriented and relational-database perspectives. You know that mapping entities is straightforward—entity classes are always mapped to database tables using <class>, <subclass>, and <joined-subclass> mapping elements.

Value types need something more, which is where mapping types enter the picture. Consider this mapping of the CaveatEmptor User and email address:

<property
name="Email"
column="EMAIL"
type="String"/>

In ORM, you have to worry about both .NET types and SQL data types. In this example, imagine that the Email field is a .NET string, and the EMAIL column is a SQL varchar. You want to tell NHibernate know how to carry out this conversion, which is where NHibernate mapping types come in. In this case, you specified the mapping type "String", which is appropriate for this particular conversion.

The String mapping type isn’t the only one built into NHibernate; NHibernate comes with various mapping types that define default persistence strategies for primitive .NET types and certain classes, such as DateTime.

6.1.4. Built-in mapping types

NHibernate’s built-in mapping types usually reflect the name of the .NET type they map. Sometimes you’ll have a choice of mapping types available to map a particular .NET type to the database. But the built-in mapping types aren’t designed to perform arbitrary conversions, such as mapping a VARCHAR field value to a .NET Int32 property value. If you want this kind of functionality, you have to define your own custom value types. We get to that topic a little later in this chapter.

We now discuss the basic types—date and time, objects, large objects, and various other built-in mapping types—and show you what .NET and System.Data.DbType data types they handle. DbTypes are used to infer the data-provider types (hence SQL data types).

.NET Primitive Mapping Types

The basic mapping types in table 6.1 map .NET primitive types to appropriate DbTypes.

Table 6.1. Primitive types

Mapping type

.NET type

System.Data.DbType

Int16

System.Int16

DbType.Int16

Int32

System.Int32

DbType.Int32

Int64

System.Int64

DbType.Int64

Single

System.Single

DbType.Single

Double

System.Double

DbType.Double

Decimal

System.Decimal

DbType.Decimal

Byte

System.Byte

DbType.Byte

Char

System.Char

DbType.StringFixedLength—one character

AnsiChar

System.Char

DbType.AnsiStringFixedLength—one character

Boolean

System.Boolean

DbType.Boolean

Guid

System.Guid

DbType.Guid

PersistentEnum

System.Enum (an enumeration)

DbType for the underlying value

TrueFalse

System.Boolean

DbType.AnsiStringFixedLength—either 'T' or 'F'

YesNo

System.Boolean

DbType.AnsiStringFixedLength—either 'Y' or 'N'

You’ve probably noticed that your database doesn’t support some of the DbTypes listed in table 6.1. But ADO.NET provides a partial abstraction of vendor-specific SQL data types, allowing NHibernate to work with ANSI-standard types when executing Data Manipulation Language (DML). For database-specific DDL generation, NHibernate translates from the ANSI-standard type to an appropriate vendor-specific type, using the built-in support for specific SQL dialects. (You usually don’t have to worry about SQL data types if you’re using NHibernate for data access and data schema definition.)

NHibernate supports a number of mapping types coming from Hibernate for compatibility (useful for those coming over from Hibernate or using Hibernate tools to generate hbm.xml files). Table 6.2 lists the additional names of NHibernate mapping types.

Table 6.2. Additional names of NHibernate mapping types

Mapping type

Additional name

Mapping type

Additional name

Binary

binary

Int16

short

Boolean

boolean

Int32

int

Byte

byte

Int32

integer

Character

character

Int64

long

CultureInfo

locale

Single

float

DateTime

datetime

String

string

Decimal

big_decimal

TrueFalse

true_false

Double

double

Type

class

Guid

guid

YesNo

yes_no

From this table, you can see that writing type="integer" or type="int" is identical to type="Int32". Note that this table contains many mapping types that will be discussed in the following sections.

Date/Time Mapping Types

Table 6.3 lists NHibernate types associated with dates, times, and timestamps. In your domain model, you may choose to represent date and time data using either System.DateTime or System.TimeSpan. Because they have different purposes, the choice should be easy.

Table 6.3. Date and time types

Mapping type

.NET type

System.Data.DbType

DateTime

System.DateTime

DbType.DateTime—ignores milliseconds

Ticks

System.DateTime

DbType.Int64

TimeSpan

System.TimeSpan

DbType.Int64

Timestamp

System.DateTime

DbType.DateTime—as specific as the database supports

Object Mapping Types

All of the .NET types in tables 6.1 and 6.3 are value types (derived from System.Value-Type). This means they can’t be null unless you use the .NET 2.0 Nullable<T> structure or the Nullables add-in, as discussed in the next section. Table 6.4 lists NHibernate types for handling .NET types derived from System.Object (which can store null values).

Table 6.4. Nullable object types

Mapping type

.NET type

System.Data.DbType

String

System.String

DbType.String

AnsiString

System.String

DbType.AnsiString

This table is completed by tables 6.5 and 6.6, which also contain nullable mapping types.

Table 6.5. Binary and large object types

Mapping type

.NET type

System.Data.DbType

Binary

System.Byte[]

DbType.Binary

BinaryBlob

System.Byte[]

DbType.Binary

StringClob

System.String

DbType.String

Serializable

Any System.Object marked with SerializableAttribute

DbType.Binary

Table 6.6. Other CLR-related types

Mapping type

.NET type

System.Data.DbType

CultureInfo

System.Globalization.CultureInfo

DbType.String—five characters for culture

Type

System.Type

DbType.String holding the Assembly Qualified Name

Large Object Mapping Types

Table 6.5 lists NHibernate types for handling binary data and large objects. Note that none of these types may be used as the type of an identifier property.

BinaryBlob and StringClob are mainly supported by SQL Server. They can have a large size and are fully loaded in memory. This can be a performance killer if you use these types to store large objects—use this feature carefully. Note that you must set the NHibernate property prepare_sql to true to enable this feature.

You can find up-to-date design patterns and tips for large object usage on the NHibernate website.

Various CLR Mapping Types

Table 6.6 lists NHibernate types for various other types of the CLR that may be represented as DbType.Strings in the database.

Certainly, <property> isn’t the only NHibernate mapping element that has a type attribute.

6.1.5. Using mapping types

All of the basic mapping types may appear almost anywhere in the NHibernate mapping document, on normal property, identifier property, and other mapping elements. The <id>, <property>, <version>, <discriminator>, <index>, and <element> elements all define an attribute named type. (There are certain limitations on which mapping basic types may function as an identifier or discriminator type.)

You can see how useful the built-in mapping types are in this mapping for the BillingDetails class:

<class name="BillingDetails"
table="BILLING_DETAILS"
lazy="false"
discriminator-value="0">
<id name="Id" type="Int32" column="BILLING_DETAILS_ID">
<generator class="native"/>
</id>
<discriminator type="Char" column="TYPE"/>
<property name="Number" type="String"/>
...
</class>

The BillingDetails class is mapped as an entity. Its discriminator, id, and Number properties are value typed, and you use the built-in NHibernate mapping types to specify the conversion strategy.

It’s often not necessary to explicitly specify a built-in mapping type in the XML mapping document. For instance, if you have a property of .NET type System.String, NHibernate will discover this using reflection and select String by default. You can easily simplify the previous mapping example:

<class name="BillingDetails"
table="BILLING_DETAILS"
lazy="false"
discriminator-value="0">
<id name="Id" column="BILLING_DETAILS_ID">
<generator class="native"/>
</id>
<discriminator type="Char" column="TYPE"/>
<property name="Number"/>
...
</class>

For each of the built-in mapping types, a constant is defined by the class NHibernate. NHibernateUtil. For example, NHibernate.String represents the String mapping type. These constants are useful for query-parameter binding, as discussed in more detail in chapter 8:

session.CreateQuery("from Item i where i.Description like :desc")
.SetParameter("desc", desc, NHibernate.String)
.List();

These constants are also useful for programmatic manipulation of the NHibernate mapping metamodel, as discussed in chapter 3.

Of course, NHibernate isn’t limited to the built-in mapping types; you can create your own custom mapping types for handling certain scenarios. We look this next and explain how the mapping type system is central to NHibernate’s flexibility.

Creating Custom Mapping Types

Object-oriented languages like C# make it easy to define new types by writing new classes. Indeed, this is a fundamental part of the definition of object orientation. If you were limited to the predefined built-in NHibernate mapping types when declaring properties of persistent classes, you’d lose much of C#’s expressiveness. Furthermore, your domain model implementation would be tightly coupled to the physical data model, because new type conversions would be impossible. In order to avoid that, NHibernate provides a powerful feature called custom mapping types.

NHibernate provides two user-friendly interfaces that applications can use when defining new mapping types. The first is NHibernate.UserTypes.IUserType. IUserType is suitable for most simple cases and even for some more complex problems. Let’s use it in a simple scenario.

The Bid class defines an Amount property, and the Item class defines an InitialPrice property; both are monetary values. So far, you’ve only used a System.Double to represent the value, mapped with Double to a single DbType.Double column.

Suppose you want to support multiple currencies in the auction application and that you have to refactor the existing domain model for this change. One way to implement this change is to add new properties to Bid and Item: AmountCurrency and InitialPriceCurrency. You can then map these new properties to additional VARCHAR columns with the built-in String mapping type. If you have currency stored in 100 places, this is a lot of changes. We hope you never use this approach!

Creating an Implementation f Iusertype

Instead, you should create a MonetaryAmount class that encapsulates both currency and amount. This is a class of the domain model and doesn’t have any dependency on NHibernate interfaces:

[Serializable]
public class MonetaryAmount
{
private readonly double value;
private readonly string currency;
public MonetaryAmount(double value, string currency)
{
this.value = value;
this.currency = currency;
}
public double Value { get { return value; } }
public string Currency { get { return currency; } }
public override bool Equals(object obj) { ... }
public override int GetHashCode() { ... }
}

You also make life simpler by making MonetaryAmount an immutable class, meaning it can’t be changed after it’s instantiated. You have to implement Equals() and GetHashCode() to complete the class—but there is nothing special to consider here aside from the facts that they must be consistent, and GetHashCode() should return mostly unique numbers.

You’ll use this new MonetaryAmount to replace the Double, as defined on the InitialPrice property for Item. You’ll benefit from using this new class in other places, such as the Bid.Amount.

The next challenge is in mapping the new MonetaryAmount properties to the database. Suppose you’re working with a legacy database that contains all monetary amounts in USD. The new class means the application code is no longer restricted to a single currency, but it will take time for the database team to make the changes. Until this happens, you’d like to store just the Amount property of MonetaryAmount to the database. Because you can’t store the currency yet, you’ll convert all Amounts to USD before you save them and from USD when you load them.

The first step in handling this is to tell NHibernate how to handle the MonetaryAmount type. To do so, you create a MonetaryAmountUserType class that implements the NHibernate interface IUserType. The custom mapping type is shown in listing 6.1.

Listing 6.1. Custom mapping type for monetary amounts in USD
using System;
using System.Data;
using NHibernate.UserTypes;
public class MonetaryAmountUserType : IUserType {
private static readonly NHibernate.SqlTypes.SqlType[] SQL_TYPES =
{ NHibernateUtil.Double.SqlType };
public NHibernate.SqlTypes.SqlType[] SqlTypes {
get { return SQL_TYPES; }
}
public Type ReturnedType { get { return typeof(MonetaryAmount); } }
public new bool Equals( object x, object y ) {
if ( object.ReferenceEquals(x,y) ) return true;
if (x == null || y == null) return false;
return x.Equals(y);
}
public object DeepCopy(object value) { return value; }
public bool IsMutable { get { return false; } }
public object NullSafeGet(IDataReader dr,
string[] names,
object owner
){
object obj = NHibernateUtil.Double.NullSafeGet(dr, names[0]);
if ( obj==null ) return null;
double valueInUSD = (double) obj;
return new MonetaryAmount(valueInUSD, "USD");
}
public void NullSafeSet(IDbCommand cmd, object obj, int index) {
if (obj == null) {
((IDataParameter)cmd.Parameters[index]).Value = DBNull.Value;
} else {
MonetaryAmount anyCurrency = (MonetaryAmount)obj;
MonetaryAmount amountInUSD =
MonetaryAmount.Convert( anyCurrency, "USD" );

((IDataParameter)cmd.Parameters[index]).Value = amountInUSD.Value;
}
}
public static MonetaryAmount Convert( MonetaryAmount m,
string targetCurrency)
{
//...
}
}

The SqlTypes property tells NHibernate what SQL column types to use for DDL schema generation . The types are subclasses of NHibernate.SqlTypes.SqlType. Notice that this property returns an array of types. An implementation of IUserType may map a single property to multiple columns, but the legacy data model has only a single Double.

ReturnedType tells NHibernate what .NET type is mapped by this IUserType .

The IUserType is responsible for dirty-checking property values . The Equals() method compares the current property value to a previous snapshot and determines whether the property is dirty and must be saved to the database.

The IUserType is also partially responsible for creating the snapshot in the first place . Because MonetaryAmount is an immutable class, the DeepCopy() method returns its argument. In the case of a mutable type, it would need to return a copy of the argument to be used as the snapshot value. This method is also called when an instance of the type is written to or read from the second-level cache.

NHibernate can make some minor performance optimizations for immutable types. The IsMutable property tells NHibernate that this type is immutable.

The NullSafeGet() method retrieves the property value from the ADO.NET IDataReader. You can also access the owner of the component if you need it for the conversion. All database values are in USD, so you have to convert the MonetaryAmount returned by this method before you show it to the user.

The NullSafeSet() method writes the property value to the ADO.NET IDbCommand . This method takes whatever currency is set and converts it to a simple Double USD value before saving.

Note that, for briefness, we haven’t provided a Convert function . If you were to implement it, it would have code that converts between various currencies.

You can map the InitialPrice property of Item as follows:

<property name="InitialPrice"
column="INITIAL_PRICE"
type="NHibernate.Auction.CustomTypes.MonetaryAmountUserType,
NHibernate.Auction"/>

This is the simplest kind of transformation that an implementation of IUserType can perform. It takes a value-type class and maps it to a single database column. Much more sophisticated things are possible; a custom mapping type can perform validation, it can read and write data to and from an Active Directory, or it can even retrieve persistent objects from a different NHibernate ISession for a different database. You’re limited mainly by your imagination and performance concerns.

In a perfect world, you’d represent both the amount and currency of the monetary amounts in the database, so you’re not limited to storing USD. You can use an IUserType for this, but it’s limited; if an IUserType is mapped with more than one property, you can’t use those properties in your HQL or Criteria queries. The NHibernate query engine won’t know anything about the individual properties of MonetaryAmount. You can access the properties in your C# code (MonetaryAmount is a regular class of the domain model, after all), but not in NHibernate queries.

To allow for a custom value type with multiple properties that can be accessed in queries, you can use the ICompositeUserType interface. This interface exposes the properties of the MonetaryAmount to NHibernate.

Creating an Implementation of Icompositeusertype

To demonstrate the flexibility of custom mapping types, you won’t have to change the MonetaryAmount domain model class at all—you’ll change only the custom mapping type, as shown in listing 6.2.

Listing 6.2. Custom mapping type for monetary amounts in new database schemas
using System;
using System.Data;
using NHibernate.UserTypes;
public class MonetaryAmountCompositeUserType : ICompositeUserType {
public Type ReturnedClass { get { return typeof(MonetaryAmount); } }
public new bool Equals( object x, object y ) {
if ( object.ReferenceEquals(x,y) ) return true;
if (x == null || y == null) return false;
return x.Equals(y);
}
public object DeepCopy(object value) { return value; }
public bool IsMutable { get { return false; } }
public object NullSafeGet(IDataReader dr, string[] names,
NHibernate.Engine.ISessionImplementor session, object owner) {
object obj0 = NHibernateUtil.Double.NullSafeGet(dr, names[0]);
object obj1 = NHibernateUtil.String.NullSafeGet(dr, names[1]);
if ( obj0==null || obj1==null ) return null;
double value = (double) obj0;
string currency = (string) obj1;
return new MonetaryAmount(value, currency);
}
public void NullSafeSet(IDbCommand cmd, object obj, int index,
NHibernate.Engine.ISessionImplementor session) {
if (obj == null) {
((IDataParameter)cmd.Parameters[index]).Value = DBNull.Value;
((IDataParameter)cmd.Parameters[index+1]).Value = DBNull.Value;
} else {
MonetaryAmount amount = (MonetaryAmount)obj;
((IDataParameter)cmd.Parameters[index]).Value = amount.Value;
((IDataParameter)cmd.Parameters[index+1]).Value = amount.Currency;
}
}
public string[] PropertyNames {
get { return new string[] { "Value", "Currency" }; }
}
public NHibernate.Type.IType[] PropertyTypes {
get { return new NHibernate.Type.IType[] {
NHibernateUtil.Double, NHibernateUtil.String }; }
}
public object GetPropertyValue(object component, int property) {
MonetaryAmount amount = (MonetaryAmount) component;
if (property == 0)
return amount.Value;
else
return amount.Currency;
}
public void SetPropertyValue(object comp, int property,
object value) {
throw new Exception("Immutable!");
}
public object Assemble(object cached,
NHibernate.Engine.ISessionImplementor session, object owner) {
return cached;
}
public object Disassemble(object value,
NHibernate.Engine.ISessionImplementor session) {
return value;
}
}

The implementation of ICompositeUserType has its own properties, defined by PropertyNames . Similarly, the properties each have their own type, as defined by PropertyTypes .

The GetPropertyValue() method returns the value of an individual property of the MonetaryAmount. Because MonetaryAmount is immutable, you can’t set property values individually . This isn’t a problem because this method is optional.

The Assemble() method is called when an instance of the type is read from the second-level cache . The Disassemble() method is called when an instance of the type is written to the second-level cache .

The order of properties must be the same in the PropertyNames, PropertyTypes, and GetPropertyValues() methods. The InitialPrice property now maps to two columns, so you declare both in the mapping file. The first column stores the value; the second stores the currency of the MonetaryAmount. Note that the order of columns must match the order of properties in your type implementation:

<property name="InitialPrice"
type="NHibernate.Auction.CustomTypes.MonetaryAmountCompositeUserType,
NHibernate.Auction">
<column name="INITIAL_PRICE"/>
<column name="INITIAL_PRICE_CURRENCY"/>
</property>

In a query, you can now refer to the Amount and Currency properties of the custom type, even though they don’t appear anywhere in the mapping document as individual properties:

from Item i
where i.InitialPrice.Value > 100.0
and i.InitialPrice.Currency = 'XAF'

In this example, you’ve expanded the buffer between the .NET object model and the SQL database schema with your custom composite type. Both representations can now handle changes more robustly.

If implementing custom types seems complex, relax; you’ll rarely need to use a custom mapping type. An alternative way to represent the MonetaryAmount class is to use a component mapping, as in section 4.4.2. The decision to use a custom mapping type is often a matter of taste.

You can use a few more interfaces to implement custom types; they’re introduced in the next section.

Other Interfaces to Create Custom Mapping Types

You may find that the interfaces IUserType and ICompositeUserType don’t let you easily add more features to your custom types. In this case, you’ll need to use some of the other interfaces that are in the NHibernate.UserTypes namespace: IParameterizedType, IEnhancedUserType, INullableUserType, and IUserCollectionType.

The IParameterizedType interface allows you to supply parameters to your custom type in the mapping file. This interface has a unique method, SetParameterValues(IDictionary parameters), that’s called at the initialization of your type. Here is an example of mapping providing a parameter:

<property name="Price">
<type name="NHibernate.Auction.CustomTypes.MonetaryAmountUserType">
<param name="DefaultCurrency">Euro</param>
</type>
</property>

This mapping tells the custom type to use Euro as the currency if it isn’t specified.

The IEnhancedUserType interface makes it possible to implement a custom type that can be marshalled to and from its string representation. This functionality allows this type to be used as an identifier or a discriminator type. To create a type that can be used as version, you must implement the IUserVersionType interface.

The INullableUserType interface lets you interpret non-null values in a property as null in the database. When you use dynamic-insert or dynamic-update, fields identified as null aren’t inserted or updated. This information may also be used when generating the WHERE clause of the SQL command when optimistic locking is enabled.

The last interface is different from the others because it’s meant to implement user-defined collection types: IUserCollectionType. For more details, see the implementation NHibernate.Test.UserCollection.MyListType in the NHibernate source code.

Now, let’s look at an extremely important application of custom mapping types. Nullable types are found in almost all enterprise applications.

Using Nullable Types

With .NET 1.1, primitive types couldn’t be null; but this is no longer the case in .NET 2.0. Let’s say you want to add a DismissDate to the class User. As long as a user is active, its DismissDate should be null. But the System.DateTime struct can’t be null. And you don’t want to use some magic value to represent the null state. With .NET 2.0 (and 3.5), you can write

public class User
{
//...
private DateTime? dismissDate;
public DateTime? DismissDate
{
get { return dismissDate; }
set { dismissDate = value; }
}
//...
}

You omit other properties and methods because you focus on the nullable property. No change is required in the mapping.

If you work with .NET 1.1, the Nullables add-in (in the NHibernateContrib package for versions prior to NHibernate 1.2.0) contains a number of custom mapping types that allow primitive types to be null. For the previous case, you can use the Nullables.NullableDateTime class:

using Nullables;
[Class]
public class User {
//...
private NullableDateTime dismissDate;
[Property]
public NullableDateTime DismissDate
{
get { return dismissDate; }
set { dismissDate = value; }
}
//...
}

The mapping is straightforward:

<class name="Example.Person, Example">
...
<property name="DateOfBirth"
type="Nullables.NHibernate.NullableDateTimeType,
Nullables.NHibernate" />
</class>

It’s important to note that, in the mapping, the type of DismissDate must be Nullables. NHibernate.NullableDateTimeType (from the file Nullables.NHibernate.dll). This type is a wrapper used to translate Nullables types from/to database types. But when you use the NHibernate.Mapping.Attributes library, this operation is automatic; that’s why you put the attribute [Property].

The NullableDateTime type behaves exactly like System.DateTime; there are even implicit operators to easily interact with it. The Nullables library contains nullable types for most .NET primitive types supported by NHibernate. You can find more details in the NHibernate documentation.

Using Enumerated Types

An enumeration (enum) is a special form of value type, which inherits from System. Enum and supplies alternate names for the values of an underlying primitive type.

For example, the Comment class defines a Rating. If you recall, in the CaveatEmptor application, users can give comments about other users. Instead of using a simple int property for the rating, you create an enumeration:

public enum Rating {
Excellent,
Ok,
Low
}

You then use this type for the Rating property of the Comment class. In the database, ratings are represented as the type of the underlying value. In this case (and by default), it’s Int32. And that’s all you have to do. You may specify type="Rating" in your mapping, but it’s optional; NHibernate can use reflection to find this.

One problem you may run into is using enumerations in NHibernate queries. Consider the following query in HQL that retrieves all comments rated Low:

IQuery q =
session.CreateQuery("from Comment c where c.Rating = Rating.Low");

This query doesn’t work, because NHibernate doesn’t know what to do with Rating. Low and will try to use it as a literal. You have to use a bind parameter and set the rating value for the comparison dynamically (which is what you need for other reasons most of the time):

IQuery q =
session.CreateQuery("from Comment c where c.Rating = :rating");
q.SetParameter("rating",
Rating.Low,
NHibernateUtil.Enum(typeof(Rating));

The last line in this example uses the static helper method NHibernateUtil.Enum() to define the NHibernate Type, a simple way to tell NHibernate about the enumeration mapping and how to deal with the Rating.Low value.

We’ve now discussed all kinds of NHibernate mapping types: built-in mapping types, user-defined custom types, and even components (chapter 4). They’re all considered value types, because they map objects of value type (not entities) to the database. With a good understanding of what value types are and how they’re mapped, we can now move on to the more complex issue of collections of value-typed instances.

6.2. Mapping collections of value types

In chapter 4, we introduced using collections to represent entity relationships. We explained how, for example, an Item can have a collection of Bids in the CaveatEmptor application. Collections aren’t limited to entity types, and this section will focus on how you create mappings where collections store instances of a value type. We start this section by showing you how to use basic collections to contain simple value types, such as a list of strings or DateTimes. We then move on to how you work with ordered and sorted collections. Finally, we discuss how you can map collections of components, along with the possible pitfalls and how they can be dealt with. You’ll see many hands-on code samples along the way (please note that all the mappings in this section work with both .NET 1.1 collections and .NET 2.0 generics).

6.2.1. Storing value types in sets, bags, lists, and maps

Suppose your sellers can attach images to Items. An image is accessible only via the containing item; it doesn’t need to support associations to any other entity in your system. In this case, it’s reasonable to model the image as a value type. Item has a collection of images that NHibernate considers to be part of the Item, without their own persistence lifecycle. In this example scenario, let’s assume that images are stored as files on the filesystem rather than as BLOBs in the database, and that you store filenames in the database to record what images each Item has. We now walk through various ways this can be implemented using NHibernate, starting with the simplest implementation: the set.

Using a Set

The simplest implementation is an ISet of string filenames. As a reminder, ISet is a container that only disallows duplicate objects and is available in the Iesi.Collections library. To store the images against the Item using an ISet, you add a collection property to the Item class as follows:

using Iesi.Collections.Generic;
private ISet<string> images = new HashedSet<string>();
[Set(Lazy=true, Table="ITEM_IMAGE")]
[Key(1, Column="ITEM_ID")]
[Element(2, TypeType=typeof(string), Column="FILENAME", NotNull=true)]
public ISet<string> Images {
get { return this.images; }
set { this.images = value; }
}

Here is the corresponding XML mapping:

<set name="Images" lazy="true" table="ITEM_IMAGE">
<key column="ITEM_ID"/>
<element type="String" column="FILENAME" not-null="true"/>
</set>

In the database, image file names are stored in a table called ITEM_IMAGE and linked to their owner through ITEM_ID. From the database perspective, you have two entities. But NHibernate hides this fact so you can present Images as merely a part of Item. The <key> element declares the foreign key, ITEM_ID, of the parent entity. The <element> tag declares this collection as a collection of value-type instances: in this case, of strings.

As you may recall, a set can’t contain duplicate elements, so the primary key of the ITEM_IMAGE table consists of both columns in the <set> declaration: ITEM_ID and FILENAME. See figure 6.3 for a table schema example.

Figure 6.3. Table structure and example data for a collection of strings

It doesn’t seem likely that you would allow the user to attach the same image more than once, but suppose you did. What kind of mapping would be appropriate then?

Using a Bag

An unordered collection that permits duplicate elements is called a bag. Curiously, the .NET framework doesn’t define an IBag interface. NHibernate lets you use an IList in .NET to simulate bag behavior; this is consistent with common usage in the .NET community. To use a bag, change the type of Images in Item from ISet to IList, probably using ArrayList as an implementation.

Changing the table definition from the previous section to permit duplicate FILENAMEs requires a different primary key. You use an <idbag> mapping to attach a surrogate key column to the collection table, much like the synthetic identifiers you use for entity classes:

[IdBag(Lazy=true, Table="ITEM_IMAGE")]
[CollectionId(1, TypeType=typeof(int), Column="ITEM_IMAGE_ID")]
[Generator(2, Class="sequence")]
[Key(3, Column="ITEM_ID")]
[Element(4, TypeType=typeof(string), Column="FILENAME", NotNull=true)]
public ISet Images { ... }

The XML mapping looks like this:

<idbag name="Images" lazy="true" table="ITEM_IMAGE">
<collection-id type="Int32" column="ITEM_IMAGE_ID">
<generator class="sequence"/>
</collection-id>
<key column="ITEM_ID"/>
<element type="String" column="FILENAME" not-null="true"/>
</idbag>

In this case, the primary key is the generated ITEM_IMAGE_ID. You can see a graphical view of the database tables in figure 6.4

Figure 6.4. Table structure using a bag with a surrogate primary key

If you’re wondering why you use <idbag> rather than <bag>, bear in mind that we’ll be discussing bags shortly. First, we’ll discuss another common approach to storing images: in an ordered list.

Using a List

A <list> mapping requires the addition of an index column to the database table. The index column defines the position of the element in the collection. Thus, NHibernate can preserve the ordering of the collection elements when retrieving the collection from the database if you map the collection as a <list>:

<list name="Images" lazy="true" table="ITEM_IMAGE">
<key column="ITEM_ID"/>
<index column="POSITION"/>
<element type="String" column="FILENAME" not-null="true"/>
</list>

The primary key consists of the ITEM_ID and POSITION columns. Notice that duplicate elements (FILENAME) are allowed, which is consistent with the semantics of a list. (You don’t have to change the Item class; the types you used earlier for the bag are the same.)

Note that even though the IList contract doesn’t specify that a list is an ordered collection, NHibernate’s implementation preserves the ordering when persisting the collection.

If the collection is [fooimage1.jpg, fooimage1.jpg, fooimage2.jpg], the POSITION column contains the values 0, 1, and 2, as shown in figure 6.5

Figure 6.5. Tables for a list with positional elements

Alternatively, you could use a .NET array instead of a list. NHibernate supports this usage, and the details of an array mapping are virtually identical to those of a list. But we strongly recommend against the use of arrays, because arrays can’t be lazily initialized (there is no way to proxy an array at the CLR level). Here is the mapping:

<primitive-array name="Images" table="ITEM_IMAGE">
<key column="ITEM_ID"/>
<index column="POSITION"/>
<element type="String" column="FILENAME" not-null="true"/>
</primitive-array>

Now, suppose your images have user-entered names in addition to the filenames. One way to model this in .NET would be to use a map, with names as keys and filenames as values.

Using a Map

Mapping a <map> (pardon us) is similar to mapping a list:

<map name="Images" lazy="true" table="ITEM_IMAGE">
<key column="ITEM_ID"/>
<index column="IMAGE_NAME" type="string"/>
<element type="String" column="FILENAME" not-null="true"/>
</map>

The primary key consists of the ITEM_ID and IMAGE_NAME columns. The IMAGE_NAME column stores the keys of the map. Again, duplicate elements are allowed; see figure 6.6 for a graphical view of the tables.

Figure 6.6. Tables for a map, using strings as indexes and elements

This Map is unordered. What if you want to always sort your map by the name of the image?

Sorted and Ordered Collections

In a startling abuse of the English language, the words sorted and ordered mean different things when it comes to NHibernate persistent collections. A sorted collection is sorted in memory using a .NET IComparer. An ordered collection is ordered at the database level using a SQL query with an order by clause.

Let’s make the map of images a sorted map. This is a simple change to the mapping document:

<map name="Images"
lazy="true"
table="ITEM_IMAGE"
sort="natural">
<key column="ITEM_ID"/>
<index column="IMAGE_NAME" type="string"/>
<element type="String" column="FILENAME" not-null="true"/>
</map>

By specifying sort="natural", you tell NHibernate to use a SortedMap, sorting the image names according to the CompareTo() method of System.String. If you want some other sorted order—for example, reverse alphabetical order—you can specify the name of a class that implements System.Collections.IComparer in the sort attribute. Here’s an example:

<map name="Images"
lazy="true"
table="ITEM_IMAGE"
sort="NHibernate.Auction.ReverseStringComparer, NHibernate.Auction">
<key column="ITEM_ID"/>
<index column="IMAGE_NAME" type="string"/>
<element type="String" column="FILENAME" not-null="true"/>
</map>

NHibernate sorted maps use System.Collections.SortedList in their implementation. A sorted set, which behaves like Iesi.Collections.SortedSet, is mapped in a similar way:

<set name="Images"
lazy="true"
table="ITEM_IMAGE"
sort="natural">
<key column="ITEM_ID"/>
<element type="String" column="FILENAME" not-null="true"/>
</set>

Bags can’t be sorted, and there is no SortedBag, unfortunately. Nor may lists be sorted, because the order of list elements is defined by the list index.

Alternatively, you may choose to use an ordered map, using the sorting capabilities of the database instead of in-memory sorting:

<map name="Images"
lazy="true"
table="ITEM_IMAGE"
order-by="IMAGE_NAME asc">
<key column="ITEM_ID"/>
<index column="IMAGE_NAME" type="String"/>
<element type="String" column="FILENAME" not-null="true"/>
</map>

The expression in the order-by attribute is a fragment of a SQL order by clause. In this case, you order by the IMAGE_NAME column, in ascending order. You can even write SQL function calls in the order-by attribute:

<map name="Images"
lazy="true"
table="ITEM_IMAGE"
order-by="lower(FILENAME) asc">
<key column="ITEM_ID"/>
<index column="IMAGE_NAME" type="String"/>
<element type="String" column="FILENAME" not-null="true"/>
</map>

Notice that you can order by any column of the collection table. Both sets and bags accept the order-by attribute; but again, lists don’t. This example uses a bag:

<idbag name="Images"
lazy="true"
table="ITEM_IMAGE"
order-by="ITEM_IMAGE_ID desc">
<collection-id type="Int32" column="ITEM_IMAGE_ID">
<generator class="sequence"/>
</collection-id>
<key column="ITEM_ID"/>
<element type="String" column="FILENAME" not-null="true"/>
</idbag>

Under the covers, NHibernate uses an Iesi.Collections.ListSet and a System. Collections.Specialized.ListDictionary to implement ordered sets and maps; use this functionality carefully because it doesn’t perform well with large numbers of elements.

In a real system, it’s likely that you’ll need to keep more than just the image name and filename; you’l probably need to create an Image class to store this extra information. Of course, you could map Image as an entity class; but because we’ve already concluded that this isn’t absolutely necessary, let’s see how much further you can get without an Image entity, which would require an association mapping and more complex lifecycle handling.

In chapter 3, you saw that NHibernate lets you map user-defined classes as components, which are considered to be value types. This is still true, even when component instances are collection elements. Let’s now look at how you can map collections of components.

6.2.2. Collections of components

The Image class defines the properties Name, Filename, SizeX, and SizeY. It has a single association, with its parent Item class, as shown in figure 6.7.

Figure 6.7. Collection of Image components in Item

As you can see from the aggregation association style depicted by a black diamond, Image is a component of Item, and Item is the entity that is responsible for the lifecycle of Image. References to images aren’t shared, so our first choice is an NHibernate component mapping. The multiplicity of the association further declares this association as many-valued—that is, zero or more Images for the same Item.

Writing the Component Class

First, you implement the Image class. This is a POCO, with nothing special to consider. As you know from chapter 4, component classes don’t have an identifier property. But you must implement Equals() and GetHashCode() to compare the Name, Filename, SizeX, and SizeY properties. This allows NHibernate’s dirty checking to function correctly. Strictly speaking, implementing Equals() and GetHashCode() isn’t required for all component classes; but we recommend it for any component class because the implementation is fairly easy, and “better safe than sorry” is a good motto.

The Item class hasn’t changed, but the objects in the collection are now Images instead of Strings. Let’s map this to the database.

Mapping the Collection

Collections of components are mapped similarly to other collections of value type instances. The only difference is the use of <composite-element> in place of the familiar <element> tag. An ordered set of images can be mapped like this:

<set name="Images"
lazy="true"
table="ITEM_IMAGE"
order-by="IMAGE_NAME asc">
<key column="ITEM_ID"/>
<composite-element class="Namespaces.Image, Assembly">
<property name="Name" column="IMAGE_NAME" not-null="true"/>
<property name="Filename" column="FILENAME" not-null="true"/>
<property name="SizeX" column="SIZEX" not-null="true"/>
<property name="SizeY" column="SIZEY" not-null="true"/>
</composite-element>
</set>

This is a set of value-type instances, so NHibernate must be able to tell the instances apart despite the fact that they have no separate primary key column. To do this, all columns of the composite are used together to determine if an item is unique: ITEM_ID, IMAGE_NAME, FILENAME, SIZEX, and SIZEY. Because these columns will all appear in a composite primary key, they can’t be null. This is clearly a disadvantage of this particular mapping. Composite elements in a set are sometimes useful, but using a list, bag, map, or idbag lets you get around the not-null restriction.

Bidirectional Navigation

So far, the association from Item to Image is unidirectional. If you decide to make it bidirectional, you’ll give the Image class a property named Item that is a reference back to the owning Item. In the mapping file, you need to add a <parent> tag to the mapping:

<set name="Images"
lazy="true"
table="ITEM_IMAGE"
order-by="IMAGE_NAME asc">
<key column="ITEM_ID"/>
<composite-element class="Image">
<parent name="Item"/>
<property name="Name" column="IMAGE_NAME" not-null="true"/>
<property name="Filename" column="FILENAME" not-null="true"/>
<property name="SizeX" column="SIZEX" not-null="true"/>
<property name="SizeY" column="SIZEY" not-null="true"/>
</composite-element>
</set>

This leads to a potential problem: you’ll be able to load Image instances by querying for them, but the reference to their parent property will be null. The best thing to do is always load the parent in order to access its component parts, or use a full parent/ child entity association, as described in chapter 4.

You still have the issue of having to declare all properties as not-null, and it would be nice if you could avoid this. Let’s look at how you can use a better primary key for the IMAGE table.

Avoiding Not-Null Columns

If a set of Images isn’t the only solution, other more flexible collection styles are possible. For example, an idbag offers a surrogate collection key:

<idbag name="Images"
lazy="true"
table="ITEM_IMAGE"
order-by="IMAGE_NAME asc">
<collection-id type="Int32" column="ITEM_IMAGE_ID">
<generator class="sequence"/>
</collection-id>
<key column="ITEM_ID"/>
<composite-element class="Namespaces.Image, Assembly">
<property name="Name" column="IMAGE_NAME"/>
<property name="Filename" column="FILENAME" not-null="true"/>
<property name="SizeX" column="SIZEX"/>
<property name="SizeY" column="SIZEY"/>
</composite-element>
</idbag>

This time, the primary key is the ITEM_IMAGE_ID column. NHibernate now doesn’t require that you must implement Equals() and GetHashCode(), nor do you need to declare the properties with not-null="true". They may be nullable in the case of an idbag, as shown in figure 6.8

Figure 6.8. Collection of Image components using a bag with a surrogate key

We should point out that there isn’t a great deal of difference between this bag mapping and a standard parent/child entity relationship. The tables are identical, and even the C# code is extremely similar; the choice is mainly a matter of taste. Of course, a parent/child relationship supports shared references to the child entity and true bidirectional navigation.

You can even remove the Name property from the Image class and again use the image name as the key of a map:

<map name="Images"
lazy="true"
table="ITEM_IMAGE"
order-by="IMAGE_NAME asc">
<key column="ITEM_ID"/>
<index type="String" column="IMAGE_NAME"/>
<composite-element class="Image">
<property name="Filename" column="FILENAME" not-null="true"/>
<property name="SizeX" column="SIZEX"/>
<property name="SizeY" column="SIZEY"/>
</composite-element>
</map>

As before, the primary key is composed of ITEM_ID and IMAGE_NAME.

A composite element class like Image isn’t limited to simple properties of basic type like filename. It may contain components, using the <nested-composite-element> declaration, and even <many-to-one> associations to entities. It may not own collections. A composite element with a many-to-one association is useful, and we come back to this kind of mapping later in this chapter.

We’re finally finished with value types, and we hope you have an in-depth understanding of what is possible and where you can be able to make use of them. Next, we look at advanced entity-association mapping techniques. The simple parent/child association you mapped in chapter 3 is just one of many possible association mapping styles. Like the previous mappings we discussed, most of these mappings are considered “exotic” and are needed only in special cases. But having an awareness of the available techniques will help you solve the thornier mapping challenges you encounter in the wild.

6.3. Mapping entity associations

When we use the word associations, we’re always referring to relationships between entities. In chapter 4, we demonstrated a unidirectional many-to-one association, made it bidirectional, and finally turned it into a parent/child relationship (one-to-many and many-to-one).

One-to-many associations are the most important and popular type. We go so far as to discourage the use of more exotic association styles when a simple bidirectional many-to-one/one-to-many will do the job. In particular, a many-to-many association may always be represented as two many-to-one associations to an intervening class. This model is usually much more extensible, and you’ll rarely use a many-to-many mapping in your applications.

Armed with this disclaimer, let’s investigate NHibernate’s rich association mappings, starting with one-to-one associations.

6.3.1. One-to-one associations

We argued in chapter 4 that the relationships between User and Address were best represented using <component> mappings. If you recall, the user has both a BillingAddress and a HomeAddress in the sample model. Component mappings are usually the simplest way to represent one-to-one relationships, because the lifecycle of one class is almost always dependent on the lifecycle of the other class, and the association is a composition.

But what if you want a dedicated table for Address and to map both User and Address as entities? In this case, the classes have a true one-to-one association. Because an Address is an entity, you start by creating the following mapping:

<class name="Address" table="ADDRESS" lazy="false">
<id name="Id" column="ADDRESS_ID">
<generator class="native"/>
</id>
<property name="Street"/>
<property name="City"/>
<property name="Zipcode"/>
</class>

Note that Address now requires an identifier property; it’s no longer a component class. There are two different ways to represent a one-to-one association to this Address in NHibernate. The first approach adds a foreign key column to the USER table.

Using a Foreign Key Association

The easiest way to represent the association from User to its BillingAddress is to use a <many-to-one> mapping with a unique constraint on the foreign key. This may surprise you, because many doesn’t seem to be a good description of either end of a one-to-one association! But from NHibernate’s point of view, there isn’t much difference between the two kinds of foreign-key associations. You add a foreign key column named BILLING_ADDRESS_ID to the USER table and map it as follows:

<many-to-one name="BillingAddress"
class="Address"
column="BILLING_ADDRESS_ID"
cascade="save-update"/>

Note that we’ve chosen save-update as the cascade style. This means the Address will become persistent when you create an association from a persistent User. The cascade="all" cascade would also make sense for this association, because deletion of the User should result in deletion of the Address.

The database schema still allows duplicate values in the BILLING_ADDRESS_ID column of the USER table, so two users can have a reference to the same address. To make this association truly one-to-one, you add unique="true" to the <many-to-one> element, constraining the relational model so that there can be only one address per user:

<many-to-one name="BillingAddress"
class="Address"
column="BILLING_ADDRESS_ID"
cascade="all"
unique="true"/>

This change adds a unique constraint to the BILLING_ADDRESS_ID column in the DDL generated by NHibernate, resulting in the table structure illustrated in figure 6.9

Figure 6.9. A one-to-one association with an extra foreign-key column

But what if you want this association to be navigable from Address to User in .NET? To achieve this, you add a property named User that points to the Address class, and map it like so in your Address mapping:

<one-to-one name="User"
class="User"
property-ref="BillingAddress"/>

This tells NHibernate that the User association in Address is the reverse direction of the BillingAddress association in User.

In code, you create the association between the two objects as follows:

Address address = new Address();
address.Street = "73 Nowhere Street";
address.City = "Pretoria";
address.Zipcode = "1923";
using( session.BeginTransaction() ) {
User user = (User) session.Get(typeof(User), userId);
address.User = user;
user.BillingAddress = address;
session.Transaction.Commit();
}

To finish the mapping, you also have to map the HomeAddress property of User. This is easy enough; you add another <many-to-one> element to the User metadata, mapping a new foreign key column, HOME_ADDRESS_ID:

<many-to-one name="HomeAddress"
class="Address"
column="HOME_ADDRESS_ID"
cascade="save-update"
unique="true"/>

The USER table now defines two foreign keys referencing the primary key of the ADDRESS table: HOME_ADDRESS_ID and BILLING_ADDRESS_ID.

Unfortunately, you can’t make both the BillingAddress and HomeAddress associations bidirectional, because you don’t know if a particular address is a billing address or a home address. More specifically, you’d have to somehow dynamically decide which property name—BillingAddress or HomeAddress—to use for the property-ref attribute in the mapping of the user property. You could try making Address an abstract class with subclasses HomeAddress and BillingAddress and mapping the associations to the subclasses. This approach would work, but it’s complex and probably not sensible in this case.

Our advice is to avoid defining more than one one-to-one association between any two classes. If you must, leave the associations unidirectional. If you don’t have more than one—if exactly one instance of Address exists per User—there is an alternative approach to the one we’ve just shown. Instead of defining a foreign-key column in the USER table, you can use a primary-key association.

Using a Primary-Key Association

Two tables related by a primary-key association share the same primary-key values. The primary key of one table is also a foreign key of the other. The main difficulty with this approach is ensuring that associated instances are assigned the same primary-key value when the objects are saved. Before you try to solve this problem, let’s see how you map the primary-key association.

For a primary-key association, both ends of the association are mapped using the <one-to-one> declaration. This also means you can no longer map both the billing and home address—you can map only one property. Each row in the USER table has a corresponding row in the ADDRESS table. Two addresses would require an additional table, and this mapping style therefore wouldn’t be adequate. Let’s call this single address property Address and map it with the User:

<one-to-one name="Address"
class="Address"
cascade="save-update"/>

Next, here’s the User of Address:

<one-to-one name="User"
class="User"
constrained="true"/>

The most interesting thing here is the use of constrained="true". It tells NHibernate that there is a foreign-key constraint on the primary key of ADDRESS that refers to the primary key of USER.

Now, you must ensure that newly saved instances of Address are assigned the same identifier value as their User. You use a special NHibernate identifier-generation strategy called foreign:

<class name="Address" table="ADDRESS" lazy="false">
<id name="Id" column="ADDRESS_ID">
<generator class="foreign">
<param name="property">User</param>
</generator>
</id>
...
<one-to-one name="User"
class="User"
constrained="true"/>
</class>

The <param> named property of the foreign generator allows you to name a one-to-one association of the Address class—in this case, the user association. The foreign generator inspects the associated object (the User) and uses its identifier as the identifier of the new Address. Look at the table structure in figure 6.10

Figure 6.10. The tables for a one-to-one association with shared primary-key values

The code to create the object association is unchanged for a primary-key association; it’s the same code you used earlier for the many-to-one mapping style.

Just one remaining entity association multiplicity remains for us to discuss: many-to-many.

6.3.2. Many-to-many associations

The association between Category and Item is a many-to-many association, as you can see in figure 6.11

Figure 6.11. A many-to-many valued association between Category and Item

As we explained earlier in this section, we avoid the use of many-to-many associations because there is almost always other information that must be attached to the links between associated instances; the best way to represent this information is via an intermediate association class. Nevertheless, it’s the purpose of this section to implement a real many-to-many entity association. Let’s start with a unidirectional example.

A Unidirectional Many-to-many Association

If you only require unidirectional navigation, the mapping is straightforward. Unidirectional many-to-many associations are no more difficult than the collections of value-type instances we covered previously. For example, if the Category has a set of Items, you can use this mapping:

<set name="Items"
table="CATEGORY_ITEM"
lazy="true"
cascade="save-update">
<key column="CATEGORY_ID"/>
<many-to-many class="Item" column="ITEM_ID"/>
</set>

Just like a collection of value-type instances, a many-to-many association has its own table: the link table or association table. In this case, the link table has two columns: the foreign keys of the CATEGORY and ITEM tables. The primary key is composed of both columns. The full table structure is shown in figure 6.12.

Figure 6.12. Many-to-many entity association mapped to an association table

You can also use a bag with a separate primary-key column:

<idbag name="Items"
table="CATEGORY_ITEM"
lazy="true"
cascade="save-update">
<collection-id type="Int32" column="CATEGORY_ITEM_ID">
<generator class="sequence"/>
</collection-id>
<key column="CATEGORY_ID"/>
<many-to-many class="Item" column="ITEM_ID"/>
</idbag>

As usual with an <idbag> mapping, the primary key is a surrogate key column, CATEGORY_ITEM_ID; duplicate links are therefore allowed (the same Item can be added to a particular Category twice).

Other variations you can use are the indexed map and list collections. The following example uses a list:

<list name="Items"
table="CATEGORY_ITEM"
lazy="true"
cascade="save-update">
<key column="CATEGORY_ID"/>
<index column="DISPLAY_POSITION"/>
<many-to-many class="Item" column="ITEM_ID"/>
</list>

The primary key consists of the CATEGORY_ID and DISPLAY_POSITION columns. This mapping guarantees that every Item knows its position in the Category.

Creating an object association in .NET code is easy:

using( session.BeginTransaction() ) {
Category cat = (Category) session.Get(typeof(Category), categoryId);
Item item = (Item) session.Get(typeof(Item), itemId);
cat.Items.Add(item);
session.Transaction.Commit();
}

Bidirectional many-to-many associations are slightly more difficult.

A Bidirectional Many-to-Many Association

When you mapped a bidirectional one-to-many association in section 3.6, we explained why one end of the association must be mapped with inverse="true". Feel free to review that section, because it’s relevant for bidirectional many-to-many associations too. In particular, each row of the link table is represented by two collection elements: one element at each end of the association. For example, you may create an Item class with a collection of Category instances, and a Category class with a collection of Item instances. When it comes to creating relationships in .NET code, it may look something like this:

cat.Items.Add(item);
item.Categories.Add(cat);

Regardless of multiplicity, a bidirectional association requires that you set both ends of the association.

When you map a bidirectional many-to-many association, you must declare one end of the association using inverse="true" to define which side’s state is used to update the link table. You can choose for yourself which end that should be.

Recall this mapping for the Items collection from the previous section:

<class name="Category" table="CATEGORY">
...
<set name="Items"
table="CATEGORY_ITEM"
lazy="true"
cascade="save-update">
<key column="CATEGORY_ID"/>
<many-to-many class="Item" column="ITEM_ID"/>
</set>
</class>

You can reuse this mapping for the Category end of the bidirectional association. You map the Item end as follows:

<class name="Item" table="ITEM">
...
<set name="Categories"
table="CATEGORY_ITEM"
lazy="true"
inverse="true"
cascade="save-update">
<key column="ITEM_ID"/>
<many-to-many class="Category" column="CATEGORY_ID"/>
</set>
</class>

Note the use of inverse="true". Once again, this setting tells NHibernate to ignore changes made to the categories collection and use the other end of the association—the items collection—as the representation that should be synchronized with the database.

We’ve chosen cascade="save-update" for both ends of the collection, which suits your needs well. Note that cascade="all", cascade="delete", and cascade= "all-delete-orphans" aren’t meaningful for many-to-many associations, because an instance with potentially many parents shouldn’t be deleted when just one parent is deleted.

Another thing to consider is the kinds of collections that may be used for bidirectional many-to-many associations. Do you need to use the same type of collection at each end? It’s reasonable to use, for example, a list at the end not marked inverse ="true" (or explicitly set false) and a bag at the end marked inverse="true".

You can use any of the mappings we’ve shown for unidirectional many-to-many associations for the noninverse end of the bidirectional association. <set>, <idbag>, <list>, and <map> are all possible, and the mappings are identical to those shown previously.

For the inverse end, <set> is acceptable, as is the following bag mapping:

<class name="Item" table="ITEM">
...
<bag name="Categories"
table="CATEGORY_ITEM"
lazy="true"
inverse="true" cascade="save-update">
<key column="ITEM_ID"/>
<many-to-many class="Category" column="CATEGORY_ID"/>
</bag>
</class>

This is the first time we’ve shown the <bag> declaration: it’s similar to an <idbag> mapping, but it doesn’t involve a surrogate-key column. It lets you use an IList (with bag semantics) in a persistent class instead of an ISet. Thus it’s preferred if the nonin-verse side of a many-to-many association mapping is using a map, list, or bag (which all permit duplicates). Remember that a bag doesn’t preserve the order of elements.

No other mappings should be used for the inverse end of a many-to-many association. Indexed collections such as lists and maps can’t be used, because NHibernate won’t initialize or maintain the index column if inverse="true". This is also true and important to remember for all other association mappings involving collections: an indexed collection, or even arrays, can’t be set to inverse="true".

We already frowned at the use of a many-to-many association and suggested the use of composite element mappings as an alternative. Let’s see how this works.

Using a Collection of Components for a Many-to-Many Association

Suppose you need to record some information each time you add an Item to a Category. For example, you may need to store the date and the name of the user who added the item to this category. You use a C# class to represent this information:

public class CategorizedItem {
private string username;
private DateTime dateAdded;
private Item item;
private Category category;
//...
}

This code omits the properties and Equals() and GetHashCode() methods, but they would be necessary for this component class.

You map the Items collection on Category as shown next. If you prefer using mapping attributes in your code, you should be able to easily deduce the mapping using attributes; just be careful when ordering them:

<set name="Items" lazy="true" table="CATEGORY_ITEMS">
<key column="CATEGORY_ID"/>
<composite-element class="CategorizedItem">
<parent name="Category"/>
<many-to-one name="Item"
class="Item"
column="ITEM_ID"
not-null="true"/>
<property name="Username" column="USERNAME" not-null="true"/>
<property name="DateAdded" column="DATE_ADDED" not-null="true"/>
</composite-element>
</set>

You use the <many-to-one> element to declare the association to Item, and you use the <property> mappings to declare the extra association-related information. The link table now has four columns: CATEGORY_ID, ITEM_ID, USERNAME and DATE_ADDED. The columns of the CategorizedItem properties should never be null; otherwise you can’t identify a single link entry, because they’re all part of the table’s primary key. You can see the table structure in figure 6.13

Figure 6.13. Many-to-many entity association table using a component

Rather than mapping just the Username, you may want to keep an actual reference to the User object. In this case, you have the following ternary association mapping:

<set name="Items" lazy="true" table="CATEGORY_ITEMS">
<key column="CATEGORY_ID"/>
<composite-element class="CategorizedItem">
<parent name="Category"/>
<many-to-one name="Item"
class="Item"
column="ITEM_ID"
not-null="true"/>
<many-to-one name="User"
class="User"
column="USER_ID"
not-null="true"/>
<property name="DateAdded" column="DATE_ADDED" not-null="true"/>
</composite-element>
</set>

This is a fairly exotic beast! If you find yourself with a mapping like this, you should ask whether it may be better to map CategorizedItem as an entity class and use two one-to-many associations. Furthermore, there is no way to make this mapping bidirectional: a component, such as CategorizedItem can’t, by definition, have shared references. You can’t navigate from Item to CategorizedItem.

We talked about some limitations of many-to-many mappings in the previous section. One of them, the restriction to nonindexed collections for the inverse end of an association, also applies to one-to-many associations, if they’re bidirectional. Let’s take a closer look at one-to-many and many-to-one again, to refresh your memory and elaborate on what we discussed in chapter 4.

One-To-Many Associations

You already know most of what you need to know about one-to-many associations from chapter 3. You mapped a typical parent/child relationship between two entity persistent classes, Item and Bid. This was a bidirectional association, using a <one-to-many> and a <many-to-one> mapping. The “many” end of this association was implemented in C# with an ISet; you had a collection of Bids in the Item class. Let’s reconsider this mapping and walk through some special cases.

Using a Bag With Set Semantics

For example, if you absolutely need an IList of children in your parent C# class, it’s possible to use a <bag> mapping in place of a set. In the example, first you have to replace the type of the Bids collection in the Item persistent class with an IList. The mapping for the association between Item and Bid is then left essentially unchanged:

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

You rename the <set> element to <bag>, making no other changes. Note that this change isn’t useful: the underlying table structure doesn’t support duplicates, so the <bag> mapping results in an association with set semantics. Some tastes prefer the use of ILists even for associations with set semantics, but ours doesn’t, so we recommend using <set> mappings for typical parent/child relationships.

The obvious (but wrong) solution would be to use a real <list> mapping for the Bids with an additional column holding the position of the elements. Remember the NHibernate limitation we introduced earlier in this chapter: you can’t use indexed collections on an inverse side of an association. The inverse="true" side of the association isn’t considered when NHibernate saves the object state, so NHibernate ignores the index of the elements and doesn’t update the position column.

But if your parent/child relationship is unidirectional only where navigation is possible only from parent to child, you can use an indexed collection type because the “many” end is no longer inverse. Good uses for unidirectional one-to-many associations are uncommon in practice, and you don’t have one in the auction application. You may remember that you started with the Item and Bid mapping in chapter 4, making it first unidirectional, but you quickly introduced the other side of the mapping.

Let’s find a different example to implement a unidirectional one-to-many association with an indexed collection.

Unidirectional Mapping

For the purposes of this discussion, we now suppose that the association between Category and Item is to be remodeled as a one-to-many association; an Item now belongs to at most one category and doesn’t own a reference to its current category. In C# code, you model this as a collection named Items in the Category class; you don’t have to change anything if you don’t use an indexed collection. If Items is implemented as an ISet, you use the following mapping:

<set name="Items" lazy="true">
<key column="CATEGORY_ID"/>
<one-to-many class="Item"/>
</set>

Remember that one-to-many association mappings don’t need to declare a table name. NHibernate already knows that the column names in the collection mapping (in this case, only CATEGORY_ID) belong to the ITEM table. The table structure is shown in figure 6.14

Figure 6.14. A standard one-to-many association using a foreign-key column

The other side of the association, the Item class, has no mapping reference to Category. You can now also use an indexed collection in the Category—for example, after you change the Items property to List:

<list name="Items" lazy="true">
<key>
<column name="CATEGORY_ID" not-null="false"/>
</key>
<index column="DISPLAY_POSITION/>
<one-to-many class="Item"/>
</list>

Note the new DISPLAY_POSITION column in the ITEM table, which holds the position of the Item elements in the collection.

There is an important issue to consider, which, in our experience, puzzles many NHibernate users at first. In a unidirectional one-to-many association, the foreign-key column CATEGORY_ID in the ITEM table must be nullable. An Item could be saved without knowing anything about a Category—it’s a standalone entity! This is a consistent model and mapping, and you may have to think about it twice if you deal with a not-null foreign key and a parent/child relationship. Using a bidirectional association (and a Set) is the correct solution.

Now that you know about all the association mapping techniques for normal entities, you may want to consider inheritance; how do all these associations work between various levels of an inheritance hierarchy? What you want is polymorphic behavior; let’s see how NHibernate deals with polymorphic entity associations.

6.4. Mapping polymorphic associations

Polymorphism is a defining feature of object-oriented languages like C#. Therefore, support for polymorphic associations and queries is a fundamental requirement of an ORM solution like NHibernate. Surprisingly, we’ve managed to get this far without needing to talk much about polymorphism. Even more surprisingly, there isn’t much to say on the topic—polymorphism is so easy to use in NHibernate that we don’t need to spend a lot of effort explaining this feature.

To give you a good overview of how polymorphic associations are used, we first consider a many-to-one association to a class that may have subclasses. In this case, NHibernate guarantees that you can create links to any subclass instance just as you would to instances of the base class. Following that, we guide you through setting up polymorphic collections and then explain the particular issues with the “table per concrete class” mapping.

6.4.1. Polymorphic many-to-one associations

A polymorphic association is an association that may refer to instances of a subclass, where the parent class was explicitly specified in the mapping metadata. For this example, imagine that you don’t have many BillingDetails per User, but only one, as shown in figure 6.15

Figure 6.15. The user has only one billing information object.

The user needs a unidirectional association to some BillingDetails, which can be CreditCard details or BankAccount details. You map this association to the abstract class BillingDetails as follows:

<many-to-one name="BillingDetails"
class="BillingDetails"
column="BILLING_DETAILS_ID"
cascade="save-update"/>

But because BillingDetails is abstract, the association must refer to an instance of one of its subclasses—CreditCard or BankAccount—at runtime.

All the association mappings we’ve introduced so far in this chapter support polymorphism. You don’t have to do anything special to use polymorphic associations in NHibernate—you specify the name of any mapped persistent class in your association mapping. Then, if that class declares any <subclass> or <joined-subclass> elements, the association is naturally polymorphic.

The following code demonstrates the creation of an association to an instance of the CreditCard subclass:

CreditCard cc = new CreditCard();
cc.Number = ccNumber;
cc.Type = ccType;
cc.ExpiryDate = ccExpiryDate;
using( ISession session = sessionFactory.OpenSession() )
using( session.BeginTransaction() ) {
User user = (User) session.Get(typeof(User), uid);
user.BillingDetails = cc;
session.Transaction.Commit();
}

Now, when you navigate the association in a second transaction, NHibernate automatically retrieves the CreditCard instance:

using( ISession session = sessionFactory.OpenSession() )
using( session.BeginTransaction() ) {
User user = (User) session.Get(typeof(User), uid);
user.BillingDetails.Pay(paymentAmount);
session.Transaction.Commit();
}

Note that, in user.BillingDetails.Pay(paymentAmount), the call is against the appropriate subclass.

You must watch out for one thing: if BillingDetails was mapped with lazy="true", NHibernate would proxy the BillingDetails association. In this case, you wouldn’t be able to perform a typecast to the concrete class CreditCard at runtime, and even the is operator would behave strangely:

User user = (User) session.Get(typeof(User), uid);
BillingDetails bd = user.BillingDetails;
Assert.IsFalse( bd is CreditCard );
CreditCard cc = (CreditCard) bd;

In this code, the typecast on the last line fails because bd is a proxy instance, and when creating it, NHibernate doesn’t know yet that bd is a CreditCard; all it knows is that bd is a BillingDetails. When a method is invoked on the proxy, the call is delegated to an instance of CreditCard that is fetched lazily. To perform a proxy-safe typecast, use Session.Load():

User user = (User) session.Get(typeof(User), uid);
BillingDetails bd = user.BillingDetails;
CreditCard cc =
(CreditCard) session.Load( typeof(CreditCard), bd.Id );
expiryDate = cc.ExpiryDate;

After the call to load, bd and cc refer to two different proxy instances, which both delegate to the same underlying CreditCard instance. Also, because proxy instances were created, no database hit has been incurred yet.

Note that you can avoid these issues by avoiding lazy fetching, as in the following code, using a query technique discussed in the next chapter:

User user = (User) session.CreateCriteria(typeof(User))
.Add(Expression.Expression.Eq("id", uid) )
.SetFetchMode("BillingDetails", FetchMode.Eager)
.UniqueResult();
CreditCard cc = (CreditCard) user.BillingDetails;
expiryDate = cc.ExpiryDate;

BillingDetails is fetched eagerly in this case, avoiding the lazy load. Truly object-oriented code shouldn’t use is or numerous typecasts. If you find yourself running into problems with proxies, you should question your design, asking whether there is a more polymorphic approach.

One-to-one associations are handled the same way. What about many-valued associations?

6.4.2. Polymorphic collections

Let’s refactor the previous example to its original form, as introduced in the CaveatEmptor application. If User owns many BillingDetails, you use a bidirectional one-to-many. In BillingDetails, you have the following:

<many-to-one name="User"
class="User"
column="USER_ID"/>

In the Users mapping, you have this:

<set name="BillingDetails"
lazy="true"
cascade="save-update"
inverse="true">
<key column="USER_ID"/>
<one-to-many class="BillingDetails"/>
</set>

Adding a CreditCard is easy:

CreditCard cc = new CreditCard();
cc.Number = ccNumber;
cc.Type = ccType;
cc.ExpiryDate = ccExpiryDate;
using( ISession session = sessionFactory.OpenSession() )
using( session.BeginTransaction() ) {
User user = (User) session.Get(typeof(User), uid);
user.AddBillingDetails(cc);
session.Transaction.Commit();
}

As usual, the user.AddBillingDetails(cc) function ensures that the association is set at both ends by calling BillingDetails.Add(cc) and cc.User=this.

You can iterate over the collection and handle instances of CreditCard and BankAccount:

using( ISession session = sessionFactory.OpenSession() )
using( session.BeginTransaction() ) {
User user = (User) session.Get(typeof(User), uid);
foreach(BillingDetails bd in user.BillingDetails){
bd.Pay(ccPaymentAmount);
}
session.Transaction.Commit();
}

Note that bd.Pay(...) calls the appropriate BillingDetails subclass instance. In the examples so far, we’ve assumed that BillingDetails is a class mapped explicitly in the NHibernate mapping document, and that the inheritance mapping strategy is table-per-hierarchy or table-per-subclass. We haven’t yet considered the case of a table-per-concrete-class mapping strategy, where BillingDetails isn’t mentioned explicitly in the mapping file, but only in the C# definition of the subclasses.

6.4.3. Polymorphic associations and table-per-concrete-class

In section 3.8.1, we defined the table-per-concrete-class mapping strategy and observed that this mapping strategy makes it difficult to represent a polymorphic association, because you can’t map a foreign-key relationship to the table of the abstract base class. There is no table for the base class with this strategy; you only have tables for concrete classes.

Suppose that you want to represent a polymorphic many-to-one association from User to BillingDetails, where the BillingDetails class hierarchy is mapped using this table-per-concrete-class strategy. You have CREDIT_CARD and BANK_ACCOUNT tables but no BILLING_DETAILS table. You need two pieces of information in the USER table to uniquely identify the associated CreditCard or BankAccount:

  • The name of the table in which the associated instance resides
  • The identifier of the associated instance

The USER table requires the addition of a BILLING_DETAILS_TYPE column in addition to the BILLING_DETAILS_ID. You use an NHibernate <any> element to map this association:

<any name="BillingDetails"
meta-type="String"
id-type="Int32"
cascade="save-update">
<meta-value value="CREDIT_CARD" class="CreditCard"/>
<meta-value value="BANK_ACCOUNT"class="BankAccount"/>
<column name="BILLING_DETAILS_TYPE"/>
<column name="BILLING_DETAILS_ID"/>
</any>

The meta-type attribute specifies the NHibernate type of the BILLING_DETAILS_TYPE column; the id-type attribute specifies the type of the BILLING_DETAILS_ID column (CreditCard and BankAccount must have the same identifier type). Note that the order of the <column> elements is important: first the type, then the identifier.

The <meta-value> elements tell NHibernate how to interpret the value of the BILLING_DETAILS_TYPE column. You can use any value you like as a type discriminator. For example, you can encode the information in two characters:

<any name="BillingDetails"
meta-type="String"
id-type="Int32"
cascade="save-update">
<meta-value value="CC" class="CreditCard"/>
<meta-value value="CA" class="BankAccount"/>
<column name="BILLING_DETAILS_TYPE"/>
<column name="BILLING_DETAILS_ID"/>
</any>

The <meta-value> elements are optional; if you omit them, NHibernate uses the fully qualified names of the classes. An example of this table structure is shown in figure 6.16

Figure 6.16. Using a discriminator column with an <any> association

Here is the first major problem with this kind of association: you can’t add a foreign-key constraint to the BILLING_DETAILS_ID column, because some values refer to the BANK_ACCOUNT table and others to the CREDIT_CARD table. You need to come up with some other way to ensure integrity. A database trigger may be one way of achieving this.

Furthermore, it’s difficult to write SQL table joins for this association. In particular, the NHibernate query facilities don’t support this kind of association mapping, nor may this association be fetched using an outer join. We discourage the use of <any> associations for all but the most special cases.

As you can see, polymorphism is messier in the case of a table-per-concrete-class inheritance-mapping strategy. We don’t usually use this mapping strategy when polymorphic associations are required. As long as you stick to the other inheritance-mapping strategies, polymorphism is straightforward, and you don’t usually need to think about it.

6.5. Summary

This chapter covered the finer points of ORM and techniques sometimes required to solve the structural mismatch problem. You can now fully map all the entities and associations in the CaveatEmptor domain model.

We also discussed the NHibernate type system, which distinguishes entities from value types. An entity instance has its own lifecycle and persistent identity; an instance of a value type is completely dependant on an owning entity.

NHibernate defines a rich variety of built-in value mapping types that bridge the gap between .NET types and SQL types. When these predefined types are insufficient, you can easily extend them using custom types or component mappings and even implement arbitrary conversions from .NET to SQL data types.

Collection-valued properties are considered to be of value type. A collection doesn’t have its own persistent identity and belongs to a single owning entity. You’ve seen how to map collections, including collections of value-typed instances and many-valued entity associations.

NHibernate supports one-to-one, one-to-many, and many-to-many associations between entities. In practice, we recommend against the overuse of many-to-many associations. Associations in NHibernate are naturally polymorphic. We also talked about bidirectional behavior of such relationships.

Having covered mapping techniques in great detail, the next chapter will now move away from the topic of mapping and turn to the subject of object retrieval. This builds on the concepts introduced in chapter 4 and gives more detailed information that will let you build more efficient and flexible queries. We’ll also cover some of the more advanced topics that surround object retrieval, such as report queries, fetching associations, and caching.