1
votes

I'm upgrading an old NHibernate 1.2 solution I've taken over to NHib 3.1. We're having problems with persisting a parent child relationship. Which gives us this error:

NHibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)

This code was working in NHib 1.2 but does not work in 3.1

We're saving much like this code below:

Film f = NewFilm();
Recipe r = new Recipe("2", TimeSpan.FromMinutes(15), TimeSpan.FromMinutes(15));
f.Recipe = r;

SaveAndFlush(f, r); //custom code that saves f then saves r then flushes through the session.

However if we save r then f and flush it works.

I'd like to know why this happens, why the change between NHib versions. Is it the way the sesison thinks entities are transient now? Does it handle the foreign key id generator differently?

On a side note, the ID of the recipe doesn't equal the ID of the film, which I would expect it to do.

HMB files. - UPDATED to include full files

Film:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" schema="dbo">
<subclass name="Application.Core.Domain.Film, Application.Core" extends="Application.Core.Domain.VideoContent, Application.Core" discriminator-value="film" lazy="true">
    <list inverse="false" lazy="true" name="Resources" access="field.camelcase-underscore" cascade="all-delete-orphan">
        <key column="FilmId" />
        <index column="PositionInFilm"/>
        <one-to-many class="Application.Core.Domain.ContentResource, Application.Core" />
    </list>
    <list inverse="false" lazy="true" name="Steps" access="field.camelcase-underscore" cascade="all-delete-orphan">
        <key column="FilmId" />
        <index column="PositionInWebText"/>
        <one-to-many class="Application.Core.Domain.WebText, Application.Core" />
    </list>
    <property name="FilmType" column="FilmType" />
    <property name="PosterFrameTimeCode" column="PosterFrameTimeCode" />
    <one-to-one name="Recipe"  class="Application.Core.Domain.Recipe, Application.Core" cascade="save-update" access="field.camelcase-underscore"/>

<bag lazy="true" name="Shapes" access="field.camelcase-underscore" cascade="save-update" where="Archived=0">
  <key column="ContentId"/>
  <one-to-many class="Application.Core.Domain.FilmShape, Application.Core"/>
</bag>

<bag lazy="true" name="ArchivedShapes" access="field.camelcase-underscore" cascade="save-update"  where="Archived=1">
  <key column="ContentId"/>
  <one-to-many class="Application.Core.Domain.FilmShape, Application.Core" />
</bag>

<many-to-one name="FilmToReplace" column="ReplacesFilmId" class="Application.Core.Domain.Film, Application.Core" access="field.camelcase-underscore" />

</subclass>

Recipe:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" schema="dbo">
<class name="Application.Core.Domain.Recipe,Application.Core" table="tbl_Recipe" lazy="false">
    <id name="Id" column="HeaderId" type="System.Guid" access="field.camelcase-underscore">
        <generator class="foreign">
            <param name="property">Content</param>
        </generator>
    </id>
    <list inverse="false" lazy="true" name="RecipeIngredients" access="field.camelcase-underscore" cascade="all-delete-orphan">
  <key column="RecipeId" />
  <index column="PositionInRecipe"/>
  <one-to-many class="Application.Core.Domain.RecipeIngredient, Application.Core" />
</list>
<property name="Serves" column="Serves" type="System.String"/>
<property name="PreparationTime" column="PreparationTime" type="TimeSpan"/>
<property name="CookingTime" column="CookingTime" type="TimeSpan"/>
<property name="OvenTemperature" column="OvenTemperature" type="Application.Data.UserTypes.TemperatureType, Application.Data"/>
    <one-to-one name="Content" class="Application.Core.Domain.Content, Application.Core" constrained="true" access="field.camelcase-underscore"/>
</class>
</hibernate-mapping>
2
On a side note, the ID of the recipe doesn't equal the ID of the film, which I would expect it to do. For ID equality you should use generator class="foreign"jjjjj
This didn't seem to effect the 1.2 version. They were persisted with the correct associations.big_tommy_7bb
Can you add full mappings of Film and Recipe?jjjjj

2 Answers

0
votes

If you have a cascading save on a "child" entity, you just save the parent. You don't need to save the child.

So here you should try saving only "f".

0
votes

From the documentation: http://nhibernate.info/doc/nh/en/index.html#mapping-declaration-onetoone

Primary key associations don't need an extra table column; if two rows are related by the association then the two table rows share the same primary key value. So if you want two objects to be related by a primary key association, you must make sure that they are assigned the same identifier value!

For a primary key association, add the following mappings to Employee and Person, respectively.

<one-to-one name="Person" class="Person"/>

<one-to-one name="Employee" class="Employee" constrained="true"/>

Now we must ensure that the primary keys of related rows in the PERSON and EMPLOYEE tables are equal. We use a special NHibernate identifier generation strategy called foreign:

<class name="Person" table="PERSON">
    <id name="Id" column="PERSON_ID">
        <generator class="foreign">
            <param name="property">Employee</param>
        </generator>
    </id>
    ...
    <one-to-one name="Employee"
        class="Employee"
        constrained="true"/>
</class>

A newly saved instance of Person is then assigned the same primar key value as the Employee instance refered with the Employee property of that Person.