web-dev-qa-db-ja.com

エンティティは、equals()、hashCode()、およびtoString()です。それらを正しく実装する方法は?

Beanで使用可能なすべてのフィールドを使用して、エンティティのequals()hashCode()、およびtoString()を実装しています。

同等性を比較しようとしたとき、またはobj状態を出力したときに、フロントエンドでレイジー初期化例外が発生します。これは、エンティティの一部のリストを遅延初期化できるためです。

エンティティオブジェクトにequals()およびtoString()を実装するための正しい方法は何でしょうか。

32
spike07

equals()およびhashCode()ビジネスキー を使用して実装する必要があります。つまり、オブジェクトを一意に識別しますが、自動生成されたIDではありません。

toString()には、すべてのフィールドなど、興味深い情報を入力できます。

IDE(Eclipse、NetBeans、IntelliJ)を使用して、これらすべてを生成します。

LazyInitializationExceptionを回避するために、equals()でもビュー(jsp)でも、 OpenSessionInView を使用できます。

17
Bozho

HibernateオブジェクトのequalsメソッドとhashCodeメソッドを実装する場合、次のことが重要です。

  1. クラスのプロパティに直接アクセスする代わりに、ゲッターを使用します。
  2. オブジェクトのクラスを直接比較するのではなく、代わりにinstanceofを使用します

詳しくは:

Stackoverflow:overriding-equals-and-hashcode-in-Java

Hibernateドキュメント:EqualsとHashCode

編集:クラスのプロパティに直接アクセスしないことに関する同じルールがtoStringメソッドにも適用されます。ゲッターを使用するだけで、クラスに実際に含まれている情報が確実に返されます。

10
simon
  1. 2つのオブジェクトが等しい場合、それらは必須同じハッシュコードです。
  2. equals()メソッドは、デフォルトで、2つの参照がJavaヒープ上の同じメモリ内インスタンスを参照しているかどうかを確認します

あなたはエンティティ識別子に依存できますを使用してエンティティを比較します

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フレームワークでは、このメソッドを使用してその値を表示します。

7
Arthur Ronald

Hibernateエンティティでequals()をオーバーライドした場合は、必ずその契約を満たしていることを確認してください。

  • [〜#〜]対称[〜#〜]
  • [〜#〜]リフレクティブ[〜#〜]
  • [〜#〜]推移的[〜#〜]
  • [〜#〜]一貫した[〜#〜]
  • NON NULL

また、hashCodeの実装はコントラクトに依存しているため、equalsをオーバーライドします。

Joshua Bloch(コレクションフレームワークの設計者)は、このルールに強く従う必要があります

  • 項目9:等しいをオーバーライドするときは常にhashCodeをオーバーライドする

契約に従わないと、意図しない重大な影響があります。たとえば、List.contains(Object o)は、一般契約が履行されていないため、誤ったboolean値を返す可能性があります。

0
Awan Biru

他の人が言ったこととは別に、怠惰な情報を取得するには、Hibernateオブジェクトをセッションにアタッチする必要があると思います。データベース接続がないと、これらのリストはロードできません:)

0
extraneon

これはおそらく、それを行うための最良かつ最も簡単な方法です。

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();
}
0
doofus

Equals()とhashCode()をスーパークラスに実装します。これは、特にマップやリストなどで問題なく機能します。多くの推移的な永続性を行うため、これは正しくなければなりませんでした。

equals():

/**
 * 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();
}
0
Kango_V
  1. ビジネスキー がある場合は、equals/hashCodeに使用する必要があります。
  2. ビジネスキーがない場合は、Objectおよびエンティティの後では機能しないため、デフォルトのmerge equalsおよびhashCode実装のままにしないでください。
  3. この投稿で提案されているエンティティ識別子を使用 。唯一の問題は、次のように常に同じ値を返す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
    }
    
0
Vlad Mihalcea

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)でも、遅延読み込みされた複数のオブジェクトとプロパティにカスケードすると、大きなアクティビティが発生する可能性があります。

0