10

我有一个名为“客户”的对象,它将在其他表中用作外键。

问题是我想知道是否可以删除“客户”(即,它没有在任何其他表中被引用)。

Nhibernate可以做到这一点吗?

4

7 回答 7

5

您要问的是Customer在引用的表 FK 列中查找 PK 值的存在。有很多方法可以解决这个问题:

  1. 正如 kgiannakakis 指出的那样,尝试删除,如果抛出异常则回滚。有效但丑陋且无用。这还要求您在数据库中设置 CASCADE="RESTRICT"。此解决方案的缺点是您必须尝试删除对象才能发现无法删除

  2. 映射作为集合引用的实体,Customer然后映射每个集合(如果它们Count > 0不允许删除)。这很好,因为只要映射完成,这对架构更改是安全的。这也是一个糟糕的解决方案,因为必须进行额外的选择。

  3. 有一个执行查询的方法,例如bool IsReferenced(Customer cust). 很好,因为您可以有一个查询,您可以在需要时使用它。不太好,因为它可能由于架构和/或域更改而容易出错(取决于您将执行的查询类型:sql/hql/criteria)。

  4. 它自己的类的计算属性,具有类似的映射元素<property name="IsReferenced" type="long" formula="sql-query that sums the Customer id usage in the referenced tables" />。很好,因为它是一个快速的解决方案(至少与您的数据库一样快),没有额外的查询。不太好,因为它容易受到架构更改的影响,因此当您更改数据库时,您一定不要忘记更新此查询。

  5. 疯狂的解决方案:创建一个进行计算的模式绑定视图。需要时对其进行查询。很好,因为它的模式绑定并且不太容易受到模式更改的影响,很好,因为查询很快,不太好,因为您仍然需要进行额外的查询(或者您将此视图的结果映射到解决方案 4。)

2,3,4 也很好,因为您也可以将此行为投射到您的 UI(不允许删除)

就我个人而言,我会选择 4,3,5

于 2010-02-25T09:24:41.543 回答
4

我想知道是否可以删除“客户”(即,它没有在任何其他表中被引用)。

确定是否可以删除客户实际上并不是数据库的责任。它是您业务逻辑的一部分

您要求检查数据库的引用完整性。

在非 OOP 世界中没问题。但是在处理对象时(就像您一样),您最好将逻辑添加到您的对象中(对象具有状态和行为;DB - 只有状态)。

所以,我会在 Customer 类中添加一个方法来确定它是否可以被删除。这样您就可以正确(单元)测试功能

例如,假设我们有一条规则,客户只有在没有订单且未参加论坛的情况下才能被删除

然后您将拥有与此类似的 Customer 对象(最简单的情况):

public class Customer
{
    public virtual ISet<Order> Orders { get; protected set; }
    public virtual ISet<ForumPost> ForumPosts { get; protected set; }

    public virtual bool CanBedeleted
    {
        get
        {
            return Orders.Count == 0 && ForumPosts.Count == 0
        }
    }
}

这是一个非常干净和简单的设计,易于使用、测试并且不严重依赖 NHibernate 或底层数据库。

你可以像这样使用它:

if (myCustomer.CanBeDeleted)
    session.Delete(mycustomer)

除此之外,如果需要,您可以微调 NHibernate 以删除相关订单和其他关联。


注意:当然上面的例子只是最简单的说明性解决方案。您可能希望将此类规则作为删除对象时应强制执行的验证的一部分。

于 2010-03-03T01:08:32.303 回答
2

考虑实体和关系而不是表和外键,有以下不同的情况:

  • 客户有一个一对多的关系,它建立了客户的一部分,例如他的电话号码。它们也应该通过级联的方式删除。
  • 客户具有不属于客户的一对多或多对多关系,但客户知道/可以访问它们。
  • 其他一些实体与客户有关系。它也可以是任意类型(它不是数据库中的外键)。例如客户的订单。客户不知道订单。这是最难的情况。

据我所知,NHibernate 没有直接的解决方案。有元数据 API,它允许您在运行时探索映射定义。恕我直言,这是错误的做法。

在我看来,验证实体是否可以删除是业务逻辑的责任。(即使有确保数据库完整性的外键和约束,它仍然是业务逻辑)。

我们实现了一个在删除实体之前调用的服务。软件的其他部分注册某些类型。他们可以否决删除(例如通过抛出异常)。

例如,订单系统注册删除客户。如果要删除一个客户,订单系统会搜索该客户的订单,如果找到则抛出。

于 2010-03-02T08:10:30.693 回答
1

直接是不可能的。大概您的域模型包括客户的相关对象,例如地址、订单等。您应该为此使用规范模式

public class CustomerCanBeDeleted
{

    public bool IsSatisfiedBy(Customer customer)
    {
        // Check that related objects are null and related collections are empty
        // Plus any business logic that determines if a Customer can be deleted
    }
}

编辑添加:

也许最直接的方法是创建一个存储过程来执行此检查并在删除之前调用它。您可以从 NHibernate ( ) 访问 IDbCommand,ISession.Connection.CreateCommand()以便调用与数据库无关。

另请参阅对此问题的答复。

于 2010-02-19T13:03:31.797 回答
0

可能值得查看 cascade 属性,特别是 hbm.xml 文件中的 all-delete-orphan ,这可能会为您解决问题。

请参见此处,16.3 - 级联生命周期

于 2010-02-19T12:14:25.453 回答
0

一个天真的解决方案是使用事务。启动事务并删除对象。异常会通知您无法删除该对象。无论如何,做一个回滚。

于 2010-02-25T08:39:21.963 回答
0

将引用 Customer 的实体映射为集合。用特定的后缀命名您的 Customer 类中的每个集合。例如,如果您的 Customer 实体有一些 Orders,则将 Orders 集合命名如下:

public virtual ISet<Order> Orders_NHBSet { get; set; } // add "_NHBSet" at the end 

现在通过使用反射,您可以在运行时获取客户的所有属性,并获取其名称以您定义的后缀结尾的那些属性(在本例中为“_NHBSet”)然后检查每个集合是否包含任何元素,如果是,请避免删除客户。

public static void DeleteCustomer(Customer customer)
{
   using (var session = sessions.OpenSession())
   {
       using (var transaction = session.BeginTransaction())
       {

           var listOfProperties =typeof(Customer).GetProperties();
           foreach (var classProperty in listOfProperties )
           {
                if (classProperty.Name.EndsWith("_NHBSet"))
                {
                    PropertyInfo myPropInfo = typeof(Customer).GetProperty(classProperty.Name);
                    dynamic Collection =  myPropInfo.GetValue(customer, null);
                    if (Enumerable.FirstOrDefault(Collection) !=null)// Check if collection contains any element
                    {
                       MessageBox.Show("Customer Cannot be deleted");
                       return;
                    }   
                }  
            }
            session.Delete(customer);
            transaction.Commit();
      }
   }
}

The Advantage of this approach is that you don't have to change your code later if you add new collections to your customer class.And you don't need change your sql query as Jaguar suggested. The only thing you must care about is to add the particular suffix to your newly added collections.

于 2018-07-16T17:06:52.953 回答