我想覆盖等于基于业务密钥而不是对象身份,但我不能让它与休眠一起工作。
我知道有人问过类似的问题,阅读答案(像这样)有助于理解不破坏等价关系,使用 instanceof 代替 getClass,使用 getter 代替字段引用。等等,但我的 equals/hashCode 实现仍然会破坏地图的休眠人口。
我没有收到错误,休眠将所有 3 个实体正确保存在数据库中的地图中,但是当我再次使用地图读取实体时,它只读取 1 而不是全部 3。hibernate 记录的 sql 在 mysql 中执行时选择正确的所有 3 个实体。
该项目基于 Spring 3.2.1、Hibernate 4.1.5 和 JPA 注释
更新
在调试休眠时,它似乎为 3 个映射实体中的每一个调用 hashCode,但除了休眠生成的 id 之外,所有值都是空的。因此,由覆盖方法计算的 hashCode 始终相同。我想这就是为什么只有一个实体被添加到地图中的原因。但我不明白为什么hibernate在从数据库加载数据之前计算hashCode?
例子
在下面的示例中,我在具有 3 个简单实体的项目中重现了错误(所有实体都具有休眠生成的 id):
- 物品
- 语言
- 语言详情
Item 有一个 LanguageDetails 的 Map,其中 Language 是关键:
@OneToMany(mappedBy="item", orphanRemoval=true, cascade=CascadeType.ALL, fetch=FetchType.EAGER)
@MapKey(name="language")
private Map<Language, LanguageDetails> languageDetails = new TreeMap<Language, LanguageDetails>();
Language 中的业务键是一个名为code的字符串。我对覆盖equals的最佳猜测是使用eclipse生成equals/hashCode(并用instanceof替换getClass,用getter替换字段引用):
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((getCode() == null) ? 0 : getCode().hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof Language))
return false;
Language other = (Language) obj;
if (getCode() == null) {
if (other.getCode() != null)
return false;
} else if (!getCode().equals(other.getCode()))
return false;
return true;
}
以下集成测试在上面的 equals/hashCode 下失败,在没有以下情况下成功:
Item item = new Item();
Language english = languageService.getByCode("en");
Language german = languageService.getByCode("de");
Language spanish = languageService.getByCode("es");
item.getLanguageDetails().put(english, new LanguageDetails(item, english, "ice cream"););
item.getLanguageDetails().put(spanish, new LanguageDetails(item, spanish, "helado"););
item.getLanguageDetails().put(german, new LanguageDetails(item, german, "eis"););
//save - stores all 3 languageDetails correct in mysql
int id = itemService.create(item);
//read - loads only 1 languageDetails (de)
Item readItem = itemService.getById(id);
//java.lang.AssertionError: expected:<3> but was:<1>
Assert.assertEquals(item.getLanguageDetails().size(), readItem.getLanguageDetails().size());
我究竟做错了什么?