An exploration of the joys and frustrations when programming with Microsoft .NET. Taken with the perspective of working in a faith-based ministry, striving to release children from poverty in Jesus' name.

Problems With NHibernate

Posted 1.13.08 in Uncategorized

In our latest project, our team made use of NHibernate to act as an ORM (Object-Relational Mapper) and to handle all of our data access work.

In the current phase that we are working on, we’re running into a problem. We can’t get NHibernate to delete a child object in a collection on a parent.

Here’s the basic situation:

We have a Proposal, which has a collection of ProposalMilestones. The ProposalMilestone has a reference to the parent Proposal, as well as a reference to the Milestone that it refers to.

When trying to remove a ProposalMilestone from the collection and flushing the session, NHibernate throws a StaleObjectException.

Here are images of the class diagram for this section and the database schema that applies:

NHSample NHSampleDatabase-NoNotify

Nothing special about either one. The relationships are as would be expected. 

Here are the related mapping files:

Proposal.hbm.xml

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="CIV.Entities" assembly="NHSample" default-access="nosetter.camelcase" default-lazy="false">
    <class name="CIV.Entities.Proposal, NHSample" table="Proposal">
        <id name="Id" column="ID">
            <generator class="guid.comb" />
        </id>
        <version name="Version" column="Version" type="CIV.Entities.Timestamp, NHSample" generated="always" />
        <component name="Audit" class="CIV.Entities.Audit, NHSample">
            <property name="CreatedBy" column="CreatedBy"/>
            <property name="CreatedOn" column="CreatedOn" />
            <property name="ModifiedBy" column="ModifiedBy" />
            <property name="ModifiedOn" column="ModifiedOn" />
        </component>

        <property name="Code" column="Code" />
        <property name="Description" column="Description" />
        <property name="Name" column="Name" />
        <property name="ProposedStartDate" column="ProposedStartDate" />

        <bag name="Milestones" table="ProposalMilestone" inverse="true" cascade="all">
            <key column="ProposalID" />
            <one-to-many class="CIV.Entities.ProposalMilestone, NHSample" />
        </bag>
    </class>
</hibernate-mapping>

ProposalMilestone.hbm.xml

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="CIV.Entities" assembly="NHSample" default-access="nosetter.camelcase" default-lazy="false">
    <class name="CIV.Entities.ProposalMilestone" table="ProposalMilestone">
        <id name="Id" column="ID" unsaved-value="00000000-0000-0000-0000-000000000000">
            <generator class="guid.comb" />
        </id>
        <version name="Version" column="Version" type="CIV.Entities.Timestamp, NHSample" generated="always" />
        <component name="Audit" class="CIV.Entities.Audit, NHSample">
            <property name="CreatedBy" column="CreatedBy"/>
            <property name="CreatedOn" column="CreatedOn" />
            <property name="ModifiedBy" column="ModifiedBy" />
            <property name="ModifiedOn" column="ModifiedOn" />
        </component>
        <property name="OriginalDueDate" column="OriginalDueDate" type="DateTime" />
        <many-to-one name="Milestone" column="MilestoneID" class="CIV.Entities.Milestone, NHSample"/>
        <many-to-one name="Proposal" column="ProposalID" not-null="true" class="CIV.Entities.Proposal, NHSample" />
        <bag name="Notifications" inverse="true" cascade="all">
            <key column="ProposalMilestoneID" />
            <one-to-many class="CIV.Entities.Notification, NHSample" />
        </bag>
    </class>
</hibernate-mapping>

Milestone

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHSample"  namespace="CIV.Entities" default-access="nosetter.camelcase" default-lazy="false">
    <class name="CIV.Entities.Milestone, NHSample" table="Milestone">
        <id name="Id" column="ID">
            <generator class="guid.comb"/>
        </id>
        <version name="Version" column="Version" type="CIV.Entities.Timestamp, NHSample" generated="always" />
        <component name="Audit" class="CIV.Entities.Audit, NHSample">
            <property name="CreatedBy" column="CreatedBy" type="String" />
            <property name="CreatedOn" column="CreatedOn" type="DateTime" />
            <property name="ModifiedBy" column="ModifiedBy" type="String" />
            <property name="ModifiedOn" column="ModifiedOn" type="DateTime" />
        </component>
        <property name="Name" column="Name" type="String" not-null="true" />
        <property name="Code" column="Code" type="String" not-null="true" />
        <property name="Description" column="Description" type="String" />

    </class>
</hibernate-mapping>

Any suggestions would be greatly appreciated.

As a side note, I think my relations expressed in the mapping could be better. ProposalMilestone is being treated as an entity right now, but there is no reason (unless NHibernate requires it for the relationships) that it needs to be. I believe Milestone is a value object, but again, it’s being treated as an entity.

Suggestions here would also be appreciated.

Updated (3.29.08)

I did get this problem solved: The final thing that worked for me was putting the optimistic-lock=”false” tag on the collection declaration (Proposal). This avoided updating the Proposal before the collection of ProposalMilestones was updated, which was causing my stale object error.

3 Responses to “Problems With NHibernate”

  1. Brendan Says:

    Hi Mate, One thing you might need to read up on is how collections and inverse collections are handled by NHibernate.

    I’m going to have a stab in the dark, but maybe this will help: For your ProposalMilestone to be deleted you might have to delete it from the collection and then delete the ProposalMilestone itself before you call flush. This is because you have a bi-directional relationship between ProposalMilestone and Proposal. When this is the case you need to delete both sides of the relationship for NHibernate to figure out it’s supposed to be deleted.

  2. brendan Says:

    i take it that wasn’t the problem then? oh well, good luck to you…

  3. freqken Says:

    Brendan,

    that was part of the problem. The final thing that worked for me was putting the optimistic-lock=”false” tag on the collection declaration (Proposal). This avoided updating the Proposal before the collection of ProposalMilestones was updated, which was causing my stale object error.

    I had missed that tag in all my trips through the docs, until I was specifically reading for each and every tag on a bag, set, etc.

    Thanks for the help and suggestion

Leave a Reply

About

This is a collection of the thoughts and ideas of a software developer and solutions architect with Compassion International. Topics of interest include software architecture, design patterns, software factories, team dynamics and the art of computer programming.

Meet Kenneth Scott

Categories

  • No categories

For "All flesh is like grass and all its glory like the flower of grass. The grass withers, and the flower falls, but the word of the Lord remains forever." And this word is the good news that was preached to you. (1 Peter 1:24-25)

Copyright © 2007 – 2008, Kenneth Scott.