当实现具有生成的id的实体的equals()时,最佳实践是什么?

12
如果我有一个带有A,B,C和D列的表格:
 A:自动生成的id (PK)
 B&C:组合必须唯一(实际上在业务意义上定义身份的列)
 D:其他一些列。
现在,如果基于这个表格创建业务对象(例如在Java中),哪种equals()方法的实现更好:
  1. 基于A来定义相等性。
  2. 基于B和C来定义相等性。
或者,选择其中之一真的没有什么区别。
3个回答

21

肯定是 B 和 C,因为你希望在实体持久化之前,equals() 协定就能够生效。你自己说了:

这些列实际上定义了业务意义上的标识

如果是这样的话,那么逻辑运算equals()就应该使用它们。数据库键是数据库的问题,不应该成为业务层面的关注点。

别忘了在 hashcode() 中也要使用相同的属性。


为什么在equals()方法中要排除A - Buhake Sindi
3
从商业角度来看,一个持久化实体和一个非持久化实体,只要它们的属性B和C相等,它们就是相等的。商业角度不应关心实现细节,比如DB ID,这只与模型有关。 - Sean Patrick Floyd
谢谢。我觉得我在思考方面有些颠倒了(把实现放在第一位,商业意义放在第二位)。你的回答让我认识到了这一点。谢谢。 - willy wonka

3

我也同意@S.P.Floyd的看法。但是我想再补充一些内容。

有些情况下,实体并没有独特的业务属性。例如,一个实体可能只有A(主键)和B(业务属性),但是许多实体具有相同的B值。

在这种情况下,创建equals()hashcode()方法会很困难。你肯定不希望以A为基础进行比较,因为你无法将已持久化对象与尚未持久化的对象进行比较。而且你也不能仅基于B进行比较,因为这样很多本来是不同的实体将被视为相同。

在这种情况下,我会添加一个Date created = new Date();属性。当一个实体被创建时,它会自动获得一个创建时间戳。在我的equals()hashcode()方法中,我包括了Bcreated两个属性。虽然这并不完美,因为有非常小的几率两个对象会在同一时间创建(尤其是在集群解决方案中),但至少这是一个开始。如果必要的话,可以添加一个UID或其他生成的业务属性,而不是数据库的主键。


创建的字段是否映射到数据库中,还是只是一个人为(瞬时)字段?我不希望仅因为使用特定技术(如JPA),就污染我的数据库表。 - Kawu
1
@Kawu - 在我的情况下,created字段确实映射到了数据库,但我仍然需要那些信息。如果使用临时字段,当从数据库中获取对象集合时,日期可能会发生冲突,因此使其正常工作将更加困难。我认为创建日期是指数据库实体最初创建的日期,而不是从数据库中检索并再次转换为POJO的对象的日期。 - Tauren

2
如果(B,C)是唯一的一对,那么就不需要再添加自动生成的id。对于表格而言,A等同于(B,C)(一对一关系)。
你可能想要或需要额外的键,但我同意seanizer的观点,使用(B,C)进行匹配,并且由于A是冗余的(在对象持久化之前是null),所以不要将其用于匹配(和哈希码)。

2
但是Hibernate推荐使用代理键,自然键应该标记为@NaturalId - Rosdi Kasim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接