在插入之前强制NHibernate级联删除

时间:2009-04-01 17:52:52

标签: nhibernate nhibernate-mapping nhibernate-cascade

我有一个父对象,它与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先执行删除?

3 个答案:

答案 0 :(得分:30)

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

  

SQL语句按以下顺序发布

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

(例外情况是,保存时会插入使用本机ID生成的对象。)

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

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

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

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

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

答案 1 :(得分: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'
  • 我的所有数据库访问都通过存储库进行;在我的存储库的save方法中,我有我的实体的代码:

    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级联设置确保没有插入或更新OrderLines,我必须遍历我的集合,并为每个OrderLine调用'SaveOrUpdate'。 这将确保插入新的OrderLines,并更新已修改的OrderLines。 不会对未更改的OrderLines执行任何操作。

虽然它不是理想的解决方案(它是一个丑陋的黑客恕我直言),它有点工作,它被抽象在存储库后面,所以这就是我现在处理这个问题的方法......

答案 2 :(得分:0)

如果你摆脱子主键(ContentPage.Id)并使复合键成为主键(即PageNum和ContentID),那么我相信它可能有效。这是针对hibernate中出现的同一问题提供的解决方案。