2012-06-13 7 views
10

私は、idとversionプロパティを抽象化する1つのBaseEntityを持っています。このクラスは、ハッシュコードとPK(id)プロパティに基づくequalsも実装します。Hibernateは等しいとプロキシ

BaseEntity{ 

    Long id; 
    Long version; 

public int hashCode() { 
    final int prime = 31; 
    int result = 1; 
    result = prime * result + ((id == null) ? 0 : id.hashCode()); 
    return result; 
} 

public boolean equals(Object obj) { 
    if (this == obj) 
     return true; 
    if (obj == null) 
     return false; 
    if (getClass() != obj.getClass()) 
     return false; 
    BaseEntity other = (BaseEntity) obj; 
    if (id == null) { 
     if (other.id != null) 
      return false; 
    } else if (!id.equals(other.id)) 
     return false; 
    return true; 
} 


} 

今2エンティティAとBは怠け者Bとa1.getBを試してみてください()。等号(B1)との

A extends BaseEntity{ 
    `B b` 
    B getB(){return b;) 
    void setB(B b){this.b=b;} 
} 

B extends BaseEntity{ 
} 

object b1; 
object a1; 
a1.set(b1); 
session.save(a1) //cascade save; 

近いセッション 負荷A以下のようにBaseEntityを拡張することは が、私の場合はfalse提供しますa1.getB()。getId()。equals(b1.getId())と比較すると、本当に奇妙なことが起こります! 私はそれがとにかくこれを解決するJavaアシストプロキシオブジェクトのためだと思いますか?

答えて

15

a.bアソシエーションを遅延ロードできるようにするため、Hibernateはaのフィールドをプロキシに設定します。プロキシは、Bを拡張するクラスのインスタンスですが、Bではありません。したがって、プロキシ以外のBインスタンスとプロキシBインスタンスを比較すると、equals()メソッドは常に失敗します。 Hibernateのエンティティの場合

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

、あなたはまた、

if (!(obj instanceof B)) { 
    return false; 
} 

でこれを置き換える必要があり、

  • Hibernateは実装しないことをお勧めしますのでご注意equals()hashCode()にIDを使用して、代わりに自然の識別子を使用します。 IDを使用して実装すると問題が発生する可能性があります。これは、IDが保存されてIDが生成されるまでエンティティにIDがないためです。
  • エンティティの継承を使用すると、問題はさらに悪化します。 Bが2つのサブエンティティB1とB2のスーパークラスであるとします。 Hiberanteは、ロードする前にどのタイプ(B1またはB2)がa.bかを知ることができません。したがって、a.bは、BのサブクラスであるがB1またはB2のサブクラスではないプロキシに初期化されます。したがって、hashCode()equals()メソッドはBで実装する必要がありますが、B1とB2ではオーバーライドできません。 2つのBインスタンスは、Bのインスタンスであれば等しいとみなされ、同じ識別子を持ちます。
+0

ありがとう、それを得ました。 idに基づいてequals()およびhashcode()によって発生する可能性のある問題については、あなたに同意します。私は現時点では、アプリケーションのこの段階でナチュラルIDを導入することは難しいだろうと私はinstanceofオプションで行くと思います。 –

-1

これは、主に標準的なJava継承の影響です。

a1.getB().equals(b1)は、Object.equals()を使用します(ただし、クラスでequals()をオーバーライドした場合を除く)。a1.getB()とb1が同じインスタンスである場合のみtrueを返します。あなたのコードフォーマットが壊れていますが、別のセッションでaを再度ロードしたように見えるので、aa.getB()の新しいインスタンスを取得すると、Object.equals()はfalseを返します。

a1.getB().getId().equals(b1.getId())は、長い値が同じ場合(Longオブジェクトの異なるインスタンスの場合でも)、trueを返します。これらの値は明らかに同じです。

+0

修正されたコードのフォーマット:と私はこの問題に気づいたことはありません) 'わからない理由は –

+0

A.getB()(どのタイプ?)、BaseEntity.equals()、A(少なくともidとメンバb)のマッピングにコードを追加できますか?多分私はもっと見ることができます。 – Johanna

+0

getter setterのコードが更新されました。 –

1

あなたはウィッヒインスタンスを知らない場合にも、それはこのように動作する便利を作ることができるB(あなたequalsがスーパークラスである場合に起こる場合があります)私は長年にわたってHibernate.getClassを使用

if (HibernateProxyHelper.getClassWithoutInitializingProxy(this) != HibernateProxyHelper.getClassWithoutInitializingProxy(obj)) 
    return false 
+0

私のコメントはRalphの答えをご覧ください。また、あなたのドメインクラスをHibernate依存関係に結合しています。 –

8

です私のコードのとおり、私ははObject.equals() `、現在それが呼び出している`はObject.equals( `ではなくBaseEntity``に等しく、それが呼び出すべきだと思い、

@Override  
public boolean equals(final Object obj) { 
    if (this == obj) { 
     return true; 
    } 
    if (obj == null) { 
     return false; 
    } 
    if (Hibernate.getClass(this) != Hibernate.getClass(obj)) { 
     return false; 
    } 

    ... check for values 

    return true; 
} 
+3

あなたのドメインクラスを除いて、Hibernateとほとんど結合していません。休止状態を使用していない他のアプリケーションとドメインを共有したいとします。あなたは困っています。それがうまくいくわけではありませんが、それは非常にひどいにおいがします。また、休止状態の人が\ proxyプロキシを削除して休止状態にすると、ハイバーネイトをアップグレードすると、ドメインはもはやコンパイルされません。 –

関連する問題