23

我有一个与子对象具有一对多关系的父ISet对象。子对象具有唯一约束(PageNumContentID- 父对象的外键)。

<set name="Pages" inverse="true" cascade="all-delete-orphan" access="field.camelcase-underscore">
    <key column="ContentId" />
    <one-to-many class="DeveloperFusion.Domain.Entities.ContentPage, DeveloperFusion.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</set>

我遇到的问题是,如果我ContentPage从父集合中删除一个元素,然后在同一个事务中添加一个具有相同唯一键的新元素......你会得到一个唯一约束违规,因为NHibernate尝试删除。

有没有办法强制 NHibernate 先执行删除?

4

3 回答 3

31

没有选项可以指定事务中的操作顺序,因为它被硬编码如下(来自文档):

SQL 语句按以下顺序发出

  • 所有实体插入,以相同的顺序使用 ISession.Save() 保存相应的对象
  • 所有实体更新
  • 所有集合删除
  • 所有集合元素的删除、更新和插入
  • 所有集合插入
  • 所有实体删除,以相同的顺序使用 ISession.Delete() 删除相应的对象

(一个例外是使用原生 ID 生成的对象在保存时被插入。)

因此,我可以挑战您回答为什么要添加具有现有标识符的新实体吗?标识符应该对特定的“实体”是唯一的。如果该实体消失了,它的标识符也应该消失。

另一种选择是对该记录进行更新,而不是删除/插入。这使 ID 保持不变,因此不会违反唯一约束(至少在键上),并且您可以更改所有其他数据,使其成为“新”记录。

编辑:显然,当我回答时,我并没有完全注意这个问题,因为这是对非主键列的唯一约束的问题。

我认为您有两种解决方案可供选择:

  1. 在您删除之后调用Session.Flush(),它将执行到该点为止对会话的所有更改,之后您可以继续进行其余操作(插入新对象)。这也适用于事务内部,因此您无需担心原子性。
  2. 创建一个ReplacePage函数,用新数据更新现有实体,但保持主键和唯一列相同。
于 2009-04-01T21:17:15.693 回答
4

我遇到了同样的问题......我有一个实体,它有一个集合,该集合映射到一个包含唯一约束的表。

我不明白的是,根据 Stuarts 的回复,集合删除应该发生在集合插入之前?当我深入研究 NHibernate 源代码时,我找到了CollectionUpdateAction该类,该类在其Execute方法中包含以下代码:

persister.DeleteRows(collection, id, session);
persister.UpdateRows(collection, id, session);
persister.InsertRows(collection, id, session);

然后,我会假设删除是在插入之前执行的,但显然情况并非如此。这个场景中没有使用 CollectionUpdateAction 吗?何时使用 CollectionUpdateAction?

回答:

我已经像这样解决了这个问题:

  • 在我的映射中,我将级联选项设置为“delete-orphan”而不是“all-delete-orphan”
  • 我所有的数据库访问都通过存储库进行;在我的存储库的保存方法中,我的实体有以下代码:

    public void Save( Order orderObj )
    {
        // Only starts a transaction when there is no transaction
        // associated yet with the session
       With.Transaction(session, delegate()
       {
           session.SaveOrUpdate (orderObj);
           session.Flush();
    
           foreach( OrderLine line in orderObj.Lines )
           {
                session.SaveOrUpdate (line);
           }
        };
     }
    

因此,我保存了 orderObj,并且因为级联设置为 delete-orphan,要删除的对象将从数据库中删除。

调用后SaveOrUpdate,我必须确保将更改刷新到数据库。

由于 delete-orphan 级联设置确保没有插入或更新 OrderLine,我必须遍历我的集合,并为每个 OrderLine 调用“SaveOrUpdate”。这将确保插入新的 OrderLines,并更新修改过的 OrderLines。不会对未更改的 OrderLines 执行任何操作。

虽然它不是理想的解决方案(恕我直言,这是一个丑陋的黑客攻击),但它确实有效,并且它在存储库后面被抽象出来,所以这就是我现在处理这个问题的方式......

于 2009-05-15T09:48:55.170 回答
0

如果您摆脱子主键(ContentPage.Id)并将复合键作为主键(即 PageNum 和 ContentID),那么我相信它可能会起作用。这是针对休眠中发生的相同问题提供的解决方案。

于 2010-11-03T13:35:15.880 回答