我希望将equals重写为基于业务键而不是对象标识,但我无法使其与hibernate一起使用。
我知道类似的问题已被提出并且阅读答案(如this)有助于理解不破坏等价关系,使用instanceof而不是getClass,使用getters而不是field refs。等等,但我的equals / hashCode实现仍然打破了地图的冬眠人口。
我没有收到错误,hibernate在数据库中正确地保存了地图中的所有3个实体,但是当我再次使用地图读取实体时,它只读取1而不是全部3。 hibernate记录的sql在mysql中执行时会选择所有3个实体。
该项目基于Spring 3.2.1,Hibernate 4.1.5和JPA注释
调试hibernate时,似乎为3个map实体中的每一个调用hashCode,但除了hibernate生成的id之外,所有值都为null。因此,重写方法计算的hashCode始终是相同的。我猜这就是为什么只有一个实体被添加到地图中的原因。但我不明白为什么hibernate在从数据库加载数据之前计算hashCode? p>
在下面的例子中,我在一个带有3个简单实体的项目中重现了错误(所有实体都有hibernate生成的id):
项目有一个LanguageDetails地图,其中语言是关键:
@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替换字段refs。)
@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());
我做错了什么?
答案 0 :(得分:1)
我花了将近一个星期的时间进行调试,编写解决方法并尝试理解这个问题,并且在我越来越绝望的尝试和错误尝试中找到了解决方案:
我将我的休眠版本从4.1.5更改为4.1.8,我所有的痛苦都消失了。这是hibernate中的一个错误,已在版本4.1.7中修复...
下次我肯定会检查错误报告......