読者です 読者をやめる 読者になる 読者になる

この日記は私的なものであり所属会社の見解とは無関係です。 GitHub: takahashikzn

Lazy Loadingしていると、エンティティのequalsメソッドがおかしくなる(かも)

EclipseLink

EclipseLinkを使ってLazy Loading設定しているとき、
エンティティのequalsメソッドの扱いが面倒になることが判りました。


例えば次のようなケースを考えてみます。
1つの店舗(Shop)は、一人の従業員(Employee)を店長として持っているという関係です。

この図ではN:1の関係なので従業員は店長を兼務できるのですが、
それは今回の記事と関係ないので気にしないでください(-_-)



さて、このときに、Lazy Loading設定された状況下において、次のようなコードを書いたとします。

サンプルコードその1

まずはエンティティクラスの定義から。

import javax.persistence.*;
import org.apache.commons.lang.builder.EqualsBuilder;

// 店舗クラス
@Entity
public class Shop {

    private long id;
    private String shopName;

    // 従業員エンティティを遅延ロード
    @ManyToOne(fetch = FetchType.LAZY)
    private Employee chief;


    //getterとかsetterとか


    public boolean equals(Object o) {
        return EqualsBuilder.reflectionEquals(this, o);
    }
}


// 従業員クラス
@Entity
public class Employee {

    private long id;
    private String employeeName;


    //getterとかsetterとか


    public boolean equals(Object o) {
        return EqualsBuilder.reflectionEquals(this, o);
    }
}


で、実行コードその1。

import javax.persistence.*;


EntityManager em = ...;

long shopId1 = ...;
long shopId2 = ...;


//shop1とshop2は違うエンティティ。
assert shopId1 != shopId2;


Shop shop1 = em.find(Shop.class, shopId1);
Shop shop2 = em.find(Shop.class, shopId2);


boolean isSameShop = shop1.equals(shop2);

isSameShopの値はfalseです。まあ当たり前ですね。

サンプルコード2

では次のコードはどうでしょう。

import javax.persistence.*;


EntityManager em = ...;

long shopId1 = ...;


Shop shop1 = em.find(Shop.class, shopId1);
Shop shop1Another = em.find(Shop.class, shopId1);


// 参照は別であるとする
assert shop1 != shop1Another;


boolean isSameShop = shop1.equals(shop1Another);

このとき、isSameShopの値はfalseになります。同じショップなのに。
ちなみにLazy Loadingしていない場合はtrueになります。


なぜこのような現象が発生するのでしょうか。
まあ、Lazy LoadingがONかOFFかで結果が異なることからすると、Lazy Loadingが原因なのは明らかなんですが。

原因の考察

原因は、バイトコードエンハンスされた部分にあります。


しばらく前の記事で、バイトコードエンハンスしたコードの中身が
こんな感じになることをご紹介しました。

Shop@1db3f6c[
  id=1
  shopName=店舗1
  chief=null
  _persistence_chief_vh={UnitOfWorkQueryValueHolder: not instantiated}
]


Shopエンティティへ、_persistence_chief_vhフィールドがバイトコードエンハンスで追加されています。


このとき、_persistence_chief_vhフィールドはequalsでfalseを返してしまうので、
EqualsBuilder.reflectionEqualsが必ず失敗するようになるのが原因だと考えられます。


この問題を解決するには、エンティティのequalsメソッドをEqualsBuilderで手抜きなんかせず、
きちんと手書きすれば大丈夫です。


でも…メンドクサイですよね(-_-)