NHibernate не поддерживает отношения "многие-ко-многим"

В настоящее время я использую NHibernate в качестве уровня доступа к данным, используя Fluent NHibernate для создания файлов сопоставления для меня. У меня есть два класса, TripItem и TripItemAttributeValue, между которыми существует отношение "многие ко многим".

Отображение выглядит следующим образом:

public class TripItemMap : ClassMap<TripItem2>
{
    public TripItemMap()
    {
        WithTable("TripItemsInt");
        NotLazyLoaded();

        Id(x => x.ID).GeneratedBy.Identity().WithUnsavedValue(0);
        Map(x => x.CreateDate, "CreatedOn").CanNotBeNull();
        Map(x => x.ModifyDate, "LastModified").CanNotBeNull();

        /* snip */

        HasManyToMany<TripItemAttributeValue>(x => x.Attributes).AsBag()
            .WithTableName("TripItems_TripItemAttributeValues_Link")
            .WithParentKeyColumn("TripItemId")
            .WithChildKeyColumn("TripItemAttributeValueId")
            .LazyLoad();
    }
}

public class TripItemAttributeValueMap : ClassMap<TripItemAttributeValue>
{
    public TripItemAttributeValueMap()
    {
        WithTable("TripItemAttributeValues");

        Id(x => x.Id).GeneratedBy.Identity();
        Map(x => x.Name).CanNotBeNull();

        HasManyToMany<TripItem2>(x => x.TripItems).AsBag()
            .WithTableName("TripItems_TripItemAttributeValues_Link")
            .WithParentKeyColumn("TripItemAttributeValueId")
            .WithChildKeyColumn("TripItemId")
            .LazyLoad();
    }
}

В какой-то момент в своем приложении я извлекаю существующие атрибуты из базы данных, добавляю их в tripItem.Attributes, а затем сохраняю объект tripItem. В конце концов, TripItems_TripItemAttributeValues_Link никогда не получает новых записей, в результате чего отношения не сохраняются.

Если это помогает, это файлы сопоставления, созданные Fluent NHibernate для этих классов:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="true" assembly="ETP.Core" namespace="ETP.Core.Domain">
  <class name="TripItem2" table="TripItemsInt" xmlns="urn:nhibernate-mapping-2.2" lazy="false">
    <id name="ID" column="ID" type="Int32" unsaved-value="0">
      <generator class="identity" />
    </id>
    <property name="CreateDate" column="CreatedOn" type="DateTime" not-null="true">
      <column name="CreatedOn" />
    </property>
    <property name="ModifyDate" column="LastModified" type="DateTime" not-null="true">
      <column name="LastModified" />
    </property>
    <bag name="Attributes" lazy="true" table="TripItems_TripItemAttributeValues_Link">
      <key column="TripItemId" />
      <many-to-many column="TripItemAttributeValueId" class="ETP.Core.Domain.TripItemAttributeValue, ETP.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </bag>
  </class>
</hibernate-mapping>

и

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="true" assembly="ETP.Core" namespace="ETP.Core.Domain">
  <class name="TripItemAttributeValue" table="TripItemAttributeValues" xmlns="urn:nhibernate-mapping-2.2">
    <id name="Id" column="Id" type="Int32">
      <generator class="identity" />
    </id>
    <property name="Name" column="Name" length="100" type="String" not-null="true">
      <column name="Name" />
    </property>
    <bag name="TripItems" lazy="true" table="TripItems_TripItemAttributeValues_Link">
      <key column="TripItemAttributeValueId" />
      <many-to-many column="TripItemId" class="ETP.Core.Domain.TripItem2, ETP.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </bag>
  </class>
</hibernate-mapping>

Что я здесь делаю не так?


person efdee    schedule 30.12.2008    source источник


Ответы (9)


@efdee

У меня была такая же проблема, и я потратил на это почти два дня. У меня были отношения "многие ко многим", и таблица ссылок тоже не обновлялась. Я новичок в NHibernate, просто пытаюсь его изучить, поэтому относитесь ко всему, что я говорю, с недоверием.

Что ж, оказалось, что это не Fluent NHibernate и не отображение, но я не понимаю, как NHibernate работает со многими ко многим. В отношении «многие ко многим», если коллекции на обоих объектах не заполнены, NHibernate не сохраняет данные в таблице ссылок.

Скажем, у меня есть эти сущности в отношении «многие ко многим»:


partial class Contact
{
   public string ContactName {get; set;}
   public IList Locations {get; set;}

}

partial class Location
{
   public string LocationName {get; set;}
   public string LocationAddress {get;set;}
   public IList Contacts {get;set;}
}

когда я добавляю местоположение в Contact.Locations, я должен убедиться, что контакт также присутствует внутри location.Contacts.

поэтому, чтобы добавить местоположение, у меня есть этот метод внутри моего класса Contact.


public void AddLocation(Location location)
        {
            if (!location.Contacts.Contains(this))
            {
                location.Contacts.Add(this);
            }
            Locations.Add(location);
        }

Кажется, это решило мою проблему, но, как я уже сказал, я просто беру NHibernate и изучаю его, может быть, есть способ получше. Если у кого-то есть лучшее решение, напишите.

Это сообщение, в котором я указал мне проверить обе коллекции: http://www.coderanch.com/t/217138/Object-Relational-Mapping/link-table-of-ManyToMany-annotation

person Emmanuel    schedule 21.01.2009

Вызовите Session.Flush () или используйте транзакцию.

person Vladimir Kotov    schedule 17.10.2009
comment
Спасибо! Я была такая же проблема. Я вызывал Session.SaveOrUpdate (entity) для одной стороны отношения. Но это не создавало таблицу ссылок. Вызов Session.Flush () решил эту проблему. - person Nic Strong; 27.08.2010
comment
Может ли кто-нибудь объяснить, ПОЧЕМУ нам нужно это сделать в этом сценарии? - person Oliver; 21.08.2011
comment
Хотел бы я проголосовать больше одного раза - это поставило меня в тупик примерно на час! - person Darbio; 21.09.2011
comment
Я отклонил этот ответ, потому что: не вызывать Session.Flush () действительно не будет правильно сохраняться ... никогда. Это просто не спасет. Этот вопрос более конкретно касается несохранения отношения «многие ко многим», которое имело другую причину. - person Bertvan; 09.01.2012

У меня тоже была та же проблема - данные объединения для многих-ко-многим не сохранялись. Я скопировал отображение из другого отношения «многие ко многим» (изменил его для отношения «многие ко многим»), но сохранил атрибут inverse = "true". Когда я удалил этот атрибут, проблема была решена.

person CodeCat    schedule 05.03.2011

Я не уверен, как вы это делаете с Fluent NHibernate, но вам нужно установить параметр Cascade в сумке (TripItems). Как обычно, У Айенде есть полезный пост о каскадных параметрах.

Я бы посоветовал вам попробовать:

HasManyToMany<TripItem2>(x => x.TripItems).AsBag()
        .WithTableName("TripItems_TripItemAttributeValues_Link")
        .WithParentKeyColumn("TripItemAttributeValueId")
        .WithChildKeyColumn("TripItemId")
        .LazyLoad()
/*-->*/ .Cascade.All(); /*<-- this is the bit that should make it work */
person kͩeͣmͮpͥ ͩ    schedule 02.01.2009

Дэвид Кемп прав: вы хотите добавить в свою сумку каскад.

Я всегда вручную редактировал (и вручную создавал) файлы сопоставления, поэтому я естественным образом склоняю их туда. Вы можете сделать это следующим образом:

<bag name="TripItems" lazy="true" table="TripItems_TripItemAttributeValues_Link" cascade="all">
  <key column="TripItemAttributeValueId" />
  <many-to-many column="TripItemId" class="ETP.Core.Domain.TripItem2, ETP.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</bag>

Я обнаружил, что сохранение моих классов «чистыми» и сохранение всего, что связано с nHibernate в файле .hbm.xml, делает мое решение более чистым. Таким образом, если есть новое программное обеспечение ORM, которое я хочу использовать, я просто заменяю файлы сопоставления и не переписываю классы. Мы используем наши модульные тесты для тестирования классов и обеспечения тестируемости xml, хотя мне в некотором роде нравятся методы Fluent NHibernate.

person Jeff    schedule 02.01.2009

У меня точно такая же проблема, но я использовал NHibernate.JetDriver. Я безуспешно пытался использовать рекомендованный ответ. Кто-нибудь знает, есть ли у NHibernate.JetDriver ограничение по отношению к многим ко многим?

Вот мои файлы hbm на тот случай, если кому-то захочется ненадолго их просмотреть:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Ace.Docs.Core.Domain" assembly="Ace.Docs.Core">
<class name="Ace.Docs.Core.Domain.Address, Ace.Docs.Core" table="Addresses" lazy="true">
    <id name="Id" column="ID">
        <generator class="identity" />
    </id>
    <property name="Address1" column="Address1" />
    <property name="Address2" column="Address2" />
    <property name="City" column="City" />
    <property name="EmailAddress" column="EmailAddress" />
    <property name="Phone1" column="Phone1" />
    <property name="Phone2" column="Phone2" />
    <property name="PostalCode" column="PostalCode" />
    <property name="StateOrProvince" column="StateOrProvince" />
    <many-to-one name="AddressTypeMember" column="AddressTypeID" class="AddressType" />
    <bag name="HasPersonalInfo" table="Link_PersonalInfo_Addresses" lazy="true" cascade="save-update" inverse="true" >
        <key column="AddressID"></key>
        <many-to-many column="PersonalInfoID" class="PersonalInfo" />
    </bag>
</class>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Ace.Docs.Core.Domain" assembly="Ace.Docs.Core">
<class name="Ace.Docs.Core.Domain.PersonalInfo, Ace.Docs.Core" table="PersonalInfo" lazy="true">
    <id name="Id" column="ID">
        <generator class="identity" />
    </id>
    <property name="Prefix" column="Prefix" />
    <property name="FirstName" column="FirstName" />
    <property name="MiddleName" column="MiddleName" />
    <property name="LastName" column="LastName" />
    <property name="SIN" column="SIN" />
    <property name="Birthdate" column="Birthdate" />
    <property name="Note" column="Notes" />
    <bag name="HasAddress" table="Link_PersonalInfo_Addresses" lazy="true" cascade="save-update" inverse="true" >
        <key column="PersonalInfoID"></key>
        <many-to-many column="AddressID" class="Address" />
    </bag>
</class>

person Daver    schedule 12.06.2009

У меня есть это, и я надеюсь, что это поможет кому-то еще. Проблема в том, что у меня на обеих сумках было inverse = 'true'. Если вы прочитаете отрывок ниже, то заметите, что необходимо установить обратное значение true только для одного из пакетов:

Обратите внимание на использование inverse = "true". Еще раз, этот параметр указывает NHibernate игнорировать изменения, внесенные в коллекцию категорий, и использовать другой конец ассоциации - коллекцию элементов - в качестве представления, которое должно быть синхронизировано с базой данных.

person Daver    schedule 12.06.2009

Боюсь, что Cascade.All () на самом деле не является решением моей проблемы - это была одна из тех вещей, которые я пробовал. Проблема не в том, что элементы, добавленные в коллекцию, не сохраняются - они уже находятся в базе данных на момент добавления в коллекцию. Просто записи в таблице ссылок не создаются. Кроме того, я думаю, что Cascade.All () также приведет к удалению дочерних элементов, что в моем сценарии нежелательно. Я пробовал использовать Cascade.SaveUpdate (), но, как я уже отмечал, это решает кое-что, что на самом деле не является моей проблемой :-)

Однако, чтобы убедиться, я попробую это решение еще раз и сообщу вам результат.

Что касается сохранения чистоты классов, это 100% случай с Fluent NHibernate. Создаваемые вами сопоставления классов представляют собой файлы кода C #, которые идут вместе с вашими классами сущностей, почти так же, как файлы .hbm.xml.

person efdee    schedule 03.01.2009
comment
То же самое для меня - каскадирование - это не то, что я хотел, и это не помогло. Промывка была ответом! - person mindplay.dk; 15.04.2011

Я тоже боролся с этим, и у меня была совсем другая причина моих проблем. В моем примере, если бы у меня был объект без каких-либо отношений «многие ко многим», я мог бы просто вызвать saveOrUpdate, и все было бы хорошо. Но если у меня были отношения «многие ко многим», я должен был убедиться, что мой вызов saveOrUpdate находится в пределах BeginTransaction и CommitTransaction. Я новичок в использовании Nhibernate, поэтому извиняюсь, если это очевидно. Но это было не для меня.

person Community    schedule 20.06.2009