Hibernate:什么时候需要实现equals()和hashCode(),如果有,如何实现? [英] Hibernate: When is it necessary to implement equals() and hashCode(), and if so, how?

查看:162
本文介绍了Hibernate:什么时候需要实现equals()和hashCode(),如果有,如何实现?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

基于各种不好的经历作为Java程序员,我的经验法则是只实现 equals() hashCode()关于不可变对象,其中对象的两个实例是可以互换的。



基本上我想避免像这个链接中的 HashMap 键问题,或者像下面这样:


  1. 获取具有特定身份的东西。

  2. 修改它。

  3. 将其添加到集合中。

  4. (稍后)获取具有相同身份的另一件物品。

  5. 修改它。
  6. 没有注意到这个添加实际上没有发生,因为该集合认为该东西已经存在。

  7. 做一些事情
  8. 没有注意到步骤(5)中的更改被忽略,并且我们仍然具有步骤(2)中的状态。

以及lar ge在我的Java职业生涯中我不会(1)值对象和(2)将东西放入集合中,除之外, 找到了很多使用方法。我还发现不变性+复制和修改构造函数/构建器通常比setter更快乐。两个对象可能具有相同的ID并可能表示相同的逻辑实体,但是如果它们具有不同的数据 - 如果它们表示概念实体在不同时间的快照 - 则它们不是 equal() 。



无论如何,我现在在Hibernate商店,而且我的更多Hibernate精明的同事告诉我这种方法不会上班。具体来说,这种说法似乎是在下面的场景中 - H b从数据库中加载一个东西 - 我们称它为:


  1. 实例 h1

  2. 这个东西被封送并通过一个web服务发送到某个地方。
  3. Web服务客户端摆弄它并发送修改后的版本。
  4. 修改后的版本在服务器上解组 - 我们称之为实例 h4
  5. code>。
  6. 我们希望Hibernate通过修改来更新数据库。

- 除非 h1.equals(h4)(或者 h4.equals(h1),我不是清楚,但我希望它是传递,所以无论如何),Hibernate将无法辨别出这些是相同的东西,并且会发生不好的事情。



所以,我想知道:


  • 这是真的吗?

  • 如果是这样,为什么?如果Hibernate使用 equals() h4 应该是相同的,它是如何(以及我们如何)跟踪哪一个是修改版本?






注意:我读过在Hibernate文档中实现equals()和hashCode()它并没有处理我担心的情况,至少是直接的,也没有详细解释Hibernate真的需要从 equals() hashCode()方法等于和Hibernate中的哈希码的答案也没有,或者我不会感到困扰发布这个。

首先,你的原始想法,你应该只实现equals()和hashCode()不可变的对象当然有用,但它比需要的更严格。你只需要这两种方法来依赖不可变的字段。任何值可能改变的字段都不适合在这两种方法中使用,但其他字段不必是不可变的。

话虽如此,Hibernate通过比较它们的主键知道它们是同一个对象。这导致很多人写这两种方法来依赖主键。 Hibernate文档建议你不要这样做,但很多人没有太多麻烦就忽略了这个建议。这意味着你不能将实体添加到Set中,直到它们被保存之后,这是一个不太难以忍受的限制。

Hibernate文档建议使用商业密钥。但商业密钥应该依赖唯一标识对象的字段。 Hibernate文档说使用一个业务密钥,它是独特的,通常是不可变的属性的组合。我使用在数据库中对它们有唯一约束的字段。因此,如果您的Sql CREATE TABLE语句将约束指定为

  CONSTRAINT uc_order_num_item UNIQUE(order_num,order_item)

code>

那么这两个字段就可以成为您的商业密钥。这样,如果您更改其中的一个,Hibernate和Java都会将修改的对象视为不同的对象。当然,如果你确实改变了这些不可变字段中的一个,你会搞乱它们所属的任何Set。所以我想你需要清楚地记录哪些字段构成了业务关键字,并且应该明白,对于持久对象永远不应该更改业务关键字中的字段。我可以看到为什么人们忽略了建议,只是使用主键。但是您可以像这样定义主键:

  CONSTRAINT pk_order_num_item PRIMARY KEY(order_num,order_item)

而且你仍然会遇到同样的问题。



就我个人而言,喜欢查看指定商业密钥中每个字段的注释,并且拥有一个IDE检查,用于检查是否为持久对象修改它。也许这就是要求太高了。另一种解决所有这些问题的方法是使用UUID作为主键,当你在客户端生成主键时首先构造一个未被实体的实体。由于您从不需要向用户显示,因此您的代码一旦设置就不可能改变其值。这可以让你编写总是有效的hashCode()和equals()方法,并保持一致。

还有一件事:如果你想避免将一个对象添加到一个已经包含它的不同(修改)版本的Set中,唯一的方法是在添加它之前总是询问该集合是否已经存在。然后你可以编写代码来处理这个特殊情况。


Based on various bad experiences my rule of thumb as a Java programmer is to only implement equals() and hashCode() on immutable objects, where two instances of the object really are interchangeable.

Basically I want to avoid situations like the HashMap key problem in that link, or like the following:

  1. Get a thing with a certain identity.
  2. Modify it.
  3. Add it to a set.
  4. (later) Get another thing with the same identity.
  5. Modify it.
  6. Add it to the same set.
  7. Fail to notice that this add doesn't actually happen, since the set thinks the thing is already there.
  8. Do something with the things in the set.
  9. Fail to notice that the change from step (5) is ignored, and we still have the state from step (2).

And by and large over the course of my Java career I haven't found a lot of use for equals() except for (1) value objects and (2) putting things into collections. I've also found that immutability + copy-and-modify constructors/builders is generally a much happier world than setters. Two objects might have the same ID and might represent the same logical entity, but if they have different data -- if they represent snapshots of the conceptual entity at different times -- then they're not equal().

Anyway, I'm now in a Hibernate shop, and my more Hibernate-savvy colleagues are telling me this approach isn't going to work. Specifically, the claim seems to be that in the following scenario --

  1. Hibernate loads a thing from the database -- we'll call it instance h1.
  2. This thing is marshaled and sent somewhere via a web service.
  3. The web service client fiddles with it and sends a modified version back.
  4. The modified version is unmarshalled on the server -- we'll call it instance h4.
  5. We want Hibernate to update the database with the modifications.

-- unless h1.equals(h4) (or perhaps h4.equals(h1), I'm not clear, but I would hope it's transitive anyway so whatever), Hibernate will not be able to tell that these are the same thing, and Bad Things Will Happen.

So, what I want to know:

  • Is this true?
  • If so, why? What is Hibernate using equals() for?
  • If Hibernate needs h1 and h4 to be equal, how does it (and how do we) keep track of which one is the modified version?

Note: I've read Implementing equals() and hashCode() in the Hibernate docs and it doesn't deal with the situation I'm worried about, at least directly, nor does it explain in any detail what Hibernate really needs out of equals() and hashCode(). Neither does the answer to equals and hashcode in Hibernate, or I wouldn't have bothered to post this.

解决方案

First of all, your original idea, that you should implement equals() and hashCode() only on immutable objects, certainly works, but it's stricter than it needs to be. You just need these two methods to rely on immutable fields. Any field whose value may change is unsuitable for use in those two methods, but the other fields need not be immutable.

Having said that, Hibernate knows they're the same object by comparing their primary keys. This leads many people to write those two methods to rely on the primary key. Hibernate docs recommend you don't do it this way, but many people ignore this advice without much trouble. It means you can't add entities to a Set until after they've been persisted, which is a restriction that's not too hard to live with.

Hibernate docs recommend using a business key. But the business key should rely on fields that uniquely identify an object. The Hibernate docs say "use a business key that is a combination of unique, typically immutable, attributes." I use fields that have a unique constraint on them in the database. So, if your Sql CREATE TABLE statement specifies a constraint as

CONSTRAINT uc_order_num_item UNIQUE (order_num, order_item)

then those two fields can be your business key. That way, if you change one of them, both Hibernate and Java will treat the modified object as a different object. Of course, if you do change one of these "immutable" fields, you mess up any Set they belong to. So I guess you need to document clearly which fields comprise the business key, and write your application with the understanding that fields in the business key should never be changed for persisted objects. I can see why people ignore the advice and just use the primary key. But you could define the primary key like this:

CONSTRAINT pk_order_num_item PRIMARY KEY (order_num, order_item)

And you would still have the same problem.

Personally, I would like to see an annotation that specifies every field in the business key, and have an IDE inspection that checks if I modify it for persisted objects. Maybe that's asking too much.

Another approach, one that solves all of these problems, is to use a UUID for the primary key, which you generate on the client when you first construct an unpersisted entity. Since you never need to show it to the user, your code is not likely to change its value once you set it. This lets you write hashCode() and equals() methods that always work, and remain consistent with each other.

One more thing: If you want to avoid the problem of adding an object to a Set that already contains a different (modified) version of it, the only way is to always ask the set if it's already there before adding it. Then you can write code to handle that special case.

这篇关于Hibernate:什么时候需要实现equals()和hashCode(),如果有,如何实现?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆