I originally blogged about this here and here on my blog but Fabio suggested I add these posts to the Forge, so here I am. I have to say I am a little exited to make my first contribution to the Wiki [H]. I will try to add a bit more to them and show some examples that do not use the
TableGenerator class but directly implement
IIdentifierGenerator . Please let me know if there are any mistakes or anything else you want to see.
Before beginning I recommend that you download the NH source code and take a quick look at the
IPersistentIdentifierGenerator classes as you will be working with these classes and interfaces to implement your custom generator.
Currently I am doing some work developing a utility that imports securities data into a fairly old legacy database. It is still SQL but the database and software havn't been updated in about 15 years (maybe more) and never will be again.
The original application for this database uses a primitive hilo-style ID generator mechanism. Tt simply queries an integer value from a table in the database and increments it, then it adds a fixed value to it and uses that as the next unique ID. It doesn't seem very optimal, but that's how it works and there's no changing it.
It isn't complex, but it also isn't one of the identity strategies available in nHibernate and I fully intend to use nHibernate.
So what to do? One option would be to set
generator="assigned" and do it all myself, assigning the ID using
or something similar... but what fun would that be? No, I've decided to
take a peak at the nHibernate code and learn to extend it. Read on to
find out how to create a custom class that inherits from
IIdentifierGenerator to implement your own custom ID generator strategy for nHibernate.
One of the beautiful things about nHibernate the ability to extend it. You can extend its functionality without ever touching their source code.
It was very easy to locate the classes responsible for ID generation in the
NHibernate.ID namespace. It was also clear immediately that most classes inherited from
IIdentifierGenerator. I also knew that my strategy was similar to the procedure that hilo implements so I started with
TableHiLoGenerator which inherts from
After playing around with this for a while and trying different things, it was clear that
TableGenerator, basically takes a
the configuration, reads a value, increments it and passes it on as an
ID. Voila! That was almost exactly what I needed to do with little else
to change. I was quickly able to create my own class inheriting from
public class FDPSequence : TableGenerator
private const Int32 SeedValue = 1048576;
private static readonly ILog Log = LogManager.GetLogger(typeof(FDPSequence));
public override object Generate(ISessionImplementor session, object obj)
int counter = Convert.ToInt32(base.Generate(session, obj));
return counter + SeedValue + 1;
Not much to that is there? Now that is a fairly simple case, but
even getting more complex isn't much harder than that. You can
IIdentifierGenerator directly and create
something far more customized. TableGenerator actually implements
IPersistentIdentifierGenerator which is an interface for identifiers
which need to write and updated value to the database. However, since
nHibernate provides so many versatile and inheritable base class
generators that you will likely get by with inheriting one of those and
extending it as I did.
In order to use this custom generator class I setup my configuration to use it. Using Fluent I wrote a mapping like this:
public class InvestSpecMap : FDPEntityBaseMap<InvestSpec>
Id(x => x.Id).ColumnName(tablename + "ID")
.AddGeneratorParam("table", "zSysCounters") //Name of the ID counter table
.AddGeneratorParam("column", "InvestSpec"); //ID counter column for this table
Map(x => x.AnnualDividend).ColumnName("AnnlzdDiv");
Map(x => x.AssetClass);
Map(x => x.CurrencyType);
Map(x => x.CusipNumber).ColumnName("CusipNum");
Map(x => x.DividendFreq).ColumnName("DivFrequency");
Map(x => x.FullName);
Map(x => x.MaturityDate).ColumnName("MaturityDt");
Map(x => x.PriceAsOf).ColumnName("PriceAsOfDt");
Map(x => x.ShortName).ColumnName("AbbrName");
Map(x => x.TaxTreatment);
Map(x => x.TickerSymbol);
Map(x => x.Type);
Map(x => x.UnitPrice);
You can do the same with XML mappings just declare the assembly and class as you usually would in XML. This is actually the first time I've used FluentNH.
What is great about this is that even when testing my mappings with SQLLite,
actually able to create the identity table based on the tablename and
columnname properties in the nHibernate configuration. The main benefit
here however is that identity generation remains transparent to the
rest of my application. Also doing something cool with nHibernate,
that's priceless of course
A while ago I posted about writing a custom id generator using nHibernate and extending one of the existing Generator classes nHibernate provides. This was done to support working with a legacy database which uses a custom id generation strategy. One thing I like to do with all my apps is write some basic CRUD tests to make sure my mappings and queries are all working properly.
To do this I use Oren's approach of creating a temporary in-memory database using SQLite to work with. This involves using the
in nHibernate to generate the DDL to create the database. If you are
using a table generator strategy it also needs to create the table and
columns for the custom id generator. In some cases you may need to
customize the DDL for your Id table.
As I discovered the DDL to generate the table to store the next id value for my custom id is only created once and does not check the column name value for each class. For my id generator there is a separate column used for each table in the database, so this was a problem. For example, the Price table has a column in the Counter table called Price to store it's counter and the Holding table has a column called Holding, but I was only getting one column (this appears to be a problem with hilo and SchemaExport, you can currently only have one column per database)
The TableGenerator class supports two parameters which are set in the HBM files (or using fluent) tablename and columnname which
set the table and column names used for the id counter. If you have a
copy of the nHibernate source code you will see that the TableGenerator
to create and drop the table for the id generator. Unfortunately, as I
mentioned above this is only called once, and so it only creates one
table with one column for whichever mapping is handled first.
In order to fix this I had to copy the
TableGenerator class (yes, yes, I know copy-paste is bad form) and modify those functions. I also added a third parameter allcolumnnames in which I can provide a comma separated list of all the columns the table needs to have (one per table in my database). The
SqlCreateStrings()is simple changed to:
(Yes, yes, I know I should have learned from my last post and used a
StringBuilder here as I pointed out in my last post, but seriously, this method will only ever be called once)
Now when it is called it will create the correct table with all the columns I need. A couple of example mappings for the database in FluentNH are shown below:
To be completely "clean code" and avoid repeating myself I should probably factor out the
Id() code into a method in an inhereted base class so I can call it from each mapping class like this:
Now I can unit test using a temporary in-memory SQLite database and all my tests are passing.