JPA 实体的 id 字段是否应以 equals 和 hashCode 的形式考虑?

2022-09-03 10:15:50

在使用 JPA2 和 EclipseLink 为数据库应用程序编写测试时,我遇到了一个问题:

我将一些实体添加到数据库中,稍后检索它,并希望将其与具有我期望的值的实例进行比较,以确认添加按我的预期工作。

首先我写了类似的东西

assertEquals(expResult, dbResult);

失败了,因为我无法真正知道字段的值,该字段由数据库生成,因此与我手动创建和填充的字段不同。iddbResultexpResultnew

我看到两个选项:

  • 要么我从中删除该字段,以便比较仅基于“实际值”。不过,我不知道这是否会导致数据库或其他地方出现问题。idequalshashCode

  • 或者,我编写测试以显式检查除手动之外的每个字段。id

我该怎么办?


答案 1

你可能会发现很多关于这个的争议。我的立场是,您绝对不要对应用程序中的任何内容使用数据库主键。它应该是完全看不见的。通过某些其他属性或属性组合来标识应用程序中的对象。

在“测试持久性操作”方面,您真正想要的可能是检查字段是否已保存并正确加载,并且可能在保存主键时为其分配了一些值。这可能根本不是等式方法的工作。


答案 2

不建议在和实现中依赖数据库生成的 Id。您应该依靠类的真正唯一/半唯一属性来检查相等性,并生成哈希码值。Hibernate文档有一个广泛的页面来讨论这个问题,其中的事实或多或少适用于每个JPA提供者。equalshashCode

在和实现中使用业务键而不是数据库生成的值的根本原因是,在数据库中持久保存实体后,JPA 提供程序必须实际发出 a。如果使用数据库生成的 Id 比较对象,则最终将遇到在以下情况下失败的相等性测试:equalshashCodeSELECT

  • 如果 和 是 E 类的实体(使用数据库生成的 Id 验证相等性),则 如果 和 尚未存储在数据库中,则 如果 和 将相等。这不是你想要的,特别是如果要存储和在一些之前持久化。如果属性和具有不同的值,则情况更糟;实现将防止将两个明显不同的实体添加到 中,并且当从使用主键查找实体时,实现将为您提供查找时间。E1E2E1E2E1E2SetE1E2equalsSethashCodeO(n)HashMap
  • 如果 是已持久化的托管实体,并且是尚未持久化的实体,则在 和 (Ids 除外) 的所有属性值都相似的情况下,相等性测试将被视为 !=。同样,这可能不是您想要的,特别是如果您想避免数据库中的重复实体仅在数据库生成的ID上有所不同。E1E2E1E2E1E2

因此,和 实现应该使用业务密钥,以便为持久化和未存在的实体展示一致的行为。equalshashCode


推荐