This may not make a ton of sense because I may be using the incorrect ORM terms. I'm having trouble mapping a relationship where children can have either one or two parent objects.
Quick summary of the data model: Some customers have "business unit" entities, and these customers have their projects modeled thru these business units. Normal customers have a normal child collection of projects.
(I personally think this relationship is confusing and not optimal but obviously I cannot change this, or else I wouldn't have a need for this question.)
My problem is mapping the project-business unit relationship bi-directionally.
Code:
<class name="Customer, AssemblyName" table="customer">
<id name="Id" column="customer_id" unsaved-value="0">
<generator class="assigned"/>
</id>
<property name="Name" column="Customer_name" type="String"/>
<bag name="BusinessUnits" table="customer_business_unit" lazy="true" order-by="business_unit_name">
<key column="customer_id"/>
<one-to-many class="CustomerBusinessUnit, AssemblyName"/>
</bag>
<bag name="Projects" table="project" lazy="true" order-by="project_name">
<key column="customer_id"/>
<one-to-many class="Project, AssemblyName"/>
</bag>
</class>
Code:
<class name="Project, AssemblyName" table="project">
<id name="Id" column="project_id" unsaved-value="0">
<generator class="assigned"/>
</id>
<property name="Name" column="Project_Name" type="String"/>
<many-to-one name="ParentCustomer" class="Customer, AssemblyName"
column="customer_id" />
<many-to-one name="BusinessUnit" class="CustomerBusinessUnit, AssemblyName" insert="false" update="false" >
<column name="customer_business_unit"/>
<column name="customer_id"/>
</many-to-one>
</class>
Code:
<class name="CustomerBusinessUnit, AssemblyName" table="customer_business_unit">
<composite-id>
<key-property name="Id" column="business_unit_no"/>
<key-many-to-one name="ParentCustomer" column="customer_id" class="Customer, AssemblyName"/>
</composite-id>
<property name="Name" column="business_unit_name" type="String"/>
<bag name="Projects" table="project" lazy="true" order-by="project_name">
<key>
<column name="customer_business_unit"/>
<column name="customer_id"/>
</key>
<many-to-many class="Project, AssemblyName" column="project_id" />
</bag>
</class>
I think the problem is further compounded by the fact that the original designers of the database gave the Customer_Business_Unit table a composite key of CustomerID and BusinessUnitNumber, rather than a unique key (i.e. customer id 755 has business unit #s 1, 2, 3; customer id 843 has business unit #s 1, 2, 3, 4, 5, etc.)
The <many-to-one> reference in the Project mapping to the CustomerBusinessUnit class works correctly for those projects that have both a parent customer and a parent business unit. However for (normal) projects no parent business unit, I get a UnresolvableObjectException:
Quote:
NHibernate.UnresolvableObjectException : No row with the given identifier exists: Advisor.CustomerBusinessUnit, of class: Advisor.CustomerBusinessUnit
From the logs, I can see that when NH materializes the Project object, a null value is returned for the customer_business_unit column of the project table. However, when resolving associations, instead of realizing that there is no ParentBusinessUnit for this Project object, it attempts to materialize a CustomerBusinessUnit with an identifier of the correct customer_id but a customer_business_unit of 0 (the default value of the integer property).
I'm really confused on how to correctly map this parent-optional parent-child relationship from the child project perspective. Can anyone be of any help? Am I using the wrong type of mapping in the Project class for the ParentBusinessUnit reference?
edit: One possible solution would be to remove the mapping from the Project hbm file all together, and change the ParentBusinessUnit getter to walk through the ParentCustomer.BusinessUnits collection, attempting to find one that contained the current object
Code:
public CustomerBusinessUnit BusinessUnit
{
get
{
Customer parent = this.ParentCustomer;
CustomerBusinessUnit parentCbu = null;
foreach (CustomerBusinessUnit cbu in parent.BusinessUnits)
{
if (cbu.Projects.Contains(this))
{
parentCbu = cbu;
break;
}
}
return parentCbu;
}
}
but this is a lot slower and somehow feels dirty - I would much rather map this association directly from the database.