Ayende has a great example of using the <any> mapping here, which I am reposting as part of my question since comments are closed on that blog post. Given his original mapping:
<class name="Order" table="Orders">
<id name="Id">
<generator class="native"/>
</id>
<any name="Payment" id-type="System.Int64" meta-type="System.String" cascade="all">
<meta-value value="CreditCard" class="CreditCardPayment"/>
<meta-value value="Wire" class="WirePayment"/>
<column name="PaymentType"/>
<column name="PaymentId"/>
</any>
</class>
<class name="CreditCardPayment" table="CreditCardPayments">
<id name="Id">
<generator class="native"/>
</id>
<property name="IsSuccessful"/>
<property name="Amount"/>
<property name="CardNumber"/>
</class>
<class name="WirePayment" table="WirePayments">
<id name="Id">
<generator class="native"/>
</id>
<property name="IsSuccessful"/>
<property name="Amount"/>
<property name="BankAccountNumber"/>
</class>
... how would I go about mapping a property named Order on the CreditCardPayment and WirePayment classes which would be the "flip side" of the association allowing traversal from these payments back up to the Order they are associated with?
The gotcha for me here is that CreditCardPayment and WirePayment can potentially have the same IDs since they are in different tables, so I need some way to tell NHibernate to take the PaymentType into consideration.
Bidirectional heterogeneous associations (i.e. <any>) are not supported by NHibernate.
I know this isn't what you wanted to hear, but that's it.
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.
My question is similar to this question.
But I want to query by the discriminator of a child entity associated with a one-to-one relationship, and without knowing the exact discriminator value, i.e., by type not by string.
Given an hbm like:
<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" />
</class>
<subclass name="ConcreteChild" extends="IChild" discriminator-value="Concrete1">
<property name="SomeProperty"/>
</subclass>
Or any other one-to-one configuration, I would like to run a query similar to this:
public IEnumerable<Parent> FindByChild(Type childType)
{
return session.CreateCriteria<Parent>()
.Add(Restrictions.Eq("Child.class", childType))
.List<Parent>();
}
Further information:
The above criteria query fails because "Child.class" is not recognized as valid.
A similar query using HQL fails because NHibernate uses the fullname of childType rather than its discriminator value in the query.
Have you tried using an alias? I can't test it right now on my machine, but should be similar to this..
return session.CreateCriteria<Parent>()
.CreateAlias("ParentChild","Child")
.Add(Restrictions.Eq("ParentChild.class", childType))
.List<Parent>();
where ParentChild is just the name I used to refer to the Child entity of the Parent class
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.
I have a Member Table with fields
MemID - Primary Key
Business_Name
Business_Address
Business_Phone
I need to make an Employer Class which has properties that come from the same Members Table.
EmployerName
EmployerAddress
EmployerPhone
Here is my Employer Mapping
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="true">
<class name="Employer, Entities" lazy="true" table="Members" dynamic-update="true">
<id name="MemberID" column="MemID" type="Int64">
<generator class="native" />
</id>
<many-to-one name="EmployerAddress" column="Business_Address" class="Address, Entities" lazy="proxy" />
<many-to-one name="EmployerPhone" column="Business_Phone" class="Phone, Entities" lazy="proxy"/>
<property name="EmployerName" column="Business_Name" not-null="false" />
</class>
</hibernate-mapping>
I thought that I could map the Members class like this but I get a "System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary."
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="true">
<class name="Member, Entities" lazy="true" table="Members" dynamic-update="true">
<id name="MemberID" column="MemID" type="Int64">
<generator class="native" />
</id>
<one-to-one name="EmployerInformation" class="Employer, Entities" lazy="false"/>
</class>
</hibernate-mapping>
Also please note. I can't move the Business Information to another table due to constraints on the current system. Business_Address and Business_Phone are FK to another table that is why they are many-to-one mappings.
I'm not sure if this is what you're looking for, but you could try the "component" mapping. This allows you to have a nested class within the same table.
Search google for "nhibernate component" - it appears that the hibernate.org site is still down (!), but you might be able to get the component info from the google cache for the page "Chapter 7 - Component Mapping."
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>