I have been able to get NHibernate to work with Oracle databases of which the primary key values are automatically generated in the before-insert triggers, e.g.:
Code:
TRIGGER "NIEK"."ORGANISATION_BEFORE_INSERT"
BEFORE
INSERT
for each row
DECLARE
BEGIN -- executable part starts here
select NIEK.ORGAN_ORGANID_SEQ.nextval into :new.ORGANISATIONID from dual;
END;
But unfortunately not without having to modify NHibernate's code itself.
I have tried different strategies but the problem that always came back was the fact that Oracle needs a parameter to send back anything from the insert statement. But, even with a custom dialect, I haven't been able to define my own parameter on the command object in the current version of NHibernate.
So I solved the problem by adding a method "IsReturnIdentityViaParameter" in NHibernate's Dialect class:
Code:
public virtual bool IsReturnIdentityViaParameter()
{
return false;
}
... and extended the AbstractEntityPersister's Insert method to make use of it:
Code:
if (Dialect.IsReturnIdentityViaParameter())
{
IDbDataParameter identityParameter = insertSelect.CreateParameter();
identityParameter.ParameterName = ":id";
identityParameter.DbType = DbType.Int64;
identityParameter.Direction = ParameterDirection.ReturnValue;
insertSelect.Parameters.Add(identityParameter);
session.Batcher.ExecuteNonQuery(insertSelect);
return identityParameter.Value;
}
else
{
rs = session.Batcher.ExecuteReader(insertSelect);
return GetGeneratedIdentity(obj, session, rs);
}
As a result, an output parameter with the name ":id" will be created if the dialect's "IsReturnIdentityViaParameter" method returns true.
So now I can make a custom dialect that returns true in its overridden "IsReturnIdentityViaParameter" method and adds "returning " + identityColumn + " into :id" to the insert statement so that the parameter actually gets populated with the newly generated id:
Code:
using NHibernate.Dialect;
using NHibernate.SqlCommand;
namespace Framework.Dialect
{
public class IdentityColumnOracleDialect : OracleDialect
{
public override bool SupportsIdentityColumns
{
get
{
return true;
}
}
public override SqlString AddIdentitySelectToInsert(SqlString insertSql, string identityColumn, string tableName)
{
return insertSql.Append(" " + GetIdentitySelectString(identityColumn, tableName));
}
public override string GetIdentitySelectString(string identityColumn, string tableName)
{
return "returning " + identityColumn + " into :id";
}
public override bool IsReturnIdentityViaParameter()
{
return true;
}
}
}
Don't forget to specify it in the hibernate.fg.xml file:
Code:
<property name="hibernate.dialect">Framework.Dialect.IdentityColumnOracleDialect,Framework</property>
If everyone is ok with this solution, is there a procedure to request NHibernate's source code to be adapted?
Thanks for any advice,
TolomaĆ¼s.