Recently I had a case where I needed to switch from the Identity generator to the HiLo generator in an existing project with existing data. Why? Well for one thing it gives better performance since identities brakes the ability to use batch inserts, since the generated Id needs to be returned (selected) after each insert.
I didn’t want to go through and configure params for each entity, hence I created a custom implementation of an Id-generator and added some conventionbased configuration in that class instead.
How to setup HiLo the normal way
The xml below is taken from 5.1.4.2 – Hi/Lo Algorithm – (http://www.nhforge.org/doc/nh/en/index.html#mapping-declaration-id) You can of course skip the params and go with default values.
<id name="Id"> <generator class="hilo"> <param name="table">hi_value</param> <param name="column">next_value</param> <param name="max_lo">100</param> </generator> </id>
This will let every entity share the same table (that’s ok) and the same column for the next hi-value. For me, this wasn’t something I wanted. I wanted one specific value per entity. I also wanted one row per entity so that I could keep away from unnecessary update locks.
I wanted the following table:
create table dbo.NHibHiLoIdentities ( EntityName varchar(128) not null primary key, NextHiValue bigint not null )
Custom generator
To get the behavior I wanted I extended the existing TableHiLoGenerator.
public class NHibIdGenerator : TableHiLoGenerator
{
public override void Configure(IType type, IDictionary<string, string> parms, Dialect dialect)
{
if (!parms.ContainsKey("table"))
parms.Add("table", "NHibHiLoIdentities");
if (!parms.ContainsKey("column"))
parms.Add("column", "NextHiValue");
if (!parms.ContainsKey("max_lo"))
parms.Add("max_lo", "100");
if (!parms.ContainsKey("where"))
parms.Add("where", string.Format("EntityName='{0}'", parms["target_table"]));
base.Configure(type, parms, dialect);
}
}
This way, if I want, I can specify the params in the xml-mapping, but if I don’t they will be injected in the Configure override. The trick is the where criteria. This tells NHibernate that I want to have a where criteria against the column “EntityName” generated when the next Hi-value is going to be checked out. I use the name of the entity which is allready populated in the dictionary.
Determine the new value
Since there allready was data in the tables for each entity I just looked at the existing Max(Id) for each and inserted a row in the NHibHiLoIdentities with concerns taken to the value of max_lo above.
insert into dbo.NHibHiLoIdentities (EntityName, NextHiValue) values ( 'Customer', coalesce((select max(Id)/100 from dbo.Customer) + 1, 1) )
Update your mappings
You will also have to update the mappings with information to make use of your Id-generator. It’s not hard. Just specifiy the fullname of your class for the generator element.
<id name="Id"> <generator class="NHibAdventures.Storage.NHibIdGenerator, NHibAdventures.Storage" /> </id>
That’s it. Happy tweaking.
//Daniel
Ps! If you need a easy get going sandbox environment, checkout https://github.com/danielwertheim/NHibernate-Adventures
Pingback: DotNetShoutout
Pingback: NHibernate – Custom Id-generator « Daniel... | .NET and NHibernate | Syngu
Does this solution generate the extra EntityName column when you run a schemaexport or does this depend on creating the table manually?
Thanks
Jason
I created it manually.
//Daniel
ive adapted your code to java and hilo reads from the table i tell , but it doesnt care about the entityname. whatever i write as the entry name to the database , it always uses the first row’s NextHiValue column.
have any idea? i
Nope, sorry. Definitively not a Java guy. That was to long time ago.
//Daniel
Pingback: hibernate HiLo – one table for all entities | PHP Developer Resource