When we use a Domain-Driven Design (DDD) NHibernate supports us with the possibility to automatically generate the necessary database schema. Developing the object model first and then derive the database schema from the object model is in our opinion a more "natural" way of implementing a "green field" application than the more "traditional" way of implementing an application where the Entity Relationship Diagram (ERD) is designed first.
When using NHibernate one immediately is looking for a way to avoid the tedious task to manually write the xml-mapping files for each object in the domain. Fortunately the Castle Project has ActiveRecord as one of its sub-projects. ActiveRecords allows us to decorate the classes in our domain directly with Attributes instead of writing xml-mapping files. ActiveRecord is well documented and I do not want to repeat this documentation here. But I want to discuss a concrete example.
But wait! Do I really have to write my classes by hand and decorate them and its respective properties with Attibutes? Isn't there a "better" way of doing it? Yes, indeed there is: Gökhan Altinören has written a VS PlugIn which allows us to visually desing our object model. Its the ActiveWriter PlugIn which I'll be using in the following example.
- A person has hobbies. The relation between a person and a hobby is many-to-many since a person can have many hobbies and a single hobby like swimming can be exercised by many different persons.
- A person has assigned tasks. The relation between a person and a task is also many-to-many since a person can have many assigned tasks and a single task like "write a blog entry on topic xyz..." can be assigned to many different persons.
- A person can blog - as I do here. A person can maintain several blogs but each blog belongs to a single person (at least in my sample). Thus the relation between person and blog is one-to-many.
- To a person we want to be able to store a photo. Since a photo can be rather large we decide NOT to store the photo directly in the Person object but create an extra PersonPhoto object which is owned by the Person object. So as a benefit we have the possibility to only load the PersonPhoto data if really needed. The relation between a person and a photo is one-to-one.
- A person has an address. But also e.g. a company has an address. So we decide to not make the relation between person and address as one-to-one but rather many-to-one.But the relation has a further restriction in so far that the "many" part of the relation is effectively a unique restriction since a single address cannot (in reality) be shared among different persons (or e.g. companies).
This leads us to the following design (in ActiveWriter)
For each class we have to define the mapping via the property window. Just select the corresponding class and modify the atributes similar to the example below
In the above example the mapping is between the Person class and a table also called Person which is defined in the (database) schema gns.
Note that when not defining the Table attribute NHibernate automatically takes the value of the attribute Name as the name for the underlying table.
Note that each object (or class) has a property Key. This is a value which uniquely identifies an instance of the respective type. The Key property is mapped to the primary key column in the underlying database table.
To make our application multi-user friendly I also introduced a Version property for each object. This allows us to handle any concurrent (write) access to a specific object. NHibernate offers built-in functionality for such properties.
The other properties are common properties and are all of type string except for the Photo property in the PersonPhoto class which is of type BinaryBlob.
Each Property of a class can be configured through the Properties Windows. Just activate the corresponding property in the ActiveWriter designer.
For the Version property I have set the following values
For the Key Property of the Person object I have set the following values. Note that I use Guid's for the key. One can as well use int or long.
In the Column attribute you define how the corresponding column will be called in the underlying table Person. If you don't specify a value then NHibernate will automatically take the value of the attribute Name as the column name. This fact was used for the Version property defined above.
The other properties are easy and I'll show here as an example the property Photo of the PersonPhoto class
The most interesting part are the relations among the different classes (or objects). The first time you use ActiveWriter you might be a little bit confused about the naming convention. The author of ActiveWriter is working on it and will soon provide a detailed description. Never the less let's have a look at the various relation types
Let's look at the one-to-one relation between Person and PersonPhoto. I have defined the following attributes
What does this mean? Source Constrained = True means that a photo cannot exist without a corresponding person but vice versa a person can exist without having a photo (Target Constrained = False).Target Cascade = All means that when saving or deleting a person the owned photo is automatically save or deleted too. On the other hand when I save or delete a photo the corresponding person object is not affected (Source Cascade = None).
As an example let's have a look at the relation between Person and Blog(s).
In this example consider the "Source..." as the One-side (here the Person) and the "Target..." as the Many-side (here the Blog) of the relation.
the Target Lazy = True setting which means that I do not want to have all blogs automatically loaded when I load a person object. Only if I access the person's blog(s) NHibernate shall load the corresponding blog(s). The Property Blogs in the Person class is of type IList
Here we look at the relation between the Person and Task classes.
Any many-to-many relation needs a link table in the database. Here we define the link table to be named PersonTask and residing in the schema gns.The setting Source Lazy = True means that I do not want all tasks to be automatically loaded when I load a person. They should only be loaded when I want to access them.The other settings are more or less self-describing.
to be continued...