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
Related
I have the following UML structure:
I'm trying to map it similar to this:
<class name="Parent" table="ParentTable">
<id name="Id">
<generator class="guid.comb" />
</id>
<one-to-one name="Child" class="IChild" property-ref="Parent" cascade="all" />
</class>
<class name="IChild" table="ChildTable" abstract="true">
<id name="Id">
<generator class="foreign">
<param name="property">Parent</param>
</generator>
</id>
<discriminator column="TypeKey" type="String"/>
<one-to-one name="Parent" class="Parent" />
<one-to-one name="Child" class="IGrandchild" property-ref="Parent" cascade="all" />
</class>
<subclass name="ConcreteChild" extends="IChild" discriminator-value="ConcreteChild1">
<property name="SomeProperty"/>
</subclass>
<class name="IGrandchild" table="GrandchildTable" abstract="true">
<id name="Id">
<generator class="guid.comb" />
</id>
<discriminator column="TypeKey" type="String"/>
<many-to-one name="Parent" class="IChild" unique="true" column="ChildTableFk" />
</class>
<subclass name="ConcreteGrandchild" extends="IGrandchild" discriminator-value="ConcreteGrandchild1">
<property name="SomeOtherProperty"/>
</subclass>
Working against SQL this doesn't work (strangely, with SQLite this does work). NHibernate first inserts the Parent with the generated guid. It then inserts the child with the same guid. But when it comes to inserting the grandchild, it inserts it with ChildTableFk null (and never attempts to update the FK value).
Additional points:
I prefer not changing the IChild mapping to use many-to-one with FK if possible (I prefer a shared PK).
I can't change the IGrandChild mapping to use a foreign generator because the grandchild can be changed after the object graph is changed, which NHibernate does not support with a foreign generator (an IChild instance, on the other hand, will never change for the lifecycle of a given IParent).
Other than that, any suggestions are welcome, including alternate mapping styles (as long as they support polymorphic IChild and IGrandchild objects).
Oops, seems that I just forgot to set the Parent property on the Grandchild. Should have been the first thing I looked at.
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.
Currently I have the following classes:
class Article with properties id, title and body
class Question : Article with an extra PostedBy property
Then I have a table called Article with the above properties and a table called questions with an ID a foreign key articleID and a PostedBy. Both are in different schemas
I would like to know how are my mappings going to look to represent this relation. Both classes are in different assemblies and i would be very reluctant to put Question logic in Article class/mapping and its assembly.
NHibernate supports three basic inheritance strategies.
table per class hierarchy
table per subclass
table per concrete class
It sounds like you are looking for the table per subclass strategy as you have a table for your Article class and another table for the extra properties on the Question subclass. The mapping might looks something like this:
<class name="Article" table="Article">
<id name="Id" type="Int64" column="ArticleId">
<generator class="native"/>
</id>
<property name="Title" column="Title"/>
<property name="Body" column="Body"/>
...
<joined-subclass name="Question" table="Question">
<key column="ArticleId"/>
<property name="PostedBy" column="PostedBy"/>
...
</joined-subclass>
</class>
However, this doesn't meet your desire to keep the mappings entirely separate. You could have entirely separate mappings, but this might have some side effects as allowing Question to be loaded as a plain Article instead of a Question. With separate mapping the Article class would be straight-forward as expected. The Question class would include a join to access the properties stored in the Article table.
<class name="Article" table="Article">
<id name="Id" type="Int64" column="ArticleId">
<generator class="native"/>
</id>
<property name="Title" column="Title"/>
<property name="Body" column="Body"/>
...
</class>
<class name="Question" table="Question">
<id name="Id" type="Int64" column="QuestionId">
<generator class="native"/>
</id>
<property name="PostedBy" column="PostedBy"/>
...
<join table="Article">
<key column="ArticleId"/>
<property name="Title" column="Title"/>
<property name="Body" column="Body"/>
</join>
</class>
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>