我有一个父对象,它与ISet
个子对象有一对多的关系。子对象具有唯一约束(PageNum
和ContentID
- 父对象的外键。)
<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先执行删除?
答案 0 :(得分:30)
没有选项来指定事务中的操作顺序,因为它是硬编码的,如下所示(来自文档):
SQL语句按以下顺序发布
- 所有实体插入,使用ISession.Save()
以相同的顺序保存相应的对象- 所有实体更新
- 所有集合删除
- 所有集合元素删除,更新和插入
- 所有收集插入
- 所有实体删除,按相同顺序使用ISession.Delete()删除相应的对象
(例外情况是,保存时会插入使用本机ID生成的对象。)
因此,我是否可以挑战您回答为什么您要添加具有现有标识符的新实体?标识符应该对特定的“实体”是唯一的。如果该实体消失了,那么它应该是它的标识符。
另一种选择是对该记录进行更新而不是删除/插入。这使得ID保持不变,因此没有唯一的约束违规(至少在密钥上),您可以更改所有其他数据,使其成为“新”记录。
编辑: 因此,当我回复时,显然我没有完全关注这个问题,因为这是对非主键列的唯一约束的问题。
我认为您有两种解决方案可供选择:
Session.Flush()
,这将在此之前执行会话的所有更改,之后您可以继续其余的操作(插入新对象)。这也适用于事务内部,因此您无需担心原子性。ReplacePage
函数,该函数使用新数据更新现有实体,但保持主键和唯一列相同。答案 1 :(得分:4)
我遇到了同样的问题...... 我有一个实体,它有一个映射到包含唯一约束的表的集合。
我不明白的是,根据Stuarts的回复,集合删除应该在集合插入之前发生?
当我深入了解NHibernate源代码时,我找到CollectionUpdateAction
类,其Execute
方法中包含此代码:
persister.DeleteRows(collection, id, session);
persister.UpdateRows(collection, id, session);
persister.InsertRows(collection, id, session);
然后,我会假设在插入之前执行删除,但显然情况并非如此。 CollectionUpdateAction是否在此方案中未使用?何时使用CollectionUpdateAction?
我这样解决了这个问题:
我的所有数据库访问都通过存储库进行;在我的存储库的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中出现的同一问题提供的解决方案。