JPA Entity Equality – JPA实体平等

最后修改: 2020年 8月 13日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll take a look at handling equality with JPA Entity objects.

在本教程中,我们将看看如何用JPA实体对象处理平等问题。

2. Considerations

2.考虑因素

In general, equality simply means that two objects are the same. However, in Java, we can change the definition of equality by overriding the Object.equals() and the Object.hashCode() methods. Ultimately, Java allows us to define what it means to be equal. But first, there are a few things we need to consider.

一般来说,平等只是意味着两个对象是相同的。然而,在Java中,我们可以通过覆盖Object.equals()Object.hashCode()方法改变平等的定义。最终,Java允许我们定义平等的含义。但首先,我们需要考虑一些事情。

2.1. Collections

2.1.收藏品

Java collections group objects together. The grouping logic uses a special value known as a hash code to determine the group for an object.

Java集合将对象分组。分组逻辑使用一个被称为哈希码的特殊值来确定一个对象的分组。

If the value returned by the hashCode() method is the same for all entities, this could result in undesired behavior. Let’s say our entity object has a primary key defined as id, but we define our hashCode() method as:

如果由hashCode()方法返回的值对所有实体都是一样的,这可能会导致不希望的行为。假设我们的实体对象有一个定义为id的主键,但我们将hashCode()方法定义为。

@Override
public int hashCode() {
    return 12345;
}

Collections will not be able to distinguish between different objects when comparing them because they will all share the same hash code. Luckily, resolving this is as easy as using a unique key when generating a hash code. For example, we can define the hashCode() method using our id:

在比较不同的对象时,集合将无法区分它们,因为它们都会共享相同的哈希代码。幸运的是,解决这个问题就像在生成哈希码时使用一个唯一的键一样简单。例如,我们可以使用我们的id来定义hashCode()方法。

@Override
public int hashCode() {
    return id * 12345;
}

In this case, we used the id of our entity to define the hash code. Now, collections can compare, sort, and store our entities.

在这种情况下,我们使用我们实体的id来定义哈希代码。现在,集合可以比较、排序和存储我们的实体。

2.2. Transient Entities

2.2.暂时性的实体

Newly created JPA entity objects that have no association with a persistence context are considered to be in the transient state. These objects usually do not have their @Id members populated. Therefore, if equals() or hashCode() use the id in their calculations, this means all transient objects will be equal because their ids will all be null. There are not many cases where this is desirable.

新创建的JPA实体对象如果与持久化上下文没有关联,则被视为处于暂存状态。这些对象通常没有填充其@Id成员。因此,如果equals()hashCode()在其计算中使用id,这意味着所有暂存对象将是相等的,因为它们的ids都将是null。这样做的情况不多。

2.3. Subclasses

2.3.子类

Subclasses are also a concern when defining equality. It’s common to compare classes in the equals() method. Therefore, including the getClass() method will help to filter out subclasses when comparing objects for equality.

在定义平等性时,子类也是一个问题。在equals()方法中比较类很常见。因此,包括getClass()方法将有助于在比较对象的平等性时过滤掉子类。

Let’s define an equals() method that will only work if the objects are of the same class and have the same id:

让我们定义一个equals()方法,该方法只有在对象属于同一类别并且具有相同的id时才会起作用。

@Override
public boolean equals(Object o) {
    if (o == null || this.getClass() != o.getClass()) {
        return false;
    }
    return o.id.equals(this.id);
}

3. Defining Equality

3.平等的定义

Given these considerations, we have a few choices when handling equality. Accordingly, the approach we take depends on the specifics of how we plan to use our objects. Let’s look at our options.

鉴于这些考虑,我们在处理平等问题时有几个选择。因此,我们采取的方法取决于我们计划如何使用我们的对象的具体细节。让我们看一下我们的选择。

3.1. No Overrides

3.1 没有重写

By default, Java provides the equals() and hashCode() methods by virtue of all objects descending from the Object class. Therefore, the easiest thing we can do is nothing. Unfortunately, this means that when comparing objects, in order to be considered equal, they have to be the same instances and not two separate instances representing the same object.

默认情况下,Java提供了equals()hashCode()方法,因为所有的对象都是从Object类下来的。因此,我们能做的最简单的事情就是什么都不做。不幸的是,这意味着在比较对象时,为了被认为是相等的,它们必须是相同的实例,而不是代表同一对象的两个独立实例。

3.2. Using a Database Key

3.2.使用数据库密钥

In most cases, we’re dealing with JPA entities that are stored in a database. Normally, these entities have a primary key that is a unique value. Therefore, any instances of this entity that have the same primary key value are equal. So, we can override equals() as we did above for subclasses and also override hashCode() using only the primary key in both.

在大多数情况下,我们要处理的是存储在数据库中的JPA实体。通常情况下,这些实体有一个主键,它是一个唯一的值。因此,任何具有相同主键值的实体实例都是相等的。所以,我们可以像上面为子类重写equals()那样,也可以在两者中只使用主键重写hashCode()

3.3. Using a Business Key

3.3.使用业务钥匙

Alternatively, we can use a business key to compare JPA entities. In this case, the object’s key is comprised of members of the entity other than the primary key. This key should make the JPA entity unique. Using a business key gives us the same desired outcome when comparing entities without the need for primary or database-generated keys.

另外,我们可以使用一个业务键来比较JPA实体。在这种情况下,对象的键是由实体中除主键以外的成员组成的。这个键应该使JPA实体独一无二。在比较实体时,使用业务键给了我们同样的预期结果,而不需要主键或数据库生成的键。

Let’s say we know that an email address is always going to be unique, even if it isn’t the @Id field. We can include the email field in hashCode() and equals() methods:

比方说,我们知道电子邮件地址总是唯一的,即使它不是@Id字段。我们可以在hashCode()equals()方法中包含电子邮件字段。

public class EqualByBusinessKey {

    private String email;

    @Override
    public int hashCode() {
        return java.util.Objects.hashCode(email);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (obj instanceof EqualByBusinessKey) {
            if (((EqualByBusinessKey) obj).getEmail().equals(getEmail())) {
                return true;
            }
        }

        return false;
    }
}

4. Conclusion

4.总结

In this tutorial, we discussed various ways that we can handle equality when writing JPA entity objects. We also described the considerations we should take when choosing an approach. As always, the full source code can be found over on GitHub.

在本教程中,我们讨论了在编写JPA实体对象时可以处理平等问题的各种方法。我们还描述了我们在选择方法时应该考虑的问题。一如既往,完整的源代码可以在GitHub上找到over