#nhibernate #nhibernate-mapping
#nhibernate #nhibernate-сопоставление
Вопрос:
Я обновляю старое решение NHibernate 1.2, которое я перенял, до NHib 3.1. У нас возникли проблемы с сохранением отношения «родитель-потомок». Что приводит к этой ошибке:
NHibernate.Исключение StaleObjectStateException: строка была обновлена или удалена другой транзакцией (или отображение несохраненных значений было неправильным)
Этот код работал в NHib 1.2, но не работает в 3.1
Мы сохраняем примерно такой код ниже:
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.
Однако, если мы сохраним r, затем f и сбросим, это сработает.
Я хотел бы знать, почему это происходит, почему изменения между версиями NHib. Действительно ли sesison считает, что объекты сейчас являются временными? Обрабатывает ли он генератор идентификаторов внешнего ключа по-другому?
Кстати, идентификатор рецепта не равен идентификатору фильма, чего я бы ожидал от него.
Файлы HMB. — ОБНОВЛЕНО для включения полных файлов
Фильм:
<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>
Рецепт:
<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>
Комментарии:
1.
On a side note, the ID of the recipe doesn't equal the ID of the film, which I would expect it to do.
Для равенства идентификаторов вы должны использовать generator class =»foreign»2. Похоже, это не повлияло на версию 1.2. Они были сохранены с правильными ассоциациями.
3. Можете ли вы добавить полные сопоставления фильма и рецепта?
Ответ №1:
Если у вас есть каскадное сохранение для «дочернего» объекта, вы просто сохраняете родительский. Вам не нужно сохранять дочерний элемент.
Итак, здесь вам следует попробовать сохранить только «f».
Комментарии:
1. это тоже не работает. значит, могут быть проблемы глубже?
Ответ №2:
Из документации:http://nhibernate.info/doc/nh/en/index.html#mapping-declaration-onetoone
Ассоциации первичных ключей не нуждаются в дополнительном столбце таблицы; если две строки связаны ассоциацией, то две строки таблицы используют одно и то же значение первичного ключа. Итак, если вы хотите, чтобы два объекта были связаны ассоциацией первичного ключа, вы должны убедиться, что им присвоено одинаковое значение идентификатора!
Для ассоциации первичного ключа добавьте следующие сопоставления для Employee и Person соответственно.
<one-to-one name="Person" class="Person"/>
<one-to-one name="Employee" class="Employee" constrained="true"/>
Теперь мы должны убедиться, что основной
ключи связанных строк в PERSON и
Таблицы EMPLOYEE равны. Мы используем
специальный идентификатор NHibernate
стратегия генерации, называемая внешней:
<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>
Недавно сохраненный экземпляр Person является
затем назначен тот же основной ключ
значение, указанное в экземпляре Employee
с помощью свойства Employee этого
Человек.