双向一对多(或多对一)级联删除行为。它有效,但为什么呢?

发布于 2024-10-23 00:47:17 字数 2513 浏览 8 评论 0原文

我有两个 Nhibernate 映射用于两个类:类别和产品。我的 Category 类有两个集合属性。 Children属性是Category类型的集合,它代表子类别(代表类别菜单,典型的父子场景)。 Category 类的第二个属性是 Products 集合,它表示类别下的所有产品。

我想要实现的是,当我删除一个类别时,我希望删除该类别而不是产品。所以我希望这个产品成为孤儿。即,将 Product 表中的外键 (CategoryId) 设置为 null。我不想仅仅因为删除了一个类别就删除一个产品。我希望能够稍后重新分配到另一个类别。我的代表上述场景的映射如下。

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="naakud.domain" namespace="naakud.domain">
  <class name="Category">
    <id name="Id">
      <generator class="hilo" />
    </id>
    <version name="Version"/>
    <property name="Name" not-null="true" unique="true" />
    <set name="Products"
         cascade="save-update"
         inverse="true"
         access="field.camelcase-underscore">
      <key column="CategoryId" foreign-key="fk_Category_Product" />
      <one-to-many class="Product" />
    </set>
    <many-to-one name="Parent" class="Category" column="ParentId" />
    <set name="Children"
         collection-type="naakud.domain.Mappings.Collections.TreeCategoriesCollectionType, naakud.domain"
         cascade="all-delete-orphan"
         inverse="true"
         access="field.camelcase-underscore">
      <key column="ParentId" foreign-key="fk_Category_ParentCategory" />
      <one-to-many class="Category"/>
    </set>
  </class>
</hibernate-mapping>


<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="naakud.domain" namespace="naakud.domain">
  <class name="Product">
    <id name="Id">
      <generator class="hilo" />
    </id>
    <version name="Version" />
    <property name="Name" not-null="true" unique="true" />
    <property name="Description" not-null="true" />
    <property name="UnitPrice" not-null="true" type="Currency" />
    <many-to-one name="Category" column="CategoryId" />
  </class>
</hibernate-mapping>

通过此映射,当我删除与其关联的产品的类别时,我会收到以下约束错误。

DELETE 语句与 REFERENCE 约束“fk_Category_Product”冲突。冲突发生在数据库“naakud”、表“dbo.Product”、列“CategoryId”中。 该声明已终止。

但是,当我删除类别映射中的产品集合上的 inverse=true 属性时,它可以正常工作。产品表中的 CategoryId 外键设置为 null,从而取消产品与类别的关联。这就是我想要的。

我读过有关逆属性的内容,我知道它表示关系的拥有方,并且更新/插入/删除是以不同的顺序完成的,这就是为什么我认为它解决了我的问题。所以我的问题是,我是否以正确的方式解决我的问题?这对性能有何影响? (我怀疑的不多)。拥有单向关系而不是多对一关系并将 inverse 属性设置为 true 以获得更好的性能会更好吗?或者我疯了并且完全没有抓住重点?

I have two Nhibernate mappings for two classes, Category and Product. My Category class has two properties that are collections. The Children property is a collection of type Category which represents child categories (represents a category menu, typical parent child scenario). The second property on the Category class is a Products collection which represents all the products under a category.

What I am trying achieve is when I delete a category I want the category to deleted but not the product. So I want the product to be orphaned. i.e have its foreign key (CategoryId) in the Product table set to null. I don't want to delete a product just because I have deleted a category. I want to be able to reassign in at a later time to another category. My mappings representing the mentioned scenario are below.

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="naakud.domain" namespace="naakud.domain">
  <class name="Category">
    <id name="Id">
      <generator class="hilo" />
    </id>
    <version name="Version"/>
    <property name="Name" not-null="true" unique="true" />
    <set name="Products"
         cascade="save-update"
         inverse="true"
         access="field.camelcase-underscore">
      <key column="CategoryId" foreign-key="fk_Category_Product" />
      <one-to-many class="Product" />
    </set>
    <many-to-one name="Parent" class="Category" column="ParentId" />
    <set name="Children"
         collection-type="naakud.domain.Mappings.Collections.TreeCategoriesCollectionType, naakud.domain"
         cascade="all-delete-orphan"
         inverse="true"
         access="field.camelcase-underscore">
      <key column="ParentId" foreign-key="fk_Category_ParentCategory" />
      <one-to-many class="Category"/>
    </set>
  </class>
</hibernate-mapping>


<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="naakud.domain" namespace="naakud.domain">
  <class name="Product">
    <id name="Id">
      <generator class="hilo" />
    </id>
    <version name="Version" />
    <property name="Name" not-null="true" unique="true" />
    <property name="Description" not-null="true" />
    <property name="UnitPrice" not-null="true" type="Currency" />
    <many-to-one name="Category" column="CategoryId" />
  </class>
</hibernate-mapping>

With this mapping, when I delete a category which has products associated with it I get the following constraint error.

The DELETE statement conflicted with the REFERENCE constraint "fk_Category_Product". The conflict occurred in database "naakud", table "dbo.Product", column 'CategoryId'.
The statement has been terminated.

However, when I remove the inverse=true attribute on the Products collection in the Category mapping then it works fine. My CategoryId foreign key in the products table is set to null and thus disassociating a product with a category. Which is what I want.

I have read about the inverse attribute and I understand it signifies the owning side of a relationship and updates/inserts/deletes are done in a different order which is why I think it solves my problem. So my question is, am I solving my problem in the correct way? How does this affect performance? (not much I suspect). Would it be better to have a uni-directional relationship without the many to one side and have the inverse attribute set to true to get better performance? Or am I going crazy and completely missing the point?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

萌化 2024-10-30 00:47:17

解决删除问题的另一种方法是在刷新之前将所有相关实体上的多对一属性设置为 null。

我至少可以想到两种方法来做到这一点:

  • 在调用 session.Delete(category) 的同一方法中,执行以下操作:

    foreach(类别中的产品。产品)
        产品类别=空;
    
  • 使用 HQL:

    session.CreateQuery(
           “更新产品集类别 = null,其中类别 = :category”)
           .SetParameter("类别", 类别)
           .执行更新();
    

更新

这是一个证明使用事件监听器的概念实现。

Another way of fixing the delete problem is by setting the many-to-one property to null on all the related entities to null before flushing.

I can think of at least two ways to do it:

  • In the same method that calls session.Delete(category), do:

    foreach (var product in category.Products)
        product.Category = null;
    
  • Using HQL:

    session.CreateQuery(
           "update Product set Category = null where Category = :category")
           .SetParameter("category", category)
           .ExecuteUpdate();
    

Update:

Here's a proof-of-concept implementation using an event listener.

嗫嚅 2024-10-30 00:47:17

我假设您阅读了 NHibernate 中的反向属性

正如错误消息所示,您的 DELETE 与外键约束产生冲突,这意味着只要存在引用该特定类别的产品,数据库就无法删除该类别。

您可以做的(如果您可以更改数据库模式)是将“ON DELETE SET NULL”应用于您的外键约束。这样,当执行DELETE时,DB会自动将Product表中的所有引用设置为NULL。

如果您无法修改外键,那么您别无选择,只能删除反向属性。这样做将导致 NHibernate 首先将 Product.Category 引用设置为 NULL,然后删除该类别。

如果您经常需要 Product.Category,那么您不应该删除 Product 中的多对一属性。

关于性能,这取决于您插入产品的频率。每次插入都会导致额外的更新来设置外键。不过,这应该不是问题。

I assume that you read about Inverse Attribute in NHibernate

As the error message says, your DELETE generates a conflict with the foreign key constraint, meaning that the DB cannot delete the Category as long as there are Products referencing that particular Category.

What you could do (if you can alter the DB schema) is applying "ON DELETE SET NULL" to your foreign key constraint. That way, when the DELETE is executed, the DB will automatically set all references in the Product table to NULL.

If you cannot modify the foreign key, then you would have little choice but to remove the inverse attribute. Doing so will result in NHibernate first setting the Product.Category reference to NULL and then deleting the Category.

If you need Product.Category fairly often then you should not get rid of the many-to-one attribute in Product.

Regarding the performance, that depends on how often you insert Products. Each insert will result in an additional update to set the foreign key. That should not be a problem, though.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文