9

我目前使用 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>

我在这里做错了什么?

4

9 回答 9

8

@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

于 2009-01-21T20:51:59.467 回答
7

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

于 2009-10-17T17:49:27.203 回答
3

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

于 2011-03-05T09:44:04.340 回答
2

我不确定你是如何使用 Fluent NHibernate 的,但你需要在包上设置 Cascade 选项(TripItems)。像往常一样,Ayende 有一篇关于级联选项的有用帖子

从快速谷歌,我建议你尝试:

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 */
于 2009-01-02T13:55:37.947 回答
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 的方法。

于 2009-01-02T14:20:03.467 回答
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>

于 2009-06-12T16:44:02.163 回答
1

我已经知道了,我希望这可以帮助其他人。问题是我在两个包上都有 inverse='true' 。如果您阅读下面的摘录,您会注意到只需要在其中一个袋子上将 inverse 设置为 true:

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

于 2009-06-12T17:14:27.517 回答
0

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

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

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

于 2009-01-03T02:16:19.263 回答
0

我也在为此苦苦挣扎,而我遇到麻烦的原因完全不同。在我的示例中,如果我有一个没有任何多对多关系的对象,我可以调用 saveOrUpdate,一切都会好的。但如果我有任何多对多关系,我必须确保我的 saveOrUpdate 调用在 BeginTransaction 和 CommitTransaction 中。我对流利的 Nhibernate 很陌生,如果这很明显,请道歉。但这不是我的。

于 2009-06-20T18:36:42.577 回答