4.3. equals() と hashCode() の実装
以下の条件の場合、 equals() と hashCode() メソッドをオーバーライドしなければなりません、
永続クラスのインスタンスを Set に置く場合。 (これは多値の関連を表現するおすすめの方法です) そして同時に
分離インスタンスをセッションへ再追加する場合。
Hibernate は、永続 ID (データベースの行)と、特定のセッションスコープ内に限定ですが Java ID とが等価であることを保証します。ですから異なるセッションで検索したインスタンスを組み合わせる場合、 Set に意味のあるセマンティクスを持たせようと思っているならすぐに equals() と hashCode() を実装しなければなりません。
最も明白な方法は、両方のオブジェクトの識別子の値の比較によって equals()と hashCode() を実装する方法です。値が同じなら、両者はデータベースの同じ行でなければならないため等しくなります。 (両者が Set に追加されても、 Set には1個の要素しかないことになります) 残念なことに、生成された識別子にはこのアプローチを使うことができません。 Hibernate は永続化されたオブジェクトへ識別子の値を代入するだけであり、新しく作成されたインスタンスはどのような識別子の値も持っていません。さらに、インスタンスがセーブされておらず、現在 Set の中にあれば、セーブするとオブジェクトへ識別子の値を代入することになります。もし equals() と hashCode() が識別子の値に基づいているなら、ハッシュコードが変更されると Set の規約が破られます。この問題についての完全な議論は、 Hibernate のウェブサイトを見てください。これは Hibernate の問題ではなく、オブジェクトの同一性と等価性についての、通常の Java のセマンティクスであることに注意してください。
ビジネスキーの等価性 を使って、 equals() と hashCode() を実装することをお勧めします。ビジネスキーの等価性とは、 equals() メソッドが、ビジネスキー、つまり現実の世界においてインスタンスを特定するキー(自然 候補キー) を形成するプロパティだけを比較することを意味します。
public class Cat {
...
public boolean equals(Object other) {
if (this == other) return true;
if ( !(other instanceof Cat) ) return false;
final Cat cat = (Cat) other;
if ( !cat.getLitterId().equals( getLitterId() ) ) return false;
if ( !cat.getMother().equals( getMother() ) ) return false;
return true;
}
public int hashCode() {
int result;
result = getMother().hashCode();
result = 29 * result + getLitterId();
return result;
}
}
ビジネスキーはデータベースの主キー候補ほど安定である必要はないことに注意してください (項11.1.3. 「オブジェクト識別子を考える」 を参照してください)。更新不可なプロパティやユニークなプロパティは、通常ビジネスキーのよい候補です。