2015-09-18 11 views
7

Hibernateはクエリの結果を与えるときに実際のエンティティクラスのインスタンスを返しませんが、代わりに動的に 'プロキシ'インスタンスを返します実際のエンティティのクラスからサブクラス化されています。私はこの動作の理由を理解しています。遅延初期化の実現を可能にしています。しかし、私はこれらのプロキシクラスの実装の詳細に未回答のままにいくつかの質問を持っている:私はゲッターを使用する場合にのみHibernate:プロキシ実装の詳細(レイジーフェッチ)

  1. は怠惰フェッチされたフィールドがロードされますでしょうか?たとえば、equalsまたはhashCodeの方法でフィールドを使用するとどうなりますか?このフィールドのゲッターを前に呼び出さなかった場合、これらのメソッドを実行するとNullPointerExceptionになりますか?

  2. 初期化がトリガされたとき、Hibernateはどのようにフィールドを正確に初期化しますか?エンティティクラスで定義したフィールドのセッタメソッドを実行するか、リフレクションなどを介して変数に直接値を代入しますか?

答えて

1

まず、二つの規則:

  1. プロキシデリゲートすべて非最後の方法は、idのプロパティへのアクセスは、マッピングで定義されている場合は、idを取得するためのメソッドを除き、ターゲット・インスタンスへの呼び出し。
  2. プロキシオブジェクトのインスタンスがでない場合、が初期化され、ターゲットインスタンスが初期化されます。

1)両方abが同じエンティティのプロキシどこあなたがa.equals(b)を呼び出すと仮定します。そしてequals方法は次のように実装されていることを言うことができます:aequals方法は、その完全な初期化を強制するターゲット・インスタンスに委譲され

public boolean equals(Object other) { 
    ... 
    if (this.someField.equals(other.someField)) { 
    ... 
    } 
    ... 
} 

。したがって、aインスタンスのフィールドについては安全です(直接使用できます)。

ただし、bインスタンス(other.someField)のフィールドに直接アクセスすると、は無効となります。は有効です。 bが初期化されているかどうかは関係ありません。プロキシインスタンスは決して初期化されず、ターゲットインスタンスのみが初期化されます。したがって、someFieldは、bインスタンスでは常にnullです。

this.someField.equals(other.getSomeField()) 

や一貫性を:: - Hibernateはできない、それはfinal方法に来るとき

this.getSomeField().equals(other.getSomeField()) 

物事が異なっている

正しい実装はother例えば少なくともゲッ​​ターを使用することですそれらをオーバーライドして、コールをターゲットに委任します。したがって、前の例でメソッドがfinalだった場合は、this.someFieldにアクセスするとNullPointerExceptionになります。

これはすべて、プロキシの代わりにバイトコード計測を使用するようにHibernateを設定することで回避できますが、それは独自の落とし穴を持ち、広く採用されていません。

2)再び、プロキシインスタンス自体は初期化されません。ターゲットインスタンスの初期化では、フィールドまたはプロパティのアクセスがマッピングで定義されているかどうかによって異なります。どちらの場合も、リフレクションが使用されます(フィールドアクセスの場合はフィールドに直接値を割り当て、プロパティアクセスの場合はセッターを呼び出す)。