我有两个Nhibernate映射,用于两个类,Category和Product。我的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”中。 声明已经终止。
但是,当我在Category映射中删除Products集合上的inverse = true属性时,它可以正常工作。 product表中的CategoryId外键设置为null,从而使产品与类别取消关联。这就是我想要的。
我已经读过有关逆属性的内容,我理解它表示关系的拥有方,更新/插入/删除以不同的顺序完成,这就是为什么我认为它解决了我的问题。所以我的问题是,我是否以正确的方式解决了我的问题?这对性能有何影响? (我怀疑不多)。如果没有多边到一边的单向关系并且将inverse属性设置为true以获得更好的性能会更好吗?还是我疯了,完全忽略了这一点?
答案 0 :(得分: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使用事件监听器的概念验证实现。
答案 1 :(得分:0)
我假设您阅读了Inverse Attribute in NHibernate
正如错误消息所示,您的DELETE与外键约束产生冲突,这意味着只要存在引用该特定类别的产品,DB就无法删除该类别。
您可以执行的操作(如果可以更改数据库模式)将“ON DELETE SET NULL”应用于外键约束。这样,当执行DELETE时,DB将自动将Product表中的所有引用设置为NULL。
如果您无法修改外键,那么除了删除inverse属性之外别无选择。这样做会导致NHibernate首先将Product.Category引用设置为NULL,然后删除Category。
如果您经常需要Product.Category,那么您不应该摆脱Product中的多对一属性。
关于性能,这取决于您插入产品的频率。每个插入都将导致另外的更新以设置外键。但这应该不是问题。