代码之家  ›  专栏  ›  技术社区  ›  efdee

NHibernate不坚持多对多关系

  •  7
  • efdee  · 技术社区  · 17 年前

    我目前使用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\u TripItemAttributeValues\u链接从未获得任何新记录,导致关系无法持久化。

    如果有帮助,以下是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>
    

    9 回复  |  直到 17 年前
        1
  •  8
  •   Emmanuel    17 年前

    我也有同样的问题,为此花了将近两天的时间。我有一个多对多关系,链接表也没有更新。我是NHibernate的新手,只是想学习它,所以对我说的每一句话都要三缄其口。

    事实证明,这不是流畅的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中。

    
    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

        2
  •  7
  •   Vladimir Kotov    16 年前

    调用Session.Flush()或使用事务。

        3
  •  3
  •   CodeCat    15 年前

    我也有同样的问题-多对多的连接数据没有持久化。我从另一个多对一关系复制了映射(为多对多rel修改了映射),但保留了一个inverse=“true”属性。当我删除这个属性时,问题就解决了。

        4
  •  2
  •   kÍ©eÍ£mÍ®pÍ¥ Í©    17 年前

    我不知道如何使用Fluent NHibernate,但需要在包上设置级联选项( TripItems Ayende's got a useful post about cascade options .

    通过快速搜索,我建议您尝试:

    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 */
    
        5
  •  1
  •   Jeff    17 年前

    大卫·肯普说得对:你想在你的包里增加一个瀑布。

    我总是手工编辑(并手工创建)映射文件,所以我自然倾向于将其放在那里。您可以按如下方式执行此操作:

    <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>
    

        6
  •  1
  •   Daver    17 年前

    以下是我的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>
    

        7
  •  1
  •   Daver    17 年前

    我知道了,我希望这能帮助其他人。问题是我在两个包上都有反class='true'。如果您阅读了下面的摘录,您会注意到,只有一个行李需要设置为true:

    注意使用reverse=“true”。再次,此设置告诉NHibernate忽略对categories集合所做的更改,并使用关联的另一端(items集合)作为应与数据库同步的表示。

        8
  •  0
  •   efdee    17 年前

    恐怕Cascade.All()并不能真正解决我的问题——这是我尝试过的方法之一。问题不在于添加到集合中的项没有保存——它们在添加到集合时已经存在于数据库中。只是没有创建链接表中的条目。此外,我认为Cascade.All()还会导致子项被删除,这在我的场景中是不可取的行为。我尝试过使用Cascade.SaveUpdate(),但正如我所指出的,这解决了一些不是我真正的问题:-)

    至于保持类的纯粹性,Fluent NHibernate的情况是100%。您创建的类映射是与实体类一起使用的C#代码文件,与.hbm.xml文件非常相似。

        9
  •  0
  •   Menace Menace    17 年前

    我也在努力解决这个问题,而我遇到麻烦的原因完全不同。在我的示例中,如果我有一个没有任何多对多关系的对象,我可以调用saveOrUpdate,这一切都很好。但是,如果我有任何多对多关系,我必须确保我的saveOrUpdate调用位于BeginTransaction和CommitTransaction内。我对流利的Nhibernate很陌生,如果这是显而易见的,请道歉。但对我来说不是。

    推荐文章