I have a Group. That group can contain groups and this needs to be mapped with NHibernate. I have done this before but this time I got an existing database that does not really store the data like I would like it too.
The database looks like this:
Groups
Id
Groups_Link
ParentId
ChildId
I have no clue about how to map this?
Edit:
Seems like it's not that easy. One group can exist as a child to more than one group. So it should be an many-to-many association with itself I guess. Well, have yet to figure out how to do this. Any hints would be really appreciated!
Edit:
I thought views could help me solve the problem but there seems to be some restrictions on insert, update and delete with views that makes it difficult.
As you say, it's just a many-to-many that happens to have the same class on both sides of the join.
So:
<class name="G.Group, G" table="GROUPS">
<id column="Id" name="Id">
<generator class="guid"/>
</id>
<set name="Parents" table="Groups_Link" cascade="save-update">
<key column="ChildId"/>
<many-to-many class="G.Group, G" column="ParentId"/>
</set>
<set name="Children" table="Groups_Link" cascade="save-update" inverse="true">
<key column="ParentId" />
<many-to-many class="G.Group, G" column="ChildId"/>
</set>
</class>
I mapped a similar structure like:
<class name="Folder" lazy="false">
<id column="Id" name="Id">
<generator class="guid"/>
</id>
<property name="Name" column="FolderText"></property>
<bag name="Children" cascade="all" fetch="subselect" inverse="true">
<key column="ParentId" />
<one-to-many class="Folder" />
</bag>
<many-to-one class="Folder" name="Parent" column="ParentId" insert="true" />
Edit
I am taking a total shot in the dark now since your schema is not what I've used, and now I understand why you asked this:-) Anyways here's is a thought. Please let us know if this worked of if you find another solution:
<join fetch="join" table="GroupLinks">
<key column="ChildId"></key>
<many-to-one name="Parent" class="Folder" column="ParentId"/>
</join>
I might have the relationship backwards, but this at least would get you the parent.
Related
I have a problem with my mappings Employee and Project: For example in Employee mapping I have "bag" to map other tables. The problem is when I open the Employee window, this take a lot of time(10 sec) opening the Window, how can I make the mapping better and also faster? Maybe in the lazy or in the fetch?.
This is the mapping for Employee:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="AdminProject"
namespace="AdminProject.Business.Entity">
<class name="Employee">
<id name="EmployeeId" type="int">
</id>
<property name="OperatorNum"
generated="always"
update="false"
insert="false"
type="int"/>
<property name="Password" type="string"/>
<property name="Name" type="string"/>
<property name="LastName" type="string"/>
<property name="DateBegin" type="DateTime"/>
<property name="DateEnd" type="DateTime"/>
<property name="Telephone" type="string"/>
<property name="Address" type="string"/>
<many-to-one
name="EmployeeState"
column="EmployeeStateId"
class="EmployeeState"
fetch="join"/>
<bag name="EmployeebyProject" lazy="false">
<key column="EmployeeId"/>
<one-to-many class="EmployeebyProject"/>
</bag>
<bag name="EmployeeComments" lazy="false">
<key column="EmployeeId"/>
<one-to-many class="EmployeeComments"/>
</bag>
</class>
</hibernate-mapping>
Thanks..
You should use SQL Server Profiler to figure out what is being loaded from your database. Alternatively you can set NHibernate to log SQL. This way it will be easier for you to see what causes the delay. Very likely that it is caused by eagerly loading collections (your mapping has lazy="false"). If this is the case you can simply set it to true (default).
<bag name="EmployeebyProject" lazy="true">
<key column="EmployeeId"/>
<one-to-many class="EmployeebyProject"/>
</bag>
<bag name="EmployeeComments" lazy="true">
<key column="EmployeeId"/>
<one-to-many class="EmployeeComments"/>
</bag>
Your many-to-one association is also loaded eagerly (fetch="join").
One of the popular approaches is to have all associations lazy (Lazy Default Fetch Plan). And then use eager loading in places where you are sure you will need association loaded. This however depends on your session management because lazy loading will not work if session is no longer available. There is a very good description of fetch plans and strategies in this book.
For your bag mapping, remove lazy="false". It's lazy by default.
I am working on an existing data structure that is not perfect and I have an inheritance mapping issue to solve.
I am using a table per hierarchy and have subclasses with discriminators set up. However the subclassed properties are foreign keys back to other tables. How do I set up my subclass mapping so that when I query the fk property I get an object rather than null? Is this even possible?
My current Mapping
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="MVC3" namespace="MVC3.Models">
<class name="Image" table="Images">
<id name="Id" column="ImageId">
<generator class="identity" />
</id>
<discriminator column="ImageType" />
<property name="Url" column="Url" not-null="true" />
<property name="Caption" column="Caption" />
<subclass name="AupairImage" discriminator-value="AupairImage">
<join table="Aupairs" inverse="true">
<key column="AupairId" />
<many-to-one name="Aupair" column="AupairId" class="Aupair" />
</join>
</subclass>
<subclass name="FamilyImage" discriminator-value="FamilyImage">
<join table="Families" inverse="true">
<key column="FamilyId" />
<many-to-one name="Family" column="FamilyId" class="Family" />
</join>
</subclass>
I would have like to add an entity diagram but I cannot post images yet :0(
But the foreign keys AupairId and FamilyId link off back to two other tables and are of the type int 32
I know it would be better to restructure the Aupair And Family entities to have a hierarchy to get rid of the one in images but this would be a last resort due to existing code.
Thanks in advance any help appreciated from you mapping experts....
Realised what I was doing wrong after taking a second look, should have just used a Many-To-One under the Subclass with a defined column
<subclass name="MVC3.Models.FamilyImage, MVC3" discriminator-value="FamilyImage">
<many-to-one cascade="all" class="MVC3.Models.Family, MVC3" name="Family">
<column name="FamilyId" />
</many-to-one>
</subclass>
Hey all, I'm kicking the tires on NHibernate and have a conoundrum I have been scratching my head over for a bit now, working with a legacy database with some fairly complex relationships.
ClaimRoot has a primary key of a claimGUID.
ClaimRoot has a bag of Claimdetails associated by claimGUID (this works a treat).
The problem is that ClaimRoot also has an optional one to one relationship with ClaimFinancials (not all ClaimRoots have ClaimFinancials, but most do). But the PK for ClaimFinancials is a FormID field. This field exists in the ClaimRoot, but is not the PK.
I've posted a mapping below with extra columns removed to protect the innocent.
<class name="ClaimRoot" table="tbl_ClaimRoot" schema="DB1.dbo">
<id name="ClaimGUID">
<generator class="guid"/>
</id>
<property name="FormID" />
<property name="LastFormNoteText" />
<bag name="ClaimDetails" inverse="true">
<key column="ClaimGUID"/>
<one-to-many class="ClaimDetails"/>
</bag>
</class>
<class name="ClaimDetails" table="tbl_ClaimDetails" schema="DB2.dbo">
<id name="RowID">
<generator class="native"/>
</id>
<property name="ClaimGUID" />
<property name="SeqNo"/>
<property name="B1A_InsID" />
<many-to-one name="Root" column="ClaimGUID" foreign-key="ClaimGUID"/>
</class>
<class name="ClaimFinancials" table="tbl_ClaimFinancials" schema="DB1.dbo">
<id name="FormID">
<generator class="native"/>
</id>
<property name="CreatedDate"/>
<property name="SubmittedDate" />
</class>
Thanks in advance!
-Bob
Assuming the FormID is use only for linking ClaimRoot and ClaimFinancials, it sounds like you want a many-to-one relationship from ClaimRoot to ClaimFinancials. Replace the FormId property on ClaimRoot with a many-to-one.
<class name="ClaimRoot" table="tbl_ClaimRoot" schema="DB1.dbo">
...
<many-to-one name="ClaimFinancials" column="FormID" />
...
</class>
A many-to-one relationship can be be used even if there is only 'one' on the 'many' side. If you were generating a schema, you can specify unique="true" to generate the constraint in the database. With a legacy database, that won't matter.
Ok, new to nhibernate and I am working on a project that has already fully implemented it.
Scenerio: One class (Person) has two joined subclasses (RoleA and RoleB).
What I need is that a given person can actually be both in RoleA and RoleB. How, when given a person that is already created and in RoleA, can I then make them also in RoleB while maintaining the relationships with RoleA?
So, you have something like the following (with Students and Teachers taking the place of RoleA and RoleB):
<class name="Person" table="Persons" >
<id name="Id" column="PersonID">
<generator class="native" />
</id>
<property name="Name" column="Name" not-null="true" />
<joined-subclass name="Student" table="Students">
<key column="PersonID" />
<property name="Grade" column="Grade" not-null="true" />
</joined-subclass>
<joined-subclass name="Teacher" table="Teachers">
<key column="PersonID" />
<property name="ClassName" column="ClassName" not-null="true" />
</joined-subclass>
</class>
If that is the case, your best bet is to use a one-to-one mapping to accomplish the same thing. Here is a good reference: http://nhibernate.info/doc/nh/en/index.html#mapping-declaration-onetoone
This person had a similar problem as you, and ended up going with the one-to-one mapping option:
http://groups.google.com/group/nhusers/browse_thread/thread/1d83e0cd3c2bf58f
We have a legacy database schema which I've tried (unsuccessfully) to map with NHibernate. To give a simplified example, say I want a Person class whose first name comes from the "Person" table, but their last name comes from the "Person2" table. The "Person" table also has the Id of the person's Car and I want my Person class to have a Car property. I can map all that using the following;
<hibernate-mapping default-cascade="save-update" xmlns="urn:nhibernate-mapping-2.2" auto-import="true">
<class name="NHibernateMappingTest.Person, NHibernateMappingTest" lazy="false">
<id name="Id" >
<generator class="native" />
</id>
<property name="FirstName" />
<many-to-one name="Car" access="property" class="NHibernateMappingTest.Car, NHibernateMappingTest" column="CarId" cascade="save-update"/>
<join table="Person2">
<key column="PersonId" />
<property name="LastName" />
</join>
</class>
</hibernate-mapping>
The lets me combine the Person and Person2 tables, and the lets me find their Car - everything works fine.
But... if the Person2 table happens to have the person's HouseId, I'd like to be able to add a second element to my mapping...
<many-to-one name="House" access="property" class="NHibernateMappingTest.House, NHibernateMappingTest" column="HouseId" cascade="save-update"/>
...so that my Person class can have a House property.
However this is where it all goes wrong, because the SQL which NHibernate generates assumes that the HouseId column is in the Person table (but it's not, it's in Person2), so I get the following error;
MySql.Data.MySqlClient.MySqlException: #42S22Unknown column 'HouseId' in 'field list'
Is NHibernate able to do what I'm attempting, is there a different way to achieve this (without changing the database schema), or have I just made a beginner's error in my map file?
Vincent - thanks for your response. No I wasn't nesting the tag element inside the element. But following your suggestion, I tried and it works perfectly! Thanks very much for responding.
<hibernate-mapping default-cascade="save-update" xmlns="urn:nhibernate-mapping-2.2" auto-import="true">
<class name="NHibernateMappingTest.Person, NHibernateMappingTest" lazy="false">
<id name="Id" >
<generator class="native" />
</id>
<property name="FirstName" />
<many-to-one name="Car" access="property" class="NHibernateMappingTest.Car, NHibernateMappingTest" column="CarId" cascade="save-update"/>
<join table="Person2">
<key column="PersonId" />
<property name="LastName" />
<many-to-one name="House" access="property" class="NHibernateMappingTest.House, NHibernateMappingTest" column="HouseId" cascade="save-update"/>
</join>
</class>
</hibernate-mapping>