0

RMIを介して転送されたオブジェクトのequalsに関して奇妙な問題が発生しています。これは数日間私の頭を悩ませてきました、そして私は誰かが問題にいくらかの光を当てるのを手伝ってくれるかどうか疑問に思いました。

X over RMIと呼ばれるJavaプロセスにプッシュするガレージクラス(関連する場合はJPAエンティティでもあります)があります(したがって、このオブジェクトはシリアル化されています)。Garageオブジェクトは、シリアル化可能なCar(JPAエンティティ)と呼ばれるオブジェクトのリストを格納します。

ガレージのequalsメソッドは、基本的に車のリスト(ArrayList)でequalsを呼び出しています。

Javaプロセスでequalsを呼び出すと、リスト内のすべての車でequalsを呼び出して、リストが等しいかどうかを確認するように、何らかの理由でリスト上でequalsを呼び出すことはありません。

奇妙なことに、ユニットテストでは、CarsArrayListのすべてのメンバーでequalsが呼び出されます。単体テストの一部としてオブジェクトをシリアル化することもできましたが、これも機能しました。何か案は?問題が発生していることを願っています。何かを明確にするために、情報をリクエストしてください。

編集:車のリストでequalsを呼び出す代わりに、オブジェクトでequalsを手動で実行すると、車のリストでforeachループを実行し、各Carでequalsを呼び出したため、ArrayListが奇妙であるとほぼ確信しています(ArrayListが等しいと予想したように)とにかくやることとそれは期待通りに働いた)

@Entity
@Table(schema="pdw", name="garage")
public class Garage
    implements Comparable<Garage> , 
    Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private String id;

    private String name;


    @OneToMany(cascade = CascadeType.ALL)
    @JoinTable(schema="pdw")
    private List<Car> cars = new ArrayList<Car>();

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public List<Car> getCars() {
        return cars;
    }
    public void setCars(List<Car> cars) {
        this.cars = cars;
    }

    @Override
    public String toString() {

        StringBuffer buffer = new StringBuffer();
        buffer.append("[");
        buffer.append("Garage:");
        buffer.append("[id:" + id + "]");
        buffer.append("[Cars:" + cars + "]");
        buffer.append("]");
        return buffer.toString();
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (!(obj instanceof Garage))
            return false;
        Garage other = (Garage) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        if (cars == null) {
            if (other.cars != null)
                return false;
        } else if (!cars.equals(other.cars))
            return false;
        return true;
    }

    @Override
    public int compareTo(Garage other) {
        return this.getName().compareTo(other.getName());
    }
}

@Entity
@Table(schema="pdw", name="car")
public class Car 
    implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private String id;

    private String name;

    @OneToOne(fetch = FetchType.LAZY)
    private Garage garage;

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Garage getGarage() {
        return garage;
    }
    public void setGarage(Garage garage) {
        this.garage = garage;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Car other = (Car) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }

    @Override
    public String toString() {

        StringBuffer buffer = new StringBuffer();
        buffer.append("[");
        buffer.append("Car:");
        buffer.append("[id:" + id + "]");
        buffer.append("[name:" + name + "]");
        buffer.append("[garage:" + garage.getName() + "]");
        buffer.append("]");
        return buffer.toString();
    }   
}
4

8 に答える 8

6
  • Listdeseriazliationの後にあなたが空でないことを確認してください。
  • メソッドにブレークポイントを設定し、equals問題が発生していないかどうかを確認します
  • equalsonの実装Carが正しいことを確認してください
  • transientフィールドがないか確認してください

  • あなたが期待しているものがArrayList実際にはそうではないかどうかを確認してくださいPersistentBag。それequalsはあなたが望むことをしないからです。の場合は、ワイヤを介して送信PersistentBagする前に転送するか(したがって、潜在的な可能性を防ぐ)、要素自体ではなく各要素でequalsを呼び出すことができます。Hibernateは、遅延読み込みを提供するためにコレクションをラップするために使用しますArrayListLazyInitializationExceptionListPersistentBag

PS Hibernate以外のJPAプロバイダーを使用している場合は、おそらく同様のコレクションラッパーがあります。永続性プロバイダーを示してください。

于 2009-12-08T11:52:23.423 に答える
1

ArrayListAbstractListの実装を使用しequals()ます。それは次のように定義されています。

指定されたオブジェクトがこのリストと等しいかどうかを比較します。指定されたオブジェクトもリストであり、両方のリストが同じサイズであり、2 つのリスト内の対応する要素のすべてのペアが等しい場合にのみ、true を返します。(2 つの要素 e1 と e2 は、(e1==null ? e2==null : e1.equals(e2)) の場合に等しいです。) 言い換えると、2 つのリストは、同じ要素を同じリスト内に含む場合 > 等しいと定義されます。注文。

この実装は、指定されたオブジェクトがこのリストであるかどうかを最初に確認します。その場合は true を返します。そうでない場合は、指定されたオブジェクトがリストかどうかをチェックします。そうでない場合は false を返します。その場合、対応する要素のペアを比較しながら、両方のリストを反復処理します。いずれかの比較で false が返された場合、このメソッドは false を返します。どちらかの反復子が他方より先に要素を使い果たした場合、false を返します (リストの長さが等しくないため)。それ以外の場合は、反復が完了すると true を返します。

あなたCarの が比較されていない場合、リスト比較の初期段階ですでに比較が失敗している可能性がありますか? 比較しているリストに同じ数の要素がない可能性はありますか?

于 2009-12-08T12:47:51.833 に答える
1

Bozho を展開するには:

  • 両側のクラスが同じかどうかserialVersionUID(つまり、まったく同じコンパイル済みバージョンかどうか) を確認します。必要に応じて、private static final longクラス変数としてハードコーディングします。

これについての詳細は、java.io.SerializableAPISun のシリアライゼーションに関する記事を参照してください

于 2009-12-08T11:56:03.527 に答える
1

JPAを使用していると言いました...シリアル化の前にオブジェクトに完全なArrayListが含まれていることを確認してください。おそらく、リストの読み込みが遅く、リストをシリアル化および逆シリアル化した後に空になりますか? 私が理解できない唯一のこと(これが当てはまる場合)は、セッション中でないときにリストを遅延インスタンス化しようとしてエラーが発生しない理由です(デシリアライゼーション側の場合と思われるため)。

于 2009-12-08T12:41:49.250 に答える
1

Java ソースがインストールされている場合は、AbstractList equals 実装を介してデバッグし、どこで失敗しているかを確認できます。Java 1.6 の現在の実装は次のとおりです。

public boolean equals(Object o) {
if (o == this)
    return true;
if (!(o instanceof List))
    return false;

ListIterator<E> e1 = listIterator();
ListIterator e2 = ((List) o).listIterator();
while(e1.hasNext() && e2.hasNext()) {
    E o1 = e1.next();
    Object o2 = e2.next();
    if (!(o1==null ? o2==null : o1.equals(o2)))
    return false;
}
return !(e1.hasNext() || e2.hasNext());
}

それとは別に、いくつかのコメントがありますが、それらはあなたの問題に関連しているとは思いません:

1- equals をオーバーライドする場合は、hashCode をオーバーライドする必要があります。意図的に削除したのか、実装していないのかはわかりません。equals() と hashCode() は、equals() メソッドを使用して 2 つのオブジェクトが等しいと見なされるかどうかを指定する共同コントラクトによって結合され、同じハッシュコード値を持つ必要があります。(SCJP本より拝借)。そうしないと、HashMaps、HashSets、およびその他のコレクションクラスでこれらのクラスに問題が発生します。

2- equals 実装では、instanceof は null 性とクラス型の両方をチェックします。置き換えることができます

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

if (!(obj instanceof Car)){
            return false;
}
于 2009-12-08T13:02:32.453 に答える
1

オブジェクトのサブクラスを取得している可能性があります (hibernate が遅延読み込みをサポートするためにプロキシ クラスを作成することは知っています)。Car クラスの equals メソッドで、「getClass()」比較を行います。これは、プロキシ サブクラスを実際のインスタンスと比較している場合は false になります。getClass() の代わりに instanceof 操作を試すことができます (Garage equals メソッドのように)。toString() メソッドに「getClass()」を含めることで、これらすべてを確認できます。

また、(これも遅延読み込みです)、エンティティ クラスでメンバー変数を直接参照しないでください。常にゲッターとセッターを使用する必要があります。(したがって、equals、toString、... メソッドは getName() などを使用する必要があります。

于 2009-12-08T14:34:25.530 に答える
0

推測ですが、Garageインスタンスを送信したときに、X プロセスがそのインスタンスのスタブしか受信していない可能性はありませんか? その場合、equalsメソッドを呼び出すと、実際にはリモート呼び出しが実行され、元の JVM (X プロセスではなく) でこれらのメソッドが呼び出される可能性があります。

両方の JVM にブレークポイントを追加し、equals.

于 2009-12-08T12:57:53.003 に答える
0

私はちょうど同じ問題に遭遇しました。Hibernate がリストをその PersistentBag クラスに置き換えると、Hibernate によって返されるコレクションに対する JUnit の assertEquals() は失敗します。これは、そのクラスが equals() を不適切に実装しているためです。Hibernate 3.5.1-Final PersistentBag クラスのコードは次のとおりです。

/**
 * Bag does not respect the collection API and do an
 * JVM instance comparison to do the equals.
 * The semantic is broken not to have to initialize a
 * collection for a simple equals() operation.
 * @see java.lang.Object#equals(java.lang.Object)
 */
public boolean equals(Object obj) {
    return super.equals(obj);
}

彼らのコメントを読むと、パフォーマンス/効率の理由でこれを行っているようです。ただし、リストを含むオブジェクトがある場合、単体テストが難しくなります。私の解決策は、 areEqualNonNullLists(listA, listB) メソッドを作成し、それをリストを含むオブジェクトの eqauls() メソッドに入れることです。

public static boolean areEqualNonNullLists(List thisList, List thatList)
{
    if(thisList.size() != thatList.size()) return false;

    for(int i=0; i<thisList.size(); i++)
    {
        if(!thisList.get(i).equals( thatList.get(i) ) ) return false;
    }
    return true;
}

もっとエレガントで一般的な解決策があるのだろうか。

于 2011-02-18T05:55:40.743 に答える