CHAPTER 4
We essentially define mappings for:
Identifiers, properties, references, and collections are all members of the entity class where:
Let’s look at each of these concepts in more detail.
An entity is at the core of a NHibernate mapping. It is a .NET class that will be saved into one (or more, as we will see) table. Its most important configurable options are:
A property either maps to a column in a table or to a SQL formula, in which case it will not be persisted but instead calculated when the containing entity is loaded from the database. A property has the following attributes:
For scalar properties, either at entity level or as members of a component, NHibernate supports the following .NET types:
NHibernate Recognized Types
.NET Type | OOTB | Purpose |
|---|---|---|
Boolean | Yes | A boolean or single bit (0/1 value) |
Byte/SByte | Yes | 8-bit signed/unsigned integer number, usually for representing a single ANSI character |
Byte[] | Yes | Stored in a BLOB |
Char | Yes | Single ANSI or UNICODE character |
CultureInfo | Yes | Stored as a string containing the culture name |
DateTime | Yes | Date and time |
DateTimeOffset | Yes | Date and time with offset relative to UTC |
Decimal | Yes | 128-bit signed integers, scaled by a power of 10 |
Double | Yes | Double precision (64-bit) signed floating point values |
Enum (enumerated type) | Yes/No | Stored as an integer (default), as a character or as a string |
Guid | Yes | GUIDs or generic 128-bit numbers |
Int16/UInt16 | Yes | 16-bit signed/unsigned integer numbers |
Int32/UInt32 | Yes | 32-bit signed/unsigned integer numbers |
Int64/UInt64 | Yes | 64-bit signed/unsigned integer numbers |
Object (serializable) | No | Stored in a BLOB containing the object’s contents after serialization |
Single | Yes | Single precision (32-bit) signed floating point values |
String | Yes | ANSI or UNICODE variable or fixed-length characters |
TimeSpan | Yes | Time |
Type | Yes | Stored as a string containing the assembly qualified type name |
Uri | Yes | Stored as a string |
XDocument/XmlDocument | Yes | XML |
The second column indicates if the property’s type is supported out of the box by NHibernate, that is, without the need for additional configuration. If we need to map a nonstandard primitive type or one of those other types that NHibernate recognizes out of the box (Enum, DateTime, DateTimeOffset, Guid, TimeSpan, Uri, Type, CultureInfo, Byte[]) or if we need to change the way a given property should be handled, we need to use a custom user type. For common cases, no type needs to be specified. For other scenarios, the available types are:
NHibernate Types
NHibernate Type | .NET Property Type | Description |
|---|---|---|
AnsiCharType | Char | Stores a Char as an ANSI (8 bits per character) column instead of UNICODE (16 bits). Some national characters may be lost. |
AnsiStringType | String | Stores a String as an ANSI (8 bits per character) column instead of UNICODE (16 bits). Some national characters may be lost. |
BinaryBlobType | Byte[] | Will store a Byte[] in a database-specific BLOB. If the database requires defining a maximum length, as in SQL Server, use BinaryBlobType. |
CharBooleanType | Boolean | Converts a Boolean value to either True or False |
DateType | DateTime | Stores only the date part of a DateTime |
EnumCharType<T> | Enum (enumerated type) | Stores an enumerated value as a single character, obtained from its numeric value |
EnumStringType<T> | Enum (enumerated type) | Stores an enumerated value as its string representation instead of its numeric value |
LocalDateTimeType | DateTime | Stores a DateTime as local |
SerializableType | Object (serializable) | Serializes an Object into a database-specific BLOB type. |
StringClobType | String | Will store a String in a database-specific CLOB type instead of a standard VARCHAR |
TicksType | DateTime | Stores a DateTime as a number of ticks |
TrueFalseType | Boolean | Converts a Boolean value to either T or F |
UtcDateTimeType | DateTime | Stores a DateTime as UTC |
YesNoType | Boolean | Converts a Boolean value to either Y or N |
Complex properties differ from references to other entities because all of their inner properties are stored in the same class as the declaring entity, and complex properties don’t have anything like an identifier, just a collection of scalar properties. They are useful for logically grouping together some properties that conceptually are related (think of an address, for example). A component may be used in several classes and its only requirements are that it is not abstract and that it has a public, parameterless constructor.
A property does not have to be public but, at the least, it should be protected, not private. Its visibility will affect the ability of the querying APIs to use it; LINQ, for example, will only work with public properties. Different access levels for setters and getters are fine, too, as long as you keep them protected at most.
Finally, although you can use properties with an explicit backing field, there is really no need to do so. In fact, some functionality will not work with backing fields and, as a rule of thumb, you should stick to auto properties.
Some scenarios where we need to specify a type include, for example, when we need to store a String in a BLOB column such as VARBINARY(MAX) in SQL Server or when we need to store just the date part of a DateTime or even when we need to store a Boolean as its string representation (True/False). Like I said, in most cases, you do not need to worry about this.
You can create your own user type, for example, if you want to expose some columns as a different type—like converting a BLOB into an Image. See the following example:
|
public sealed class ImageUserType : IUserType, IParameterizedType { private Byte[] data = null;
public ImageUserType() : this(ImageFormat.Png) { }
public ImageUserType(ImageFormat imageFormat) { this.ImageFormat = imageFormat; }
public ImageFormat ImageFormat { get; private set; }
public override Int32 GetHashCode() { return ((this as IUserType).GetHashCode(this.data)); }
public override Boolean Equals(Object obj) { ImageUserType other = obj as ImageUserType; if (other == null) { return (false); }
if (Object.ReferenceEquals(this, other) == true) { return (true); }
return (this.data.SequenceEqual(other.data)); } Boolean IUserType.IsMutable { get { return (true); } }
Object IUserType.Assemble(Object cached, Object owner) { return (cached); }
Object IUserType.DeepCopy(Object value) { if (value is ICloneable) { return ((value as ICloneable).Clone()); } else { return (value); } } Object IUserType.Disassemble(Object value) { return ((this as IUserType).DeepCopy(value)); }
Boolean IUserType.Equals(Object x, Object y) { return (Object.Equals(x, y)); }
Int32 IUserType.GetHashCode(Object x) { return ((x != null) ? x.GetHashCode() : 0); }
Object IUserType.NullSafeGet(IDataReader rs, String[] names, Object owner) { this.data = NHibernateUtil.Binary.NullSafeGet(rs, names) as Byte[]; if (data == null) { return (null); }
using (Stream stream = new MemoryStream(this.data ?? new Byte[0])) { return (Image.FromStream(stream)); } }
void IUserType.NullSafeSet(IDbCommand cmd, Object value, Int32 index) { if (value != null) { Image data = value as Image; using (MemoryStream stream = new MemoryStream()) { data.Save(stream, this.ImageFormat); value = stream.ToArray(); } }
NHibernateUtil.Binary.NullSafeSet(cmd, value, index); }
Object IUserType.Replace(Object original, Object target, Object owner) { return (original); }
Type IUserType.ReturnedType { get { return (typeof(Image)); } } SqlType[] IUserType.SqlTypes { get { return (new SqlType[] { NHibernateUtil.BinaryBlob.SqlType }); } }
void IParameterizedType.SetParameterValues(IDictionary<String, String> parameters) { if ((parameters != null) && (parameters.ContainsKey("ImageFormat") == true)) { this.ImageFormat = typeof(ImageFormat).GetProperty(parameters["ImageFormat"], BindingFlags.Static | BindingFlags.Public | BindingFlags.GetProperty).GetValue(null, null) as ImageFormat; } } } |
The most important aspects are:
An identifier is either a scalar property, for the most general case of single column primary keys, or a custom class that contains properties for all the columns that make up the composite key. If it is a scalar property, not all types are allowed. You should only use primitive types (Char, Byte/SByte, Int16/UInt16, Int32/UInt32, Int64/UInt64, Decimal, String) and some specific value types (Guid, DateTime, DateTimeOffset, TimeSpan). BLOB, CLOB, and XML columns cannot be used as primary keys.
An identifier property may also be non-public but the most restricted access level you should use is protected or protected internal. Protected setters and public getters are fine, too, and are actually a good idea—unless you want to use manually assigned identifiers.
A very important concept around identifiers is the generation strategy. Basically, this is how the primary key is generated when a record is to be inserted into the database. These are the most important high-level strategies:
NHibernate Identity Generators
Strategy Type | Generator (by code/XML) | Identifier Property Type | Description |
|---|---|---|---|
Database-generated | Identity/identity Sequence/sequence | Int16/UInt16, Int32/UInt32, Int64/UInt64 | Identity columns are supported in SQL Server, MySQL, and Sybase, among others. Sequences are supported on Oracle and PostgreSQL, and can provide values for multiple tables. Both are safe for multiple simultaneous accesses but do not allow batching (more on this later). |
GUID-based | Guid/guid GuidComb/guid.comb | Guid | GUIDs are generated by NHibernate and are assumed to be unique. It is safe for multiple simultaneous accesses and it is possible to do batch insertions. GuidComb is always sequencial. |
Supported by a backing table | HighLow/hilo | Int16/UInt16, Int32/UInt32, Int64/UInt64 | A table exists where the next high value is stored. When a session needs to insert records, it increments and updates this value and combines it with the next of a range of sequential low values until this range is exhausted and another high value needs to be retrieved. It is safe for both multiple simultaneous accesses and batching. |
Manually assigned | Assigned/assigned | Any | You are responsible for assigning unique keys to your entities; use with care when you have multiple sessions inserting records. |
There are other identifier generators but is advised that you stick with these because they are the ones supported by all mapping APIs. A discussion of these strategies is required.
Database-generated keys may be appealing because they are naturally used with common database programming and may appear as the natural choice. However, they do have some drawbacks:
Tip: When using the Sequence generator, you have to supply the name for the actual sequence to be used as a parameter to the generator (see below).
GUIDs have some interesting aspects:
They have one serious disadvantage, though: If we use clustered primary keys (the default in SQL Server), because generated GUIDs are not sequential they will make the engine continually reorganize the index, adding new keys either before or after the existing ones. To deal with this problem, NHibernate has implemented an alternative version, GuidComb, which is based on an algorithm designed by Jimmy Nilsson and available at http://www.informit.com/articles/article.aspx?p=25862. It basically consists of making a GUID sequential: two GUIDs generated in sequence are also numerically sequential, while remaining unique. If you really need a GUID as your primary key, do choose GuidComb. However, do keep in mind that GUIDs do take much more space—16 bytes as opposed to the four bytes of an integer.
The HighLow generation strategy is recommended for most scenarios. It is database-independent, allows batching, copes well with the Unit of Work concept, and, although it does require a SELECT and an UPDATE when the first record is to be inserted and additional ones when the low values are exhausted, the low values are immediately available. Plus, a range can be quite large, allowing for many records to be inserted without performing additional queries.
As for manually assigned identifiers, there are certainly scenarios where they are useful. But they have a big problem. Because NHibernate uses the identifier value (or lack thereof) to tell if a record is to be updated or inserted, it needs to issue a SELECT before persisting an entity in order to know if the record already exists. This defeats batching and may present problems if multiple sessions are to be used simultaneously.
Apart from the generation strategy, an identifier also supports the following properties:
It is possible to pass parameters to identifier generators for those that require it. One example would be specifying the sequence name for the Sequence generator. Examples are included in the mapping sections.
Like table relations, an entity may also be associated with another entity. It is said that the entity’s class has a reference for another entity’s class. There are two types of relations:
A reference has the type of the class on the other endpoint. There can be even multiple references to the same class, of course, with different names. The properties of the reference are:
An entity (parent) can be associated with multiple entities (children) of some type at the same time. These collections can be characterized in a number of ways:
NHibernate has the following collection types:
NHibernate Collection Types
Collection | Relations | Items to Store | Index Type | .NET Types |
|---|---|---|---|---|
Set (non-indexed, bidirectional, inverse) | One-to-many, many-to-many | Entities, elements, components | N/A | IEnumerable<T>, ICollection<T>, Iesi.Collections.Generic.ISet<T> |
Bag (non-indexed, bidirectional, inverse or non-inverse) | One-to-many, many-to-many, values | Entities, values, components | N/A | IEnumerable<T>, ICollection<T>, IList<T> |
List (indexed, bidirectional, inverse or non-inverse) | One-to-many, many-to-many, values | Entities, values, components | Number | IEnumerable<T>, ICollection<T>, IList<T> |
Map (indexed, unidirectional, inverse or non-inverse) | Many-to-many, values | Entities, values, components | Entity, scalar value | IDictionary<TKey, TValue> |
Id Bag (non-indexed, unidirectional, inverse or non-inverse) | One-to-many, Many-to-many, values | Entities, values, components | Number | IEnumerable<T>, ICollection<T>, IList<T> |
Array (indexed, unidirectional, inverse or non-inverse) | One-to-many, many-to-many, values | Entities, values, components | Number | IEnumerable<T>, ICollection<T>, T [] |
Primitive Array (indexed, unidirectional, non-inverse) | One-to-many, many-to-many, values | Values of primitive types | Number | IEnumerable<T>, ICollection<T>, T [] |
Tip: You cannot use the .NET BCL System.Collections.Generic.ISet<T>; as of now, NHibernate requires the use of Iesi.Collections.
Collections have the following attributes:
Some remarks:
Sets and bags are stored using only one table for each entity, and one of these tables contains a foreign key for the other:
|
|
It will typically be declared like this in code:
public virtual Iesi.Collections.Generic.ISet<Comment> Comments { get; protected set; } |
Lists also need only two tables, but they use an additional, unmapped column for storing one entity’s order in its parent’s collection:
|
|
A list (and also a bag) is mapped as an IList<T> and the order of its elements is dictated by the list’s indexed column attribute, which must be an integer.
public virtual IList<Post> Posts { get; protected set; } |
Many-to-many relations do require an additional mapping table, which does not translate to any class but is used transparently by NHibernate:
|
|
These are represented by two set collections, one on each class, only one of them being inverse.
public class User { public virtual Iesi.Collections.Generic.ISet<Blog> Blogs { get; protected set; } } public class Blog { public virtual Iesi.Collections.Generic.ISet<User> Users { get; protected set; } } |
Maps also need an additional table for storing the additional property (in the case where the value is a single scalar) or the key to the other endpoint entity (for a one-to-many relation):
|
|
Maps are represented in .NET by IDictionary<TKey, TValue>. The key is always the containing class and the values can either be entities (in which we have a one-to-many or many-to-many relation), components (complex properties) or elements (primitive types).
public virtual IDictionary<String, String> Attributes { get; protected set; } |
Now we get to the bottom of it all. In the beginning, NHibernate only had XML-based mappings. This is still supported and it basically means that, for every entity, there must be a corresponding XML file that describes how the class binds to the data model (you can have a single file for all mappings but this makes it more difficult to find a specific mapping). The XML file must bind the entity class to a database table and declare the properties and associations that it will recognize. One important property that must be present is the one that contains the identifier for the entity instance, the table’s primary key.
By convention, NHibernate’s XML mapping files end with HBM.XML (in any case). The mappings for this example might be (bear with me, an explanation follows shortly):
The way to add IntelliSense to the HBM.XML files is like this:
First, the mapping for the User class, which should go in a User.hbm.xml file:
Next, the Blog class (Blog.hbm.xml):
The Post (Post.hbm.xml):
A Post’s Comments (Comment.hbm.xml):
And, finally, a Post’s Attachments (Attachment.hbm.xml):
Let’s analyze what we have here.
First, all entities have a class mapping declaration. On this declaration we have:
Next, we always need an identifier declaration. In it, we have the following attributes:
If we need to pass parameters, we can do it like this:
<?xml version="1.0" encoding="utf-8"?> <hibernate-mapping namespace="Succinctly.Model" assembly="Succinctly.Model" xmlns="urn:nhibernate-mapping-2.2"> <class name="Attachment" lazy="true" table="`attachment`"> <id name="AttachmentId" column="`attachment_id`" generator="hilo"> <param name="sequence">ATTACHMENT_SEQUENCE</param> </id> <!-- … -> </hibernate-mapping> |
Then, we have scalar property declarations. For each property we need:
We have some complex properties for which we use component declarations. They are useful for storing values that conceptually should be together (see the mappings for the Details property of the User class and the Details property of the Comment class). A component contains:
References come up next. With it, we declare that our entities are associated to the children of another entity. See, for example, the Blog association of the Post entity. A typical mapping includes:
Finally, we have collections. These can be of several types (for a discussion, see the Collections section). In this example, we have collections of entities (for example, see the Comments collection of the Post entity) and collections of strings (the Tags collections of the Post class, mapped as bags, lists and sets, respectively). The possible attributes depend on the actual collection but all include:
If you are to use mapping by XML, pay attention to this: You can either include the HBM.XML files as embedded resources in the assembly where the classes live or you can have them as external files.
If you want to include the resource files as embedded resources, which is probably a good idea because you have less files to deploy, make sure of two things:


//add an assembly by name cfg.AddAssembly("Succinctly.Model"); //add an Assembly instance cfg.AddAssembly(typeof(Blog).Assembly); |
<session-factory> <!-- … -> <mapping assembly="Succinctly.Model" /> </session-factory> |
Tip: Beware! Any changes you make to HBM.XML files are not automatically detected by Visual Studio so you will have to build the project explicitly whenever you change anything.
If you prefer instead to have external files:
//add a single file cfg.AddFile("Blog.hbm.xml"); //add all .HBM.XML files in a directory cfg.AddDirectory(new DirectoryInfo(".")); |
Mapping by code is new in NHibernate as of version 3.2. Its advantage is that there is no need for additional mapping files and, because it is strongly typed, it is more refactor-friendly. For example, if you change the name of a property, the mapping will reflect that change immediately.
With mapping by code, you typically add a new class for each entity that you wish to map and have that class inherit from NHibernate.Mapping.ByCode.Conformist.ClassMapping<T> where T is the entity class. The following code will result in the exact same mapping as the HBM.XML version. You can see that there are similarities in the code structure and the method names closely resemble the XML tags.
UserMapping class:
Tip: Add a using statement for namespaces NHibernate, NHibernate.Mapping.ByCode.Conformist, NHibernate.Mapping.ByCode, and NHibernate.Type.
BlogMapping class:
PostMapping class:
public class PostMapping : ClassMapping<Post> { public PostMapping() { this.Table("post"); this.Lazy(true);
this.Id(x => x.PostId, x => { x.Column("post_id"); x.Generator(Generators.HighLow); });
this.Property(x => x.Title, x => { x.Column("title"); x.Length(50); x.NotNullable(true); }); this.Property(x => x.Timestamp, x => { x.Column("timestamp"); x.NotNullable(true); }); this.Property(x => x.Content, x => { x.Column("content"); x.Length(2000); x.NotNullable(true); x.Type(NHibernateUtil.StringClob); });
this.ManyToOne(x => x.Blog, x => { x.Column("blog_id"); x.NotNullable(true); x.Lazy(LazyRelation.NoProxy); });
this.Set(x => x.Tags, x => { x.Key(y => { y.Column("post_id"); y.NotNullable(true); }); x.Cascade(Cascade.All); x.Lazy(CollectionLazy.NoLazy); x.Fetch(CollectionFetchMode.Join); x.Table("tag"); }, x => { x.Element(y => { y.Column("tag"); y.Length(20); y.NotNullable(true); y.Unique(true); }); }); this.Set(x => x.Attachments, x => { x.Key(y => { y.Column("post_id"); y.NotNullable(true); }); x.Cascade(Cascade.All | Cascade.DeleteOrphans); x.Lazy(CollectionLazy.Lazy); x.Inverse(true); }, x => { x.OneToMany(); }); this.Bag(x => x.Comments, x => { x.Key(y => { y.Column("post_id"); }); x.Cascade(Cascade.All | Cascade.DeleteOrphans); x.Lazy(CollectionLazy.Lazy); x.Inverse(true); }, x => { x.OneToMany(); }); } } |
CommentMapping class:
AttachmentMapping class:
Notice that for every thing (but table and columns names and raw SQL) there are strongly typed options and enumerations. All options are pretty similar to their HBM.XML counterpart, so moving from one mapping to the other should be straightforward.
For passing parameters to the generator, one would use the following method:
public class AttachmentMapping : ClassMapping<Attachment> { public AttachmentMapping() { this.Table("attachment"); this.Lazy(true);
this.Id(x => x.AttachmentId, x => { x.Column("attachment_id"); x.Generator(Generators.HighLow, x => x.Params(new { sequence = "ATTACHMENT_SEQUENCE" } )); }); //… } |
One problem with mapping by code—or more generally speaking, with LINQ expressions—is that you can only access public members. However, NHibernate lets you access both public as well as non-public members. If you want to map non-public classes, you have to use their names, for example:
this.Id("AttachmentId", x => { //… }); this.Property("Filename", x => { //… }); this.ManyToOne("Post", x => { //… }); |
Now that you have mapping classes, you need to tie them to the Configuration instance. Three ways to do this:
For completeness’ sake, let me tell you that you can use mapping by code without creating a class for each entity to map by using the methods in the ModelMapper class:
mapper.Class<Blog>(ca => { ca.Table("blog"); ca.Lazy(true); ca.Id(x => x.BlogId, map => { map.Column("blog_id"); map.Generator(Generators.HighLow); }); //… |
I won’t include a full mapping here but I think you get the picture. All calls should be identical to what you would have inside a ClassMapping<T>.
Yet another option is mapping by attributes. With this approach, you decorate your entity classes and properties with attributes that describe how they are mapped to database objects. The advantage with this is that your mapping becomes self-described, but you must add and deploy a reference to the NHibernate.Mapping.Attributes assembly, which is something that POCO purists may not like.
First, you must add a reference to NHibernate.Mapping.Attributes either by NuGet or by downloading the binaries from the SourceForge site at http://sourceforge.net/projects/nhcontrib/files/NHibernate.Mapping.Attributes. Let’s choose NuGet:

Make sure that the project that contains your entities references the NHibernate.Mapping.Attributes assembly. After that, make the following changes to your entities, after adding a reference to NHibernate.Mapping.Attributes namespace:
User class:
Blog class:
Post class:
Comment class:
Attachment class:
And, finally, the UserDetail class also needs to be mapped (it is the implementation of the Details component of the User and Comment classes):
|
public class UserDetail { [Property(Name = "Url", Column = "url", Length = 50, NotNull = false)] public String Url { get; set; }
[Property(Name = "Fullname", Column = "fullname", Length = 50, NotNull = true)] public String Fullname { get; set; }
[Property(Name = "Email", Column = "email", Length = 50, NotNull = true)] public String Email { get; set; } } |
After that, we need to add this kind of mapping to the NHibernate configuration. Here’s how:
HbmSerializer serializer = new HbmSerializer() { Validate = true }; using (MemoryStream stream = serializer.Serialize(typeof(Blog).Assembly)) { cfg.AddInputStream(stream); } |
Tip: Add a reference to the System.IO and NHibernate.Mapping.Attributes namespaces.
For adding parameters to the identifier generator:
[Class(Table = "attachment", Lazy = true)] public class Attachment { [Id(0, Column = "attachment_id", Name = "AttachmentId")] [Generator(1, Class = "hilo")] [Param(Name = "sequence", Content = "ATTACHMENT_SEQUENCE")] public virtual Int32 AttachmentId { get; protected set; } // } |
And that’s it. There are two things of which you must be aware when mapping with attributes:
Consider the following class hierarchy:

We have here an abstract concept, a Person, and two concrete representations of it, a NationalCitizen and a ForeignCitizen. Each Person must be one of them. In some countries (like Portugal, for example), there is a National Identity Card, whereas in other countries no such card exists—only a passport and the country of issue.
In object-oriented languages, we have class inheritance, which is something we don’t have with relational databases. So a question arises: How can we store this in a relational database?
In his seminal work Patterns of Enterprise Application Architecture, Martin Fowler described three patterns for persisting class hierarchies in relational databases:



You can see a more detailed explanation of these patterns in Martin’s website at http://martinfowler.com/eaaCatalog/index.html. For now, I’ll leave you with some general thoughts:
As far as NHibernate is concerned, there really isn’t any difference: classes are naturally polymorphic. See the section on Inheritance to learn how to perform queries on class hierarchies.
Let’s start with Single Table Inheritance, by code. First, the base class, Person:
public class PersonMapping : ClassMapping<Person> { public PersonMapping() { this.Table("person"); this.Lazy(true); this.Discriminator(x => { x.Column("class"); x.NotNullable(true); }); this.Id(x => x.PersonId, x => { x.Column("person_id"); x.Generator(Generators.HighLow); });
this.Property(x => x.Name, x => { x.Column("name"); x.NotNullable(true); x.Length(100); });
this.Property(x => x.Gender, x => { x.Column("gender"); x.NotNullable(true); x.Type<EnumType<Gender>>(); }); } } |
The only thing that’s new is the Discriminator option, which we use to declare the column that will contain the discriminator value for all subclasses.
Next, the mapping for the NationalCitizen class:
public class NationalCitizenMappping : SubclassMapping<NationalCitizen> { public NationalCitizenMappping() { this.DiscriminatorValue("national_citizen"); this.Lazy(true);
this.Property(x => x.NationalIdentityCard, x => { x.Column("national_identity_card"); x.Length(20); x.NotNullable(true); }); } } |
And, finally, the ForeignCitizen:
public class ForeignCitizenMapping : SubclassMapping<ForeignCitizen> { public ForeignCitizenMapping() { this.DiscriminatorValue("foreign_citizen"); this.Lazy(true);
this.Property(x => x.Country, x => { x.Column("country"); x.Length(20); x.NotNullable(true); });
this.Property(x => x.Passport, x => { x.Column("passport"); x.Length(20); x.NotNullable(true); }); } } |
If you would like to map this by XML, here’s one possibility:
<hibernate-mapping namespace="Succinctly.Model" assembly="Succinctly.Model" xmlns="urn:nhibernate-mapping-2.2"> <class name="Person" lazy="true" table="`person`" abstract="true"> <id column="`person_id`" name="PersonId" generator="hilo"/> <discriminator column="`class`"/> <property name="Name" column="`name`" length="100" not-null="true"/> <property name="Gender" column="gender"/> </class> </hibernate-mapping> |
<hibernate-mapping namespace="Succinctly.Model" assembly="Succinctly.Model" xmlns="urn:nhibernate-mapping-2.2"> <subclass name="NationalCitizen" lazy="true" extends="Person" discriminator-value="national_citizen"> <property name="NationalIdentityCard" column="`national_identity_card`" length="20" not-null="false"/> </subclass> </hibernate-mapping> |
<hibernate-mapping namespace="Succinctly.Model" assembly="Succinctly.Model" xmlns="urn:nhibernate-mapping-2.2"> <subclass name="ForeignCitizen" lazy="true" extends="Person" discriminator-value="foreign_citizen"> <property name="Country" column="`country`" length="20" not-null="false"/> <property name="Passport" column="`passport`" length="20" not-null="false"/> </subclass> </hibernate-mapping> |
And, finally, if your choice is attributes mapping, here’s how it goes:
[Class(0, Table = "person", Lazy = true, Abstract = true)] [Discriminator(1, Column = "class")] public abstract class Person { [Id(0, Name = "PersonId", Column = "person_id")] [Generator(1, Class = "hilo")] public virtual Int32 PersonId { get; protected set; }
[Property(Name = "Name", Column = "name", Length = 100, NotNull = true)] public virtual String Name { get; set; }
[Property(Name = "Gender", Column = "gender", TypeType = typeof(EnumType<Gender>), NotNull = true)] public virtual Gender Gender { get; set; } } |
[Subclass(DiscriminatorValue = "national_citizen", ExtendsType = typeof(Person), Lazy = true)] public class NationalCitizen : Person { [Property(Name = "NationalIdentityCard", Column = "national_identity_card", Length = 50, NotNull = false)] public virtual String NationalIdentityCard { get; set; } } |
[Subclass(DiscriminatorValue = "foreign_citizen", ExtendsType = typeof(Person), Lazy = true)] public class ForeignCitizen : Person { [Property(Name = "Passport", Column = "passport", Length = 50, NotNull = false)] public virtual String Passport { get; set; }
[Property(Name = "Country", Column = "country", Length = 50, NotNull = false)] public virtual String Country { get; set; } } |
When it comes to querying, a query on the Person class looks like this:
IEnumerable<Person> allPeopleFromLinq = session.Query<Person>().ToList(); |
Produces this SQL:
SELECT person0_.person_id AS person1_2_, person0_.name AS name2_, person0_.gender AS gender2_, person0_.passport AS passport2_, person0_.country AS country2_, person0_.national_identity_card AS national7_2_, person0_.[class] AS class2_2_ FROM person person0_ |
If we want to restrict on a specific type:
IEnumerable<NationalCitizen> nationalCitizensFromLinq = session.Query<NationalCitizen>().ToList(); |
Will produce this SQL:
SELECT nationalci0_.person_id AS person1_2_, nationalci0_.name AS name2_, nationalci0_.gender AS gender2_, nationalci0_.national_identity_card AS national7_2_ FROM person nationalci0_ WHERE nationalci0_.[class] = 'national_citizen' |
Moving on, we have Class Table Inheritance, which is also known in NHibernate jargon as joined subclass because we need to join two tables together to get the class’ values. Here are its loquacious mappings (the mappings for the Person class remain the same, except that we removed the Discriminator call):
public class PersonMapping : ClassMapping<Person> { public PersonMapping() { this.Table("person"); this.Lazy(true); this.Id(x => x.PersonId, x => { x.Column("person_id"); x.Generator(Generators.HighLow); });
this.Property(x => x.Name, x => { x.Column("name"); x.NotNullable(true); x.Length(100); });
this.Property(x => x.Gender, x => { x.Column("gender"); x.NotNullable(true); x.Type<EnumType<Gender>>(); }); } } |
public class NationalCitizenMappping : JoinedSubclassMapping<NationalCitizen> { public NationalCitizenMappping() { this.Table("national_citizen"); this.Lazy(true);
this.Key(x => { x.Column("person_id"); x.NotNullable(true); }); this.Property(x => x.NationalIdentityCard, x => { x.Column("national_identity_card"); x.Length(20); x.NotNullable(true); }); } } |
For ForeignCitizen:
Here, what’s different in the mapping of the subclasses is the introduction of a Key, which we use to tell NHibernate the column to use for joining with the PERSON table.
The XML equivalent would be:
<hibernate-mapping namespace="Succinctly.Model" assembly="Succinctly.Model" xmlns="urn:nhibernate-mapping-2.2"> <class name="Person" lazy="true" table="`PERSON`" abstract="true"> <id column="`PERSON_ID`" name="PersonId" generator="hilo"/> <property name="Name" column="`name`" length="100" not-null="true"/> <property name="Gender" column="gender"/> </class> </hibernate-mapping> |
<hibernate-mapping namespace="Succinctly.Model" assembly="Succinctly.Model" xmlns="urn:nhibernate-mapping-2.2"> <joined-subclass name="NationalCitizen" lazy="true" extends="Person" table="`NATIONAL_CITIZEN`"> <key column="`PERSON_ID`"/> <property name="NationalIdentityCard" column="`national_identity_card`" length="20" not-null="false"/> </joined-subclass> </hibernate-mapping> |
<hibernate-mapping namespace="Succinctly.Model" assembly="Succinctly.Model" xmlns="urn:nhibernate-mapping-2.2"> <joined-subclass name="ForeignCitizen" lazy="true" extends="Person" table="`FOREIGN_CITIZEN`"> <key column="`PERSON_ID`"/> <property name="Country" column="`country`" length="20" not-null="false"/> <property name="Passport" column="`passport`" length="20" not-null="false"/> </joined-subclass> </hibernate-mapping> |
Finally, the attributes version:
[Class(0, Table = "person", Lazy = true, Abstract = true)] public abstract class Person { //… } |
[JoinedSubclass(0, Table = "national_citizen", ExtendsType = typeof(Person), Lazy = true)] [Key(1, Column = "person_id")] public class NationalCitizen : Person { //… } |
[JoinedSubclass(0, Table = "foreign_citizen", ExtendsType = typeof(Person), Lazy = true)] [Key(1, Column = "person_id")] public class ForeignCitizen : Person { //… } |
A query for the base class produces the following SQL, joining all tables together:
SELECT person0_.person_id AS person1_6_, person0_.name AS name6_, person0_.gender AS gender6_, person0_1_.passport AS passport11_, person0_1_.country AS country11_, person0_2_.national_identity_card AS national2_12_, CASE WHEN person0_1_.person_id IS NOT NULL THEN 1 WHEN person0_2_.person_id IS NOT NULL THEN 2 WHEN person0_.person_id IS NOT NULL THEN 0 END AS clazz_ FROM person person0_ LEFT OUTER JOIN foreign_citizen person0_1_ ON person0_.person_id = person0_1_.person_id LEFT OUTER JOIN national_citizen person0_2_ ON person0_.person_id = person0_2_.person_id |
And one for a specific class:
SELECT nationalci0_.person_id AS person1_2_, nationalci0_1_.name AS name2_, nationalci0_1_.gender AS gender2_, nationalci0_.national_identity_card AS national2_8_ FROM national_citizen nationalci0_ INNER JOIN person nationalci0_1_ ON nationalci0_.person_id = nationalci0_1_.person_id |
Finally, the last mapping strategy, Concrete Table Inheritance or union-subclass. The mappings, using the loquacious API:
public class NationalCitizenMappping : UnionSubclassMapping<NationalCitizen> { public NationalCitizenMappping() { this.Table("national_citizen"); this.Lazy(true); //… } } |
public class ForeignCitizenMappping : UnionSubclassMapping<ForeignCitizen> { public ForeignCitizenMappping() { this.Table("foreign_citizen"); this.Lazy(true); //… } } |
Tip: The mapping for Person is exactly the same as for the Class Table Inheritance.
As you can see, the only difference is that the Key entry is now gone. That’s it.
Also in XML:
<hibernate-mapping namespace="Succinctly.Model" assembly="Succinctly.Model" xmlns="urn:nhibernate-mapping-2.2"> <union-subclass name="NationalCitizen" lazy="true" extends="Person" table="`NATIONAL_CITIZEN`"> <!-- … --> </union-subclass> </hibernate-mapping> |
<hibernate-mapping namespace="Succinctly.Model" assembly="Succinctly.Model" xmlns="urn:nhibernate-mapping-2.2"> <union-subclass name="ForeignCitizen" lazy="true" extends="Person" table="`FOREIGN_CITIZEN`"> <!-- … --> </union-subclass> </hibernate-mapping> |
And in attributes:
[UnionSubclass(0, Table = "national_citizen", ExtendsType = typeof(Person), Lazy = true)] public class NationalCitizen : Person { //… } |
[UnionSubclass(0, Table = "foreign_citizen", ExtendsType = typeof(Person), Lazy = true)] public class ForeignCitizen : Person { //… } |
At the end of this section, let’s see what the SQL for the base Person class looks like when using this strategy:
SELECT person0_.person_id AS person1_6_, person0_.name AS name6_, person0_.gender AS gender6_, person0_.passport AS passport11_, person0_.country AS country11_, person0_.national_identity_card AS national1_12_, person0_.clazz_ AS clazz_ FROM ( SELECT person_id, name, gender, passport, country, null AS national_identity_card, 1 AS clazz_ FROM foreign_citizen UNION ALL SELECT person_id, name, gender, null AS passport, null AS country, national_identity_card, 2 AS clazz_ FROM national_citizen ) person0_ |
And a query for a particular class, in this case, NationalCitizen will produce the following SQL:
SELECT nationalci0_.person_id AS person1_6_, nationalci0_.name AS name6_, nationalci0_.gender AS gender6_, nationalci0_.national_identity_card AS national1_12_ FROM national_citizen nationalci0_ |
We have seen a lot of things in this chapter. For mapping APIs, I risk offering my opinion: choose mapping by code. It is more readable and easy to maintain than HBM.XML, and it is strongly typed, meaning that, if you change some property, you will automatically refactor the mapping and won’t need to bother to explicitly compile your project because, since it’s code, Visual Studio will do it whenever necessary. Attributes require a reference to a NHibernate-specific assembly and are more difficult to change than mapping by code.
In the end, with whatever mapping you choose, here is the resulting database model:

For mapping inheritances, it is a decision worth thinking over, as there is no direct answer. But I am in favor of Class Table Inheritance because there is no duplication of columns for each concrete class and because each table only has a few columns.