NHibernate没有持久的多对多关系

时间:2008-12-30 11:51:01

标签: .net nhibernate fluent-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>

我在这里做错了什么?

9 个答案:

答案 0 :(得分:8)

@efdee

我遇到了同样的问题,花了将近两天的时间。我有很多关系,链接表也没有更新。我是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内。

所以要添加一个位置,我在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

答案 1 :(得分:7)

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

答案 2 :(得分:2)

我不知道你是怎么用Fluent NHibernate做的,但你需要在包上设置Cascade选项(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 */

答案 3 :(得分:2)

我也有同样的问题 - 多对多的加入数据没有持久化。我已经从另一个多对一关系复制了映射(修改了多对多关系)但保留了inverse =“true”属性。当我删除此属性时问题得到解决。

答案 4 :(得分:1)

David Kemp说得对:你想在你的包里添加一个级联。

我总是手工编辑(并手工创建)地图文件,所以我的自然倾向是把它放在那里。你可以这样做:

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

我发现保持我的类“纯粹”并在.hbm.xml文件中保持与nHibernate的关系使我的解决方案更加清晰。这样,如果我想使用新的ORM软件,我只需要替换映射文件,不要重写类。我们使用单元测试来测试类并为xml提供可测试性,尽管我有点像Fluent NHibernate的方法。

答案 5 :(得分:1)

我遇到了完全相同的问题,但我一直在使用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>

答案 6 :(得分:1)

我已经得到了它,我希望这有助于其他人。问题是我在两个包上都有逆='真'。如果您阅读下面的摘录,您会注意到只有一个行李需要反向设置为真:

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

答案 7 :(得分:0)

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

但是,为了确保,我将重试此解决方案并让您知道结果。

至于保持类纯,这与Fluent NHibernate的情况完全相同。您创建的类映射是与实体类并行的C#代码文件,与.hbm.xml文件非常相似。

答案 8 :(得分:0)

我也在努力解决这个问题,并且出现了一个完全不同的原因。在我的例子中,如果我有一个没有任何多对多关系的对象,我可以调用saveOrUpdate,一切都会好的。但如果我有任何多对多的关系,我必须确保我的saveOrUpdate调用在BeginTransaction和CommitTransaction中。我对流利的Nhibernate很新,所以如果这很明显就道歉。但这不适合我。