How to call stored proc using nhibernate? - nhibernate

I have research about it and all solution are pointing out to one solution, to use the libe of codes below and put it int .hbm.xml file. but I do not have one. What I have is hibernate.cfg.xml and nhvalidator.cfg.xml.
I have read from here : http://forums.asp.net/t/1407518.aspx/1
but where can I type the query tags? I typed it in the hibernate.cfg.xml (see below) but it is not working.
<?xml version="1.0" encoding="utf-16"?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="connection.connection_string">Server=localhost\MSSQLSERVERR2;Database=SupplierSearch;Trusted_Connection=True</property>
<property name="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
<property name="cache.use_minimal_puts">false</property>
<property name="use_outer_join">false</property>
</session-factory>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="Quarry.SupplierSearch"
assembly="SupplierSearch">
<class name="SupplierSearch" table="Client" lazy="true">
<id name="ClientId" column="ClientId">
<generator class="native" />
</id>
<property name="FirstName" column="FirstName" />
<property name="ClientId" column="ClientId" />
<loader query-ref="GetAllClient"/>
</class>
<sql-query name="GetAllClient" >
<return alias="GetAllClient" class="SupplierSearch">
<return-property name="ClientId" column="ClientId"/>
<return-property name="FirstName" column="FirstName"/>
</return>
exec GetAllClient
</sql-query>
</hibernate-mapping>
</hibernate-configuration>
since it is not working, I tried typing it in my Client.nhv.xml (see below) where client is mapped)
<?xml version="1.0" encoding="utf-8"?>
<nhv-mapping assembly="Quarry.SupplierSearch" namespace="Quarry.SupplierSearch.Model" xmlns="urn:nhibernate-validator-1.0">
<class name="Client">
<property name="Address1">
<not-null />
</property>
<property name="Address2">
<not-null />
</property>
<property name="BusinessName">
<not-null />
</property>
<property name="ClientId">
<not-null />
<digits integerDigits="10" />
</property>
<property name="Country">
<not-null />
</property>
<property name="FirstName">
<not-null />
</property>
<property name="LastName">
<not-null />
</property>
<property name="ListingType">
<not-null />
<digits integerDigits="10" />
</property>
<property name="PhoneNumber">
<not-null />
</property>
<property name="PostCode">
<not-null />
</property>
<property name="State">
<not-null />
</property>
</class>
<loader query-ref="GetAllClient"/>
<sql-query name="GetAllClient">
<load-collection alias="Clients" role ="Client"/>
exec [GetAllClient]
</sql-query>
</nhv-mapping>
any suggestion to get this working? thanks

It needs to be called Client.hbm.xml not Client.hbv.xmland an embedded resource.
edit I not familiar with any tools that generate hbv nor have I ever seen a mapping that begins with <nhv-mapping .. >. There must be a custom plugin/dll that you must use to get this working. What tool are you using?
However have you seen this blog post to get SP's to work without any custom tools.

using stored procedures is not supported with load-collection (scalar only).
use the format:
<sql-query name="GetAllClient">
<load-collection alias="Clients" role ="Client"/>
SELECT {c.*}
FROM client c
</sql-query>
(with the stuff in your stored proc innards in place of the "SELECT...FROM..." part of course.)

Related

Working with NHibernate and Oracle Sequences?

How to work with NHibernate and Oracle using Oracle Sequence?
When I call to the SaveOrUpdate() method, it only performs a select query against the Oracle sequence.
I have used an interceptor to look up the query the session was performing, and the is the instruction I get:
select INFO_ACCESS_REQS_ID_SEQ.nextval from dual;
And that is the only thing being executed against the underlying Oracle database when I call to ISession.SaveOrUpdate().
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2" >
<session-factory name="MyProject">
<property name="connection.driver_class">
NHibernate.Driver.OracleClientDriver
</property>
<property name="format_sql">true</property>
<property name="show_sql">true</property>
<property name="dialect">NHibernate.Dialect.Oracle10gDialect</property>
<property name="query.substitutions">
true 1, false 0, yes 'Y', no 'N'
</property>
</session-factory>
</hibernate-configuration>
InformationAccessRequest.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="MyProject.Model"
assembly="MyProject">
<class name="InformationAccessRequest" table="INFO_ACCESS_REQS">
<id name="Id" column="INFO_ACCESS_REQS_ID">
<generator class="native">
<param name="sequence">INFO_ACCESS_REQS_ID_SEQ</param>
</generator>
</id>
<property name="Assembly" column="ASSEMBLY_DT" />
<property name="Requested" column="INFO_ACCESS_REQ_DT" />
<property name="Expiration" column="INFO_ACCESS_REQ_EXP_DT" />
<property name="RequesterIdentification" column="INFO_ACCESS_REQ_USR_ID" />
<property name="Number" column="INFO_ACCESS_REQ_NUM" />
<property name="Reception" column="INFO_ACCESS_REQ_RECEP_DT" />
<property name="Creator" column="CREATOR_ID" />
<property name="Created" column="CREATED_DT" />
<property name="Updater" column="UPDATER_ID" />
<property name="Updated" column="UPDATED_DT" />
<property name="Deleted" column="DELETED_DT" />
</class>
</hibernate-mapping>
ISession.SaveOrUpdate()
var newRequest = new InformationAccessRequest();
newRequest.Requested = DateTime.Today.AddDays(-1);
newRequest.Expiration = DateTime.Today.AddDays(1);
newRequest.Reception = DateTime.Today;
newRequest.Number = Guid.NewGuid().ToString().Substring(0, 12);
newRequest.RequesterIndentification = Guid.NewGuid()..ToString().Substring(0, 10);
newRequest.Created = DateTime.Today;
newRequest.Creator = User.Current.Login;
newRequest.IsNew = true;
newRequest.IsDirty = true;
session.SaveOrUpdate(newRequest);
All required information provided, and all database constraints respected. The SaveOrUpdate() only does the select against the sequence, and no other sql instruction gets performed.
I just discovered a new feature of NHibernate working with identity-type, though it is an old feature actually.
Based on this article: NH2.1.0: New generators, I shall specify the generator class as sequence-identity.
From that new generators article, here's the plain explanation:
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”.
InformationAccesRequest.hbm.xml (updated accordingly)
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="MyProject.Model"
assembly="MyProject">
<class name="InformationAccessRequest" table="INFO_ACCESS_REQS">
<id name="Id" column="INFO_ACCESS_REQS_ID">
<generator class="sequence-identity">
<param name="sequence">INFO_ACCESS_REQS_ID_SEQ</param>
</generator>
</id>
<property name="Assembly" column="ASSEMBLY_DT" />
<property name="Requested" column="INFO_ACCESS_REQ_DT" />
<property name="Expiration" column="INFO_ACCESS_REQ_EXP_DT" />
<property name="RequesterIdentification" column="INFO_ACCESS_REQ_USR_ID" />
<property name="Number" column="INFO_ACCESS_REQ_NUM" />
<property name="Reception" column="INFO_ACCESS_REQ_RECEP_DT" />
<property name="Creator" column="CREATOR_ID" />
<property name="Created" column="CREATED_DT" />
<property name="Updater" column="UPDATER_ID" />
<property name="Updated" column="UPDATED_DT" />
<property name="Deleted" column="DELETED_DT" />
</class>
</hibernate-mapping>

Session.Get and Session.Load commands ignore filters

I'm using a mapping with a filter.
But when I try to persist my object, it first wants to get a snapshot (because my Id is a string).
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="DomainLayer.General" assembly="DomainLayer">
<class name="Fund" table="OPENA_BriefW" lazy="false">
<id name="Id" column="`BRWNUMMER`" type="string" >
</id>
<property name="Name" column="`BRWNAAM`" type="string" />
<property name="Contact" column="`BRWNAAM2`" type="string" />
<property name="Address" column="`BRWADRES`" type="string" />
<property name="City" column="`BRWSTAD`" type="string" />
<property name="Zip" column="`BRWPOST`" type="string" />
<property name="Phone" column="`BRWTELEFOON`" type="string" />
<property name="Fax" column="`BRWTELEFAX`" type="string" />
<property name="Iban" column="`brw_iban`" type="string" />
<property name="BankAccount" column="`BRWBANKNU`" type="string" />
<property name="Swift" column="`brw_swift`" type="string" />
<property name="ReceiveOffice" column="`BRWONTVKANT`" type="int" />
<property name="RegionDirection" column="`BRWGEWESTDIR`" type="int" />
<many-to-one name="Country" class="DomainLayer.General.CodeDescription" fetch="join" not-found="ignore">
<formula>'ALG'</formula>
<formula>'0'</formula>
<formula>'WG030'</formula>
<column name="`BRWLAND`" />
<formula>:LanguageFilter.Id</formula>
</many-to-one>
</class>
</hibernate-mapping>
As you can see the filter :LanguageFilter.Id is the one causing the troubles. When I do a normal .List() it doesn't cause problems.
But when I persist, it first wants to check if the Fund already exists yes/no. By doing a .Get (with the Id).
Then I get the error can't retrieve snapshot because in my query he doesn't replace :LangaugeFilter.Id with the effective value i set on my session.
I enable the filter on my session like so:
session.EnableFilter("LanguageFilter").SetParameter("Id", 1);
Here's the filter-def mapping:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<filter-def name='LanguageFilter' >
<filter-param name='Id' type='System.Int32' />
</filter-def>
</hibernate-mapping>
This post (comment 4) http://ayende.com/blog/3993/nhibernate-filters says that session.Get and Load ignores filters.
Are there any alternatives, because I need that language to be variable.
Ok, what I did was the following: I didn't use SaveOrPersist, but Save when new and Persist when I had an old one. This didn't execute the extra get.

Nhibernate ignoring unsaved-value with Guid (SQL Server CE)

This is my mapping:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DomainModel" namespace="DomainModel">
<class name="Cliente" table="Clientes">
<id name="Id" column="ID" type="guid" unsaved-value="00000000-0000-0000-0000-000000000000">
<generator class="guid.comb" />
</id>
<property name="Nombre" column="Nombre" not-null="true" type="string" length="50" />
<property name="Direccion" column="Direccion" not-null="false" type="string" length="75" />
<property name="Telefono" column="Telefono" not-null="false" type="string" length="15" />
<property name="Email" column="Email" not-null="false" type="string" length="50" />
<property name="FechaAlta" column="FAlta" not-null="true" type="DateTime" />
<list name="Pedidos" cascade="all-delete-orphan">
<key column="Cliente"/>
<index column="FechaEntrada"/>
<one-to-many class="Pedido"/>
</list>
</class>
</hibernate-mapping>
When I try to insert a new "Cliente" (Customer) into the database, NHibernate generates instead an UPDATE command, looking to update the customer with ID 00000000-0000-0000-0000-000000000000. As it doesn't exist, I naturally get the exception:
InnerException: NHibernate.StaleStateException
Message="Unexpected row count: 0; expected: 1"
I'm guessing this has to be a noob NHibernate error, but I've been checking my mapping with other guid example mappings on the web and I don't see my mistake.
Thanks!
PS.- As requested, this is the code I use to do the insertion:
// METHOD 1
//BusinessTransaction bt = new BusinessTransaction(sesion);
//bt.Attach(cliente);
//bt.Commit();
//bt.Close();
// METHOD 2
ITransaction trans = sesion.BeginTransaction();
sesion.SaveOrUpdate(cliente);
trans.Commit();
sesion.Flush();
sesion is an ISession object created with the BuildSessionFactory method of NHibernate Configuration. Method 1 provokes the error described, an UPDATE when it should be doing an INSERT, while Method 2 works, however (I'm an idiot and was looking a the wrong database file).
PPS.- For the sake of testing I've changed the guid fields for auto-incremented int identity fields, but my problems are the same.
PPPS.- Just in case, this are the contents of my hibernate.cfg.xml file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="dialect">NHibernate.Dialect.MsSqlCeDialect</property>
<property name="connection.driver_class">NHibernate.Driver.SqlServerCeDriver</property>
<property name="connection.connection_string">Data Source=Pedidos.sdf</property>
<property name="show_sql">true</property>
<property name="proxyfactory.factory_class">NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu</property>
<mapping assembly="PedidosDomainModel" />
</session-factory>
</hibernate-configuration>
Here's one solution: Map the Id (that's how I map it in Fluent, you will sort out an equivalent in XML mapping)
Id(x => x.Id).GeneratedBy.Assigned();
And before saving you can do something like this:
if (client.HasEmptyId())
{
client.Id = Guid.NewGuid();
session.Save(client);
}
else
{
session.Update(client);
}

Many to one mapping problem with NHibernate

Firstly I am relatively new to NHibernate. Got two tables on TaxMapping and Address. A single TaxMapping must have one address and one address can belong to more than one Tax Mapping. They are linked through foreign key
TaxMapping hbm
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="ITAPDTO" assembly="ITAPDTO">
<class name="TaxMapping" table="tblTaxMapping">
<id name="Tax_Mapping_ID">
<column name="Tax_Mapping_ID" sql-type="bigint" not-null="true"/>
<generator class="identity" />
</id>
<property name="Tax_ID" />
<property name="Client_Code" />
<property name="NRA_Sub_Account" />
<property column="Domicile" type="String" name="Domicile" length="5" />
<many-to-one name="Address" column="AddressID" cascade="none" not-found="exception" not-null="true" fetch="join" class="ITAPDTO.Address,ITAPDTO" />
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="ITAPDTO" assembly="ITAPDTO">
<class name="Address" table="Address">
<id name="AddressID" column="AddressID" type="Int32" unsaved-value="0">
<generator class="identity"/>
</id>
<property column="Client" type="String" name="Client" not-null="true" length="100" />
<property column="Contact" type="String" name="Contact" not-null="true" length="50" />
<property column="Address1" type="String" name="Address1" not-null="true" length="100" />
<property column="Address2" type="String" name="Address2" not-null="true" length="100" />
<property column="Address3" type="String" name="Address3" not-null="true" length="100" />
<property column="City" type="String" name="City" not-null="true" length="50" />
<property column="State" type="String" name="State" not-null="true" length="50" />
<property column="PoBox" type="String" name="PoBox" not-null="true" length="50" />
<property column="PostCode" type="String" name="PostCode" not-null="true" length="20" />
<property column="Country" type="String" name="Country" not-null="true" length="50" />
<property column="InsertedBy" type="String" name="Modified_By" length="20" />
<property column="InsertedOn" type="DateTime" name="Modified_Date" />
<property column="ConfirmedBy" type="String" name="Approved_By" length="20" />
<property column="ConfirmedOn" type="DateTime" name="Approved_Date" />
<property column="Status" type="String" name="Status" />
<property column="IUD" type="String" name="IUD" />
</class>
</hibernate-mapping>
I can pull in the data and bind to grid view with no issue. However when I update the Taxmapping the AddressID for the Address oject is always null event thoughh the other address fields are correctly populated and I dont know why. I currently have a hack in place the pulls the id from the db before I call update but I really shouldn't have to do this. Any thoughts would be welcome
regards
Is this due to cascade being set to "none" on the Taxmapping object's Address property? Try changing it to "save-update". You may also want to read the nhibernate documentation to figure out which cascade setting would be best for your situation, though it sounds like "save-update" would be most appropriate.
In addition to Krazzy's point, you should also check that your sessions are closed and / or flushed appropriately. A long-running session which is never flushed can exhibit similar problems.

NHibernate named query and 2nd level cache

I have the following mapping
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Domain.Core"
namespace="Domain.Core">
<class name="Industry" table="INDUSTRY">
<cache usage="read-write" region="IndustryCache" include="all"/>
<id name="Id" type="Int64" column="IID">
<generator class="sequence">
<param name="sequence">INDUSTRY_SEQ</param>
</generator>
</id>
<version name="Version" column="VERSION" access="property" unsaved-value="null" generated="never"/>
<property name="CreationTime" column="CREATE_DATE" type="DateTime" not-null="true" />
<property name="CreatedBy" column="CREATE_USER" type="String" not-null="true" />
<property name="LastUpdateTime" column="MODIFY_DATE" type="DateTime" not-null="false" />
<property name="LastUpdateBy" column="MODIFY_USER" type="String" not-null="false" />
<property name="Code" column="INDUSTRY" type="String" not-null="false" />
<map name="Resources" table="INDUSTRY_TL" fetch="subselect">
<cache region="IndustryCache" usage="read-write" include="all"/>
<key column="INDUSTRY_ID"/>
<composite-index class="Framework.Globalization.UILanguage, Framework">
<key-property name="Code" column="LANG" access="property" />
</composite-index>
<composite-element class="Industry+Translation">
<property name="Name" column="Industry_TL" />
</composite-element>
</map>
</class>
<query name="GetIndustyOrderByName">
<![CDATA[
from Industry as i left join fetch i.Resources as res where INDEX(res) = :lang order
by res.Name
]]>
</query>
</hibernate-mapping>
and the following configuration in hibernate.cfg.xml
<property name="show_sql">true</property>
<property name='proxyfactory.factory_class'>NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
<property name='prepare_sql'>true</property>
<property name='query.substitutions'>Y=true,N=false,0=false,1=true</property>
<property name="generate_statistics">true</property>
<property name="cache.use_second_level_cache">true</property>
<property name="cache.provider_class">Framework.Cache.SossCacheProvider, Framework.Cache.SossNHibernateCache</property>
<property name="cache.use_query_cache">true</property>
Now when I run the named query with SetCacheable(true) which calls out to the mapped collection, it doesn't get to the 2nd level cache. Is there any reason why?
More Generically, is there a way to put the resultset of a named query into second level cache?
Thanks!
In order for queries to use the second level cache, two things must be done:
1. Enable the query cache in the NHibernate config:
<property name="cache.use_query_cache">true</property>
2. Enable the query for caching when getting the IQuery instance:
IQuery = session.GetNamedQuery("from Industry as i left join fetch i.Resources as res where INDEX(res) = :lang order by res.Name")
.SetCacheable(true)
.Setxxx();
These settings cause the results of the queries to be put in the second level cache. But the second level query cache stores only identifiers of entities, not the entities themselves. In order for the execution of the query to avoid the database completely, the entities must be cached as well. See the NH Docs for more explanation of the interaction of the second level caches.