I'm a new NHibernate user (v 1.0.2.0) and have been trying to learn how to use it.
It's been going OK, but I've hit a hurdle. Our relational model has a join table between two entities that has additional columns I would like to use. I've tried two approaches to mapping this, and scoured the Hibernate documentation for examples without any luck.
I have the following tables (only pertinent fields are listed):
Organizations
Code:
OrganizationId (PK)
ContactsCode:
ContactId (PK)
OrganizationContactCode:
OrganizationId [PK]
ContactId [PK]
BusinessPhoneNumber
Position
Our model states that contacts may be members of one or more organizations and each organization may have one or more contacts. Each contact in an organization has a position and a business telephone number.
The object model looks like:
OrganizationCode:
public class Organization {
private int organizationId;
private IList contactRoles;
//...
public int OrganizationId {
get { return organizationId; }
set { organizationId = value; }
}
public IList ContactRoles {
get { return contactRoles; }
set { contactRoles= value; }
}
//...
}
ContactCode:
public class Contact {
private int contactId;
private IList organizationRoles;
//...
public int ContactId{
get { return contactId; }
set { contactId= value; }
}
public IList OrganizationRoles{
get { return organizationRoles; }
set { organizationRoles= value; }
}
//...
}
OrganizationContactCode:
public class OrganizationContact {
private string position;
private string businessPhone;
private Contact contact;
private Organization organization;
public string Position {
get { return position; }
set { position = value; }
}
public string BusinessPhone {
get { return businessPhone; }
set { businessPhone = value; }
}
public Contact Contact {
get { return contact; }
set { contact = value; }
}
public Organization Organization {
get { return organization; }
set { organization = value; }
}
}
My first attempt to map this was to create a one-to-many association between Organization and OrganizationContact and Contact and OrganizationContact. So I created 3 mappings:
Organization.hbm.xmlCode:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
<class name="Test.Organization, Test" table="Organizations">
<id name="OrganizationId" column="OrganizationId" type="Int32" length="4" unsaved-value="0">
<generator class="identity"/>
</id>
<!-- ... -->
<bag name="ContactRoles" lazy="true" inverse="true">
<key column="OrganizationId"/>
<one-to-many class="Test.OrganizationContact, Test"/>
</bag>
</class>
</hibernate-mapping>
Contact.hbm.xmlCode:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
<class name="Test.Contact, Test" table="Contacts">
<id name="ContactId" column="ContactId" type="Int32" length="4" unsaved-value="0">
<generator class="identity"/>
</id>
<!-- ... -->
<bag name="OrganizationRoles" lazy="true" inverse="true">
<key column="ContactId"/>
<one-to-many class="Test.OrganizationContact, Test"/>
</bag>
</class>
</hibernate-mapping>
OrganizationContact.hbm.xmlCode:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
<class name="Test.OrganizationContact, Test" table="OrganizationContacts">
<id name="OrganizationContactId" column="OrganizationContactId" type="Int32" length="4" unsaved-value="0">
<generator class="identity"/>
</id>
<property name="Position" />
<property name="BusinessPhone" />
<many-to-one name="Contact" column="ContactId" cascade="save-update" class="Test.Contact, Test" />
<many-to-one name="Organization" column="OrganizationId" cascade="save-update" class="Test.Organization, Test" />
</class>
</hibernate-mapping>
This all seemed to be OK (at least I wasn't getting any mapping errors) when loading my assembly. The problems arose when I tries to save an OrganizationContact. It would save OK and persist any associated Contact and Organization objects:
Code:
Contact contact = new Contact();
...
Organization organization = new Organization();
...
OrganizationContact organizationContact = new OrganizationContact();
organizationContact.BusinessPhone = "1234 5678";
organizationContact.Position = "Assistant Co-Chief Inspector of Bottle Caps";
organizationContact.Contact = contact;
organizationContact.Organization = organization;
Session.Save(organizationContact);
However, when I later tried to load an Organization or Contact that had an association via OrganizationContact, the
Contact.OrganizationRoles or
Organization.ContactRoles collections were always
null:
Code:
Contact contact = (Contact) ISession.Get(typeof(Contact), 123);
int count = contact.OrganizationRoles.Count; // <-- throws a NullReferenceException
I went on a search for information and found
a post in this forum that suggested reading up on "Collections of dependent objects" in the Hibernate documentation. To that end I changed the mapping files as follows:
Organization.hbm.xmlCode:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
<class name="Test.Organization, Test" table="Organizations">
<id name="OrganizationId" column="OrganizationId" type="Int32" length="4" unsaved-value="0">
<generator class="identity"/>
</id>
<!-- ... -->
<bag name="ContactRoles" table="OrganizationContacts" lazy="true">
<key column="OrganizationId"/>
<composite-element class="Test.OrganizationContact, Test">
<property name="Position" />
<property name="BusinessPhone" />
<many-to-one name="Contact" class="Test.Contact, Test" />
</composite-element>
</bag>
</class>
</hibernate-mapping>
Contact.hbm.xmlCode:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
<class name="Test.Contact, Test" table="Contacts">
<id name="ContactId" column="ContactId" type="Int32" length="4" unsaved-value="0">
<generator class="identity"/>
</id>
<!-- ... -->
<bag name="OrganizationRoles" table="OrganizationContacts" lazy="true">
<key column="ContactId"/>
<composite-element class="Test.OrganizationContact, Test">
<property name="Position" />
<property name="BusinessPhone" />
<many-to-one name="Organization" class="Test.Organization, Test" />
</composite-element>
</bag>
</class>
</hibernate-mapping>
OrganizationContact.hbm.xmlThis mapping was removed.
Now when I try to load an Organization I get an error. In the Hibernate log file there's an SQL error:
Incorrect syntax near the keyword 'Contact'. This is as a result of executing the following generated SQL statement (reformmatted for easier reading):
Code:
SELECT OU.OrganizationId as Organiza5___
, OU.Position as Position__
, OU.BusinessPhone as Business3___
, OU.Contact as Contact__
, C.ContactId as ContactId0_
, C.EmailAddress as EmailAdd4_0_
, C.Mobile as Mobile0_
, C.PostalAddress1 as PostalA16_0_
, C.PostalAddress2 as PostalA17_0_
, C.StreetAddressSuburb as StreetA12_0_
, C.Password as Password0_
, C.PostalAddressCountry as PostalA21_0_
, C.StreetAddressCountry as StreetA15_0_
, C.StreetAddress1 as StreetA10_0_
, C.FirstName as FirstName0_
, C.StreetAddress2 as StreetA11_0_
, C.StreetAddressPostcode as StreetA14_0_
, C.PostalAddressPostcode as PostalA20_0_
, C.LastName as LastName0_
, C.Title as Title0_
, C.StreetAddressState as StreetA13_0_
, C.Phone as Phone0_
, C.Fax as Fax0_
, C.PostalAddressState as PostalA19_0_
, C.PostalAddressSuburb as PostalA18_0_
FROM OrganizationContacts OU
left outer join
Contacts C on OU.Contact=C.ContactId
WHERE OU.OrganizationId = @p0
I don't know what to do. I'm not sure why the original version isn't working correctly and I can see that the 2nd mapping attempt is incorrectly thinking that a Contact column exists in OrganizationContacts, but I'm not sure why!
Sorry for the massive post, I just wanted to provide as much information as I could. Any help would really be appreciated. Apart from this snag, my forays into NHibernate are going really well. I just can't work this one out.