如何在Hibernate映射中覆盖键的等于?

时间:2013-02-01 14:18:33

标签: hibernate orm map override equals

我希望将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?

实施例

在下面的例子中,我在一个带有3个简单实体的项目中重现了错误(所有实体都有hibernate生成的id):

  • 项目
  • 语言
  • LanguageDetails

项目有一个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());

我做错了什么?

1 个答案:

答案 0 :(得分:1)

我花了将近一个星期的时间进行调试,编写解决方法并尝试理解这个问题,并且在我越来越绝望的尝试和错误尝试中找到了解决方案:

我将我的休眠版本从4.1.5更改为4.1.8,我所有的痛苦都消失了。这是hibernate中的一个错误,已在版本4.1.7中修复...

下次我肯定会检查错误报告......