web-dev-qa-db-ja.com

JPAおよびHibernateを使用する場合、equalsおよびhashcodeはどのように実装する必要がありますか

モデルクラスのequalsとハッシュコードをHibernateに実装するにはどうすればよいですか?よくある落とし穴は何ですか?ほとんどの場合、デフォルトの実装で十分ですか?ビジネスキーを使用する意味はありますか?

遅延フェッチ、ID生成、プロキシなどを考慮に入れると、あらゆる状況で正しく動作するのはかなり難しいように思えます。

91
egaga

Hibernateには、 documentationequals()/hashCode()をいつ/どのようにオーバーライドするかについての長文の説明があります。

その要点は、エンティティがSetの一部になる場合、またはそのインスタンスをデタッチ/アタッチする場合にのみ心配する必要があるということです。後者はそれほど一般的ではありません。前者は通常、次の方法で最適に処理されます。

  1. ビジネスキーに基づいてequals()/hashCode()を基にする-例:オブジェクト(または少なくともセッション)の存続期間中に変更されない属性の一意の組み合わせ。
  2. 上記が不可能な場合は、equals()/hashCode()が設定されている場合はそれを設定し、それ以外の場合はオブジェクトID/System.identityHashCode()を設定します。ここでのimportant部分は、新しいセットをreloadセットする必要があることですエンティティが追加され、永続化されました。そうしないと、エンティティが現在のhashCode()に一致しないバケットに割り当てられる可能性があるため、奇妙な動作(最終的にエラーやデータ破損が発生する)になる可能性があります。
65
ChssPly76

受け入れられた答えが正確だとは思わない。

元の質問に答えるには:

ほとんどの場合、デフォルトの実装で十分ですか?

答えはイエスで、ほとんどの場合そうです。

エンティティがSet(非常に一般的)で使用される場合にのみ、equals()hashcode()をオーバーライドする必要があります[〜#〜] and [〜#〜]エンティティは、休止状態セッションから切り離され、その後、再び接続されます(休止状態の一般的な使用法です)。

受け入れられた答えは、either条件が真の場合、メソッドをオーバーライドする必要があることを示しています。

33
Phil

エンティティが遅延読み込みによって読み込まれた場合、エンティティは基本型のインスタンスではなく、javassistによって生成された動的に生成されたサブタイプであるため、同じクラス型のチェックは失敗するため、使用しないでください:

if (getClass() != that.getClass()) return false;

代わりに使用します:

if (!(otherObject instanceof Unit)) return false;

Java Practices

同じ理由で、フィールドに直接アクセスすると、基礎となる値の代わりに機能せず、nullを返す場合があります。そのため、基礎となる値をロードするためにトリガーする可能性があるため、プロパティの比較は使用せず、ゲッターを使用します。

12
stivlo

最適なequals/hashCode実装は、 一意のビジネスキー を使用する場合です。

ビジネスキーは、すべてで一貫している必要があります エンティティ状態の遷移 (一時的、アタッチ、デタッチ、削除).

別のオプションは、アプリケーションロジックによって割り当てられた ID識別子 を使用するように切り替えることです。この方法では、エンティティがフラッシュされる前にIDが割り当てられるため、equals/hashCodeにUUIDを使用できます。

equalshashCodeにエンティティ識別子を使用することもできますが、エンティティのhashCode値が一貫していることを確認するために、常に同じhashCode値を返す必要がありますすべてのエンティティ状態遷移にわたって。 このトピックの詳細についてはこの投稿 をご覧ください。

11
Vlad Mihalcea

ええ、難しいです。私のプロジェクトでは、equalsとhashCodeは両方ともオブジェクトのIDに依存しています。このソリューションの問題は、オブジェクトがまだ永続化されていない場合、IDがデータベースによって生成されるため、どちらも機能しないことです。私の場合、ほとんどすべての場合、オブジェクトはすぐに永続化されるため、これは許容範囲です。それ以外は、うまく機能し、実装が簡単です。

6
Carlos

Hibernate 5.2のドキュメントでは、状況に応じてhashCodeとequalsをまったく実装したくない場合があります。

https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#mapping-model-pojo-equalshashcode

一般に、同じセッションからロードされた2つのオブジェクトは、データベース内で等しい場合(hashCodeとequalsを実装せずに)等しくなります。

2つ以上のセッションを使用している場合は複雑になります。この場合、2つのオブジェクトの等価性は、equalsメソッドの実装に依存します。

さらに、equalsメソッドが、オブジェクトを初めて永続化するときにのみ生成されるIDを比較する場合、問題が発生します。 equalsが呼び出されたとき、それらはまだ存在しない可能性があります。

1
Nina

ここに非常に素晴らしい記事があります: 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 
}

}
0
Ravi Shekhar

たまたまequalsをオーバーライドした場合は、必ず契約を履行してください:-

  • 対称
  • 反射する
  • 推移的
  • 一貫性のある
  • NULL以外

また、その契約はhashCode実装に依存しているため、equalsをオーバーライドします。

Joshua Bloch(コレクションフレームワークの設計者)は、これらの規則に従うことを強く求めました。

  • アイテム9:等しい場合は常にhashCodeをオーバーライドする

これらの契約に従わない場合、意図しない深刻な影響があります。たとえば、List#contains(Object o)は、一般契約が満たされていないため、間違ったboolean値を返す可能性があります。

0
Awan Biru