7

我认为实体应该默认通过主键比较实现相等,但是 nhibernate 文档建议使用业务标识:

最明显的方法是通过比较两个对象的标识符值来实现 Equals()/GetHashCode()。如果值相同,则两者必须是同一数据库行,因此它们相等(如果将两者都添加到 ISet,则 ISet 中将只有一个元素)。不幸的是,我们不能使用这种方法。NHibernate 只会将标识符值分配给持久的对象,新创建的实例不会有任何标识符值!我们建议使用业务键相等来实现 Equals() 和 GetHashCode()。

业务键相等意味着 Equals() 方法仅比较形成业务键的属性,该键将识别我们在现实世界中的实例(自然候选键)

还有这个例子(也来自文档):

public override bool Equals(object other)
{
    if (this == other) return true;

    Cat cat = other as Cat;
    if (cat == null) return false; // null or not a cat

    if (Name != cat.Name) return false;
    if (!Birthday.Equals(cat.Birthday)) return false;

    return true;
}

这让我头晕目眩,因为业务标识的概念(根据示例)与语法比较相同,这基本上是我与 ValueObjects 相关联的语义类型。不使用数据库主键作为比较值的原因是因为如果主键不是在客户端生成(例如增量)并且您使用某种哈希表集合(例如 ISet),这将更改对象的哈希码用于存储您的实体。

如何创建一个不违反相等/哈希码 ( http://msdn.microsoft.com/en-us/library/bsc2ak47.aspx ) 的一般规则并符合 nhibernate 规则的良好相等实现?

4

1 回答 1

13

这是 ORM 的一个已知问题。在这里,我概述了我所知道的解决方案并给出了一些建议。

1 代理/主键:自动生成

正如你所提到的,如果对象没有被保存,这不起作用。

2 代理/主键:赋值

您可以决定在代码中分配 PK 的值,这样对象总是有一个 ID 并且可以用于比较。请参阅不要让 hibernate 窃取您的身份

3 自然键

如果对象有另一个自然键,而不是主键,你可以使用这个。对于具有数字主键字符串客户端编号的客户端实体就是这种情况。客户编号识别现实世界中的客户,是一个不会改变的自然密钥。

4 对象值

使用相等的对象值是可能的。但是你提到的还有其他缺点。如果值更改并且对象在集合中,这可能会出现问题。例如,如果您有Set两个最初不同的对象,但是当它们在集合中被引用时您更改了值,以使它们变得相等。然后你打破了Set. 请参阅Hibernate equals 和 hashcode

5 混合:值 + 自动生成主键/代理键

如果要比较的对象已经有 ID,请使用它。否则,使用对象值进行比较。

都有一些优点和缺点。恕我直言,最好是 3,如果您的域模型可能的话。否则,我使用了 5 并且它起作用了,尽管在使用集合时仍然存在一些陷阱。我从未使用过 2,但如果您找到一种在代码中生成 PK 的方法,这听起来也是一个明智的解决方案。也许其他人对此有指示。

于 2010-01-20T10:07:49.470 回答