本文介绍了在NHibernate中定义多对多关系以允许删除但避免重复记录的正确方法是什么的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经与NHibernate设置进行了几天的战斗,只是无法找出正确的方法来设置我的映射,因此它的工作方式就像我期望的那样.

I've been fighting with an NHibernate set-up for a few days now and just can't figure out the correct way to set out my mapping so it works like I'd expect it to.

在我着手解决这些问题之前,需要经过一些代码,因此,为获得更多阅读内容,我们表示歉意.

There's a bit of code to go through before I get to the problems, so apologies in advance for the extra reading.

目前,仅使用以下表格即可进行设置:

The setup is pretty simple at the moment, with just these tables:

类别
CategoryId
名称

Category
CategoryId
Name

项目
ItemId
名称

Item
ItemId
Name

ItemCategory
ItemId
CategoryId

ItemCategory
ItemId
CategoryId

一个项目可以有很多类别,每个类别可以有很多项目(简单的多对多关系).

An item can be in many categories and each category can have many items (simple many-to-many relationship).

我的映射设置为:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="..."
                   namespace="...">

  <class name="Category" lazy="true">

    <id name="CategoryId" unsaved-value="0">
      <generator class="native" />
    </id>
    <property name="Name" />

    <bag name="Items" table="ItemCategory" cascade="save-update" inverse="true" generic="true">
      <key column="CategoryId"></key>
      <many-to-many class="Item" column="ItemId"></many-to-many>
    </bag>

  </class>

</hibernate-mapping>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="..."
                   namespace="...">

  <class name="Item" table="Item" lazy="true">

    <id name="ItemId" unsaved-value="0">
      <generator class="native" />
    </id>
    <property name="Name" />

    <bag name="Categories" table="ItemCategory" cascade="save-update" generic="true">
      <key column="ItemId"></key>
      <many-to-many class="Category" column="CategoryId"></many-to-many>
    </bag>

  </class>

</hibernate-mapping>

我将项目添加到类别"的项目"列表和项目"的类别"列表的方法设置了关系的两个方面.

My methods for adding items to the Item list in Category and Category list in Item set both sides of the relationship.

项目中:

    public virtual IList<Category> Categories { get; protected set; }
    public virtual void AddToCategory(Category category)
    {
        if (Categories == null)
            Categories = new List<Category>();

        if (!Categories.Contains(category))
        {
            Categories.Add(category);
            category.AddItem(this);
        }
    }

类别中:

    public virtual IList<Item> Items { get; protected set; }
    public virtual void AddItem(Item item)
    {
        if (Items == null)
            Items = new List<Item>();

        if (!Items.Contains(item))
        {
            Items.Add(item);
            item.AddToCategory(this);
        }
    }

现在这已经不合时宜了,我遇到的问题是:

Now that's out of the way, the issues I'm having are:

  1. 如果从Category.Items映射中删除'inverse ="true"',则会在查找ItemCategory表中获得重复的条目.

  1. If I remove the 'inverse="true"' from the Category.Items mapping, I get duplicate entries in the lookup ItemCategory table.

当使用'inverse ="true"'时,尝试删除类别时出现错误,因为NHibernate不会从查找表中删除匹配的记录,因此由于外键约束而失败.

When using 'inverse="true"', I get an error when I try to delete a category as NHibernate doesn't delete the matching record from the lookup table, so fails due to the foreign key constraint.

如果我在购物袋上设置了cascade ="all",则可以删除而不会出错,但是删除类别也会删除该类别中的所有商品.

If I set cascade="all" on the bags, I can delete without error but deleting a Category also deletes all Items in that category.

我设置映射的方式是否存在一些根本问题,以允许多对多映射按您期望的那样工作?

Is there some fundamental problem with the way I have my mapping set up to allow the many-to-many mapping to work as you would expect?

通过您希望的方式",我的意思是删除将只删除要删除的项目和相应的查找值(不影响关系另一端的项目)并更新其中一个集合将会使用正确和非重复的值更新查找表.

By 'the way you would expect', I mean that deletes won't delete anything more than the item being deleted and the corresponding lookup values (leaving the item on the other end of the relationship unaffected) and updates to either collection will update the lookup table with correct and non-duplicate values.

任何建议将不胜感激.

推荐答案

为了使映射按预期工作,您需要做的是将inverse="true"Category.Items集合移至Item.Categories集合.这样,您将使NHibernate了解哪一个是关联的拥有方,而那是类别"的一方.

What you need to do in order to have your mappings work as you would expect them to, is to move the inverse="true" from the Category.Items collection to the Item.Categories collection. By doing that you will make NHibernate understand which one is the owning side of the association and that would be the "Category" side.

如果这样做,则通过删除Category对象,它会根据需要从查找表中删除匹配记录,因为它是关联的拥有者,因此允许这样做.

If you do that, by deleting a Category object it would delete the matching record from the lookup table as you want it to as it is allowed to do so because it is the owning side of the association.

为了不删除分配给要删除的类别对象的项目,您需要将级联属性保留为:cascade="save-update".

In order to NOT delete the Items that are assigned to a Category object that is to be deleted you need to leave have the cascade attribe as: cascade="save-update".

cascade="all"将删除与已删除的Category对象关联的项目.

cascade="all" will delete the items that are associated with the deleted Category object.

尽管有一个副作用,那就是删除inverse = tru所在的一侧的实体会引发外键冲突异常,因为关联表中的条目未清除.

A side effect though would be that deleting the entity on the side where the inverse=tru exists will thow a foreign key violation exception as the entry in the association table is not cleared.

一种解决方案,可以完全按照您希望映射的方式工作(根据问题中提供的描述),以显式映射关联表.您的映射应如下所示:

A solution that will have your mappings work exactly as you want them to work (by the description you provided in your question) would be to explicitly map the association table.Your mappings should look like that:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="..."
                   namespace="...">

  <class name="Category" lazy="true">

    <id name="CategoryId" unsaved-value="0">
      <generator class="native" />
    </id>
    <property name="Name" />

    <bag name="ItemCategories" generic="true" inverse="true" lazy="true" cascade="none">
        <key column="CategoryId"/>
        <one-to-many class="ItemCategory"/>
    </bag>

    <bag name="Items" table="ItemCategory" cascade="save-update" generic="true">
      <key column="CategoryId"></key>
      <many-to-many class="Item" column="ItemId"></many-to-many>
    </bag>

  </class>

</hibernate-mapping>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="..."
                   namespace="...">

  <class name="Item" table="Item" lazy="true">

    <id name="ItemId" unsaved-value="0">
      <generator class="native" />
    </id>
    <property name="Name" />

    <bag name="ItemCategories" generic="true" inverse="true" lazy="true" cascade="all-delete-orphan">
        <key column="ItemId"/>
    <one-to-many class="ItemCategory"/>
    </bag>

    <bag name="Categories" table="ItemCategory" inverse="true" cascade="save-update" generic="true">
      <key column="ItemId"></key>
      <many-to-many class="Category" column="CategoryId"></many-to-many>
    </bag>

  </class>

</hibernate-mapping>

如上所示,它使您可以进行以下操作:

As it is above it allows you the following:

  1. 删除类别,仅删除关联表中的条目,而不删除任何项目
  2. 删除项目,仅删除关联表中的条目,而不删除任何类别
  3. 通过填充Category.Items集合并保存Category,仅从Category一侧进行级联保存.
  4. 由于Item中的inverse = true是必需的.Category,因此无法从这一侧进行级联保存.通过填充Item.Categories集合,然后保存Item objec,您将获得对Item表的插入和对Category表的插入,但对关联表的插入.我想这就是NHibernate的工作方式,但我还没有找到解决方法.

以上所有内容均通过单元测试进行了测试.您需要创建ItemCategory类映射文件和类才能使上述内容正常工作.

All the above are tested with unit tests.You will need to create the ItemCategory class mapping file and class for the above to work.

这篇关于在NHibernate中定义多对多关系以允许删除但避免重复记录的正确方法是什么的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-30 00:34