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>.
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 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>
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 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.
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! =)