为什么 Hibernate 要求我们在有私有 id 字段的情况下实现 equals/hashcode 方法?

Why Hibernate requires us to implement equals/hashcode methods when I have a private id field?

首先,考虑片段,

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));

整个问题从这里开始 在实时应用程序中,这必须 return 为真。

因此,每个人都知道存在解决方案(实现 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);

我正在浏览这篇优秀的文章 Don't Let Hibernate Steal Your Identity。但是有一件事我没有完全理解。上面讨论的整个问题及其解决方案以及链接的文章都在处理 2 个 Employee 对象 ID 相同时的问题。

考虑当我们有一个 private setter for id field with 生成器class生成的id字段在hbm.xml。一旦我开始保留 Employee 对象( 并且我无法更改 id),我发现没有必要实现 equals 和 hashcode 方法。我确定我遗漏了一些东西,因为我的直觉告诉我,当一个特定的概念在网络上轮换太多时,为了避免一些常见错误,它一定一直摆在你面前?当我的 id 字段有一个私有 setter 时,我还需要实现这两种方法吗?

如果实体定义了自然业务键,那么您应该将其用于 equals and hashCode。自然标识符或业务键在所有实体状态转换中都是一致的,因此 hashCode 不会在 JPA 实体状态更改时更改(例如,从新建到托管再到分离)。

在您的示例中,您使用的是 assigned 标识符,当您持久化您的实体时它不会改变。

然而,如果你没有一个自然标识符,而你有一个生成的 PRIMARY KEY(例如,IDENTITYSEQUENCE),那么你可以像这样实现 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 getClass().hashCode();
    }
 
    //Getters and setters omitted for brevity
}

实体标识符可用于equals和hashCode,但前提是hashCodereturns的值始终相同。这听起来像是一件很糟糕的事情,因为它违背了在 HashSetHashMap.

中使用多个桶的目的。

但是,出于性能原因,您应该始终限制存储在集合中的实体数量。您永远不应该在 @OneToMany Set 中获取数千个实体,因为数据库端的性能损失比使用单个散列桶高出多个数量级。

这个版本的equalshashCode能工作的原因是hashCode value does not change from one entity state to another,只有当它不是null时才检查标识符。