Beanで使用可能なすべてのフィールドを使用して、エンティティのequals()
、hashCode()
、およびtoString()
を実装しています。
同等性を比較しようとしたとき、またはobj状態を出力したときに、フロントエンドでレイジー初期化例外が発生します。これは、エンティティの一部のリストを遅延初期化できるためです。
エンティティオブジェクトにequals()
およびtoString()
を実装するための正しい方法は何でしょうか。
equals()
およびhashCode()
は ビジネスキー を使用して実装する必要があります。つまり、オブジェクトを一意に識別しますが、自動生成されたIDではありません。
toString()
には、すべてのフィールドなど、興味深い情報を入力できます。
IDE(Eclipse、NetBeans、IntelliJ)を使用して、これらすべてを生成します。
LazyInitializationException
を回避するために、equals()
でもビュー(jsp)でも、 OpenSessionInView
を使用できます。
HibernateオブジェクトのequalsメソッドとhashCodeメソッドを実装する場合、次のことが重要です。
instanceof
を使用します詳しくは:
Stackoverflow:overriding-equals-and-hashcode-in-Java
Hibernateドキュメント:EqualsとHashCode
編集:クラスのプロパティに直接アクセスしないことに関する同じルールがtoStringメソッドにも適用されます。ゲッターを使用するだけで、クラスに実際に含まれている情報が確実に返されます。
あなたはエンティティ識別子に依存できますを使用してエンティティを比較します
public boolean equals(Object o) {
if(o == null)
return false;
Account account = (Account) o;
if(!(getId().equals(account.getId())))
return false;
return true;
}
しかし非永続エンティティがあるとどうなりますか? 機能しません識別子が割り当てられていないため。
それでは、Java Hibernate Bookによる永続化がそれについて語っていることを見てみましょう
ビジネスキーは、プロパティ、またはプロパティのいくつかの組み合わせ同じデータベースIDを持つインスタンスごとに一意です。
そう
使用する自然キーです代わりに代理主キーを使用していなかった場合。
それでは、ユーザーエンティティがあり、その自然キーがfirstNameとlastNameであるとします(少なくとも、彼/彼女のfirstNameとlastNameは変更されないことがよくあります)。したがって、次のように実装されます
public boolean equals(Object o) {
if(o == null)
return false;
if(!(o instanceof User))
return false;
// Our natural key has not been filled
// So we must return false;
if(getFirstName() == null && getLastName() == null)
return false;
User user = (User) o;
if(!(getFirstName().equals(user.getFirstName())))
return false;
if(!(getLastName().equals(user.getLastName())))
return false;
return true;
}
// default implementation provided by NetBeans
public int hashcode() {
int hash = 3;
hash = 47 * hash + ((getFirstName() != null) ? getFirstName().hashcode() : 0)
hash = 47 * hash + ((getLastName() != null) ? getLastName().hashcode() : 0)
retrun hash;
}
うまくいきます!リポジトリやサービスなどのモックオブジェクトでも使用しています
そして、@ Bozhoが言ったように、toString()メソッドについては、どんな興味深い情報でも入れることができます。ただし、たとえばWicketやVaadinなどの一部のWebフレームワークでは、このメソッドを使用してその値を表示します。
Hibernateエンティティでequals()をオーバーライドした場合は、必ずその契約を満たしていることを確認してください。
また、hashCode
の実装はコントラクトに依存しているため、equals
をオーバーライドします。
Joshua Bloch(コレクションフレームワークの設計者)は、このルールに強く従う必要があります
契約に従わないと、意図しない重大な影響があります。たとえば、List.contains(Object o)
は、一般契約が履行されていないため、誤ったboolean
値を返す可能性があります。
他の人が言ったこととは別に、怠惰な情報を取得するには、Hibernateオブジェクトをセッションにアタッチする必要があると思います。データベース接続がないと、これらのリストはロードできません:)
これはおそらく、それを行うための最良かつ最も簡単な方法です。
public String toString() {
return "userId: " + this.userId + ", firstName: " + this.firstName + ", lastName: " + this.lastName + ", dir: " + this.dir + ", unit: " + this.unit + ", contractExpiryDate: " + this.contractExpiryDate + ", email: " + this.email + ", employeeNumber: " + this.employeeNumber + ", employeeType: " + this.employeeType + ", phone: " + this.phone + ", officeName: " + this.officeName + ", title: " + this.title + ", userType: " + this.userType;
}
public boolean equals(Object obj) {
[...]
return (toString().equals(other.toString()));
}
public int hashCode() {
return toString().hashCode();
}
Equals()とhashCode()をスーパークラスに実装します。これは、特にマップやリストなどで問題なく機能します。多くの推移的な永続性を行うため、これは正しくなければなりませんでした。
/**
* Compare two entity objects, following hibernate semantics for equality. Here we assume that
* new objects are always different unless they are the same object. If an object is loaded from
* the database it has a valid id and therefore we can check against object ids.
*
* @see com.dolby.persist.bean.EntityObject#equals(Java.lang.Object)
*/
@SuppressWarnings("unchecked")
@Override
public final boolean equals(final Object object) {
if (this == object) return true;
if (object == null || this.getClass() != object.getClass()) return false;
final AbstractModelObject<ID> other = (AbstractModelObject<ID>) object;
if (this.getId() == null || other.getId() == null) return false;
return this.getId().equals(other.getId());
}
/**
* Returns an enttiy objects hashcode.
* <p>
* What we are doing here is ensuring that once a hashcode value is used, it never changes for
* this object. This allows us to use object identity for new objects and not run into the
* problems.
* </p>
* <p>
* In fact the only case where this is a problem is when we save a new object, keep it around
* after we close the session, load a new instance of the object in a new session and then
* compare them.
* </p>
* <p>
* in this case we get A==B but a.hashcode != b.hashcode
* </p>
* <p>
* This will work in all other scenarios and don't lead to broken implementations when the
* propety of the object are edited. The whole point in generating synthetic primary keys in the
* first place is to avoid having a primary key which is dependant on an object property and
* which therefore may change during the life time of the object.
* </p>
*
* @see Java.lang.Object#hashCode()
*/
@Override
public final synchronized int hashCode() {
if (this.hashcodeValue == null) {
if (this.getId() == null) {
this.hashcodeValue = new Integer(super.hashCode());
}
else {
final int generateHashCode = this.generateHashCode(this.getId());
this.hashcodeValue = new Integer(generateHashCode);
}
}
return this.hashcodeValue.intValue();
}
equals
/hashCode
に使用する必要があります。Object
およびエンティティの後では機能しないため、デフォルトのmerge
equalsおよびhashCode実装のままにしないでください。この投稿で提案されているエンティティ識別子を使用 。唯一の問題は、次のように常に同じ値を返すhashCode
実装を使用する必要があることです。
@Entity
public class Book implements Identifiable<Long> {
@Id
@GeneratedValue
private Long id;
private String title;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Book)) return false;
Book book = (Book) o;
return getId() != null &&
Objects.equals(getId(), book.getId());
}
@Override
public int hashCode() {
return 31;
}
//Getters and setters omitted for brevity
}
HibernateエンティティのtoString()の実装は次のとおりです。
@Override
public String toString() {
return String.format("%s(id=%d)", this.getClass().getSimpleName(), this.getId());
}
上記のAbstractEntityのすべてのサブクラスは、必要に応じてそのメソッドをオーバーライドします。
@Override
public String toString() {
return String.format("%s(id=%d, name='%s', status=%s)",
this.getClass().getSimpleName(),
this.getId(),
this.getName(),
this.getStatus());
}
HashCode()とequals()の場合、Hibernateはプロキシクラスをよく使用することに注意してください。したがって、equals()は通常、次のようになります。
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
Class<AbstractEntity> c1 = Hibernate.getClass(this);
Class<AbstractEntity> c2 = Hibernate.getClass(obj);
if (!c1.equals(c2)) return false;
final AbstractEntity other = (AbstractEntity) obj;
if (this.getId() == null) {
if (other.getId() != null) return false;
}
else if (!this.getId().equals(other.getId())) return false;
return true;
}
そして、他の人がすでに述べたように...遅延ロードされたプロパティへのアクセスには注意してください!単純なtoString()またはlog.debug(entity)でも、遅延読み込みされた複数のオブジェクトとプロパティにカスケードすると、大きなアクティビティが発生する可能性があります。