Hibernate遅延負荷機構


http://blog.163.com/xi_zhqi/blog/static/858509420081269505939/
遅延ローディング:
   遅延負荷機構は、データが本当に必要とされるときにデータローディングを実行するために、無駄な性能オーバーヘッドを回避するために提示されるものである.Hbernateでは、エンティティオブジェクトへの遅延負荷およびセットへの遅延負荷が提供され、また、Hbernate 3では、属性への遅延負荷が提供される.これらの種類の遅延負荷の詳細について説明します.
A、エンティティオブジェクトの遅延負荷:
エンティティオブジェクトに対して遅延負荷を使用するには、エンティティのマッピングプロファイルに対応する構成が必要です.
<hibernate-mapping>

<class name=”com.neusoft.entity.User” table=”user” lazy=”true”>

    ……

</class>

</hibernate-mapping>
 
classのlazy属性をtrueに設定することで、エンティティの遅延負荷特性をオープンします.もし私たちが次のコードを実行したら:
User user=(User)session.load(User.class,”1”);(1)

System.out.println(user.getName());(2)
 
(1)に実行したとき、ヒップホップはデータに対する問い合わせを開始していませんでした.もし私たちはこの時、いくつかのデバッグツール(例えばJBuider 2005のDebugツール)を通じて、この時userオブジェクトのメモリスナップショットを観察して、この時戻ってきたのはUser$EnhancerByCGLOIB$bede 8986タイプのオブジェクトであり、その属性はnullです.これはどういうことですか?前にSession.load()の方法を話したことがありますが、エンティティオブジェクトのエージェントのオブジェクトに戻ります.ここで戻ってくる対象のタイプはUserオブジェクトのエージェントのオブジェクトです.Hbernateでは、CGLOIBを使用して、1つのオブジェクトを動的に構成するプロキシオブジェクトを実現し、プロキシオブジェクトにターゲットオブジェクトのすべての属性と方法を含み、すべての属性はnullに割り当てられている.調合器によって表示されたメモリのスナップショットにより、この時の真のUserオブジェクトは、エージェントの対象となるCGLOIB$CALBACK_であることが分かります.0.target属性では、コードが(2)に実行されると、user.getName()メソッドを呼び出し、CGLOIBによって付与されたコールバック機構により、実際にCGLOIB$CALBACK_を呼び出します.0.getName()メソッドは、この方法を呼び出すと、HbernateはまずCGLOIB$CALBACK_をチェックします.0.target属性がnullかどうかは、空でなければ対象のgetNameメソッドを呼び出し、空であればデータベースクエリを開始し、このようなSQL文を生成する.を選択してデータを検索し、ターゲットを作成し、CGLOIB$CALBACK_に値を割り当てます.0.target属性にあります.
    このように、1つの中間エージェントを介して、Hbernateはエンティティの遅延負荷を実現し、ユーザがエンティティオブジェクト属性を取得する動作を本当に開始したときにのみ、データベースクエリ動作を開始する.したがって、エンティティの遅延負荷は、中間エージェントクラスを介して行われるので、Session.load()方法のみがエンティティ遅延負荷を利用することができ、session.load()方法のみがエンティティクラスのエージェントオブジェクトに戻る.
B、        セットタイプの遅延ローディング:
Hibernateの遅延負荷機構においては、集合タイプの応用に対して最も重要な意義がある.これは性能を大幅に向上させる可能性があるので、HibernateはJDK Collectionの独立実現を含む多くの努力を行い、一対のマルチ関連において、関連対象を収容するためのSet集合を定義し、java.util.Setタイプまたはそのサブタイプではなく、net.sf.hibernate.co llection.Setタイプであり、カスタムのセットクラスを使用することによって、Hibernateは、セットタイプの遅延負荷を実現する.セットタイプに対して遅延負荷を使用するために、私たちは以下のようにエンティティタイプの関連部分を構成しなければならない.
<hibernate-mapping>

    <class name=”com.neusoft.entity.User” table=”user”>

…..

<set name=”addresses” table=”address” lazy=”true” inverse=”true”>

<key column=”user_id”/>

<one-to-many class=”com.neusoft.entity.Arrderss”/>

</set>

    </class>

</hibernate-mapping>
 
セットタイプの遅延負荷特性は、要素のlazy属性をtrueに設定することによりオンになります.私たちは下のコードを見ます.
User user=(User)session.load(User.class,”1”);

Collection addset=user.getAddresses();       (1)

Iterator it=addset.iterator();                (2)

while(it.hasNext()){

Address address=(Address)it.next();

System.out.println(address.getAddress());

}
 
プログラムが(1)に実行されると、関連データに対するクエリーを開始して関連データをロードすることはなく、(2)に実行される場合にのみ、真のデータ読取動作が開始され、Hbernateは、キャッシュにおいて条件に該当するデータインデックスに基づいて、条件に該当するエンティティオブジェクトを検索する.
ここでは全く新しい概念を導入しました.データインデックスとは、まず何かのデータインデックスを接続します.Hbernateにおいて、集合タイプをキャッシュするときは、2つの部分に分けてキャッシュするものであり、まず、セット中のエンティティのIDリストをキャッシュし、エンティティオブジェクトをキャッシュする.これらのエンティティオブジェクトのidリストは、いわゆるデータインデックスである.データインデックスを検索すると、対応するデータインデックスが見つからない場合、select SQLの実行は、条件に合致するデータを取得し、エンティティオブジェクトのセットとデータインデックスを構築し、エンティティオブジェクトのセットに戻り、エンティティオブジェクトとデータロープをHbernateのキャッシュに組み入れる.一方、対応するデータインデックスが見つかったら、データインデックスからIDリストを取り出し、IDに基づいてキャッシュで対応するエンティティを検索し、見つけたらキャッシュから戻ります.見つけられなかったら、select SQLクエリを開始します.ここではもう一つの問題を示しました.この問題は性能に影響を与えるかもしれません.これがセットタイプのキャッシュポリシーです.私たちが次のようにセットを設定したら、
<hibernate-mapping>

    <class name=”com.neusoft.entity.User” table=”user”>

…..

<set name=”addresses” table=”address” lazy=”true” inverse=”true”>

<cache usage=”read-only”/>

<key column=”user_id”/>

<one-to-many class=”com.neusoft.entity.Arrderss”/>

</set>

    </class>

</hibernate-mapping>
 
ここでは、集合のエンティティオブジェクトをキャッシュすることなく、構成を適用します.上のように構成されています.私たちは下記のコードを実行します.
User user=(User)session.load(User.class,”1”);

Collection addset=user.getAddresses();      

Iterator it=addset.iterator();               

while(it.hasNext()){

Address address=(Address)it.next();

System.out.println(address.getAddress());

}

System.out.println(“Second query……”);

User user2=(User)session.load(User.class,”1”);

Collection it2=user2.getAddresses();

while(it2.hasNext()){

Address address2=(Address)it2.next();

System.out.println(address2.getAddress());

}
 
このコードを実行すると、以下のような出力が得られます.
Select * from user where id=’1’;

Select * from address where user_id=’1’;

Tianjin

Dalian

Second query……

Select * from address where id=’1’;

Select * from address where id=’2’;

Tianjin

Dalian
 
私たちは、第二回クエリーを実行する時、2つのアドラステーブルに対するクエリー操作を実行しているのを見ましたが、なぜですか?これは、エンティティを第1回ローディングした後、セットタイプキャッシュポリシーの設定により、セットデータインデックスのみがキャッシュされ、セット中のエンティティオブジェクトはキャッシュされていないため、エンティティを第2回ローディングしたときに対応するエンティティのデータインデックスがHbernateによって発見されたが、データインデックスによってはキャッシュに対応するエンティティが見つけられなくなり、だから、Hibernateでは、見つかったデータインデックスに基づいて、二つのselect SQLの調査操作を開始しました.ここでは、性能の浪費を引き起こしました.どうやってこのような状況を回避できますか?集合タイプのエンティティにもキャッシュポリシーを指定しなければならないので、以下のようにセットタイプを設定します.
<hibernate-mapping>

    <class name=”com.neusoft.entity.User” table=”user”>

…..

<set name=”addresses” table=”address” lazy=”true” inverse=”true”>

<cache usage=”read-write”/>

<key column=”user_id”/>

<one-to-many class=”com.neusoft.entity.Arrderss”/>

</set>

    </class>

</hibernate-mapping>
 
このとき、Hbernateは、セットタイプのエンティティもキャッシュします.この構成によって上記のコードを再実行すると、以下のような出力が得られます.
Select * from user where id=’1’;

Select * from address where user_id=’1’;

Tianjin

Dalian

Second query……

Tianjin

Dalian
 
この場合は、データーインデックスに基づいてクエリーを行うSQL文は存在しません.このとき、セットタイプに格納されているエンティティオブジェクトはキャッシュから直接取得できます.
C、       属性ディレイローディング:
   Hibernate 3には,新しい特性である属性の遅延負荷が導入されており,この機構はまた高性能クエリを得るための強力なツールを提供している.前の話では、ビッグデータオブジェクトの読み込み時に、Userオブジェクトの中には、このフィールドがjava.sql.Clobタイプであり、ユーザーの履歴情報が含まれています.このオブジェクトを読み込むたびに、このフィールドをロードしなければなりません.また、このような大きなデータオブジェクトの読み取り自体は、大きな性能オーバーヘッドをもたらす.Hbernature 2では、前述した面性能の粒度細分化によってUser類を分解して解決していますが(その節の論述を参照してください)、Hibernate 3では、属性遅延負荷機構により、このフィールドを操作する必要がある時のみ、このフィールドデータを読み取る能力を得られます.このためには下記のように私達の実体類を配置しなければなりません.
<hibernate-mapping>

<class name=”com.neusoft.entity.User” table=”user”>

……

<property name=”resume” type=”java.sql.Clob” column=”resume” lazy=”true”/>

    </class>

</hibernate-mapping>
 
<property>要素のlazy属性にtrueを設定することにより、属性の遅延負荷を開始し、Hbernate 3において、属性の遅延負荷を実現するために、クラス増強器を使用して、エンティティクラスのClassファイルを強化処理し、増強器の増強により、CGLOIBのフィードバック機構論理を実体類に追加し、ここでは属性の遅延負荷が見られます.それともCGIBによって実現されました.CGLOIBはApacheのオープンソースプロジェクトであり、このクラスはjavaクラスのバイトコードを操作し、バイトコードに基づいて要求に合うクラスのオブジェクトを動的に構築することができる.上の配置によって、下記のコードを実行します.
String sql=”from User user where user.name=’zx’ ”;

Query query=session.createQuery(sql);    (1)

List list=query.list();

for(int i=0;i<list.size();i++){

User user=(User)list.get(i);

System.out.println(user.getName());

System.out.println(user.getResume());    (2)

}
 
(1)において実行されると、以下のようなSQL文が生成される.
Select id,age,name from user where name=’zx’;
 
このとき、HbernateはUserエンティティのすべての非遅延負荷属性に対応するフィールドデータを検索し、(2)に実行すると、以下のようなSQL文が生成される.
Select resume from user where id=’1’;
 
このとき、レムフィールドのデータの本格的な読み込み動作が開始されます.