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

发布于 2024-12-03 08:36:33 字数 566 浏览 8 评论 0原文

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

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

首先,我写了类似的内容

assertEquals(expResult, dbResult);

,但失败了,因为我无法真正知道 id 字段的值,该字段是由数据库生成的,因此 dbResultexpResult 不同 我用 new 创建并手动填充。

我看到两个选项:

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

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

我应该怎么办?

I hit a problem when writing tests for a database application using JPA2 and EclipseLink:

I add some entity to a database, retrieve it later and want to compare it to an instance which has the values I expect to confirm that the addition worked as I intended.

First I wrote something like

assertEquals(expResult, dbResult);

which failed, because I can't really know the value of id field, which is generated by the database and therefore dbResult differs from expResult which I created with new and populated manually.

I see two options:

  • Either I remove the id field from equals and hashCode so that the comparison is only based on the "real values". I don't know if this causes problems in the database or elsewhere, though.

  • Or I write my tests to explicitly check every field except id manually.

What should I do?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

雄赳赳气昂昂 2024-12-10 08:36:33

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

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

You might find a lot of controversy about this one. My stance is that you absolutely don't use a database primary key for anything in your application. It should be completely invisible. Identify your objects in your application by some other property or combination of properties.

On the "testing persistence operations" front, what you really want is probably to check that the fields were saved and loaded correctly and maybe that the primary key got assigned some value when you saved it. This probably isn't a job for the equals method at all.

流年已逝 2024-12-10 08:36:33

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

equalshashCode 实现中使用业务键而不是数据库生成的值的根本原因是,JPA 提供程序必须在持久化之后实际发出 SELECT数据库中的实体。如果您使用数据库生成的 Id 来比较对象,那么您最终会遇到在以下情况下失败的相等测试:

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

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

Relying on database generated Ids in your equals and hashCode implementation is not advisable. You ought to rely on the truly unique/semi-unique attributes of your classes in checking for equality, and in generating the hashcode values. The Hibernate documentation has an extensive page that discusses this, and the facts therein are applicable to more or less every JPA provider.

The underlying reason for using business keys over database generated values in your equals and hashCode implementation is that the JPA provider must actually issue a SELECT after persisting the entity in the database. If you compare objects using the database generated Ids, then you will end up having an equality test that fails in the following scenarios:

  • If E1 and E2 are entities of class E (that verifies equality using database generated Ids), then if E1 and E2 will be equal if they haven't been stored in the database yet. This is not what you want, especially if want to store E1 and E2 in some Set before persistence. This is worse if the attributes of E1 and E2 possess different values; the equals implementation would prevent two significantly different entities from being added to a Set, and the hashCode implementation will give you a O(n) lookup time when entities are looked up from a HashMap using the primary key.
  • If E1 is a managed entity that has been persisted, and E2 is an entity that has not been persisted, then the equality test would deem that E1 != E2 in the scenario where all the attribute values of E1 and E2 (except for the Ids) are similar. Again, this is probably not what you want, especially if you want to avoid duplicate entities in the database that differ only in their database generated Ids.

The equals and hashCode implementations therefore ought to use business keys, in order to exhibit consistent behavior for both persisted and unpersisted entities.

甚是思念 2024-12-10 08:36:33

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

From the book Hibernate in Action, its recommended to defined a business key and test equality on that. A business key is "a property, or some combination of properties, that is unique for each instance with the same database identity." In other areas it says to not use the id as one of those properties, and don't use values in collections.

奈何桥上唱咆哮 2024-12-10 08:36:33

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

从 equals 中删除 ID 是没有道理的,只是因为测试有点困难。您放弃了严重的性能优势和代码完整性。

I would write my test to explicitly check for fields. To make this easy, before performing the assertEqual test, I will set the id of both the expected and actual result to the same predefined value and then use the normal equals method.

Removing ID from equals is not justifiable, just because testing is slightly difficult. You are foregoing serious performance benefits and also code integrity.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文