一部のプロパティに の注釈が付けられている JPA エンティティがあります@Transient
。
これらのプロパティをequals/hashCode/toString
メソッドで使用する必要がありますか?
私の最初の考えはNOですが、理由はわかりません。
- チップ?
- アイデア?
- 説明?
の場合toString()
は異なります。好きなことを行うことができるので、 (および)toString()
についてのみ説明します。equals()
hashCode()
まず、ルール:オブジェクトをList
、Map
またはに格納する場合は、ドキュメントで指定されている標準契約に従うように およびを実装Set
する必要がequals
hashCode
あります。
さて、どのように実装するequals()
のhashCode()
ですか?「自然な」アイデアはId
、の一部としてマップされたプロパティを使用することequals()
です。
public class User {
...
public boolean equals(Object other) {
if (this==other) return true;
if (id==null) return false;
if ( !(other instanceof User) ) return false;
final User that = (User) other;
return this.id.equals( that.getId() );
}
public int hashCode() {
return id==null ? System.identityHashCode(this) : id.hashCode();
}
}
残念ながら、このソリューションには大きな問題があります。生成された識別子を使用する場合、エンティティが永続化されるまで値が割り当てられないため、一時Set
エンティティが保存される前に に追加されると、そのハッシュ コードが にある間に変更され、Set
これにより が壊れます。の契約Set
。
したがって、推奨されるアプローチは、ビジネス キーの一部である属性を使用することです。つまり、同じデータベース ID を持つ各インスタンスに固有の属性の組み合わせです。たとえば、User クラスの場合、これはユーザー名になります。
public class User {
...
public boolean equals(Object other) {
if (this==other) return true;
if ( !(other instanceof User) ) return false;
final User that = (User) other;
return this.username.equals( that.getUsername() );
}
public int hashCode() {
return username.hashCode();
}
}
Hibernate Reference Documentation は、これを次のように要約しています。
"データベース ID を使用して等価性を実装しないでください。一意の、通常は不変の属性の組み合わせであるビジネス キーを使用してください。データベース ID は、一時オブジェクトが永続化されると変更されます。一時インスタンス (通常は切り離されたインスタンスと一緒に) がビジネス キーの属性は
Set
、データベースの主キーほど安定している必要はありません。オブジェクトが同じセット内にある限り、安定性を保証する必要があるだけです。- 12.1.3。オブジェクト ID の考慮hashcode
Set
"ビジネス キーの等価性を実装
equals()
してhashCode()
使用することをお勧めします。ビジネス キーの等価性とは、equals()
メソッドがビジネス キーを形成するプロパティのみを比較することを意味します。これは、実世界でインスタンスを識別するキーです (自然な候補キー)" - 4.3。equals() と hashCode() の実装
では、最初の質問に戻ります。
@Transient
属性は、そのようなキーの一部ではない可能性が非常に高いです。List
、Map
に追加する前に割り当てられた値を取得してSet
ください。私が知っている@Transient
との 2 つの典型的な使用法は、シリアル化/永続化できないもの (リモート リソースハンドルなど) または他のものから再構築できる計算されたプロパティのいずれかに使用することです。transient
計算されたデータの場合、equals/hashCode
冗長になるため、等式関係 ( ) で使用しても意味がありません。値は、等価で既に使用されている他の値から計算されます。ただし、それらを印刷することは理にかなっていますtoString
(たとえば、基本価格と比率を使用して実際の価格を計算します)。
シリアライズ可能/パーシタブルでないデータの場合、依存します。シリアル化できないリソースへのハンドルを想像できますが、ハンドルが表すリソース名を比較することはできます。についても同様でtoString
、おそらくハンドル リソース名を出力すると便利です。
これは私の 2 セントでした@Transient
.
例外は、それを放置するtransient
と同時に、提供 writeObject()
しreadObject()
、処理する場所から生じる場合があります。