【问题标题】:Bidirectional one to many (or many to one) cascade delete behaviour. It works, but why?双向一对多(或多对一)级联删除行为。它有效,但为什么呢?
【发布时间】:2011-07-17 18:25:57
【问题描述】:

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

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

<?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”中。 该语句已终止。

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

我已阅读有关 inverse 属性的信息,并且我理解它表示关系的拥有方,并且更新/插入/删除以不同的顺序完成,这就是我认为它解决了我的问题的原因。所以我的问题是,我是否以正确的方式解决了我的问题?这对性能有何影响? (我怀疑不多)。是否有一个单向关系而没有多向一侧并将反向属性设置为 true 以获得更好的性能?还是我要疯了,完全错过了重点?

【问题讨论】:

    标签: nhibernate cascading-deletes


    【解决方案1】:

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

    我至少能想到两种方法:

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

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

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

    更新

    Here's 使用事件侦听器的概念验证实现。

    【讨论】:

    • 嗨迭戈。感谢您的回答。我确实想到了这一点,但目前我正在编写类别测试以检查我的级联是否正常工作,因此如果我可以使用 Nhibernate 级联功能获得上述行为,那么我更愿意这样做。
    • @dezzy:NHibernate 不支持“on delete set null”级联行为。您也许可以通过使用事件侦听器来伪造它,但这需要更多的工作。
    • 你让我想到了这个......如果我能让它工作,我会写一篇关于它的博客文章(sessionfactory.blogspot.com,敬请期待:-))
    • 好吧,我想我现在是在通过取消 Products 集合上的 inverse=true 属性来伪装它。权衡是额外的更新声明,我可以忍受。我只是想知道这是否是处理事情的正确方法。似乎是非内置功能的合理解决方案。所以我很高兴。再次感谢。非常感谢您的意见。
    【解决方案2】:

    我假设您阅读过关于Inverse Attribute in NHibernate

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

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

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

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

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

    【讨论】:

    • 是的,我已经读过 inverse 并且知道它的用途。我可以随意修改架构,但我想我会将反向属性设置为 false,因为我想在一个地方进行配置。我也喜欢让数据库为我维护引用完整性的想法。我在管理应用程序的上下文中使用 NHibernate,因此更新将是最少的并且是用户驱动的。我写这篇文章很晚了,在思考了更多之后我想问的问题是为什么 Nhibernate 没有检测到这种关系并设置逆适当?毕竟所有其他值都有合理的默认值。
    • 顺便感谢您的回复。非常感激。无法将其放入我的第一条评论中;)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-04
    • 2015-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    相关资源
    最近更新 更多