nhibernate newbe question ... easy one here - nhibernate

I'm new to nhibernate so this should be easy. I have a mapping file as below although I deleted some fields that aren't relevant to this question. The streamfields class contains a bag of fieldmappings. I want the join to be on field_no column but the sql that is sent is on the id field (str_fld_id") as seen below.
I see what the below sql is doing but it's not what I wanted. It's trying to query the field_mappings table based on the values found in the id column str_fld_id in the StreamFields class when I thought it was clear I wanted the field_no to be used on both ends. I say I thought it was clear because the mapping for the field_mapping class has the below attribute and they both have the same named field
Below is in my FieldMappings mapping file.
<many-to-one name="FieldNo" cascade="none" column="`Field_No`" not-null="true">
Sql sent
NHibernate: SELECT fkfieldmap0_.[field_no] as field5_1_, fkfieldmap0_.[Mapping_Id] as Mapping1_1_, fkfieldmap0_.[Mapping_Id] as Mapping1_3_0_, fkfieldmap0_.[Std_fld_Id] as Std2_3_0_, fkfieldmap0_.[Field_Position] as Field3_3_0_, fkfieldmap0_.[Field_No] as Field4_3_0_ FROM [Field_Mappings] fkfieldmap0_ WHERE fkfieldmap0_.[field_no]=#p0; #p0 = '20'
StreamFields mapping
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="DataTransfer.StreamFields,DataTransfer" table="`stream_fields`" lazy="true">
<id name="StrFldId" column="`str_fld_id`" type="int">
<generator class="native" />
</id>
<property type="int" not-null="true" name="FieldNo" column="`field_no`" />
<many-to-one name="StreamId" cascade="none" column="`stream_Id`" />
<bag name="FkFieldMappingsStreamFields" inverse="true" lazy="false" cascade="all">
<key column="`field_no`" />
<one-to-many class="DataTransfer.FieldMappings,DataTransfer"/>
</bag>
</class>

[Edited - with old comments]
Okay, i think i finally got you right and i might admit the problems i had understanding what you want took me a while and result of the lack of information you provided. In the future please provide the mapping of both tables clarify on the point wheather it is a mapping or a query issue. Thx.
IMO you have misunderstood the idea of a parent/child-relation.
The bag you mentioned like to have within the StreamFields class shouldn't be a bag but a direct association. Like this:
<class name="DataTransfer.StreamFields,DataTransfer" table="stream_fields" >
<id name="StrFldId" column="str_fld_id" type="int">
<generator class="native" />
</id>
<property type="int" not-null="true" name="FieldNo" column="field_no" />
<many-to-one name="FieldMapping" class="FiueldMapping" column="Field_No" />
</class>
This of course will only work if you have a property of type FiledMapping in your class.
You want to map FieldMapping to the column Field_No within StreamFields class. There can only be one value within this column, so a bag makes no sense at all. If you want to have a bag of course you can keep it the way it already worked but be aware that the 'key-column' within the bag refers to the child table - in an other way it makes no sense cause a ForeignKey has to map to a PrimaryKey on its parent table. This ensures it is unique and set.
I really don't want to rant but would strongly encourage you to review the hibernate reference about collection mapping to get a deeper clue however.
Hopely this will solve your problem.

Below are the mappings for the classes.
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="DataTransfer.StreamFields,DataTransfer" table="`stream_fields`" lazy="true">
<id name="StrFldId" column="`str_fld_id`" type="int">
<generator class="native" />
</id>
<property type="string" length="50" name="FieldName" column="`field_name`" />
<property type="int" name="InputFieldPosition" column="`input_field_position`" />
<property type="int" name="Start" column="`start`" />
<property type="int" name="Width" column="`width`" />
<property type="string" length="50" name="Datatype" column="`datatype`" />
<property type="int" not-null="true" name="FieldNo" column="`field_no`" />
<property type="int" name="FieldOrder" column="`field_order`" />
<property type="int" name="StdId" column="`Std_Id`" />
<many-to-one name="StreamId" cascade="none" column="`stream_Id`" />
<bag name="FkFieldMappingsStreamFields" inverse="true" lazy="false" cascade="all">
<key column="`field_no`" />
<one-to-many class="DataTransfer.FieldMappings,DataTransfer"/>
</bag>
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="DataTransfer.FieldMappings,DataTransfer" table="`Field_Mappings`" lazy="false">
<id name="MappingId" column="`Mapping_Id`" type="int">
<generator class="native" />
</id>
<property type="int" name="StdFldId" column="`Std_fld_Id`" />
<property type="int" name="FieldPosition" column="`Field_Position`" />
<many-to-one name="FieldNo" cascade="none" column="`Field_No`" not-null="true" property-ref="FieldNo" />
</class>
</hibernate-mapping>
To make things easy on myself, there is one record in stream_fields and the field_no value is 1 and 20 is the value in StrFldId.
SELECT fkfieldmap0_.[field_no] as field5_1_, fkfieldmap0_.[Mapping_Id] as Mapping1_1_, fkfieldmap0_.[Mapping_Id] as Mapping1_3_0_, fkfieldmap0_.[Std_fld_Id] as Std2_3_0_, fkfieldmap0_.[Field_Position] as Field3_3_0_, fkfieldmap0_.[Field_No] as Field4_3_0_ FROM [Field_Mappings] fkfieldmap0_ WHERE fkfieldmap0_.[field_no]=#p0; #p0 = '20' –

Related

nhibernate and All-delete-orphan

i have a an entity class which has a bag of child entity class like so(copied relevant lines):
<class name="Entity" table="Entities" lazy="false">
<id name="ID">
<generator class="guid"/>
</id>
<bag name="SubEntities" table="SubEntities" cascade="all-delete-orphan">
<key column="EntityID"/>
<one-to-many class="SubEntity"/>
</bag>
</class>
Now, the mapping works well and as expected in most cases (when i delete/save it cascades), but when i try to remove some subentity(child) from the bag in the Entity(parent) class - the change does not cascade and all i see in the DB is that the subentitiy's foreign key was changed to null, and not deleted as i would like.
I've read something about nhibernate not realizing which line it needs to delete in the database (no unique id for the row) - so i tried to use the idbag instead of the bag - but the idbag does not allow a one-to-many collection in it, i've tried something of the sort:
<idbag name="SubEntities" table="SubEntities" cascade="all-delete-orphan">
<collection-id column="Id" type="Guid">
<generator class="guid"/>
</collection-id>
<key column="EntityID"/>
<one-to-many class="SubEntity"/>
</idbag>
which of course gives the error that one-to-many isnt allowed there.
Even when i try to use the component (which i dont want as i want the child is also an entity) - i cant use an external hbm file to define it (the subentity is a rather large class by itself)
so setting the entity's propertise in the parent's hbm files isnt a good idea as well.
Could anyone help me out with explaning whats wrong and how am i supposed to fix it? i really need the subentity to be removed!
Thanks!
As requested - I"m pasting my hbm files:
For Entity:
<?xml version="1.0" encoding="utf-8" ?>
- <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Com.Project.Shared.Common" namespace="Com.Project.Shared.Common.Entities">
- <class name="Entity" table="Entities" lazy="false">
- <id name="ID">
<generator class="guid" />
</id>
<property name="Name" />
<property name="Description" />
<property name="EndTime" />
<property name="StartTime" />
<property name="State" />
<property name="Stored" />
<property name="ClassRoomID" />
<property name="Score" />
<many-to-one name="Network" class="Network" column="NetworkID" />
- <bag name="Scenarios" cascade="all">
<key column="EntityID" />
<one-to-many class="EntScenario" />
</bag>
- <bag name="TimeLineEvents" order-by="TimeStamp" cascade="all">
<key column="EntityID" />
<one-to-many class="TimeLineEvent" />
</bag>
- <bag name="SubEntity" table="SubEntities" cascade="all-delete-orphan">
<key column="EntityID" />
<one-to-many class="SubEntity" />
</bag>
</class>
</hibernate-mapping>
for SubEntity:
<?xml version="1.0" encoding="utf-8" ?>
- <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Com.Project.Shared.Common" namespace="Com.Project.Shared.Common.Entities">
- <class name="SubEntity" table="SubEntities" lazy="false">
- <id name="ID">
<generator class="guid" />
</id>
<many-to-one name="Name" class="EntName" column="NameID" />
<many-to-one name="Station" class="EntStation" column="StationID" />
- <bag name="Performances" table="EntPerformances" cascade="all">
<key column="SubEntityID" />
- <composite-element class="Performance">
<property name="Rank" />
<property name="Remark" />
<many-to-one name="Category" class="PerformanceCategory" column="CategoryID" index="ListIndex" />
</composite-element>
</bag>
</class>
</hibernate-mapping>
The tester i use is:
Entity newEntity = _dal.GetAll<Entity>()[0];
ObservableCollection<SubEntity> subEntities = newEntity.ObservableSubEntities;
subEntities .RemoveAt(1);
_dal.SaveItem<Entity>(newEntity);
This just turned the EntityID column in subentity into Null - but doesnt delete it.
I appericiate your help, guys.
The bag needs to have inverse="true" on it if you want it to work as expected.
This tells NHibernate that Entity is responsible to managing the relationship (and you'll be able to make your FK column non-null)

Joining tables in NHibernate gets "collection element mapping has wrong number of columns"

I'm having trouble mapping a many-to-many relationship with a join table. The errror message I am getting is "collection element mapping has wrong number of columns"
My db schema is basically:
.
Despite the picture from SQL Server Management Studio, the database is Sqlite.
My mapping file is:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="Design" table="Design">
<id name="DesignID" column="DesignID" type="Int32">
<generator class="native"/>
</id>
<property name="Name" type="String" column="Name" />
<property name="DisplayOrder" type="Int32" column="DisplayOrder" />
<property name="ChangeDate" column="ChangeDate" type="DateTime"/>
<set name="DesignMeasures" table="DesignProperties" cascade="save-update,delete-orphan" >
<key column="DesignID" />
<many-to-many column="PropertyID" class="DesignProperties" unique="true" />
</set>
</class>
<class name="Properties" table="Properties">
<id name="PropertyID" column="PropertyID" type="Int32">
<generator class="native" />
</id>
<property name="Name" type="String" column="Name" />
<property name="Value" type="String" column="Value" />
</class>
<class name="DesignProperties" table="DesignProperties">
<composite-id>
<key-many-to-one class="Design" name="DesignID" column="DesignID" />
<key-many-to-one class="Properties" name="PropertyID" column="PropertyID" />
</composite-id>
</class>
</hibernate-mapping>
I know I'm doing something wrong, but I can't figure it out. What am I doing wrong? How do I fix it?
DesignProperties is either a many-to-many relationship or an entity, not both.
Since it doesn't have any properties of its own, I'd remove the DesignProperties class along with its mapping, and just leave a collection of Property (not Properties; your class name should be singular) in Design.

NHibernate WARNING and the data is not saved

While I am saving by calling SaveOrUpdate(), I got this warning and the data is not saving in the database after calling Transaction.Commit().
NHibernate.Engine.ForeignKeys - Unable
to determine if [project name] with
assigned identifier [primarykey] is
transient or detached; querying the
database. Use explicit Save() or
Update() in session to prevent this.
I am inserting a new object. Google search tell me to call Save() instead of SaveOrUpdate(): means Save() is only for inserting.
I search in Google and do not see much about this.
Could anyone give me suggestion for this problem or this warning?
Edit:
Here is the simulated sample mapping files -
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly=""
namespace="">
<class name="Customer" table="[dbo].[Customer]" optimistic-lock="none" >
<id name="CustomerId" column="CustomerId" >
<generator class="assigned"/>
</id>
<property name="Name" column="Name" />
<property name="Age" column="Age" />
<set name="CustomerDetails" cascade="none" inverse="true" fetch="select">
<key>
<column name="CustomerId"/>
</key>
<one-to-many class="CustomerDetail"/>
</set>
<many-to-one name="MGender" fetch="select" cascade="none">
<column name="GenderCode"/>
</many-to-one>
</class>
</hibernate-mapping>
<class name="CustomerDetails" table="[dbo].[CustomerDetail]" optimistic-lock="none" >
<id name="CustomerDetailId" column="CustomerDetailId" >
<generator class="assigned"/>
</id>
<property name="Detail1" column="Detail1" />
<many-to-one name="Customer" fetch="select" cascade="none">
<column name="CustomerId"/>
</many-to-one>
</class>
<class name="MGender" table="[dbo].[MGender]" optimistic-lock="none" >
<id name="GenderCode" column="GenderCode" >
<generator class="assigned"/>
</id>
<property name="Description" column="Description" />
<set name="Customers" cascade="none" inverse="true" fetch="select">
<key>
<column name="GenderCode"/>
</key>
<one-to-many class="Customer"/>
</set>
</class>
You're using an assigned identifier so you need to set the unsaved-value attribute so that NHibernate can determine if an entity should be inserted or updated. Or you can explicitly call Save for new entities.
<id name="CustomerId" column="CustomerId" unsaved-value="???" >
<generator class="assigned"/>
</id>
Please note that if you are using a custom id, ie not using Id(x => x.Id).GeneratedBy.Assigned() and you do not have any version or timestamp columns, there is no way NHibernate would know if thuis is a new row or an existing one. So, I fixed my problem by using GeneratedBy.UuidString(). Works like a charm. Almost put my code into production with this error message, fortunately my information was being saved.

NHibernate not inserting parent into the db

When I save a new Report, NHibernate inserts the Report, ignores the Publication and tries to insert the UserPublication. However SQL then complains about violation of FK constraint.
Its like NHibernate doesn't think the Publication is new even though the row doesn't exist in the db.
Think of the entity relationship as:
A Report can have many Publications (Publications belong to a Report)
A Publication can have many UserPublications (UserPublications belong to a Publication)
Any ideas what I've done wrong?
Thanks in advance.
Here's the mappings:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="true">
<class name="Model.Report, Model" table="Report" lazy="true">
<id name="Id" access="property" column="ReportID">
<generator class="assigned"></generator>
</id>
<property name="DeleteUnread" access="property" />
<property name="Description" access="property" />
<property name="Name" access="property" />
<bag name="Publications" access="property" lazy="true" cascade="all-delete-orphan">
<key column="ReportID"/>
<one-to-many class="Model.Publication, Model"/>
</bag>
</class>
</hibernate-mapping>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="true">
<class name="Model.Publication, Model" table="Publication" lazy="true">
<id name="Id" access="property" column="PublicationID">
<generator class="assigned"></generator>
</id>
<property name="CreatedOn" access="property" />
<property name="FileExtension" access="property" />
<property name="IsDownloaded" access="property" />
<property name="ToBeDownloaded" access="property" />
<property name="Name" access="property"/>
<bag name="UserPublications" access="property" lazy="true" cascade="all-delete-orphan">
<key column="PublicationID"></key>
<one-to-many class="Model.UserPublication, Model" />
</bag>
<many-to-one name="Report" class="Model.Report, Model" lazy="false" column="ReportID" not-null="true" cascade="none">
</many-to-one>
</class>
</hibernate-mapping>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="true">
<class name="Model.UserPublication, Model" table="UserPublication" lazy="true">
<id name="Id" access="property" column="UserPublicationID">
<generator class="native"></generator>
</id>
<property name="IsFlaggedForDeletion" access="property" column="IsFlaggedForDeletion" />
<property name="HasBeenRead" access="property" column="HasBeenRead" />
<property name="DateReceived" access="property" column="DateReceived" />
<property name="MustRead" access="property" column="MustRead" />
<property name="ShowToolbar" access="property" column="ShowToolbar" />
<property name="MaxAge" access="property" column="MaxAge" />
<property name="FeedId" access="property" column="FeedId" />
<property name="CanEdit" access="property" column="CanEdit" />
<many-to-one name="User" access="property" column="ClientUserID" class="Model.ClientUser, Model" not-null="true" cascade="none">
</many-to-one>
<many-to-one name="Publication" access="property" class="Model.Publication, Model" column="PublicationID" not-null="true" cascade="none">
</many-to-one>
</class>
I think the problem is that id of publications is an assigned id therefore NHibernate can't recognize when it should insert a publication.
When you flush a session it first insert all inserted objects , then it updates all updated objects and then deletes all deleted objects.
So I think that's going to happen here:
You save a Report that has publications that have userpublications.Since publication id is assigned NHibernate assumes it has to be updated and ignore it but UserPublication id is native and NHibernates knows when it should be inserted and tries to insert it thus a FK violation happens.
To solve this problem you can add a version property to publication so NHibernate can insert it based on its version value.
The UserPublications bag in the Publication class has a wrong key element. It should be:
<key column="PublicationID"/>
This works. I set the unsaved-value attribute to "any".
I don't think there will be any repurcussions.
<id name="Id" access="property" column="PublicationID" unsaved-value="any">
<generator class="assigned"></generator>
</id>

NHibernate Many-to-many

I have a legacy database and I am trying to create a NHibernate DAL.
I have a problem with a mapping on Many-To-Many table.
The Database tables:
studio_Subscribers
studio_Groups (contains a IList of Subscribers)
studio_Subscribers_Groups - Many-To-Many table with primary keys
The problem is when I create a SubscriberGroup instance and fill it with Subscribers they gets saved to the studio_Subscribers table but not to the Many-To-Many table.
I cant figure out whats wrong?
studio_Subscribers table mapping:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Meridix.Studio.Common"
namespace="Meridix.Studio.Common">
<class name="SubscriberItem" table="studio_Subscribers">
<id name="StorageId" column="Id" unsaved-value="0" access="nosetter.camelcase">
<generator class="identity" />
</id>
<property name="Id" column="DomainId" not-null="true" />
<property name="Subscriber" column="Subscriber" not-null="true" length="50" />
<property name="Description" column="Description" not-null="false" length="100" />
<property name="Type" column="Type" not-null="true" length="40"
type="Meridix.Studio.Data.Repositories.EnumStringTypes.SubscriberTypeEst, Meridix.Studio.Data.Repositories" />
</class>
</hibernate-mapping>
studio_Groups table mapping:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Meridix.Studio.Common"
namespace="Meridix.Studio.Common">
<class name="SubscriberGroup" table="studio_Groups">
<id name="StorageId" column="Id" unsaved-value="0" access="nosetter.camelcase">
<generator class="identity" />
</id>
<property name="Id" column="DomainId" not-null="true" />
<property name="Name" column="Name" not-null="true" length="200" />
<property name="Description" column="Description" not-null="false" length="300" />
<bag name="Subscribers" table="studio_Groups_Subscribers" access="nosetter.camelcase">
<key column="GroupId"></key>
<many-to-many column="SubscriberId" class="SubscriberItem" />
</bag>
</class>
</hibernate-mapping>
Shouldn't you have an appropriate bag on your subscriber aswell with a many-to-many relation to the group?
<bag name="Groups" table="studio_Groups_Subscribers" access="nosetter.camelcase">
<key column="SubscriberId"></key>
<many-to-many column="GroupId" class="GroupItem" />
</bag>
and maybe have a cascade="save-update" on your SubscriberItems collection so that saving the Group will update your children with the appropriate relation from "the other side".
I believe it is to do with the cascade options, check out Ayende's post on it and I imagine with a bit of Googling you can find the correct syntax to go into your hbm file. I use fluent-NHibernate so I can't help you with the XML file.
http://ayende.com/Blog/archive/2006/12/02/NHibernateCascadesTheDifferentBetweenAllAlldeleteorphansAndSaveupdate.aspx