java - 当我有私有(private) id 字段时,为什么 Hibernate 要求我们实现 equals/hashcode 方法?

标签 java hibernate jpa equals hashcode

首先,考虑代码片段,

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

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

考虑一下,当我们为 id 字段提供一个私有(private) setter 时,其 id 字段由生成器类<生成/em>hbm.xml 中提供。一旦我开始保留 Employee 对象(并且我无法更改 id),我发现不需要实现 equals 和 hashcode 方法。我确信我错过了一些东西,因为我的直觉告诉我,当一个特定的概念在网络上旋转太多时,它一定总是放在你面前,以避免一些常见的错误?当我有 id 字段的私有(private) setter 时,我是否仍然需要实现这两种方法?

最佳答案

如果实体定义了自然业务 key ,那么您应该将其用于 equalshashCode 。自然标识符或业务 key 在所有实体状态转换中都是一致的,因此当 JPA 实体状态发生变化(例如从 New 到 Managed 到 Detached)时,hashCode 不会改变。

在您的示例中,您使用的是分配的标识符,当您保留实体时,该标识符不会更改。

但是,如果您没有自然标识符,但有生成的 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,但前提是hashCode 始终返回相同的值。这听起来可能是一件可怕的事情,因为它违背了在 HashSetHashMap 中使用多个存储桶的目的。

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

此版本的 equalshashCode 之所以有效,是因为 hashCode value does not change from one entity state to another ,并且仅当标识符不为 null 时才会检查该标识符。

关于java - 当我有私有(private) id 字段时,为什么 Hibernate 要求我们实现 equals/hashcode 方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28268328/

相关文章:

java - Java/Derby 世界有类似 Django Graphviz 的东西吗?

java - 找不到注释处理器 'org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor'

hibernate - Hibernate 从哪个版本开始支持 JPA 2.2?

java - 如何在创建时填充嵌入式数据库(例如 hsqldb)

java - 如何防止抽象方法被调用?

java - 如何在 java 中找到匿名类的 GC 根?

Spring Data Jpa无法创建存储库bean

java - Spring JPA 优化传递@ManyToOne 获取

java - 将字符串拆分为整数数组

java - OneToMany 外键为空