Try fast search NHibernate

07 February 2009

NH2.1.0: New generators

This post is an recognition that we had lost the fight with Identity's fans. As you probably know I don’t like Identity, but as a NHibernate-developer I can’t ignore ours users requests.
This is the complete list of POID (Persistent Object IDentifier) generators provided by NHibernate2.1.0:
  • native
  • identity (improved)
  • sequence
  • hilo
  • seqhilo
  • assigned
  • guid
  • guid.comb
  • guid.native (new)
  • select (new)
  • sequence-identity (new)
  • trigger-identity (new)
  • uuid.hex
  • uuid.string
  • counter
  • increment
  • foreign

guid.native

Is a new generator that allow to use the RDBMS native function to generate GUID. The behavior is similar to the “sequence” generator: when a new object is saved NH run two query; the first to retrieve the GUID value and the second to insert the entity using the Guid retrieved from RDBMS. The type in your entity is System.Guid and the SQLtype depend from the dialect (RAW(16) in Oracle, UniqueIdentifier in MsSQL for example).
Queries that runs in ORACLE are:
  1. select rawtohex(sys_guid()) from dual
  2. INSERT INTO MyEntityTable (Id, Description) VALUES (:p0, :p1)
The  parameter “:p0” has the value retrieved in the first query.

sequence-identity

The “sequence-identity” is based on “sequence” but work as an “identity”. The POID values is retrieved with the INSERT query. The types, in your entity, maybe are System.Int32 or System.Int64 depending on your RDBMS sequence generator.
The query that run in ORACLE is:
INSERT INTO my_entity (id, name) VALUES (hibernate_sequence.nextval, :p0) returning id into :nhIdOutParam
The “hibernate_sequence” is the default name for a sequence where no alternative name is provided trough the mapping. As you can see, in this case, the “sequence” are working like “identity”, the value of the POID is retrieved immediately and the generator has the same problem of “identity”.

trigger-identity

The “trigger-identity” is a NHibernate specific feature where the POID is generated by the RDBMS at the INSERT query through a BEFORE INSERT trigger. In this case you can use any supported type, including custom type, with the limitation of “single-column” (so far).
The query in ORACLE is:
INSERT INTO my_entity (Name) VALUES (:p0) returning Id into :nhIdOutParam
As you can see the query is very similar to the query used to work with “identity”; the “Id” field is not present in the FieldsNameList nor in VALUES list and the value of the POID is retrieved immediately. What the trigger are doing to generate the “Id” field value is out-side of NH scope.

select

The “select” generator is a deviation of the “trigger-identity”. This generator work together with natural-id feature. The difference “trigger-identity” is that the POID value is retrieved by a SELECT using the natural-id fields as filter. In practice giving
<class name="MyEntity" table="my_entity">
<id name="id">
<generator class="select"/>
</id>
<natural-id>
<property name="name"/>
</natural-id>
</class>

and having a trigger to generate the POID, the queries runs in ORACLE are:
  1. INSERT INTO my_entity (name) VALUES (:p0)
  2. SELECT id FROM my_entity WHERE name = :p0
The POID still retrieved immediately.

identity

The “identity” generator is well known by NH<->MsSQL users but, before NH2.1.0, can’t be used for others RDBMS if the RDBMS don’t support native identity-generator. What happen if you have one multi-RDBMS-application and your DBA want use an identity-style generator in each RDBMS ? Which is your work with mappings files for NHibernate ? Well… we have changed the meaning of “identity”. In NH2.1.0 defining <generator class="identity"/> your are saying : “I want work with an identity-style generator; check my dialect to know which is the correct generator for identity”.
By default, when you specify “identity”, NH run the follow:
if (SupportsIdentityColumns)
{
return typeof(IdentityGenerator);
}
else if (SupportsSequences)
{
return typeof(SequenceIdentityGenerator);
}
else
{
return typeof(TriggerIdentityGenerator);
}
If you need a different behavior you can inherit from the default dialect, for your RDBMS, and override the property IdentityStyleIdentifierGeneratorClass.

Conclusion

Now you have a more easy way to break the unit-of-work pattern, and to nullify the batcher, for all NH’s supported dialects: specify “identity” as your identifier generator.


kick it on DotNetKicks.com

9 comments:

  1. Can you please try to explain again way using Identity in MSSQL is bad.

    I have read the to blog's you have written but I still don't get it.

    ReplyDelete
  2. Identity-style mean, basically, two things:
    1) to know the ID you must run an INSERT, with all field properly values, and the only way to revert the insert is a DELETE or a ROLLBACK of the DB transaction so you are breaking the Unit-Of-Work pattern where the hit to DB are happening at the end of the UoW (see the first link above).
    2) the NHibernate batcher feature is nullified because NH must hit the DB on each insert where the insert is INSERT+SELECT (select the last identity)
    Using a guid generator you don't need to go to the DB to know the ID, the INSERT mean INSERT without come back with something and the UoW work as expected.

    ReplyDelete
  3. >>Now you have a more easy way to break the unit-of-work pattern, and to nullify the batcher, for all NH’s supported dialects: specify “identity” as your identifier generator.

    I love it! Conclusion: its now easier for you to do what we don't recommend you do :D

    ReplyDelete
  4. Until majors continue to recommend or use it in samples I think there will be eternal fight!!!

    tomb aka Tommaso Caldarola

    ReplyDelete
  5. @Tommaso
    A lot of NH experts know the issue of identity-style generators and don't recommend to use it.
    Not every time "majors usage" mean that something is good.

    ReplyDelete
  6. please correct me if am wrong, but i understood that:
    1. batching becomes nullified, since NH has to make to trips to DB. one is to insert the record, second is to get the identity value.
    2. this means that sequence is not the best choice, since it forces to make two calls.

    But what then is the good choide?
    GUID? HILO ?

    ReplyDelete
  7. There is not "THE BEST", it depend...
    BTW, sure, identity-style generators are absolutely not the BEST. Probably I should write something else about this issue so you guy may understand all things around generators.
    BTW, depending on prj, my choice is guid.comb or HiLo (seq-HiLo where available).

    ReplyDelete
  8. Hi Fabio
    I tried to download the latest version of Nhibernate but I cant use select since its not a part of mapping schema that comes along. Also I cant see this verion of 2.10 on source forge am I missing something here?

    ReplyDelete
  9. NH2.1.0 is the trunk. We will release the Alpha1 in may.

    ReplyDelete