0

オブジェクト 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());

私は何を間違っていますか?

4

1 に答える 1

1

私はデバッグ、回避策の作成、およびこの問題の理解にほぼ 1 週間を費やしました。

休止状態のバージョンを 4.1.5 から 4.1.8 に変更したところ、すべての苦痛がなくなりました。バージョン4.1.7で修正されたのは hibernate のバグでした...

次回はバグレポートをもっと早くチェックします...

于 2013-02-02T23:37:32.117 に答える