Java 在equals和hashCode中是否应该考虑JPA实体的id字段?

Java 在equals和hashCode中是否应该考虑JPA实体的id字段?,java,jpa,persistence,eclipselink,equals,Java,Jpa,Persistence,Eclipselink,Equals,我在使用JPA2和EclipseLink为数据库应用程序编写测试时遇到了一个问题: 我将一些实体添加到数据库中,稍后检索它,并希望将其与一个实例进行比较,该实例具有我期望的值,以确认添加操作是否按照我的预期工作 首先我写了一些像 assertEquals(expResult, dbResult); 它失败了,因为我无法真正知道由数据库生成的id字段的值,因此dbResult与我用new创建并手动填充的expResult不同 我看到两种选择: 或者从equals和hashCode中删除id字段

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

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

首先我写了一些像

assertEquals(expResult, dbResult);
它失败了,因为我无法真正知道由数据库生成的
id
字段的值,因此
dbResult
与我用
new
创建并手动填充的
expResult
不同

我看到两种选择:

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

  • 或者我编写测试来显式地检查除
    id
    之外的每个字段


我应该怎么做?

我会编写测试来显式检查字段。为了方便起见,在执行assertEqual测试之前,我将预期结果和实际结果的id设置为相同的预定义值,然后使用normal equals方法


从equals中删除ID是不合理的,只是因为测试有点困难。您正在放弃重要的性能优势和代码完整性。

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


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

从《Hibernate in Action》一书中,建议定义一个业务密钥并在其上测试相等性。业务密钥是“对于具有相同数据库标识的每个实例都是唯一的属性或某些属性组合”。在其他区域,它表示不要将id用作这些属性之一,不要在集合中使用值。

equals
hashCode
实现中依赖数据库生成的ID是不可取的。在检查相等性和生成hashcode值时,应该依赖类的真正唯一/半唯一属性。有一个广泛的页面讨论这一点,其中的事实或多或少适用于每个JPA提供商

equals
hashCode
实现中对数据库生成的值使用业务键的根本原因是,JPA提供者必须在将实体持久化到数据库中后实际发出
SELECT
。如果使用数据库生成的ID比较对象,则在以下情况下,平等性测试将失败:

  • 如果
    E1
    E2
    是E类实体(使用数据库生成的ID验证相等),则如果
    E1
    E2
    尚未存储在数据库中,则它们将相等。这不是您想要的,特别是如果您想在持久化之前将
    E1
    E2
    存储在某些
    Set
    中。如果
    E1
    E2
    的属性具有不同的值,则情况更糟;
    equals
    实现将防止两个显著不同的实体被添加到
    集合
    ,而
    hashCode
    实现将在使用主键从
    HashMap
    查找实体时为您提供一个
    O(n)
    查找时间
  • 如果
    E1
    是已持久化的托管实体,而
    E2
    是未持久化的实体,则平等性测试将认为
    E1
    !=
    E2
    E1
    E2
    的所有属性值(ID除外)相似的场景中。同样,这可能不是您想要的,特别是如果您希望避免数据库中的重复实体只在其数据库生成的ID中有所不同

因此,
equals
hashCode
实现应该使用业务密钥,以便为持久化实体和非持久化实体显示一致的行为。

不考虑id会带来哪些性能和完整性问题?在我的
equals
hashCode
方法中使用
对象#hashCode
检查数据库id不是一回事吗?假设您有项目实体。您必须从服务层发送唯一项目的列表。现在有一种可能性,一个错误等于两个独特的记录作为同一个记录。拥有身份证可以减少这个问题。而且这个解决方案是不可伸缩的,如果我们决定改变使一个对象唯一的因素,我们必须更新equals方法。拥有一个保证唯一的ID是散列的完美选择。我们保证避免散列冲突。否则,哈希的质量就成了一个函数,我们是否考虑了所有应该唯一的属性,而忽略了所有不唯一的属性。我们有一个不同的(与数据库ID不同的)“业务”ID,它是唯一的。这解决了问题吗?是的,我想是的,只要它保证是独一无二的。(很抱歉我之前的评论中有很多错误。我的借口是睡眠剥夺:)。可能会引起很多争议吗?这是乐观的。