この記事では、冬眠レイテンシロードテクノロジーについて説明します。次のように、参照のために共有してください。
Hibernaeの怠zyな荷重は非常に一般的な手法です。エンティティのコレクション属性はデフォルトで遅延され、エンティティに関連するエンティティもデフォルトで延期されます。 Hibernateは、この遅延荷重を使用してシステムのメモリのオーバーヘッドを減らし、冬眠の動作性能を保証します。
最初に、冬眠遅延荷重の「秘密」を分析しましょう。
収集プロパティの怠zyな負荷
Hibernateがデータベースから永続的なエンティティを初期化する場合、そのエンティティの収集属性は永続的なクラスで初期化されていますか?コレクション属性に100,000または数百万のレコードが含まれている場合、永続的なエンティティを初期化しながらすべてのコレクション属性をraw索すると、パフォーマンスが急激に低下します。システムは、すべてのコレクション属性ではなく、永続クラスのコレクション属性の一部のレコードのみを使用する必要がある可能性があります。このようにして、すべてのコレクション属性を一度にロードする必要はありません。
通常、収集プロパティには怠zyなロード戦略が推奨されます。いわゆる遅延ロードは、システムがコレクション属性を使用する必要がある場合、データベースから関連データをロードすることです。
たとえば、次の人クラスにはコレクション属性があり、コレクション属性の要素にはタイプアドレスがあり、個人クラスのコードスニペットは次のとおりです。
リスト1。Person.java
パブリッククラスの人{//属性プライベート整数IDを識別します。 //個人の名前属性プライベート文字列名。 //人の年齢属性をプライベートイントアングに保ちます。 //セットを使用してコレクション属性を保存しますプライベートセット<アドレス>アドレス= new Hashset <Address>(); //各属性のセッターとゲッターのメソッドは以下に省略されています...}Hibernateが永続的なクラスの収集プロパティを管理するために、プログラムは永続的なクラスの次のマッピングファイルを提供します。
リスト2。Person.hbm.xml
<?xml version = "1.0" encoding = "gbk"?> <!doctype hibernate-mapping public " - // hibernate/hibernateマッピングPackage = "org.crazyit.app.domain"> <! - マッピング担当者の永続性クラス - > <class name = "person" table = "person_inf"> <! - マッピング識別プロパティid-> <id name = "id" column = "person_id"> <! - プライマリキージェネレーターポリシーの定義 - > < name = "age" type = "int"/> <! - マップコレクション属性 - > <set name = "addresses" table = "person_address" lazy = "true"> <! - > <キー列= "persone_id"/> <composite-rement> < name = "zip"/> </composite-element> </set> </class> </hibernate-mapping>
ファイルをマップする上記のコードから、個人のコレクション属性のアドレスクラスは通常のpojoにすぎないことがわかります。アドレスクラスには、詳細とzipの2つの属性が含まれています。アドレスクラスコードは非常に簡単であるため、このクラスのコードはここでは説明されていません。
上記のマッピングファイルの<set .../>要素のコードは、lazy = "true"(<set .../>要素、lazy = "true"はデフォルト値です)を指定します。
たとえば、次のコードに従って、ID 1の個人エンティティをロードします。
セッションセッション= sf.getCurrentsession();トランザクションtx = session.begintransaction(); person p =(person)session.get(person.class、1); // <1> system.out.println(p.getname());
上記のコードは、ID 1の個人エンティティにアクセスする必要があり、この個人エンティティに関連付けられているアドレスオブジェクトにアクセスしたくありません。現時点では2つの状況があります。
1.読み込みが遅れていない場合、Hibernateは、個人エンティティに対応するデータレコードをロードするときに、個人エンティティに関連付けられたアドレスオブジェクトをすぐにつかみます。
2.怠zyな荷重を使用する場合、Hibernateは個人エンティティに対応するデータレコードのみをロードします。
2番目のアプローチがデータベースとの相互作用を減らすだけでなく、アドレスエンティティの読み込みによって引き起こされるメモリオーバーヘッドを回避することは明らかです。これが、冬眠がデフォルトで怠zyなロードを可能にする理由でもあります。
今の問題は、怠zyなロードがどのように実装されているのかということです。 hibernate個人エンティティをロードするときの個人エンティティのプロパティ値は何ですか?
この問題を解決するために、コード<1>にブレークポイントを設定し、Eclipseでデバッグします。この時点で、図1に示すように、Eclipseコンソールウィンドウの出力があることがわかります。
図1。レイジーロードコレクションプロパティのコンソール出力
図1の出力に示されているように、Hibernateは個人エンティティに対応するデータテーブルからデータのみを取得し、アドレスオブジェクトに対応するデータテーブルからデータを取得しません。これは怠zyなロードです。
では、個人エンティティの住所財産は何ですか?この時点で、Eclipseの変数ウィンドウから図2に示されている結果を見ることができます。
図2。レイジーロードされたコレクション属性値
図2のボックス内のコンテンツから、アドレスプロパティは、ハッシュセットやツリーセットなどの馴染みのある実装クラスではなく、SETインターフェイスのHibernateが提供する実装クラスであるPersistentesTセットの実装クラスであることがわかります。
PersistentSetコレクションオブジェクトは、基礎となるデータテーブルのデータを実際にはキャプチャしないため、コレクション内のアドレスオブジェクトを実際に初期化することは自然に不可能です。ただし、PersistentSetコレクションにはセッション属性があります。これは冬眠セッションです。プログラムがPersistentest Collection要素にアクセスする必要がある場合、PersistentSetはこのセッション属性を使用して、実際のアドレスオブジェクトに対応するデータレコードを取得します。
それでは、それらのアドレスエンティティに対応するデータレコードを正確に取得しますか? PersistentSetコレクションには所有者属性もあるため、これは困難ではありません。これは、アドレスオブジェクトが属する人物を示すものです。 Hibernateは、データテーブルに対応するアドレスに対応するデータテーブルからデータを検索します。
たとえば、図2に示すウィンドウのアドレス行をクリックします。つまり、アドレス属性をデバッグして出力するようにEclipseに指示します。これは、アドレス属性にアクセスするためです。この時点で、Eclipse Consoleウィンドウに次のSQLステートメントを見ることができます。
addresses0_.person_id as person1_0_0_、addresses0_.detail as details0_、addresses0_.zip as zip0_from from_address dresdess0_where destresses0_.person_id =?
これは、所有者の属性に従って特定のアドレスレコードをキャプチャするPersistentsetコレクションとSQLステートメントです。この時点で、Eclipseの変数ウィンドウから図3に示す出力を見ることができます。
図3。ロードされたコレクション属性値
図3からわかるように、この時点でのアドレス属性は初期化されており、セットには2つのアドレスオブジェクトが含まれています。
上記の紹介から、Hibernateの設定属性の負荷を遅らせる鍵は、Persistentset実装クラスにあることがわかります。怠zyなロード中、Persistentestセットのコレクションには要素がありません。ただし、PersistentSetはHibernateセッションを保持します。これにより、プログラムがコレクションにアクセスする必要がある場合、データレコードが「すぐに」ロードされ、コレクション要素がロードされることが保証されます。
PersistentSetの実装クラスと同様に、HibernateはPersistentList、PersistentMap、PersistentEnterSortedMap、PersistentENTENTORTEDSET、およびその他の実装クラスも提供し、それらの機能はPersistentSetの機能とほぼ類似しています。
Hibernate Collection属性に精通している読者は、覚えておく必要があります。Hibernateでは、宣言属性は、セット、リスト、マップ、ソートセット、ソートマップなどのインターフェイスでのみ使用でき、ハッシュセット、ハッシュマップ、ツリーセット、Treemap、その他の実装クラスを使用して実装できません。その理由は、収集属性の読み込みを遅らせる必要があるため、休止状態の遅延荷重は、永続性セット、永続性エントリスト、永続的なマップ、継続センテントセット、つまり、継続的なハイバーネートは、独自のコレクション実装クラスを使用して、コレクションのコレクションを使用して、コレクションを使用する必要があります。
Hibernateは、デフォルトでコレクション属性に怠zyなロードを使用します。いくつかの特別な場合、<set .../>、<list .../>、<map .../>などの要素のlazy = "false"属性を設定して、怠zyなロードをキャンセルします。
関連するエンティティの読み込みを遅らせます
デフォルトでは、Hibernateは怠zyなロードを使用して関連するエンティティをロードします。 1対1の関連性、1対1の関連性、または多対マニュアソシエーションであろうと、Hibernateはデフォルトで怠zyなロードを使用します。
関連するエンティティの場合、それらは2つのケースに分けることができます。
1.関連するエンティティが複数のエンティティ(1対多、多数を含む)である場合:現時点では、関連するエンティティはコレクションの形で存在し、冬眠はlazyセット、永続的なリスト、永続的なマップ、westernterStortedSet、およびその他のコレクションを使用して、レイジーロードエンティティを管理します。これは、以前に導入された状況です。
2。関連するエンティティが単一のエンティティである場合(1対1と多数のエンティティを含む):Hibernateがエンティティをロードする場合、遅延関連エンティティは動的に生成されたプロキシオブジェクトになります。
関連するエンティティが単一のエンティティである場合、つまり、関連するエンティティが<buly-one .../>または<one-one .../>を使用してマッピングされた場合、これらの2つの要素は、怠zyな属性を介して怠zyなロードを指定することもできます。
次の例は、アドレスクラスを永続的なクラスにマッピングします。現時点では、アドレスクラスもエンティティクラスになり、個人エンティティとアドレスエンティティが1対多くの双方向の関連付けを形成します。この時点でのマッピングファイルコードは次のとおりです。
リスト3。Person.hbm.xml
<?xml version = "1.0" encoding = "gbk"?> <! - hibernateのDTD情報を指定 - > <! 3.0 // en "" http://www.hibernate.org/dtd/hibernate-mapting-3.0.dtd "> <hibernate-mapping package =" org.crazyit.app.domain "> <! - マッピング人の永続性クラス - > <クラス名="人列= "person_id"> <! - プライマリキージェネレーターポリシーを定義 - > <ジェネレーター/> <! - 共通属性をマッピングするために使用される - > <プロパティ名= "name" type = "string"/> <プロパティ名= "age" type = "int"/> <! - コレクション要素は別の永続的なエンティティです。 inverse = "true"> <! - 関連する外部キー列を指定 - > <key column = "person_id"/> <! - 関連するクラス属性にマッピングするために使用 - > <on-many/> </set> </class> <! - マップアドレス永続的なクラス - > <クラス名= "アドレステーブル="アドレス_INF "> <プライマリキージェネレーターポリシーを指定 - > <Generator/> </id> <! - 通常の属性詳細 - > <プロパティ名= "詳細"/> <! - 通常の属性zip-> <プロパティ名= "zip"/> <! - 列名はperson_idとして指定する必要があります。 not-null = "true"/> </class> </hibernate-mapping>
次に、プログラムは、次のコードスニペットを介してID 1で個人エンティティをロードします。
//コンテキスト依存セッションセッション= sf.getCurrentssession(); transaction tx = session.begintransaction(); address address =(address)session.get(address.class、1); // <1> system.out.println(address.getDetail());
アドレスエンティティをロードするときにHibernateの関連するエンティティの処理を確認するために、コード<1>にブレークポイントを設定し、Eclipseでデバッグします。この時点で、Eclipse Consoleウィンドウが次のSQLステートメントを出力していることがわかります。
address0.address_idをaddress1_1_0_、address0_.detail as detail1_0_、address0_.zip as zip1_0_、address0_.person_id as persons_inf address0_where destress0_.address_id =?
このSQLステートメントから、Hibernateがアドレスエンティティに対応するデータテーブルをクロールレコードにロードするが、個人エンティティに対応するデータテーブルからレコードをクロールすることはないことを確認することは難しくありません。
Eclipseの変数ウィンドウから、図4に示す出力を参照してください。
図4。負荷の遅延エンティティ
図4から、アドレスエンティティに関連付けられている個人エンティティは個人のオブジェクトではなく、個人_ $$ _ javassist_0クラスのインスタンスであることが明確にわかります。このクラスは、Javassist Projectを使用してHibernateによって動的に生成されるプロキシクラスです。 Hibernateが関連するエンティティのロードを遅らせると、Javassistは動的プロキシオブジェクトを生成するために使用され、このプロキシオブジェクトは「まだロードされていない」をプロキシする責任があります。
アプリケーションが「まだロードされていない」関連エンティティを使用する必要がある限り、人_ $$ _ javassist_0プロキシオブジェクトは、実際の関連エンティティのロードと実際の関連エンティティの返却に責任があります - これは最も典型的なプロキシパターンです。
図4に示されている変数ウィンドウ(つまり、デバッグモードで使用する人属性を強制する)で属性をクリックすると、Eclipseのコンソールウィンドウに次のSQLステートメント出力が表示されます。
person0.person_idはperson1_0_0_、person0.name as name0_0_、person0.ageとしてperson_inf person0_where person0_.person_id =?
上記のSQLステートメントは、「遅延荷重」の関連するエンティティをキャプチャするステートメントです。現時点では、変数ウィンドウ出力の図5に示す結果を見ることができます。
図5。ロードされたエンティティ
Hibernateは、「遅延負荷」モードを採用して、関連するエンティティを管理します。実際、メインエンティティをロードするとき、関連するエンティティの対応するデータを実際に取得するのではなく、関連するエンティティのプロキシとしてオブジェクトを動的に生成するだけです。アプリケーションが実際に関連するエンティティを使用する必要がある場合、プロキシオブジェクトは、基礎となるデータベースからレコードを取得し、実際の関連エンティティを初期化する責任があります。
Hibernate Delay Loadingでは、クライアントプログラムが取得し始めるのは動的に生成されたプロキシオブジェクトです。一方、実際のエンティティは管理のためのプロキシオブジェクトに委任されます。これは典型的なプロキシパターンです。
エージェントモード
プロキシモードは、非常に広いアプリケーションを備えた設計モードです。クライアントコードがオブジェクトを呼び出す必要がある場合、クライアントは実際にオブジェクトを正確に取得するかどうかを気にしません。関数を提供できるオブジェクトのみが必要です。この時点で、オブジェクトのプロキシ(プロキシ)を返すことができます。
この設計方法では、システムはプロキシオブジェクトを備えたオブジェクトを提供し、プロキシオブジェクトはソースオブジェクトへの参照を制御します。プロキシは、別のJavaオブジェクトに代わって作用するJavaオブジェクトです。場合によっては、クライアントコードはCalleeを必要としないか、直接呼び出すことができず、プロキシオブジェクトはクライアントとターゲットオブジェクトの間の仲介者として機能することができます。
クライアントの場合、プロキシオブジェクトと実際のオブジェクトの違いを区別することも、プロキシオブジェクトと実際のオブジェクトの違いを区別する必要もありません。クライアントコードは、実際のプロキシオブジェクトを知りません。クライアントコードはインターフェイス指向であり、プロキシオブジェクトのインターフェイスのみを保持します。
要するに、クライアントコードが呼び出されたオブジェクトに直接アクセスすることができないか、そうでない限り、この状況には高いシステムのオーバーヘッドを持つオブジェクトを作成したり、呼び出されたオブジェクトがリモートホストにあるか、ターゲットオブジェクトの関数はニーズを満たすのに十分ではありません...しかし、追加のプロキシオブジェクトは、使用のためにクライアントに戻すために作成されます。
以下は、単純なプロキシモードを示しています。このプログラムは、最初に大きな画像オブジェクトによって実装されたインターフェイスを表す画像インターフェイスを提供します。インターフェイスコードは次のとおりです。
リスト3。image.java
パブリックインターフェイス画像{void show();}このインターフェイスは、大きな画像オブジェクトをシミュレートする実装クラスを提供し、実装クラスのコンストラクターはThread.sleep()メソッドを使用して3Sを一時停止します。以下は、BigImageのプログラムコードです。
リスト4。bigimage.java
//このbigimageを使用して大画像をシミュレートしますpublic class bigimageはimage {public bigimage(){try {//プログラムモードシミュレーションシステムオーバーヘッドスレッド.sleep(3000); System.out.println( "画像の読み込みに正常に...");} catch(arternedexception ex){ex.printstacktrace();}} //画像にshow()メソッドを実装public void show(){system.out.println( "実際の大きな画像を描く");}}}}}}}}上記のプログラムコードは3Sを一時停止します。これは、BigImageオブジェクトを作成するのに3秒の時間がかかることを示しています。プログラムは、この遅延を使用して、この画像をロードすることによって引き起こされるシステムオーバーヘッドをシミュレートします。プロキシモードが使用されない場合、プログラムでBigImageが作成されると、システムは3S遅延を生成します。この遅延を回避するために、プログラムはBigImageオブジェクトのプロキシオブジェクトを提供し、BigImageクラスのプロキシクラスは次のとおりです。
リスト5。ImageProxy.java
パブリッククラスの画像プロキシは画像を実装します{//画像インスタンスをプロキシオブジェクトとして組み合わせますプライベート画像画像; //プロキシオブジェクトの初期化パブリックイメージProxy(this.image = image;}/*** show interface*のshow()メソッドを書き直します*この方法は、proxyオブジェクトを使用しているのです。 void show(){// proxyオブジェクトを作成する場合にのみ(image == null){image = new bigimage();} image.show();}}}上記のImageProxyプロキシクラスは、同じshow()メソッドをBigImageとして実装します。これにより、クライアントコードはプロキシオブジェクトを取得した後、プロキシオブジェクトをBigImageとして使用できます。
コントロールロジックは、ImageProxyクラスのshow()メソッドに追加されます。この制御ロジックは、システムが実際に画像のshow()を呼び出す場合にのみプロキシBigImageオブジェクトが作成されることを制御するために使用されます。次のプログラムはBigImageオブジェクトを使用する必要がありますが、プログラムはBigImageインスタンスを直接返すのではなく、最初に次のプログラムに示すようにBigImageプロキシオブジェクトを返します。
リスト6。bigimagetest.java
public class bigimagetest {public static void main(string [] args){long start = system.currenttimemillis(); //プログラムは画像オブジェクトを返します。これは、bigimage画像のプロキシオブジェクト= new imageproxy(null); system.out.println( "system of the system of offering(" start)); //プログラムは、画像プロキシのshow()メソッドが実際に呼び出されると、実際にプロキシオブジェクトを作成します。 Image.show();}}上記のプログラムは、プログラムが実際にBigImageオブジェクトを作成するのではなく、ImageProxyプロキシオブジェクトを取得するだけで画像を非常に迅速に初期化します - プログラムがImage.show()メソッドを呼び出すまで、プログラムは実際にBigImageオブジェクトのショー()メソッドを呼び出す必要があり、プログラムは実際にBigImageオブジェクトを作成します。上記のプログラムを実行し、図6に示す結果を参照してください。
図6。プロキシモードを使用してパフォーマンスを向上させます
図6に示されている実行の結果を見ると、読者はプロキシモードを使用すると画像オブジェクトの取得のシステムパフォーマンスが向上することに同意できるはずです。しかし、一部の読者は質問をするかもしれません:プログラムがImageProxyオブジェクトのshow()メソッドを呼び出すと、BigImageオブジェクトも作成する必要がありますが、システムオーバーヘッドは実際には削減されていませんか?このシステムのオーバーヘッドが遅れているだけですか?
次の2つの視点からこの質問に答えることができます。
実際に必要になるまでBigImageの作成を遅らせると、以前のプログラムのスムーズな動作を確保し、メモリ内のBigImageの生存時間を短縮し、マクロの観点からシステムのオーバーヘッドを保存します。
場合によっては、プログラムが実際にImageProxyオブジェクトのShow()メソッドを呼び出すことはないかもしれません - つまり、システムはBigImageオブジェクトをまったく作成する必要がありません。この場合、プロキシモードを使用すると、システムの動作性能が大幅に向上する可能性があります。
Hibernateは、まったく似ていますが、プロキシモードを使用して、関連するエンティティをロードする時間を「遅らせる」こともします。プログラムが関連するエンティティにアクセスする必要がない場合、プログラムは関連するエンティティをクロールしません。これにより、システムのメモリが頭上を保存し、冬眠がエンティティをロードする時間を短くすることができます。
まとめ
Hibernate Lazy Loadは、本質的にプロキシモードのアプリケーションです。過去数年間で、システムのオーバーヘッドを減らし、アプリケーションのパフォーマンスを向上させるために、プロキシモードを使用してきました。 Hibernateは、プロキシモードのこの利点を活用し、JavassistまたはCGLIBを組み合わせてプロキシオブジェクトを動的に生成し、プロキシモードに柔軟性を追加します。 Hibernateは、この使用法に新しい名前を与えます:Lazy Loading。いずれにせよ、これらのオープンソースフレームワークの実装を完全に分析および理解することで、古典的なデザインモデルの利点をよりよく体験できます。
この記事の説明が、Hibernate Frameworkに基づいた全員のJavaプログラミングに役立つことを願っています。