为什么 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
(例如,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 getClass().hashCode();
}
//Getters and setters omitted for brevity
}
实体标识符可用于equals
和hashCode,但前提是hashCode
returns的值始终相同。这听起来像是一件很糟糕的事情,因为它违背了在 HashSet
或 HashMap
.
中使用多个桶的目的。
但是,出于性能原因,您应该始终限制存储在集合中的实体数量。您永远不应该在 @OneToMany
Set
中获取数千个实体,因为数据库端的性能损失比使用单个散列桶高出多个数量级。
这个版本的equals
和hashCode
能工作的原因是hashCode
value does not change from one entity state to another,只有当它不是null
时才检查标识符。
首先,考虑片段,
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
(例如,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 getClass().hashCode();
}
//Getters and setters omitted for brevity
}
实体标识符可用于equals
和hashCode,但前提是hashCode
returns的值始终相同。这听起来像是一件很糟糕的事情,因为它违背了在 HashSet
或 HashMap
.
但是,出于性能原因,您应该始终限制存储在集合中的实体数量。您永远不应该在 @OneToMany
Set
中获取数千个实体,因为数据库端的性能损失比使用单个散列桶高出多个数量级。
这个版本的equals
和hashCode
能工作的原因是hashCode
value does not change from one entity state to another,只有当它不是null
时才检查标识符。