NHibernate Forge
The official new home for the NHibernate for .NET community

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 trough 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.


Posted feb 09 2009, 09:12 p.m. by Fabio Maulo
Filed under: , ,

Comments

zoechi wrote re: NH2.1.0: New generators
on 05-30-2009 10:28

I wonder while the HiLo generator is working in such a weird way.

The next_hi column is incremented only by 1 no matter which value is set for max_lo.

The generated ids are somewhat of a multiply of next_hi and max_lo.

Why isn't the the last reserved id or alternatively the next available id stored in the HiLo table next_hi column

incremented by max_lo on each access?

This way it would be easy to share the number sequence for id generation with other applications sharing the same database.

The max_lo may be set differently in each class mapping file.

Which one is effectively taken for the calculation of the generated id?

I have found very few posts complaining about this (the first one about 2004) and consider it a bug, as I do.

I wonder why this isn't a problem for more other NHibernate users.

swtmin wrote re: NH2.1.0: New generators
on 06-26-2009 18:29

I have tried using trigger-identity with Oracle, and the SQL generated is missing the RETURNING keyword, so it fails.

Example:

INSERT INTO BANK_ACK_FILE (DATA, UPLOAD_ID, SUB_TYPE_ID, ENTITY_ID, DATA_TYPE_ID) VALUES (:p0, :p1, :p2, :p3, :p4);:p0 = 'Iz in your bank ack phile. Now can haz loan?', :p1 = 1, :p2 = 2006, :p3 = 3070, :p4 = 1019, :nhIdOutParam = NULL

Note the presence of the Id out parameter, but again the RETURNING syntax is missing.

Mapping file has:

   <id name="Id" column="ID" type="Int64">

<generator class="trigger-identity">

</generator>

</id>

Am I doing something wrong, or is this a bug?

swtmin wrote re: NH2.1.0: New generators
on 06-26-2009 19:32

Should probably have include my config:

<?xml version="1.0" encoding="utf-8" ?>

<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2" >

   <session-factory>

       <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>

       <property name="connection.driver_class">NHibernate.Driver.OracleDataClientDriver</property>

       <property name="connection.connection_string">Data Source=dev;User ID=xxxx;password=******;Connection Timeout=45;Max Pool Size=25;Incr Pool Size=2; Decr Pool Size=2</property>

       <property name="show_sql">false</property>

       <property name="dialect">NHibernate.Dialect.Oracle10gDialect</property>

       <property name="connection.release_mode">after_transaction</property>

       <property name="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>

       <mapping assembly="Sfs.Efc.Core.DataModel"/>

   </session-factory>

</hibernate-configuration>

Yesterday's news wrote What's new in NHibernate 2.1
on 07-20-2009 20:44

What's new in NHibernate 2.1

Powered by Community Server (Commercial Edition), by Telligent Systems