オブジェクト ID ではなくビジネス キーに基づいて equals をオーバーライドしたいのですが、休止状態で機能させることができません。
同様の質問が寄せられていることを知っており、(このような) 回答を読むと、等価関係を壊さないこと、getClass の代わりに instanceof を使用すること、フィールド ref の代わりにゲッターを使用することを理解するのに役立ちます。などですが、私の equals/hashCode の実装はまだマップの休止状態の人口を壊しています。
エラーは発生しません。休止状態はマップ内の 3 つのエンティティすべてをデータベースに正しく保存しますが、マップを使用してエンティティを再度読み取ると、3 つすべてではなく 1 つしか読み取れません。hibernate によってログに記録された SQL は、mysql で実行されたときに 3 つのエンティティすべてを正しく選択します。
このプロジェクトは、Spring 3.2.1、Hibernate 4.1.5、および JPA アノテーションに基づいています。
アップデート
休止状態をデバッグするとき、3 つのマップ エンティティのそれぞれに対して hashCode を呼び出すように見えますが、休止状態で生成された ID を除いてすべての値が null です。したがって、オーバーライドされたメソッドによって計算される hashCode は常に同じです。そのため、エンティティが 1 つだけマップに追加されていると思います。しかし、データベースからデータをロードする前に hibernate が hashCode を計算する理由がわかりません。
例
以下の例では、3 つの単純なエンティティ (すべて休止状態で生成された ID を持つ) を持つプロジェクトでエラーを再現しました。
- アイテム
- 言語
- 言語詳細
アイテムには、Language がキーである 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 を生成することです (そして、getClass を instanceof に、フィールド refs. を 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());
私は何を間違っていますか?