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);
}
Related
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>
I'm using NHibernate 3.2 with appfabric 1.1 for the 2nd level cache.
I’ve 2 classes mapped on the same table. The first class AFullEntity (MonitorLayoutData in the sample), inherit a second class which is lightweight class (MonitorLayout). MonitorLayoutData contains a heavy property that is not present in the base class. For example :
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="Entities.MonitorLayout,Entities" table="MonitorLayouts2" lazy="false" schema="dbo" polymorphism="explicit">
<cache usage="nonstrict-read-write"/>
<id name="Id" column="MonitorLayout_ID" type="int">
<generator class="native" />
</id>
<property name="Name" column="MonitorLayout" type="string" />
<property name="UserId" column="User_ID" type="int" />
<property name="IsPublic" column="IsPublic" type="Boolean" not-null="true" />
<property name="ViewGuid" column="ViewGuid" type="string" not-null="true" />
<property name="TreeNode" column="TreeNode" type="string" />
<property name="IncludeNodeChildren" column="IncludeNodeChildren" type="Boolean" />
</class>
<class name="Entities.MonitorLayoutData,Entities" table="MonitorLayouts2" lazy="false" schema="dbo" polymorphism="explicit">
<cache usage="nonstrict-read-write"/>
<id name="Id" column="MonitorLayout_ID" type="int">
<generator class="native" />
</id>
<property name="Name" column="MonitorLayout" type="string" />
<property name="UserId" column="User_ID" type="int" />
<property name="IsPublic" column="IsPublic" type="Boolean" not-null="true" />
<property name="ViewGuid" column="ViewGuid" type="string" not-null="true" />
<property name="TreeNode" column="TreeNode" type="string" />
<property name="IncludeNodeChildren" column="IncludeNodeChildren" type="Boolean" />
<property name="LayoutData" column="LayoutData" type="BinaryBlob" not-null="false"/>
</class>
</hibernate-mapping>
Those classes use an explicit polymorphism to retrieve only entities for the selected type like note in the documentation : “Explicit polymorphism is useful when two different classes are mapped to the same table (this allows a "lightweight" class that contains a subset of the table columns)”.
However I got a problem when entities are cached. When I update a AFullEntity changes are not report in the lightweight class and this is a big problem for us.
I try other mechanism like subclass or extends but NHibernate force me to declare a discriminator element, which is not required for me.
Is there a way to do this ?
No, they are two separate objects, each with their own identity in the cache. Possible workaround would be to disable caching for lightweight objects or evict the lightweight object from the cache when the heavy object is loaded. Loading the heavy and light objects in the same session seems to me to defeat the purpose.
I have a named query in my nhibernate mapping file, and every time i run the code, it fails to create the Session with the message 'Errors in named queries', and no inner exception or anything else pointing to what's wrong with my named query. I'm very new to using nhibernate but am sure i have everything set up correctly (i.e. mapping file is an embedded resource, and the classes used in the query are mapped correctly).
Could anyone suggest if there is an obvious error or problem with my mapping file which may cause this error.
Mapping file:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
schema="SmokefreeServices.Common"
assembly="Model.Smoking"
namespace="Model.Smoking">
<class name="CommonReferral" table="Referral">
<id name="ID">
<generator class="identity" />
</id>
<property name="PatientID" />
<property name="Module" />
<property name="OriginalModule" />
<property name="ReferralSource" />
<property name="OtherReferralSource" />
<property name="ReferralDate" />
<property name="ReferralComments" />
<property name="CreatedBy" />
<property name="CreatedDate" />
<property name="UpdatedBy" />
<property name="UpdatedDate" />
<one-to-one name="Status" class="CurrentReferralStatus" fetch="join" />
<bag name="StatusHistory" inverse="true" lazy="true">
<key column="ReferralID" />
<one-to-many class="ReferralStatus" />
</bag>
</class>
<query name="GetOpenReferral">
<![CDATA[
from CommonReferral ref
inner join fetch ref.Status referralStatus
where ref.PatientID = :patientId
and referralStatus.IsResolved = 0
]]>
</query>
</hibernate-mapping>
Is it a mapping problem between CommonReferal and CurrentReferralSttaus. Out of interest what happens if you try this:-
<query name="GetOpenReferral">
<![CDATA[
from CommonReferral ref
where ref.PatientID = :patientId
]]>
</query>
Another quick thought can you try and remove the keyword fetch as one-to-one are always eager fetched anyway.
<query name="GetOpenReferral">
<![CDATA[
from CommonReferral ref
inner join ref.Status referralStatus
where ref.PatientID = :patientId
and referralStatus.IsResolved = 0
]]>
</query>
This is probably a simple question to answer but I just can't figure it out.
I have a "Company" class with a many-to-one to "Address" which has a many to one to a composite id in "City". When I load a "Company" it loads the "Address", but if I call any property of "Address" I get the error:
{"could not load an entity: [IDI.Domain.Entities.Address#2213][SQL: SELECT address0_.AddressId as AddressId13_0_, address0_.Street as Street13_0_, address0_.floor as floor13_0_, address0_.room as room13_0_, address0_.postalcode as postalcode13_0_, address0_.CountryCode as CountryC6_13_0_, address0_.CityName as CityName13_0_ FROM Address address0_ WHERE address0_.AddressId=?]"}
The inner exception is:
{"Invalid column name 'CountryCode'.\r\nInvalid column name 'CityName'."}
What I don't understand is that I can run the query in sql server 2005 and it works, furthermore both those columns exist in the address table.
Here are my HBMs:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="IDI.Domain" namespace="IDI.Domain.Entities" >
<class name="IDI.Domain.Entities.Company,IDI.Domain" table="Companies">
<id column="CompanyId" name="CompanyId" unsaved-value="0">
<generator class="native"></generator>
</id>
<property column="Name" name="Name" not-null="true" type="String"></property>
<property column="NameEng" name="NameEng" not-null="false" type="String"></property>
<property column="Description" name="Description" not-null="false" type="String"></property>
<property column="DescriptionEng" name="DescriptionEng" not-null="false" type="String"></property>
<many-to-one name="Address" column="AddressId" not-null="false" cascade="save-update" class="IDI.Domain.Entities.Address,IDI.Domain"></many-to-one>
<property column="Telephone" name="Telephone" not-null="false" type="String"></property>
<property column="TelephoneTwo" name="TelephoneTwo" not-null="false" type="String"></property>
<property column="Fax" name="Fax" not-null="false" type="String"></property>
<property column="ContactMan" name="ContactMan" not-null="false" type="String"></property>
<property column="ContactManEng" name="ContactManEng" not-null="false" type="String"></property>
<property column="Email" name="Email" not-null="false" type="String"></property>
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="IDI.Domain" namespace="IDI.Domain.Entities" >
<class name="IDI.Domain.Entities.Address,IDI.Domain" table="Address">
<id name="AddressId" column="AddressId" type="Int32">
<generator class="native"></generator>
</id>
<property name="Street" column="Street" not-null="false" type="String"></property>
<property name="Floor" column="floor" not-null="false" type="Int32"></property>
<property name="Room" column="room" not-null="false" type="Int32"></property>
<property name="PostalCode" column="postalcode" not-null="false" type="string"></property>
<many-to-one class="IDI.Domain.Entities.City,IDI.Domain" name="City" update="false" insert="false">
<column name="CountryCode" sql-type="String" ></column>
<column name="CityName" sql-type="String"></column>
</many-to-one>
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="IDI.Domain" namespace="IDI.Domain.Entities" >
<class name="IDI.Domain.Entities.City,IDI.Domain" table="Cities">
<composite-id>
<key-many-to-one class="IDI.Domain.Entities.Country,IDI.Domain" name="CountryCode" column="CountryCode">
</key-many-to-one>
<key-property name="Name" column="Name" type="string"></key-property>
</composite-id>
</class>
</hibernate-mapping>
Here is my code that calls the Company:
IList<BursaUser> user;
if(String.IsNullOrEmpty(email) && String.IsNullOrEmpty(company))
return null;
ICriteria criteria = Session.CreateCriteria(typeof (BursaUser), "user").CreateCriteria("Company", "comp");
if(String.IsNullOrEmpty(email) || String.IsNullOrEmpty(company) )
{
user = String.IsNullOrEmpty(email) ?
criteria.Add(Expression.Eq("comp.Name", company)).List<BursaUser>()
: criteria.Add(Expression.Eq("user.Email", email)).List<BursaUser>();
}
And finally here is where i get the error:
"user" was already initialized with the code above:
if (user.Company.Address.City == null)
user.Company.Address.City = new City();
Thanks.
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.