当我有一个私有id字段时,为什么Hibernate要求我们实现equals / hashcode方法?

雪吉尔·法汉(Shirgill Farhan)

首先,考虑一下代码段

public class Employee
{
    private Integer id;
    private String firstname;
    private String lastName;
    private String department;
 // public getters and setters here, i said PUBLIC
}

我创建2个具有相同ID的对象,其余所有字段也相同。

Employee e1 = new Employee();
Employee e2 = new Employee();

e1.setId(100);
e2.setId(100);

//Prints false in console
System.out.println(e1.equals(e2));

整个问题从此处开始。在实时应用程序中,此问题必须返回true。

因此,每个人都知道存在解决方案(实现equals()和hashcode())

public boolean equals(Object o) {
    if(o == null)
    {
        return false;
    }
    if (o == this)
    {
        return true;
    }
    if (getClass() != o.getClass())
    {
        return false;
    }

    Employee e = (Employee) o;
    return (this.getId() == e.getId());

}
@Override
public int hashCode()
{
    final int PRIME = 31;
    int result = 1;
    result = PRIME * result + getId();
    return result;
}

现在,像往常一样:

        Employee e1 = new Employee();
        Employee e2 = new Employee();

        e1.setId(100);
        e2.setId(100);

        //Prints 'true' now
        System.out.println(e1.equals(e2));

        Set<Employee> employees = new HashSet<Employee>();
        employees.add(e1);
        employees.add(e2);

        //Prints ofcourse one objects(which was a requirement)
        System.out.println(employees);

我正在阅读这篇出色的文章,“不要让休眠模式窃取您的身份”但是有一件事我没有完全理解。当2个Employee对象ID相同时,上面讨论的整个问题及其解决方案以及链接的文章都在解决这些问题。

考虑一下我们是否有一个专用id设置器,其id字段由hbm.xml中提供generator类生成一旦我开始保留Employee对象(并且绝不能更改id),我便无需实现equals和hashcode方法。我确信我会丢失一些东西,因为我的直觉说,当某个特定概念在网络上旋转过多时,为了避免某些常见错误,它一定总是摆在您面前吗?当我有一个ID字段的专用设置器时,是否仍需要实现这两种方法?

弗拉德·米哈尔西娅(Vlad Mihalcea)

如果您的实体定义了自然业务密钥,则应将其用于equals和hashCode,如本文所述自然标识符或业务密钥在所有实体状态转换之间都是一致的,因此,当JPA实体状态更改(例如,从NEW到MANAGED到DETACHED)时,hashCode不会更改。

在您的示例中,您使用的是assigned标识符,当您保留实体时标识符不会改变。

但是,如果您没有自然标识符,并且生成了PRIMARY KEY(例如IDENTITY,SEQUENCE),则可以实现equals和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 other = (Book) o;

        return id != null &&
               id.equals(other.getId());
    }

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

    //Getters and setters omitted for brevity
}

实体标识符可用于equals和hashCode,但前提是hashCode返回值始终都返回相同的值。这听起来很可怕,因为它违反了在HashSet中使用多个存储桶的目的HashMap

但是,出于性能原因,您应始终限制集合中存储的实体数量。您绝对不应在中获取数千个实体,@OneToMany Set因为与使用单个哈希存储桶相比,数据库方面的性能损失要高多个数量级。

之所以使用此版本的equals和,hashCode是因为该hashCode值不会从一个实体状态更改为另一种实体状态,并且仅当标识符不是时才检查标识符null

有关此主题的更多详细信息,请参阅本文

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

为什么需要覆盖Java中的equals和hashCode方法?

集合的hashCode方法的最佳实现

Java中的hashcode方法的目的是什么?

hashcode()和equals()方法

为什么我们有==运算符时使用equals()方法?

如何实现hashCode和equals方法

为什么hashCode()和getClass()本机方法?

当我覆盖equals()方法时,为什么要覆盖hashCode()?

实现hashCode()的首选方法是什么?

Groovy:生成equals和hashCode方法

为什么我们不能用私有扩展类方法覆盖基类方法?

Groovy特质要求我在另一个接口上实现所有超类的方法

为什么对于equals方法我们需要一个Object参数?

实现hashCode和equals方法,但类是最终的

如何为包含双重字段的类实现equals / hashCode方法

为什么我要在另一个实例上调用私有方法?

当我们拥有currentThread()方法时,为什么Thread类具有静态方法?

如果我的类在Java中实现了可比性,是否需要一个equals和Hashcode方法?

为什么UserRole实现Serializable并覆盖equals()和hashcode()方法

为什么我们不能在方法内部声明私有本地内部类?

为什么要覆盖hashCode方法?

当我们在方法类中拥有私有属性时,为什么要使用final作为修饰符

当我们覆盖一个方法时,我们应该使用方法签名中的所有参数吗?

当我们已经具有MVC提供的默认功能时,为什么我们需要Ajax / jQuery调用colntroller方法?

如果我们可以使用访问器访问私有数据成员,那么为什么我们不能访问私有方法呢?

Java - “拦截”一个私有方法

HashMap 忽略覆盖的 hashCode 和 equals 方法

当我们在一个接口中添加两个抽象方法并只实现一个方法时,为什么我们不能使用 lambda 实现另一个方法呢?

当我们只有一个通用特征时,Rust 处理常量数学的方法是什么