在 NHibernate 中定义多对多关系以允许删除但避免重复记录的正确方法是什么 [英] What is the correct way to define many-to-many relationships in NHibernate to allow deletes but avoiding duplicate records

查看:16
本文介绍了在 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:

类别
类别 ID
姓名

Category
CategoryId
Name

项目
商品编号
姓名

Item
ItemId
Name

项目类别
商品编号
类别 ID

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>

我在Category中的Item列表和Item中的Category列表中添加项目的方法设置了双方的关系.

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.

如果我在袋子上设置了级联="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. 删除一个Category,只删除关联表中的条目,不删除任何Item
  2. 删除一个Item,只删除关联表中的条目,不删除任何类别
  3. 通过填充 Category.Items 集合并保存类别,仅在类别端使用级联进行保存.
  4. 由于在 Item.Categories 中需要 inverse=true,因此无法从这一侧进行级联保存.通过填充 Item.Categories 集合,然后保存 Item 对象,您将获得对 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 中定义多对多关系以允许删除但避免重复记录的正确方法是什么的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆