简体   繁体   English

我应该在 JPA 实体中编写 equals() 和 hashCode() 方法吗?

[英]Should I write equals() and hashCode() methods in JPA entities?

我想检查实体是否在另一个实体的集合成员( @OneToMany@ManyToMany )中:

if (entity2.getEntities1().contains(entity1)) { }

Not necessarily.不必要。 There are three options:共有三个选项:

  • don't override - thus you will be working with instances.不要覆盖 - 因此您将使用实例。 This is fine in cases when you are working with the collections with only entities that are attached to the session (and hence guaranteed to be the same instance).如果您使用的集合只有附加到会话的实体(因此保证是相同的实例),这很好。 This is (for me) the preferred way in many cases, because it requires less code and less consideration when overriding这是(对我而言)在许多情况下的首选方式,因为它在覆盖时需要更少的代码和更少的考虑

  • override hashCode() and equals() with a business key.使用业务键覆盖hashCode()equals() That may be a subset of properties that identify the entity.这可能是标识实体的属性的子集。 For example, for a User a good business key might be the username or the email .例如,对于User一个好的业务密钥可能是usernameemail This is considered good practice.这被认为是很好的做法。

  • override hashCode() and equals() using the ID field only.仅使用 ID 字段覆盖hashCode()equals() This is fine in some cases, especially if you have a manually-assigned identifier (like an UUID).这在某些情况下很好,特别是如果您有手动分配的标识符(如 UUID)。 It is also fine if your entity will never go into a collection.如果您的实体永远不会进入集合,那也没关系。 But for transient entities (with no identifier) that go into collections, it causes problems, so careful with this option.但是对于进入集合的瞬态实体(没有标识符),它会导致问题,所以要小心使用这个选项。 As seanizer noted - you should avoid it.正如 seanizer 指出的那样 - 你应该避免它。 Generally, always, unless you are really aware of what you are doing (and perhaps documenting it)一般来说,总是,除非你真的知道你在做什么(并且可能记录它)

See this article for more details.有关更多详细信息,请参阅此文章 Also note that equals() and hashCode() are tied and should be implemented both with exactly the same fields.另请注意, equals()hashCode()是绑定的,应该使用完全相同的字段来实现。

Yes, you should!是的你应该!

If you don't override the default Java.lang.Object equals and hashCode implementation:如果您不覆盖默认的Java.lang.Object equalshashCode实现:

@Entity(name = "Book")
public class Book implements Identifiable<Long> {
 
    @Id
    @GeneratedValue
    private Long id;
 
    private String title;
 
    //Getters and setters omitted for brevity
}

the merge operation will return a different object instance and the equality contract is going to be broken. merge操作将返回一个不同的对象实例,平等契约将被打破。

The best way is to use a business key, like this:最好的方法是使用业务密钥,如下所示:

@Entity
public class Book implements Identifiable<Long> {
 
    @Id
    @GeneratedValue
    private Long id;
 
    private String title;
 
    @NaturalId
    private String isbn;
 
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Book)) return false;
        Book book = (Book) o;
        return Objects.equals(getIsbn(), book.getIsbn());
    }
 
    @Override
    public int hashCode() {
        return Objects.hash(getIsbn());
    }
 
    //Getters and setters omitted for brevity
}

You can also use the identifier for equality, but mind that the hashCode implementation should always return the same value, which for entities is not really a problem since you don't fetch many entities per DB transaction, as otherwise, the cost of fetching data is orders of magnitude larger than the single-bucket HashMap penalty imposed by using a fixed hashCode :您也可以使用标识符来表示相等,但请注意hashCode实现应始终返回相同的值,这对于实体来说并不是真正的问题,因为您不会为每个 DB 事务获取许多实体,否则,获取数据的成本比使用固定hashCode施加的单桶HashMap惩罚大hashCode

@Entity
public class Book implements Identifiable<Long> {
 
    @Id
    @GeneratedValue
    private Long id;
 
    private String title;
 
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Book)) return false;
        Book book = (Book) o;
        return Objects.equals(getId(), book.getId());
    }
 
    @Override
    public int hashCode() {
        return getClass().hashCode();
    }
 
    //Getters and setters omitted for brevity
}

Yes, you should define corresponding equals() and hashcode() methods, but you should NEVER let the id be part of either.是的,你应该定义相应的equals()hashcode()方法,但你不应该让 id 成为其中的一部分。 (See this recent answer of mine in a similar question) (请参阅我最近在类似问题中的回答)

We tend to let IDE generate hashCode() and equals() for us.我们倾向于让 IDE 为我们生成hashCode()equals() Be careful though.不过要小心。 When you generate those methods for JPA Entities.当您为 JPA 实体生成这些方法时。 Some versions of equals() checks for class identity某些版本的equals()检查类标识

// ... inside equals() - wrong approach for Entities (cause of generate proxies)
if (o == null || this.getClass() != o.getClass()) {
        return false;
}
// ...

This would break your collections with some JPA libraries as those libraries create proxies to your entities (subclasses), like for example MyGreatEntity_$$_javassist_7 in Hibernate.这会破坏一些 JPA 库的集合,因为这些库会创建实体(子类)的代理,例如 Hibernate 中的MyGreatEntity_$$_javassist_7

In Entities always allow subclasses in equals() .在实体中总是允许子类在equals()

That's the only way.这是唯一的办法。 You may want to try Pojomatic library which does the hard job for you.您可能想尝试为您完成艰苦工作的Pojomatic库。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM