【问题标题】:NHibernate cascade="all-delete-orphan" is deleting non-orphaned rowsNHibernate cascade="all-delete-orphan" 正在删除非孤立行
【发布时间】:2011-10-25 00:43:06
【问题描述】:

当使用 cascade="all-delete-orphan" NHibernate 正在删除一个不是孤立的子行 - 它只是被移动到一个新的父行。

using (var session = sessionFactory.OpenSession())
{
    using (var transaction = session.BeginTransaction())
    {
        // Get the store we're moving him to 
        Store newStore = session.QueryOver<Store>().Where(...).SingleOrDefault();

        // Get existing employee 
        Employee jack = session.QueryOver<Employee>().Where(...).SingleOrDefault();

        // Do the move
        jack.Store.Staff.Remove(jack);
        jack.Store = newStore;
        jack.Store.Staff.Add(jack);

        transaction.Commit(); 
    }
}

当提交发生时,会生成一个单独的 DELETE 语句来从数据库中删除“jack”。如果 'jack' 实际上是孤儿,这种行为是有道理的,但他现在应该很高兴地被分配到他的新商店。

如果我将级联更改为“全部”,则会生成预期的 UPDATE 语句,并且很高兴按预期重新分配“杰克”。但是,这会导致真正的孤立行保留在数据库中,这是不可接受的。

这是一个错误还是我做错了什么?

以下是课程:

public class Store
{
    public virtual Guid Id { get; private set; }
    public virtual string Name { get; set; }
    public virtual IList<Employee> Staff { get; set; }
}

public class Employee
{
    public virtual Guid Id { get; private set; }
    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }
    public virtual Store Store { get; set; }
}

和 FluentNHibernate 映射:

public class EmployeeMap : ClassMap<Employee>
{
    public EmployeeMap()
    {
        Id(x => x.Id).GeneratedBy.Guid();
        Map(x => x.FirstName);
        Map(x => x.LastName);
        References(x => x.Store);
    }
}

public class StoreMap : ClassMap<Store>
{
    public StoreMap()
    {
        Id(x => x.Id).GeneratedBy.Guid();
        Map(x => x.Name);
        HasMany(x => x.Staff)
          .Inverse()
          .Cascade.AllDeleteOrphan();
    }
}

更新

在对此进行更多测试之后,它似乎确实是一个错误,因为加载对象的顺序会改变结果(请注意,在这两种情况下,Move Employee 都是按上述完成的):

1) 加载 Store B,加载 Store A,在 A.Staff 中查找员工,将员工移动到 B ==> 员工被删除(不抛出异常,员工未重新插入)

2) 加载 Store A,加载 Store B,在 A.Staff 中查找员工,将员工移动到 B ==> ObjectDeletedException(已删除的对象将被级联重新保存(从关联中删除已删除的对象))

第二种情况似乎是@Cole W 提到的场景,并讨论了herehere 以及NHibernate 中孤儿处理的已知限制。

但是,第一种情况似乎是一个错误。在我的理解中,不应该存在加载对象的顺序会改变 NHibernate 所做的数据库更改的情况。这似乎是一个可能导致数据丢失的错误。

更新 2

鉴于依赖于加载顺序的不一致行为和数据丢失的可能性,我将其记录为bug in NHibernate JIRA。错误报告包含完整的代码和映射来演示问题。

【问题讨论】:

  • 您的 Store 示例类文件没有示例代码中提到的 Staff 属性,请您更新一下吗?
  • 删除再重新插入是不是有问题?我意识到仅进行更新会更有效,但我不确定如果这一切都在事务中完成,这会是什么大不了的事。我也希望这是 NH 的预期行为。
  • @Cole W,不幸的是,该行也没有重新插入。该行被简单地删除。无论如何,这个例子是真正问题的简化测试用例。在实际场景中,由于删除,必须发生许多级联操作。在真正的应用程序中删除/插入比更新慢大约 10,000 倍。

标签: c# nhibernate fluent-nhibernate


【解决方案1】:

这实际上似乎是其他使用 NHibernate 的常见问题。查看这些文章,看看它们是否对您有帮助。

查看这篇 SO 文章:Fluent NHibernate exception moving objects between collections

该文章中还链接了 Fabio Maulo 的这篇文章:
http://fabiomaulo.blogspot.com/2009/09/nhibernate-tree-re-parenting.html

【讨论】:

  • 感谢@Code W。我将您的答案标记为正确,因为当 NHibernate 产生 ObjectDeletedException 时,您的答案是适用的。对于另一种情况,NHibernate 静默删除行,这已被报告为错误,希望修复后也会导致 ObjectDeletedException。
【解决方案2】:

这些问题可能是由于覆盖多个引用而没有告诉 NHibernate 发生了什么

在我们等待更新的 Q 时快速尝试一下(请参阅我对缺少的 Staff 属性的评论)我建议您更改您的查询,以便您直接询问 Store 而不是使用您的商店导航器员工对象。从该Store.Staff 集合中删除jack,发出session.SaveOrUpdate(store),然后尝试将jack.Store 引用覆盖到新的引用(在提交事务之前发出另一个SaveOrUpdate

【讨论】:

  • 谢谢@Alex。尝试了您的解决方法,但结果是相同的 - 该行在不应该被删除时被删除。
猜你喜欢
  • 1970-01-01
  • 2011-08-07
  • 2011-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-25
相关资源
最近更新 更多