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
Related
I'm having some difficulties mapping the following sitation:
I have a person with a connection id, I use this to get map a bag and a property.
This works if the database already exists. But for our unit tests, we generate one from the schema, it gives an error : "duplicate column names".
Here is the mapping:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="DomainLayer.SearchDomain" assembly="Test">
<class name="SearchPerson" table="person" lazy="false" mutable="false">
<id name="Id" column="`id`" type="int">
<generator class="identity" />
</id>
<property name="Name" column="`NAAM`" type="string" />
<property name="FirstName" column="`VOORNAAM`" type="string" />
<property name="ConnectionId" column="`Koppelid`" type="int" />
<bag name="Languages" lazy="false" mutable="false" access="field.camelcase-underscore">
<key property-ref="ConnectionId" column="Koppelid" />
<one-to-many class="DomainLayer.Person.LanguageSkill, Test" />
</bag>
</class>
</hibernate-mapping>
Problem: "Koppelid" both in Key property from the bag as in the property.
Edit:
The LanguageSkill mapping:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="DomainLayer.Person" assembly="Test">
<class name="LanguageSkill" table="languageskill" lazy="false">
<id name="Id" type="Int32">
<generator class="identity" />
</id>
<property name="ConnectionId" column="`KoppelId`" type="Int64" />
<property name="Remark" column="`Opmerking`" type="string" />
<property name="Source" column="`CdOorsprong`" type="string" />
<property name="MotherTongue" column="`moedertaal`" type="boolean" />
<property name="ModifiedDate" column="`wyzdat`" type="DateTime" />
</class>
</hibernate-mapping>
It should actually work. It may be because you have marked it with ` in one case, but not in the other.
Try this:
<bag ...>
<key property-ref="ConnectionId" column="`Koppelid`" />
the reason is actually that the column is defined twice in the LanguageSkillMapping. Since you mapped the column you could map it as reference and set the bag to inverse
<bag name="Languages" inverse="true" lazy="false" mutable="false" access="field.camelcase-underscore">
<key property-ref="ConnectionId" column="Koppelid" />
<one-to-many class="DomainLayer.Person.LanguageSkill, Test" />
</bag>
<many-to-one name="SearchPerson" column="`KoppelId`" />
Update: nevermind, Stefan got it
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.
I have these 2 objects in NHibernate forming a many to many relationship:
User:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Providers" namespace="Providers.Objects">
<class name="User" table="Users">
<id name="UserId" type="int">
<generator class="native" />
</id>
<many-to-one name="Application" column="ApplicationId" cascade="none" />
<property name="UserName" type="string" />
<property name="LoweredUserName" type="string" />
<property name="MobileAlias" type="string" />
<property name="IsAnonymous" type="bool" />
<property name="LastActivityDate" type="DateTime" />
<bag name="Roles" table="UsersInRoles" lazy="true" cascade="none" >
<key column="UserId"></key>
<many-to-many class="Role" column="RoleId"></many-to-many>
</bag>
</class>
</hibernate-mapping>
And Role:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Providers" namespace="Providers.Objects">
<class name="Role" table="Roles">
<id name="RoleId" type="int">
<generator class="native" />
</id>
<many-to-one name="Application" column="ApplicationId" class="Application" cascade="none" />
<property name="RoleName" type="string" />
<property name="LoweredRoleName" type="string" />
<property name="Description" type="string" />
<bag name="Users" table="UsersInRoles" lazy="true" inverse="true" cascade="none" >
<key column="RoleId"></key>
<many-to-many class="User" column="UserId"></many-to-many>
</bag>
</class>
</hibernate-mapping>
Let's say the role backupoperator has some users in it. If I try to remove one of the users from the role instance, like:
var backupoperator = GetRoleByName(session, app.ApplicationId, "backupoperator");
backupoperator.Users.RemoveAt(0);
session.Update(backupoperator);
transaction.Commit();
It doesn't work :( The association remains unchanged in the database. When I try the opposite (remove a role from a user object and updating the user object), it works.
Is it because of the inverse attribute in the NHibernate mapping?
How to accomplish what I am trying to do? (remove a user from a role, updating the role and having that persisted)?
Thanks
When you write inverse="true" you are telling NHibernate that the other side maintains the relationship.
Therefore, you have to remove the Role from the User's Roles collection if you want your change persisted.
I have two tables, Vehicle and Make. The two are joined using the MakeId as a foreign key on the Vehicle table. My mapping file looks similar to this
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="Demo.Business.Objects.Vehicle, Demo.Business.Objects" table="Vehicle" >
<id name="VehicleId" type="int" >
<generator class="native" />
</id>
<property name="RegNumber" type="String" />
<property name="VehicleId" type="int" />
<property name="CustomerId" type="int" />
<join table="Make" fetch="join">
<key column="MakeId" foreign-key="MakeId"/>
<property name="Description" type="String" />
</join>
</class>
</hibernate-mapping>
I would have thought that this would join the two tables on the make id, however the SQL that ios generated tries the following join: vehicle.vehicleid = make.makeid.
How can I get this to work? I.e. I expect:
select * from Vehicle
inner join Make on Make.MakeId = Vehicle.Make Id
You need to map the Make class differently:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="Demo.Business.Objects.Vehicle, Demo.Business.Objects" table="Vehicle" >
<id name="VehicleId" type="int" >
<generator class="native" />
</id>
<property name="RegNumber" type="String" />
<property name="VehicleId" type="int" />
<property name="CustomerId" type="int" />
<many-to-one name="Make" column="MakeId"/>
</class>
<class name="(blahblah).Make, blahblah">
<id name="MakeId" type="int">
<generator class="native" />
</id>
<property name="Description" type="String" />
</class>
</hibernate-mapping>
Your "Vehicle" class should have a Make property of type Make.
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' –