I have 3 tables Users, AccessLevels, Roles. My User class have public virtual Dictionary<Role,AccessLevel> Roles {get; set;}, and
Role class have public virtual Dictionary<User,AccessLevel> Users {get;set;} ? How such dictionaries should be represented in the mapping?
in FluentNhibernate it would look like
HasManyToMany(user => user.Roles)
.Table("UserRoleAccessLevel")
.AsEntityMap("role_id")
.KeyColumn("user_id");
HasManyToMany(role => role.Users)
.Table("UserRoleAccessLevel")
.AsEntityMap("user_id")
.KeyColumn("role_id");
can't test it right now though
Update: in hbm.xml
<map name="Roles" table="UserRoleAccessLevel">
<key column="user_id" />
<index-many-to-many class="Role" column="role_id" />
<many-to-many class="AccessLevel" column="accesslevel_id" />
</map>
<map name="Users" table="UserRoleAccessLevel">
<key column="role_id" />
<index-many-to-many class="User" column="user_id" />
<many-to-many class="AccessLevel" column="accesslevel_id" />
</map>
It would be very nice if you provided structure of your tables, especially AccessLevels table. As far as I could guess structure of your tables I can suggest you this mapping
<class name="User" table="Users">
<id name="Id" column="user_id">
<generator class="native" />
</id>
<property name="Name" column="user_name" not-null="true" />
<map name="Roles" table="AccessLevels" cascade="save-update">
<key column="user_id" />
<map-key-many-to-many column="role_id" class="Role" />
<one-to-many class="AccessLevel"/>
</map>
</class>
<class name="Role" table="Roles">
<id name="Id" column="role_id">
<generator class="native" />
</id>
<property name="Name" column="user_name" not-null="true" />
</class>
<class name="AccessLevel" table="AccessLevels">
<id name="Id" column="Id">
<generator class="native" />
</id>
<property name="Level" column="level" not-null="true" />
<many-to-one name="User" column="user_id" not-null="true" cascade="save-update" />
<many-to-one name="Role" column="role_id" not-null="true" cascade="save-update" />
</class>
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 have the below NHibernate file, however I am not allowed to delete a Question because of a foreign key constraint on either of the two Answer tables. The desired behavior is to delete Answers once corresponding Questions are deleted and cascade settings are set on the Answers element.
Below is the configuration file, can anyone see what the problem is
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" namespace="Test.Domain" assembly="Test.Domain" xmlns="urn:nhibernate-mapping-2.2">
<class name="Question" abstract="true">
<id name="Id" type="Int32">
<generator class="hilo" />
</id>
<discriminator />
<property name="Text" length="500" />
<property name="Note" length="2000" />
<property name="DateRun" />
<many-to-one name="ChoiceType" column="ChoiceTypeId" />
<bag name="Answers" inverse="true" cascade="all,delete-orphan">
<key column="QuestionId" on-delete="cascade" />
<one-to-many class="Answer" />
</bag>
</class>
<class name="Answer" abstract="true">
<id name="Id" type="Int32">
<generator class="hilo" />
</id>
<many-to-one name="Question" column="QuestionId" />
<many-to-one name="Group" column="GroupId" />
<property name="Comment" />
</class>
<class name="Choice">
<id name="Id" type="Int32">
<generator class="hilo" />
</id>
<property name="Text" length="500" />
</class>
<class name="ChoiceType">
<id name="Id" type="Int32">
<generator class="hilo" />
</id>
<property name="Name" />
<property name="Type" />
<list name="Choices" cascade="all,delete-orphan">
<key column="ChoiceTypeId" />
<list-index column="ChoicesPos" />
<one-to-many class="Choice" />
</list>
</class>
<class name="Division">
<id name="Id" type="Int32">
<generator class="hilo" />
</id>
<property name="Name" />
</class>
<union-subclass name="FreeTextAnswer" extends="Answer">
<property name="Text" length="500" />
</union-subclass>
<union-subclass name="MultipleChoiceAnswer" extends="Answer">
<many-to-one name="Choice" column="ChoiceId" />
</union-subclass>
<subclass name="MultipleChoiceQuestion" extends="Question" />
<subclass name="FreeTextQuestion" extends="Question" />
</hibernate-mapping>
Isn't it
cascade="all-delete-orphan"
... with a hyphen, as opposed to two enum values?
Your answers collection mapping should look like this:
<bag name="Answers" inverse="true" cascade="all-delete-orphan">
<key column="QuestionId"/>
<one-to-many class="Answer" />
</bag>
I removed on-delete="cascade". When you you want to remove one answer from the question you will have to 'chase pointers'. Set reference to question to NULL, in addition to removing it from answers collection:
answer.Question = null;
I am trying to switch a table from being a many-to-one mapping to being many-to-many with an intermediate mapping table. However, when I switched it over and tried to do a query on it with NHibernate, it's giving me this error: "Interceptor.OnPrepareStatement(SqlString) returned null or empty SqlString."
My query was originally something more complex, but I switched it to a basic fetch all and I'm still having the problem:
Session.QueryOver<T>().Future();
It would seem to either be a problem in my model mapping files or something in my database.
Here are my model mappings:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="GBI.Core" namespace="GBI.Core.Models">
<class name="Market" table="gbi_Market">
<id name="Id" column="MarketId">
<generator class="identity" />
</id>
<property name="Name" />
<property name="Url" />
<property name="Description" type="StringClob" />
<property name="Rating" />
<property name="RatingComment" />
<property name="RatingCommentedOn" />
<many-to-one name="RatingCommentedBy" column="RatingCommentedBy" lazy="proxy"></many-to-one>
<property name="ImageFilename" />
<property name="CreatedOn" />
<property name="ModifiedOn" />
<property name="IsDeleted" />
<many-to-one name="CreatedBy" column="CreatedBy" lazy="proxy"></many-to-one>
<many-to-one name="ModifiedBy" column="ModifiedBy" lazy="proxy"></many-to-one>
<set name="Content" where="IsDeleted=0 and ParentContentId is NULL" order-by="Ordering asc, CreatedOn asc, Name asc" lazy="extra">
<key column="MarketId" />
<one-to-many class="MarketContent" />
</set>
<set name="FastFacts" where="IsDeleted=0" order-by="Ordering asc, CreatedOn asc, Name asc" lazy="extra">
<key column="MarketId" />
<one-to-many class="MarketFastFact" />
</set>
<set name="NewsItems" table="gbi_NewsItem_Market_Map" lazy="true">
<key column="MarketId" />
<many-to-many class="NewsItem" fetch="join" column="NewsItemId" where="IsDeleted=0"/>
</set>
<!--<set name="MarketUpdates" table="gbi_Market_MarketUpdate_Map" lazy="extra">
<key column="MarketId" />
<many-to-many class="MarketUpdate" fetch="join" column="MarketUpdateId" where="IsDeleted=0" order-by="CreatedOn desc" />
</set>-->
<set name="Documents" table="gbi_Market_Document_Map" lazy="true">
<key column="MarketId" />
<many-to-many class="Document" fetch="join" column="DocumentId" where="IsDeleted=0"/>
</set>
</class>
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="GBI.Core" namespace="GBI.Core.Models">
<class name="MarketUpdate" table="gbi_MarketUpdate">
<id name="Id" column="MarketUpdateId">
<generator class="identity" />
</id>
<property name="Description" />
<property name="CreatedOn" />
<property name="ModifiedOn" />
<property name="IsDeleted" />
<!--<many-to-one name="Market" column="MarketId" lazy="proxy"></many-to-one>-->
<set name="Comments" where="IsDeleted=0" order-by="CreatedOn desc" lazy="extra">
<key column="MarketUpdateId" />
<one-to-many class="MarketUpdateComment" />
</set>
<many-to-one name="CreatedBy" column="CreatedBy" lazy="proxy"></many-to-one>
<many-to-one name="ModifiedBy" column="ModifiedBy" lazy="proxy"></many-to-one>
</class>
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="GBI.Core" namespace="GBI.Core.Models">
<class name="MarketUpdateMarketMap" table="gbi_Market_MarketUpdate_Map">
<id name="Id" column="MarketUpdateMarketMapId">
<generator class="identity" />
</id>
<property name="CreatedOn" />
<property name="ModifiedOn" />
<property name="IsDeleted" />
<many-to-one name="CreatedBy" column="CreatedBy" lazy="proxy"></many-to-one>
<many-to-one name="ModifiedBy" column="ModifiedBy" lazy="proxy"></many-to-one>
<many-to-one name="MarketUpdate" column="MarketUpdateId" lazy="proxy"></many-to-one>
<many-to-one name="Market" column="MarketId" lazy="proxy"></many-to-one>
</class>
As I mentioned, MarketUpdate was originally a many-to-one with Market (MarketId column is still in there, but I'm ignoring it. Could this be a problem?). But I've added in the Market_MarketUpdate_Map table to make it a many-to-many.
I'm running in circles trying to figure out what this could be. I couldn't find any reference to this error when searching. And it doesn't provide much detail.
Using:
NHibernate 2.2
.NET 4.0
SQL Server 2005
Turns out the problem was just that the xml mapping file was set as content instead of embedded resource in visual studio. Changing that fixed all my problems.
If you're using Fluent rather than xml for mappings and encounter this issue, try deleting the generated xml files in %root%\LocalCache\NHibernate.
What's wrong with this mapping?
On saving an instance of Class3, two rows in Table_2 will be inserted!
first row has Column4 set to null and correct value of Column3,
and second row has Column3 set to null and correct value of Column4!
<class name="Class1" table="Table_1">
<id name="Column1">
<generator class="native" />
</id>
<discriminator column="ColumnDisc" />
<property name="Column2" type="int" />
<subclass name="Class2">
<join table="Table_2">
<key column="Column1" />
<property name="Column3" type="int" />
</join>
<subclass name="Class3" >
<join table="Table_2">
<key column="Column1" />
<property name="Column4" type="int" />
</join>
</subclass>
</subclass>
</class>
Where do you set the discriminator value for each of the derived types?
If I believe the link to which you pointed in your comment, the class mapping looks like this:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.javalobby.tnt.hib.User" table="USER" lazy="false">
<id name="id" type="long" column="ID">
<generator class="identity" />
</id>
<discriminator column="type" type="string"/>
<property name="firstName" column="first_name"/>
<property name="lastName" column="last_name"/>
<subclass name="com.javalobby.tnt.hib.Employee" discriminator-value="employee">
<join table="employee">
<key column="id"/>
<property name="jobTitle" column="job_title"/>
<many-to-one name="supervisor" column="supervisor_id" not-null="true" />
</join>
<subclass name="com.javalobby.tnt.hib.Employer" discriminator-value="employer">
<set name="subordinates">
<key column="supervisor_id" not-null="true"/>
<one-to-many class="com.javalobby.tnt.hib.Employee"/>
</set>
<join table="employer">
<key column="id"/>
<property name="companyCarBrand" column="company_car_brand"/>
</join>
</subclass>
</subclass>
</class>
If I compare your mapping with this one, you lack discriminator-value attribute within your <subclass> tag.
<class name="Class1" table="Table_1">
<id name="Column1">
<generator class="native" />
</id>
<discriminator column="ColumnDisc" />
<property name="Column2" type="int" />
<subclass name="Class2"> <!-- The discriminator-value="" is missing here... -->
<join table="Table_2">
<key column="Column1" />
<property name="Column3" type="int" />
</join>
<subclass name="Class3" > <!-- The discriminator-value="" is missing here... -->
<join table="Table_2">
<key column="Column1" />
<property name="Column4" type="int" />
</join>
</subclass>
</subclass>
</class>
Make sure you set the right discriminator values for each of the subclasses you intend to map. I suggest trying this and figure out whether it works better, or if this raised another problem. =)
Hope this helps! =)
I have a mapping file
<set name="Friends" table="Friends">
<key column="UserId"/>
<many-to-many class="User" column="FriendId"/>
</set>
I would like to specify extra columns for the friend table this creates.
For example Approve (the user must approve the friend request)
Is there a easy way?
And update
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping namespace="MyVerse.Domain" assembly="MyVerse.Domain" xmlns="urn:nhibernate-mapping-2.2">
<class name="User" table="[User]" lazy="true">
<id name="Id" type="Guid">
<generator class="guid" />
</id>
<property name="DateCreated" type="DateTime" not-null="true" />
<property name="Deleted" type="Boolean" not-null="true" />
<property name="Firstname" type="String" length="100" not-null="true" />
<property name="Lastname" type="String" length="100" not-null="true" />
<bag name="Friends" table="[Friend]">
<key column="UserId"/>
<many-to-many class="Friend" column="FriendId"/>
</bag>
</class>
<class name="Friend" table="[Friend]" lazy="true">
<id name="Id" type="Guid">
<generator class="guid" />
</id>
<property name="DateCreated" type="DateTime" not-null="true" />
<property name="Approved" type="Boolean" not-null="true" />
</class>
</hibernate-mapping>
Will cause a link to the friend table from the friend table
If a set has "extra properties", you must convert it into a proper entity.
So, a User doesn't have an ISet<User>; it has an ISet<Friend>.