问题描述
我读了一篇关于正确重定义 equals/hashCode 的文章: :
执行这些覆盖是为了不丢失已写入 Set 的记录。
代码:
@Entity
public class Client {
@Id
@Column
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String name;
public Client() {
}
public Client(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Client client = (Client) o;
return Objects.equals(id, client.id) &&
Objects.equals(name, client.name);
}
public int hashCode() {
return 31;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Client{");
sb.append("id=").append(id);
sb.append(", name='").append(name).append('\'');
sb.append('}');
return sb.toString();
}
}
然后我测试我的课程以确保它正常工作:
@Transactional
public class ClientTest {
@PersistenceContext
protected EntityManager em;
@Test
public void storeToSetBeforeMerge_ShouldBeContains() {
Set<Client> map = new HashSet<>();
Client client1 = new Client("John");
Client client2 = new Client("Mike");
map.add(client1);
map.add(client2);
Client merge1 = em.merge(client1);
Client merge2 = em.merge(client2);
assertTrue(map.contains(merge1)); // not true!
assertTrue(map.contains(merge2)); // not true!
}
}
我的问题是为什么不满足条件。 毕竟,我已经指出 hashCode 返回相同的值:31。我做错了什么?
我无法理解这个决定的意义。 如果这个解决方案没有解决问题,我无法从 Set 中找到我需要的元素
1楼
这是因为HashSet
不仅仅是比较hashCode
结果。
它的作用如下:
-
它比较
hashCode
的结果,如果结果不同,则返回 true。 -
如果
hashCode
结果相同,则使用equals
比较对象并返回结果。
这是因为性能 - 计算hashCode
更快,建议hashCode
不要经常产生冲突。
编辑
在您的equals
方法中,您使用id
进行比较,这是错误的,因为 id 是由数据库生成的:
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Client client = (Client) o;
return Objects.equals(id, client.id) && // <- comparison by id
Objects.equals(name, client.name);
}
在您的测试中,您正在创建没有 id 的对象并将它们放入HashSet
,然后您正在生成 id 并再次检查Collection
:
@Test
public void storeToSetBeforeMerge_ShouldBeContains() {
Set<Client> map = new HashSet<>();
Client client1 = new Client("John");
Client client2 = new Client("Mike");
map.add(client1); // <- those don't have an id
map.add(client2);
Client merge1 = em.merge(client1); // those do have an id
Client merge2 = em.merge(client2);
assertTrue(map.contains(merge1)); // whose with id are not in set
assertTrue(map.contains(merge2));
}
2楼
您没有像文章中那样在merge()
persist()
之前调用persist()
。
文章作者在第一条评论中对此进行了解释。
合并用于集成先前已持久化的分离实体上的更改。
新实体的生命周期从persist()
开始。
然后在具有 ID 的分离实体上调用merge()
,条件将被满足。