JPAとHibernateを使用する場合、equalsとhashcodeをどのように実装する必要がありますか
質問
モデルクラスのequalsとハッシュコードをHibernateに実装するにはどうすればよいですか?一般的な落とし穴は何ですか?ほとんどの場合、デフォルトの実装で十分ですか?ビジネスキーを使用する意味はありますか?
遅延フェッチ、ID生成、プロキシなどを考慮に入れると、あらゆる状況で正しく動作するのはかなり難しいように思えます。
解決
Hibernateには、 hashCode()
をいつ/どのようにオーバーライドするかについての長文の説明があります。 .org / hibernate / core / 4.0 / manual / en-US / html / persistent-classes.html#persistent-classes-equalshashcode "rel =" noreferrer ">ドキュメント
要点は、エンティティが Set
の一部である場合、またはそのインスタンスをデタッチ/アタッチする場合にのみ心配する必要があるということです。後者はそれほど一般的ではありません。前者は通常、次の方法で処理するのが最適です。
- ビジネスキーでの
equals()
/hashCode()
の基底化-例:オブジェクト(または少なくともセッション)の存続期間中に変更されない属性の一意の組み合わせ。 - 上記が不可能な場合、主キーの
equals()
/hashCode()
が設定され、オブジェクトIDが設定されている場合/System.identityHashCode()
それ以外の場合。ここでの重要の部分は、新しいエンティティが追加されて永続化された後、セットをリロードする必要があることです。そうしないと、エンティティが現在のhashCode()
と一致しないバケットに割り当てられる可能性があるため、奇妙な動作(最終的にエラーやデータ破損が発生する)になる可能性があります。
他のヒント
受け入れられた答えが正確だとは思わない。
元の質問に答えるには:
ほとんどの場合、デフォルトの実装で十分ですか?
答えはイエスです。ほとんどの場合、そうです。
エンティティが Set
で使用される場合、 equals()
および hashcode()
のみをオーバーライドする必要があります(これは非常に一般的です) ) AND エンティティは、休止状態セッションから切り離され、その後再び接続されます(これは休止状態の一般的な使用方法です)。
受け入れられた回答は、 either 条件がtrueの場合、メソッドをオーバーライドする必要があることを示しています。
遅延読み込みによってエンティティが読み込まれる場合、エンティティはベースタイプのインスタンスではなく、javassistによって生成される動的に生成されるサブタイプであるため、同じクラスタイプのチェックは失敗するため、使用しないでください
if (getClass() != that.getClass()) return false;
代わりに使用:
if (!(otherObject instanceof Unit)) return false;
これも良いプラクティスです。 Java Practicesでイコールを実装する。
同じ理由で、直接フィールドにアクセスすると、動作せず、基になる値の代わりにnullを返す場合があります。そのため、プロパティで比較を使用せずに、ゲッターを使用します。
equals
/ hashCode
の最適な実装は、固有のビジネスキー。
ビジネスキーは、すべてのエンティティ状態全体で一貫している必要があります。トランジション(トランジェント、アタッチ、デタッチ、削除)
別のオプションは、アプリケーションによって割り当てられた UUID識別子の使用に切り替えることです論理。この方法では、エンティティがフラッシュされる前にIDが割り当てられるため、 equals
/ hashCode
にUUIDを使用できます。
equals
および hashCode
のエンティティ識別子を使用することもできますが、そのためには常に同じ hashCode
値を返す必要があるため、エンティティのhashCode値がすべてのエンティティの状態遷移にわたって一貫していることを確認してください。このトピックの詳細については、この投稿をご覧ください。 。
ええ、難しいです。私のプロジェクトでは、equalsとhashCodeは両方ともオブジェクトのIDに依存しています。このソリューションの問題は、オブジェクトがまだ永続化されていない場合、IDがデータベースによって生成されるため、どちらも機能しないことです。私の場合、ほとんどすべての場合、オブジェクトはすぐに永続化されるため、これは許容範囲です。それ以外は、うまく機能し、実装が簡単です。
equals
をオーバーライドした場合は、必ず契約を履行してください:-
- 対称性
- 反射型
- TRANSITIVE
- 一貫性のある
- 非NULL
そして hashCode
をオーバーライドします。その契約は equals
の実装に依存しているためです。
Joshua Bloch(コレクションフレームワークの設計者)は、これらのルールに従うことを強く求めました。
- アイテム9:等しいをオーバーライドするときは常にhashCodeをオーバーライドする
これらの契約に従わない場合、意図しない深刻な影響があります。たとえば、 List.contains(Object o)
は、一般契約が満たされていないため、誤った boolean
値を返す場合があります。
Hibernate 5.2のドキュメントでは、状況に応じてhashCodeとequalsをまったく実装したくない場合があります。
通常、同じセッションからロードされた2つのオブジェクトは、データベース内で等しい場合(hashCodeとequalsを実装せずに)等しくなります。
2つ以上のセッションを使用している場合、複雑になります。この場合、2つのオブジェクトの等価性は、equalsメソッドの実装に依存します。
さらに、equalsメソッドが、オブジェクトを初めて永続化するときにのみ生成されるIDを比較する場合、問題が発生します。 equalsが呼び出されたとき、まだそこにいない可能性があります。
ここに非常に素晴らしい記事があります: https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/persistent-classes-equalshashcode.html
記事から重要な行を引用する:
ビジネスキーを使用してequals()およびhashCode()を実装することをお勧めします 平等。ビジネスキーの平等とは、equals()メソッドが ビジネスキーを形成するプロパティのみを比較します。 実世界でのインスタンスを識別します(自然な候補 キー):
簡単に言えば
public class Cat {
...
public boolean equals(Object other) {
//Basic test / class cast
return this.catId==other.catId;
}
public int hashCode() {
int result;
return 3*this.catId; //any primenumber
}
}