I'm stuck trying to get the mapping I want to persist correctly. For my example, I have an ItemY class which can have 0,1,* Assets. However, an Asset can belong to an ItemY or an ItemZ object. I'm trying to use a cross-reference table to store this.
Schema
TABLE [dbo].[ItemY](
[ItemYID] [int] IDENTITY(1,1) NOT NULL,
--more columns here
TABLE [dbo].[Asset](
[AssetID] [uniqueidentifier] NOT NULL,
[AssetTypeID] [int] NOT NULL,
[DisplayOrder] [int] NOT NULL,
[Location] [varchar](255) NOT NULL,
TABLE [dbo].[ItemYAsset](
[ItemYID] [int] NOT NULL,
[AssetID] [uniqueidentifier] NOT NULL,
Mappings:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="MyAssembly" namespace="My.Namespace">
<class name="ItemY" table="ItemY">
<id name="ItemYId" column="ItemYID">
<generator class="identity" />
</id>
<bag name="Images" table="ItemYAsset" inverse="true" cascade="all-delete-orphan">
<key column="ItemYID" not-null="true" />
<many-to-many class="Asset" column="AssetID" unique="true" />
</bag>
<!--more mapping here-->
</class>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="MyAssembly" namespace="My.Namespace">
<class name="Asset" table="Asset">
<id name="AssetId" column="AssetID">
<generator class="guid.comb" />
</id>
<property name="DisplayOrder"></property>
<property name="Location"></property>
</class>
</hibernate-mapping>
I don't want to expose an ItemY property on Asset, since an Asset doesn't always belong to an ItemY. My ItemY class persists fine, and also persists the Asset class, but there is nothing added to the association table (ItemYAsset). Any idea what I'm doing wrong?
inverse="true" means the "other side" is responsible for persisting the relationship.
Since you have no "other side" (the relationship is unidirectional), remove that attribute.
Related
Forgive me I am a newbie in NHibernate, I found some example in hbm.xml like below.
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="test" >
<class name="test.FieldSetMapping" table="fieldsetmapping" discriminator-value="2352" lazy="false">
<id name="Id" column="fieldsetmapping_id" type="Int64">
<generator class="test.NHibernate.IdGenerator, test">
<param name="table">nextinstanceid</param>
<param name="column">next</param>
<param name="max_lo">9</param>
</generator>
</id>
<discriminator column="mapping_type" type="Int32" />
<version name="LastUpdateNumber" column="last_update_number" type="Int32" unsaved-value="-1"/>
<property name="OwnerName" column= "owner_name" type="String"/>
<component name="FieldSetDefinitionId" class="test.ObjectId">
<property name="InstanceId" column= "fieldsetdefinition_id" type="Int64"/>
</component>
<property name="OwnerTypeClassId" column= "owner_type" />
<bag name="FieldMappings" cascade="all-delete-orphan" generic="true" lazy="false">
<key column="fieldsetmapping_id" not-null="true" />
<one-to-many class="test.FieldSet.FieldMapping, test" />
</bag>
<property name="MappingHandlerClass" column="handler_class" />
</class>
<subclass name="test.EntityFieldSetMapping, test" discriminator-value="2353" extends="test.FieldSetMapping, test" lazy="false" >
<property name="TargetEntityType" type="test.Internal.NHibernate.ClassIdType, test" not-null="false">
<column name="target_entity_type"/>
</property>
</subclass>
<class ...>
...
</class>
<class ...>
...
</class>
</hibernate-mapping>
But I don't know what does discriminator mean . I check the Nhibernate Doc 5.1.6.
The <discriminator> element is required for polymorphic persistence
using the table-per-class-hierarchy mapping strategy and declares a discriminator
column of the table. The discriminator column contains marker values that tell
the persistence layer what subclass to instantiate for a particular row.A restricted
set of types may be used: String, Char, Int32, Byte, Short, Boolean, YesNo, TrueFalse.
Does it mean if the mapping_type > 2352 the NH will initialize the subclass test.EntityFieldSetMapping for the row of the table fieldsetmapping? thanks.
NHibernate will use the discriminator to detect which class needs to instantiate on a polymorphic scenario.
If mapping_type = 2352, it will create an instance of test.FieldSetMapping
if mapping_type = 2353, it will create an instance of test.EntityFieldSetMapping
Any other value should generate an exception.
I am trying to retrieve the count of items allocated to a container in my hbm file. I've done a bit of digging and managed to get my hbm code this far (below!). I want the count to be retrieved every time a container object is queried. I could use an interceptor but I assume there's a better way. Am I on the right track or should I use a different strategy to get the count loaded up?
Thanks.
P.S. We're using NH v2.2
<?xml version="1.0" encoding="utf-8"?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="false" assembly="MyEntities" namespace="Entities.Containers"> <class name="Container" table="[Container]" xmlns="urn:nhibernate-mapping-2.2">
<id name="Id" column="Id" type="Int32" unsaved-value="0">
<generator class="native" />
</id>
<property name="Capacity" column="Capacity">
<column name="Capacity" />
</property>
<property name="Description" column="Description" length="50" type="String">
<column name="Description" />
</property>
<loader query-ref="sqCurrentContainerAllocation"/> </class>
<sql-query name="sqCurrentContainerAllocation">
<return-scalar column="AllocatedItemsCount" type="int"></return-scalar>
SELECT COUNT(*) FROM [ContainerTracking]
WHERE [ContainerId] = :Id </sql-query>
</hibernate-mapping>
If you need to get some calculated property, you can use mapping with formula.
Let's extend your C# class:
public class Container
{
... // ID, Capacity, Description
public virtual int MyCount { get; set; }
And extend your mapping
<class name="Container" table="[Container]"
...
<property name="MyCount" insert="false" update="false" >
<formula>
(
SELECT count(*)
FROM [ContainerTracking] as ct
WHERE ct.[ContainerId] = Id
)
</formula>
</property>
the Id will be replaced with somethink like '_this.Id', the name of the column Id and its alias
This will of course load the count all the time (except projections) so think twice before use it
I am getting an error in NHibernate.Collection.PersistentBag class when trying to load entities:
The value "MyProject.DomainModel.Operator" is not of type "MyProject.DomainModel.Operator" and cannot be used in this generic collection.
Notice that both value types are exactly the same. I've double-checked them in a comparer tool.
NHibernate is failing to add the value to a List collection at line bag.Add(element) .
The element variable is actually of type *object{DecoratorAopProxy_9cf850624c7e4ef9a8e2d9694bed26fd}*. I've noticed that the objects that can be successfully added to this list are of type object{MyProject.DomainModel.Operator}. This type is obtained from "quick watch" feature in VS2012 from the "Type" column.
Does anyone have an idea as to why NHibernate changes the type of this particular object to a proxy while others have pure entity types?
<class name ="PersonRole" table ="tblPersonRole" mutable ="false">
<id name="Id" column="PersonRoleID" type="Int32" access ="nosetter.lowercase-underscore">
<generator class="native"/>
</id>
<discriminator formula="case when RoleID in (2,4,5,6) THEN RoleID ELSE 0 END" />
<subclass discriminator-value="4" name="AccountManagerRole">
<bag name="Operators" >
<key column="OperatorID"></key>
<one-to-many class="BaseOperator"/>
<loader query-ref="LoadAllocatedOperators_ACCOUNTMANAGER"/>
</bag>
</subclass>
</class>
<class name="BaseOperator" table="tblOperator" lazy="true" >
<id name="Id" column="OperatorID" access ="nosetter.lowercase-underscore" type="Int32" unsaved-value="null">
<generator class="native" />
</id>
<discriminator column="OperatorType" type="string" />
<subclass discriminator-value ="OPR" name ="Operator" lazy="true">
<bag name="Customers" access="nosetter.camelcase-underscore" lazy="true" cascade="all-delete-orphan" inverse="true" fetch="join" >
<key column="OperatorId" />
<one-to-many class="MyProject.DomainModel.Customer, MyProject" not-found="ignore" />
</bag>
<subclass discriminator-value ="OPR2" name ="Operator2" lazy="true" />
</subclass>
</class>
So I do something like "select distinct accManager from AccountManagerRole accManager", which results in operators being loaded one at a time using their ID, and NHibernate crashes on one of them.
I've an issue using one-to-one mapping. I've searched internet and found many solutions but none was satisfying. Most of the examples carry overhead of storing parent instance in child class.
I want to use only parent Id in child class having foreign key constraint relationship but dont want to keep any parent instance in child.
When I try to load the records from database it throws exception "No row with the given identifier exists [AssemblyName.]". But, the record exists in Table "B" properly.
Any solutions for this issue?
The class structure:
class A {
public virtual string Id {get;set;}
public virtual B B {get;set;} // properties...... }
class B { public virtual string Id {get;set;} // properties......
public virtual string ParentId { get;set;} // class A Id }
The database structure:
CREATE TABLE [A](
[Id] [nvarchar](45) PRIMARY KEY
) ON [PRIMARY]
CREATE TABLE [B](
[Id] [nvarchar](45) PRIMARY KEY,
[ParentId] [nvarchar](45) NOT NULL
) ON [PRIMARY]
The mapping:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="A,AssemblyName" table="A" lazy="true">
<id name="Id" column="Id" type="string">
<generator class="assigned"/>
</id>
<one-to-one name="_B" cascade="all" fetch="join" foreign-key="None" constrained="true" class="B"/>
</class>
</hibernate-mapping>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="B,AssemblyName" table="B" lazy="true">
<id name="Id" column="Id" type="string"> <generator class="assigned"/> </id>
<property name="_Name" column="Name"/> </class>
</hibernate-mapping>
One-to-one associations are always bidirectional in NHibernate. Mapping of class B is incomplete:
<class name="B,AssemblyName" table="B" lazy="true">
<id name="Id" column="Id" type="string">
<generator class="assigned"/>
</id>
<many-to-one name="A" unique="true" column="A" />
<property name="_Name" column="Name"/>
</class>
This is foreign key association. For more information see this detailed Ayende's blog post or NHibernate documentation.
Read this. A "real" one-to-one needs the same primary key in both tables (and no ParentId column).
Hi
I have parent and child table like below
<?xml version="1.0" encoding="utf-8"?>
------- parent --------------
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="HibernateSample.StudMarks,HibernateSample" table="StudMarks" lazy="false">
<id name="Sno" column="SNO" type="int">
<generator class="assigned"/>
</id>
<many-to-one name="Student" column="ID" not-null="true"/>
<property name="Marks" column="Marks" type="int" not-null="true" />
<property name="Rank" column="Rank" type="int" not-null="true" />
</class>
</hibernate-mapping>
------- child --------------
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="HibernateSample.Student,HibernateSample" table="Student" lazy="false">
<id name="Id" column="ID" type="int">
<generator class="native" />
</id>
<property name="Name" column="Name" type="string" not-null="true" />
<property name="Standard" column="Standard" type="string" not-null="true" />
<bag name="StudMarks" cascade="all" lazy="false">
<key column="ID" not-null="true"/>
<one-to-many class="HibernateSample.StudMarks,HibernateSample" />
</bag>
</class>
</hibernate-mapping>
and in .cs file i have like written code like below to insert into parent and child.
StudMarks sm = new StudMarks();
Student st = new Student();
List<StudMarks> sms = new List<StudMarks>();
st.Id = 9;
st.Name = "stud 999";
st.Standard = "99";
sm.Sno = 9;
sm.Marks = 99;
sm.Rank = 9;
sm.Student = st; ------ **Line 1**
st.StudMarks = sms; ------ **Line 2**
session.Save(sm);
session.Flush();
If i commENT "Line 1" in above code only child is inserting. If i comment "Line 2" foreign key constraint error is throwing.
Tables:
CREATE TABLE [dbo].[Student](
[ID] [int] IDENTITY(1,1) NOT NULL, -- PRIMARY KEY
[Name] [varchar](50) NOT NULL,
[Standard] [varchar](50) NOT NULL,
CREATE TABLE [dbo].[StudMarks](
[SNO] [int] NOT NULL, -- PRIMARY KEY
[ID] [int] NOT NULL, -- FOREIGN KEY
[Marks] [int] NOT NULL,
[Rank] [int] NOT NULL,)
Please let me know how to insert in parent and child at a time in single save operation.
You have a cascade relation from Student->StudentMarks. So you should be saving st...
session.Save(st);
You should also specify inverse="true" on the bag so you don't get double updates of the foreign key and a cascade="all-delete-orphan" is probably appropriate here as you want all StudentMarks to be deleted when the Student is deleted.
I believe you need to specify inverse="true" on the Student mapping of the StudMarks collection property, indicating that the StudMark mapping is the owner of the relationship.