この章では、オブジェクトをOracleデータベース・サーバーで操作するためのOCIの機能を紹介します。また、OCIのオブジェクト・ナビゲーショナル関数コール、型の変更およびXMLのサポートについても説明します。
この章は、次の項目で構成されています。
オブジェクト・キャッシュは、オブジェクトの参照とメモリー管理をサポートするクライアント側のメモリー・バッファです。これはOCIアプリケーションでフェッチしたオブジェクト・インスタンスを格納し、追跡するためのものです。オブジェクト・キャッシュには、メモリー管理が用意されています。
アプリケーションでSQLのSELECT
文またはOCIのPINオペレーションによってオブジェクトをフェッチすると、オブジェクトのコピーがオブジェクト・キャッシュに格納されます。SELECT
文で直接フェッチされたオブジェクトは、値によってフェッチされる、確保できない参照不可能なオブジェクトです。確保できるのは参照可能オブジェクトのみです。
オブジェクトを確保するとき、適切なバージョンのオブジェクトがすでにキャッシュ内に存在していれば、サーバーからオブジェクトをフェッチする必要はありません。
OCIを使用してREF
を参照解除し、オブジェクトを取り出す各クライアント・プログラムでは、オブジェクト・キャッシュが使用されます。クライアント側のオブジェクト・キャッシュは、オブジェクト・モードで初期化されたすべてのOCI環境ハンドルで割り当てられます。1つのプロセスの複数のスレッドで、同一のOCI環境ハンドルを共有することによって同一のクライアント側キャッシュを共有できます。
オブジェクト・キャッシュ内には接続ごとに、各参照可能オブジェクトのコピーがそれぞれ1つあります。オブジェクト・キャッシュは、接続によって論理的にパーティション化されます。
1つのREFを複数回参照解除するか、同じ接続における等価な複数のREFを参照解除した場合は、オブジェクトの同一コピーが戻されます。
キャッシュ内にあるオブジェクトのコピーを変更した場合、変更内容をその他のプロセスに見えるようにするには、変更内容をサーバーにフラッシュする必要があります。必要のなくなったオブジェクトは、確保解除つまり解放できます。そしてキャッシュからスワップ・アウトでき、それによって、オブジェクトが使用していたメモリー領域を解放できます。
キャッシュにロードされたデータベース・オブジェクトは、C言語構造体に透過的にマップされます。オブジェクト・キャッシュでは、キャッシュ内のすべてのオブジェクト・コピーと、それぞれに対応するデータベース内のオブジェクトとの間の関連付けが、メンテナンスされます。トランザクションをコミットすると、キャッシュ内のオブジェクト・コピーに対して行った変更がデータベースに自動的に伝播されます。
オブジェクト・キャッシュではオブジェクト・コピーの内容は管理されず、オブジェクト・コピーが自動的にリフレッシュされることもありません。アプリケーションでは、オブジェクト・コピーの内容が正しく、一貫性があることを確認する必要があります。たとえば、アプリケーションでオブジェクト・コピーに挿入、更新または削除のマークを付けた後、トランザクションが終了したとします。この場合、オブジェクト・キャッシュではオブジェクト・コピーのマークが解除されるのみであり、コピーの削除や無効化は行われません。アプリケーションでrecentまたはlatestを確保するか、オブジェクトのコピーを次のトランザクションでリフレッシュします。anyを確保すると、以前終了したトランザクションから、変更をコミットしていない同じオブジェクト・コピーを取得する場合があります。
オブジェクト・キャッシュは、mode
がOCI_OBJECT
のOCIEnvCreate()
によってOCI環境が初期化されると作成されます。
オブジェクト・キャッシュでは、REF
をオブジェクトにマッピングするための高速参照表がメンテナンスされています。アプリケーションがREF
を参照解除するときに、対応するオブジェクトがオブジェクト・キャッシュにキャッシュされていない場合、オブジェクト・キャッシュは、サーバーに対して、オブジェクトをデータベースからフェッチしてオブジェクト・キャッシュにロードする要求を自動的に送信します。
同じREF
の後続の参照解除は、ローカル・キャッシュへのアクセスとなり、ネットワーク・ラウンドトリップが発生しないため、高速になります。アプリケーションによるキャッシュ内オブジェクトへのアクセスを、オブジェクト・キャッシュに通知するために、アプリケーションは、オブジェクトを確保します。アプリケーションによるオブジェクトの処理が終了すると、オブジェクトの確保解除が行われます。オブジェクト・キャッシュでは、キャッシュ内の各オブジェクトの確保カウントがメンテナンスされています。このカウントは確保コール時に増分され、確保解除コール時に減分されます。確保カウントが0(ゼロ)の場合は、そのオブジェクトがアプリケーションでは不要になったことを意味します。
オブジェクト・キャッシュでは、最低使用頻度(LRU)アルゴリズムを使用して、キャッシュのサイズが管理されます。LRUアルゴリズムは、キャッシュが最大サイズに達すると、候補オブジェクトを解放します。候補オブジェクトとは、確保カウントが0(ゼロ)のオブジェクトのことです。
同じサーバーに対して稼働中の各アプリケーション・プロセスには、図14-1「オブジェクト・キャッシュ」で示すような専用のオブジェクト・キャッシュがあります。
オブジェクト・キャッシュは、メモリーに現在あるオブジェクトの追跡、そのオブジェクトへの参照のメンテナンス、自動オブジェクト・スワッピングの管理、オブジェクトのメタ属性の追跡を行います。
オブジェクト・キャッシュ内のオブジェクト・コピーと、データベース内で対応するオブジェクトとの間における値の一貫性は、自動的にはメンテナンスされません。つまり、アプリケーションでオブジェクト・コピーを変更すると、その変更内容はデータベース内の対応するオブジェクトに自動的に適用されず、その逆も同様です。オブジェクト・キャッシュでは、変更されたオブジェクト・コピーをデータベースにフラッシュする操作や、古いオブジェクト・コピーをデータベースの最新値にリフレッシュする操作が可能であり、プログラムである程度の一貫性をメンテナンスできます。
注意: Oracleは、サーバーのバッファ・キャッシュまたはデータベースとの自動キャッシュ一貫性をサポートしていません。自動キャッシュ一貫性とは、対応するオブジェクトがサーバーのバッファ・キャッシュで変更されると、ローカル・オブジェクト・コピーがオブジェクト・キャッシュによってリフレッシュされるメカニズムを指します。このメカニズムは、オブジェクト・キャッシュが、サーバー内の対応するオブジェクトに直接アクセスする前に、ローカル・オブジェクト・キャッシュに行われた変更をバッファ・キャッシュにフラッシュしたとき実行されます。直接的なアクセスには、SQL、トリガーまたはストアド・プロシージャを使用した、サーバーのオブジェクトの読取りや変更が含まれます。 |
オブジェクト・キャッシュには、環境ハンドルの属性である、2つの重要なパラメータが関連付けられています。
これらのパラメータは、キャッシュのメモリー使用量レベルを示し、削除してもよいオブジェクトをいつ自動的にキャッシュから削除してメモリーを解放するかを判断するのに役立ちます。
現在キャッシュにあるオブジェクトのメモリー使用量が最大キャッシュ・サイズに達するかそれを超えると、キャッシュでは、確保カウントが0(ゼロ)の、マークされていないオブジェクトの自動的な解放(またはエージ・アウト)が始まります。キャッシュのメモリー使用量が最適サイズに達するか、解放対象オブジェクトがなくなるまで、オブジェクトの解放が続きます。キャッシュが指定した最大キャッシュ・サイズを超えることがあります。
OCI_ATTR_CACHE_MAX_SIZE
は、OCI_ATTR_CACHE_OPT_SIZE
のパーセントで指定します。オブジェクト・キャッシュの最大サイズ(バイト単位)は、次のようにOCI_ATTR_CACHE_OPT_SIZE
をOCI_ATTR_CACHE_MAX_SIZE
のパーセントで増分して計算します。
maximum_cache_size = optimal_size + optimal_size * max_size_percentage / 100
または
maximum_cache_size = OCI_ATTR_CACHE_OPT_SIZE + OCI_ATTR_CACHE_OPT_SIZE * OCI_ATTR_CACHE_MAX_SIZE / 100
OCI_ATTR_CACHE_MAX_SIZE
の値は、OCI_ATTR_CACHE_OPT_SIZE
の10%(デフォルト)に設定できます。OCI_ATTR_CACHE_OPT_SIZE
のデフォルト値は8MBです。
環境ハンドルのキャッシュ・サイズ属性の設定にはOCIAttrSet()
コールを、取出しにはOCIAttrGet()
関数を使用します。
この項では、オブジェクト・キャッシュで使用されるオブジェクト・コピーを操作する重要な関数について説明します。
オブジェクト・コピーの確保によって、アプリケーションは、そのコピーへのREF
を参照解除して、キャッシュ内のオブジェクト・コピーにアクセスできるようになります。
オブジェクトを確保解除すると、オブジェクトが現在使用中でないことがキャッシュに対して示されます。不要になったオブジェクトは確保解除してキャッシュからの暗黙的な解放の対象にし、メモリーを解放してください。
オブジェクトをマークすることで、キャッシュ内のオブジェクト・コピーが更新されており、オブジェクト・コピーをフラッシュするときにサーバー内の対応するオブジェクトを更新する必要があることが、キャッシュに通知されます。
オブジェクトをマーク解除すると、オブジェクトが更新されたという指示が削除されます。
オブジェクトをフラッシュすると、キャッシュ内のマークされたオブジェクト・コピーに対して行ったローカルな変更が、サーバー内の対応するオブジェクトに書き込まれます。それによって、オブジェクト・キャッシュ内のコピーのマークが解除されます。
キャッシュ内のオブジェクト・コピーをリフレッシュすると、そのコピーは、サーバー内の対応するオブジェクトの最新値に置き換わります。
注意: トップレベルのオブジェクト・メモリーへのポインタは、リフレッシュ後も有効です。2次レベル・メモリーへのポインタ(文字列テキスト・ポインタ、コレクションなど)は、リフレッシュ後に無効になる場合があります。 |
たとえば、オブジェクトの型がperson
で、salary (NUMBER)
とname (VARCHAR2(20)
)の2つの属性を持っているとします。この型は、次のとおりです。
struct Person { OCINumber salary; OCIString *name; }
クライアントがPerson
インスタンスへのポインタscott_p
を持っており、そのインスタンスに対してOCIObjectRefresh()
をコールする場合、ポインタscott_p
はリフレッシュ後も同じままですが、scott_p->name
などの第2レベルのメモリーへのポインタは異なることがあります。
この項では、確保、確保解除および解放の各関数を説明します。
オブジェクト・キャッシュ内のREF
をアプリケーションで参照解除する必要がある場合は、OCIObjectPin()
をコールします。このコールによってREF
が参照解除され、キャッシュ内のオブジェクト・コピーが確保されます。オブジェクト・コピーは、確保されているかぎり、アプリケーションからアクセス可能であることが保証されます。OCIObjectPin()
で、PINオプションであるany、recentまたはlatestを受け付けます。PINオプションのデータ型はOCIPinOpt
です。
any(OCI_PIN_ANY
)オプションが指定される場合で、キャッシュ内にオブジェクト・コピーがすでにある場合は、それをオブジェクト・キャッシュでただちに戻します。キャッシュ内にオブジェクト・コピーがない場合、オブジェクト・キャッシュでは最新のオブジェクト・コピーをデータベースからロードして、そのオブジェクト・コピーが戻されます。anyオプションは、製品、販売員、ベンダー、販売領域、部品、事務所などの、読取り専用、情報、事実またはメタのオブジェクトに適しています。これらのオブジェクトは、通常は頻繁に変更されることはなく、変更されてもその変更内容はアプリケーションに影響しません。
オブジェクト・キャッシュでは、指定された接続のキャッシュの論理パーティション内にあるオブジェクト・コピーのみが検索される点に注意してください。パーティション内にコピーがない場合、オブジェクトの最新コピーがサーバーからロードされます。
latest(OCI_PIN_LATEST
)オプションが指定されている場合、オブジェクト・キャッシュでは、最新のオブジェクト・コピーがデータベースからキャッシュにロードされます。そのオブジェクト・コピーがキャッシュ内でロックされていないかぎり、そのオブジェクト・コピーは戻されます。この場合、マークされたオブジェクト・コピーがただちに戻されます。オブジェクトがすでにキャッシュ内にあり、ロックされていない場合は、最新のオブジェクト・コピーがロードされ、既存のコピーが上書きされます。latestオプションは、発注情報、バグ、明細項目、銀行口座、株価などの操作オブジェクトに適しています。これらのオブジェクトは通常頻繁に変更され、プログラムではこれらのオブジェクトにできるだけ最新の状態でのアクセスが試行されます。
recent(OCI_PIN_RECENT
)オプションが指定される場合、2つの可能性が考えられます。
同じトランザクションにおいて、latestまたはrecentオプションを使用して、オブジェクト・コピーが以前に確保されている場合は、recentオプションはanyオプションと同等になります。
前述の条件が当てはまらない場合は、recentオプションはlatestオプションと同等になります。
プログラムでオブジェクトが確保される場合、プログラムではsessionまたはtransactionという2つの可能な値の内の1つが確保継続時間に指定されます。継続時間のデータ型はOCIDuration
です。
確保継続時間がsession(OCI_DURATION_SESSION
)の場合、セッションを終了するまで(つまり接続の終了まで)、またはプログラムで(OCIObjectUnpin()
をコールして)明示的に確保解除されるまで、オブジェクト・コピーは確保されたままとなります。
確保継続時間がtransaction(OCI_DURATION_TRANS
)の場合、トランザクションの終了まで、または明示的に確保解除されるまで、オブジェクト・コピーは確保されたままとなります。
オブジェクト・コピーをデータベースからキャッシュにロードするとき、実際にはキャッシュで次の命令が実行されます。
SELECT VALUE(t) FROM t WHERE REF(t) = :r
このコード例のt
はオブジェクトが格納されているオブジェクト表であり、r
はREF
です。フェッチされた値はキャッシュ内のオブジェクト・コピーの値になります。
オブジェクト・キャッシュでは、個々のSELECT
文が効率的に実行され、コミット読取りトランザクションで各オブジェクト・コピーがキャッシュにロードされます。オブジェクト・コピー相互の読取り一貫性は保証されません。
シリアライズ可能トランザクションでは、recentまたはlatestで確保されたオブジェクト・コピーは相互の読取り一貫性があります。これは、これらのオブジェクト・コピーをロードするSELECT
文は、同じデータベース・スナップショットに基づいて実行されるためです。
コミット読取りトランザクションとシリアライズ・トランザクションは、データベースがサポート可能な異なる分離レベルを示します。これ以外にも、非コミット読取りや反復可能読取りなど様々な分離レベルがあります。各分離レベルでは、同時トランザクション間でのある程度の干渉は許容されます。通常、分離レベルでより多くの干渉が許容される場合、同時トランザクションの同時実行性が向上します。コミット読取りトランザクションでは、問合せが複数回実行された場合、コミットされた他のトランザクションによる変更が表示されるため、一貫性のないデータ・セットが生成されることがあります。シリアライズ・トランザクションの場合は、このようなことはありません。
オブジェクト・キャッシュのモデルは、Oracleトランザクション・モデルに対して独立しています。オブジェクト・キャッシュの動作は、トランザクション・モデルによって変わることはありません。ただし、同じプログラムを異なる複数のトランザクション・モデル(コミット読取りとシリアライズ可能など)のもとで実行した場合、オブジェクト・キャッシュを介してサーバーから取り出したオブジェクトは、異なることがあります。
注意: OCIObjectArrayPin() の場合、オブジェクトは常にデータベースから取り出されるため、PINオプションは無効です。REFがキャッシュ内のオブジェクトに対するものである場合、OCIObjectArrayPin() は次のエラーで失敗します。
Ora-22881: dangling REF. |
プログラムで使用しないオブジェクト・コピーは、確保解除できます。確保解除したオブジェクト・コピーは、解放できます。キャッシュのメモリーが残り少なくなった場合は、オブジェクト・コピーを完全に確保解除すると同時にマーク解除し、キャッシュ内のそのコピーを暗黙的解放の対象にする必要があります。オブジェクト・コピーを完全に確保解除するには、N回確保した場合はN回確保解除する必要があります。
確保解除済でマーク未解除のオブジェクト・コピーは、フラッシュするか、ユーザーが明示的にマーク解除しないと暗黙的解放の対象になりません。ただし、オブジェクト・キャッシュのメモリーが残り少なくなると、オブジェクト・キャッシュのオブジェクト・コピーは暗黙的に解放されるので、確保解除したオブジェクト・コピーを必ずしも解放する必要はありません。暗黙的に解放されていないオブジェクト・コピーをプログラムで(anyまたはrecentオプションを指定して)再度確保した場合、同一のオブジェクト・コピーを入手することになります。
アプリケーションでオブジェクト・コピーを確保解除するには、OCIObjectUnpin()
またはOCIObjectPinCountReset()
をコールします。さらに、プログラムでは、特定の接続に対してキャッシュ内のすべてのオブジェクト・コピーを完全に確保解除するために、OCICacheUnpin()
をコールできます。
オブジェクト・コピーの解放とは、オブジェクト・キャッシュからオブジェクト・コピーを削除し、そのコピーが使用していたメモリーを解放することです。キャッシュのメモリーを解放するには、次の2つの方法があります。
明示的解放 − プログラムでOCIObjectFree()
をコールして、キャッシュからオブジェクト・コピーを明示的に解放または削除します。この関数は、マークまたは確保されたオブジェクト・コピーのいずれかを強制的に解放するオプションがあります。OCICacheFree()
をコールしてキャッシュ内のすべてのオブジェクトを解放することもできます。
暗黙的解放 − キャッシュのメモリーが残り少なくなった場合は、確保解除済のオブジェクト・コピーとマーク解除済のオブジェクト・コピーの両方が、暗黙的に解放されます。マークされた、確保解除済のオブジェクトは、オブジェクト・コピーをフラッシュまたはマーク解除した場合にのみ、暗黙的解放の対象になります。
メモリー管理を目的とする場合は、不要になったオブジェクトをアプリケーションで確保解除することが重要です。確保解除したオブジェクトはキャッシュから削除される対象になるので、必要なときにキャッシュのメモリーをスムーズに解放できます。
OCIでは、クライアント側キャッシュにある参照されていないオブジェクトを解放する関数は提供していません。
この項では、オブジェクト・コピーをマークする関数とマークを解除する関数を説明します。
オブジェクト・コピーはキャッシュ内でローカルに作成、更新または削除できます。(OCIObjectNew()
をコールすることによって)キャッシュ内にオブジェクト・コピーを作成すると、オブジェクト・キャッシュでオブジェクト・コピーに挿入のマークが付けられます。そのため、このオブジェクト・コピーをフラッシュすると、オブジェクトがサーバーに挿入されます。
オブジェクト・コピーがキャッシュ内で更新された場合、ユーザーは、オブジェクト・コピーに更新のマークを付ける(OCIObjectMarkUpdate()
をコールする)ことによって、オブジェクト・キャッシュに通知する必要があります。オブジェクト・コピーをフラッシュすると、サーバー内の対応するオブジェクトが、オブジェクト・コピーの値に更新されます。
オブジェクト・コピーが削除された場合、そのオブジェクト・コピーは(OCIObjectMarkDelete()
をコールすることにより)オブジェクト・キャッシュ内で削除のマークが付けられます。オブジェクト・コピーをフラッシュすると、サーバー内の対応するオブジェクトが削除されます。マークされたオブジェクト・コピーのメモリーは、そのコピーをフラッシュして確保解除しないかぎり解放されません。プログラムで削除マーク付きのオブジェクトを確保すると、参照先のない参照を参照解除した場合と同様のエラーになります。
1つのオブジェクト・コピーを複数回変更した場合、そのコピーをフラッシュしたときにサーバーのオブジェクトに適用されるのは、最後の変更結果です。たとえば、オブジェクト・コピーを更新した後で削除した場合、オブジェクト・コピーをフラッシュすると、サーバーのオブジェクトは単に削除されます。同様に、オブジェクト・コピーの属性を複数回更新した場合、オブジェクト・コピーをフラッシュしたときにサーバーで更新されるのは、属性の最終的な値です。
プログラムでは、オブジェクト・コピーがオブジェクト・キャッシュにロードされている場合のみ、オブジェクト・コピーを更新済または削除済としてマークすることができます。
この項では、キャッシュとサーバーの同期化操作(フラッシュ、リフレッシュ)を説明します。
キャッシュ内のマークされたオブジェクト・コピーに対するローカルな変更は、オブジェクト・コピーをフラッシュしたときにサーバーに書き込まれます。プログラムで単一のオブジェクト・コピーをフラッシュするには、OCIObjectFlush()
をコールします。または、OCICacheFlush()
をコールして、キャッシュ内のすべてのマークされたオブジェクト・コピーをフラッシュするか、リストに選択されているマークされたオブジェクト・コピーをフラッシュできます。OCICacheFlush()
は、特定のサービス・コンテキストに関連付けられたオブジェクトをフラッシュします。「OCICacheFlush()」を参照してください。
オブジェクト・コピーをフラッシュすると、そのオブジェクト・コピーはマーク解除されます(フラッシュ後にオブジェクトはサーバーでロックされます。そのため、キャッシュ内のオブジェクト・コピーはロック状態とマークされます)。
注意: OCICacheFlush() 操作では、複数のオブジェクトがフラッシュされる場合でも、サーバー・ラウンドトリップは1回のみです。 |
ある型の使用済オブジェクトのみをフラッシュする場合、OCICacheFlush()
コールのオプションの引数であるコールバック関数を使用して行うことが可能です。目的のオブジェクトのみを戻すコールバックを定義します。この場合でも、フラッシュに対し、サーバー・ラウンドトリップが1回のみ発生します。
OCICacheFlush()
のデフォルト・モードでは、オブジェクトは使用済とマークされるようにフラッシュされます。このフラッシュ操作のパフォーマンスは、環境ハンドル内でOCI_ATTR_CACHE_ARRAYFLUSH
属性を設定すると、非常に向上します。
ただし、OCI_ATTR_CACHE_ARRAYFLUSH
モードは、オブジェクトがフラッシュされる順序が重要でない場合にのみ使用することができます。このモード中は、使用済オブジェクトはまとめてグループ化され、サーバーによる表の更新が効率的に行われるような方法でサーバーに送信されます。このモードが有効なときは、オブジェクトが使用済としてマークされる順序の維持は保証されません。
オブジェクト・コピーをリフレッシュすると、サーバー内の対応するオブジェクトの最新値が、オブジェクト・コピーに再ロードされます。最新値は、その他のコミット済トランザクションでの変更や、同じトランザクションで(オブジェクト・キャッシュを介さずに)直接サーバー内で行った変更を含む場合があります。プログラムでは、SQL DMLまたはトリガー、ストアド・プロシージャを使用して、サーバー内のオブジェクトを直接変更できます。
プログラムで、マークされたオブジェクト・コピーをリフレッシュするには、まずオブジェクト・コピーをマーク解除する必要があります。確保解除したオブジェクト・コピーは、リフレッシュすると(つまりキャッシュ全体をリフレッシュすると)単に解放されます。
プログラムで単一のオブジェクト・コピーをリフレッシュするには、OCIObjectRefresh()
をコールします。または、OCICacheRefresh()
をコールして、キャッシュ内のすべてのオブジェクト・コピー、トランザクションでロードしたすべてのオブジェクト・コピー(つまり、recentまたはlatestオプションを指定して確保したオブジェクト・コピー)、またはリストに選択されたオブジェクト・コピーのいずれかをリフレッシュできます。
オブジェクトをサーバーにフラッシュすると、トリガーを起動してサーバー内のさらに多くのオブジェクトを変更できます。その場合、オブジェクト・キャッシュ内にある(トリガーによって変更したオブジェクトと)同一のオブジェクトは最新の状態ではなくなり、リフレッシュしないとロックまたはフラッシュできません。
各種のメタ属性フラグおよびオブジェクト継続時間は、表14-1で説明するとおり、リフレッシュされた後で変更されます。
リフレッシュ実行時、オブジェクト・キャッシュでは新しいデータがオブジェクト・コピーのトップレベル・メモリーにロードされます。つまりトップレベル・メモリーは再使用されます。オブジェクト・コピーのトップレベル・メモリーには、オブジェクトのインライン属性が入っています。その反対に、アウト・ライン属性はサイズ変更ができるので、オブジェクト・コピーのアウト・ライン属性のメモリーは解放して再配置できます。
この項では、オブジェクトのロックに関連するOCI関数について説明します。
オブジェクトを確保するときに、そのオブジェクトをロック・オプションでロックするかどうかを指定できます。オブジェクトをロックすると、サーバー側のロックが取得されます。これによって、他のユーザーによるオブジェクトへの変更を防止します。このロックは、トランザクションのコミットまたはロールバック時に解放されます。ロック・オプションには次のものがあります。
ロック・オプションOCI_LOCK_X
−ロックを取得した後でのみオブジェクトを確保するようにキャッシュに指示します。オブジェクトが現在別のユーザーによってロックされている場合、このオプションによる確保コールは、ロックが取得できるまで待機してから、コール元に戻ります。これは、SELECT
FOR
UPDATE
文の実行に相当します。
ロック・オプションOCI_LOCK_X_NOWAIT
−ロックを取得した後でのみオブジェクトを確保するようにキャッシュに指示します。OCI_LOCK_X
オプションとは異なり、OCI_LOCK_X_NOWAIT
オプションによる確保コールは、オブジェクトが現在別のユーザーによってロックされている場合、待機しません。これは、SELECT
FOR
UPDATE
WITH
NOWAIT
文の実行に相当します。
プログラムでは、オプションでOCIObjectLock()
をコールして、更新するオブジェクトをロックできます。このコールは、データベースのオブジェクトに対する行ロックの実行をオブジェクト・キャッシュに指示します。これは、次のようにして実行します。
SELECT NULL FROM t WHERE REF(t) = :r FOR UPDATE
このコード例のt
は、ロックするオブジェクトを格納しているオブジェクト表であり、r
はオブジェクトを識別するREF
です。OCIObjectLock()
をコールすると、オブジェクト・キャッシュ内のオブジェクト・コピーがロック状態とマークされます。
オブジェクトのグラフまたはオブジェクトの集合をロックするには、オブジェクトごとにOCIObjectLock()
のコールが必要なため、数回のコールが必要です。配列確保のOCIObjectArrayPin()
コールを使用すると、パフォーマンスが向上します。
オブジェクトをロックすることにより、キャッシュのオブジェクトが最新のものであることをアプリケーションで保証します。オブジェクトがアプリケーションでロックされている間は、その他のトランザクションはそのオブジェクトを変更できません。
トランザクションの最後には、すべてのロックがサーバーによって自動的に解放されます。オブジェクト・コピーのロック状態インジケータはリセットされます。
ロックしようとしているオブジェクトが、現在別のユーザーによってロックされている場合があります。この場合、アプリケーションはブロックされます。
オブジェクトをロックする際にブロッキングを避けるためには、アプリケーションでOCIObjectLock()
コールのかわりにOCIObjectLockNoWait()
コールを使用できます。この関数では、別のユーザーによるロックのためオブジェクトをロックできない場合は、すぐにエラーが戻されます。
NOWAIT
オプションは、OCI_LOCK_X_NOWAIT
の値をロック・オプション・パラメータとして渡すことで、コールを確保するためにも利用できます。
OCIアプリケーションにコミット時ロックを実装するには、2つのオプションを使用します。
第1のコミット時ロック・オプションは、シリアライズ可能なレベルでトランザクションを実行するOCIアプリケーションのためのオプションです。
OCIでは、オブジェクト・キャッシュ内のオブジェクトをロックせずに参照解除して確保し、キャッシュ内でそのオブジェクトを(ロックせずに)変更し、使用済のオブジェクトをデータベースにフラッシュするコールをサポートしています。
トランザクションの開始以降、使用済オブジェクトが、別のコミット済トランザクションによって変更された場合、フラッシュの間に、シリアライズ不能トランザクションのエラーが戻されます。トランザクションの開始以降、別のトランザクションで変更された使用済オブジェクトがない場合は、変更内容はデータベースに正常に書き込まれます。
注意: OCITransCommit() は、最初に使用済オブジェクトをデータベースにフラッシュしてから、トランザクションをコミットします。 |
この機能により、コミット時ロック・モデルが効果的に実装されます。
アプリケーションでは、オブジェクトの変更検出モードを使用可能に切り替えられます。このためには、環境ハンドルのOCI_ATTR_OBJECT_DETECTCHANGE
属性の値にTRUE
を設定します。
このモードがアクティブのときは、別のオブジェクトがコミットしたトランザクションによってサーバー内で変更されているオブジェクトをフラッシュしようとすると、アプリケーションにORA-08179エラー「並行性チェックに失敗しました」が戻されます。この後、アプリケーションではこのエラーを適切な方法で処理できます。
トランザクションをコミットすると(OCITransCommit()
)、すべてのマークされたオブジェクトがサーバーにフラッシュされます。トランザクション継続時間を指定して確保したオブジェクト・コピーは、確保解除されます。
トランザクションをロールバックすると、すべてのマークされたオブジェクトがマーク解除されます。トランザクション継続時間を指定して確保したオブジェクト・コピーは、確保解除されます。
メモリーに空き領域を維持するため、オブジェクト・キャッシュでは、可能なかぎりオブジェクトのメモリーの再利用を試みます。オブジェクト・キャッシュでは、オブジェクトの継続時間(割当て時間)またはオブジェクトの確保継続時間が期限切れとなると、オブジェクトのメモリーを再使用します。割当て時間は、OCIObjectNew()
でオブジェクトを作成するときに設定されます。確保継続時間は、OCIObjectPin()
でオブジェクトを確保するときに設定されます。継続時間値のデータ型はOCIDuration
です。
注意: オブジェクトの確保継続時間は、オブジェクトの割当て時間より長くは設定できません。 |
オブジェクトの割当て時間が終わりに達すると、オブジェクトは自動的に削除され、メモリーが再使用可能になります。確保継続時間はオブジェクトのメモリーを再使用できる時期を示し、キャッシュがいっぱいになるとメモリーは再使用されます。
OCIでは、次の2つの事前定義済の継続時間をサポートします。
トランザクション(OCI_DURATION_TRANS
)
セッション(OCI_DURATION_SESSION
)
トランザクション継続時間は、トランザクションが終了(コミットまたは終了)した時点で期限切れになります。セッション継続時間は、セッションまたは接続が終了した時点で期限切れになります。
アプリケーションでは、OCIObjectUnpin()
を使用してオブジェクトを明示的に確保解除できます。個々のオブジェクトの明示的な確保解除を最小限にするには、関数OCICacheUnpin()
を使用して、オブジェクト・キャッシュ内で現在確保済のすべてのオブジェクトを確保解除します。デフォルトでは、すべてのオブジェクトは、確保継続時間終了時に確保を解除されます。
表14-2は、アプリケーションで、異なる継続時間を使用する方法を示しています。このアプリケーションでは、1つの接続と3つのトランザクションで、4つのオブジェクトを作成または確保しています。最初の列はデータベースで実行されるアクションを示し、2番目の列はそのアクションを実行する関数を示しています。残りの列はアプリケーションの各ポイントでの各種オブジェクトの状態を示しています。
たとえば、オブジェクト1は、T2で期間を接続継続時間として作成されてから、接続が終了するT19まで存在します。オブジェクト2は、T6でフェッチされた後、期間をトランザクション継続時間としてT7で確保され、トランザクションがコミットされるT9まで確保されたままとなります。
表14-2 割当て時間および確保継続時間の例
時間 | アプリケーションのアクション | 関数 | オブジェクト1 | オブジェクト2 | オブジェクト3 | オブジェクト4 |
---|---|---|---|---|---|---|
T1 |
接続を確立する |
- |
- |
- |
- |
- |
T2 |
オブジェクト1を作成する - 割当て時間 = 接続 |
|
既存 |
- |
- |
- |
T5 |
トランザクション1を開始する |
|
既存 |
- |
- |
- |
T6 |
SQL - REFをオブジェクト2にフェッチする |
- |
既存 |
- |
- |
- |
T7 |
オブジェクト2を確保する - 確保継続時間 = トランザクション |
|
既存 |
確保済 |
- |
- |
T8 |
アプリケーション・データを処理する |
- |
既存 |
確保済 |
- |
- |
T9 |
トランザクション1をコミットする |
|
既存 |
確保解除 |
- |
- |
T10 |
トランザクション2を開始する |
|
既存 |
- |
- |
- |
T11 |
オブジェクト3を作成する - 割当て時間 = トランザクション |
|
既存 |
- |
既存 |
- |
T12 |
SQL - REFをオブジェクト4にフェッチする |
- |
既存 |
- |
既存 |
- |
T13 |
オブジェクト4を確保する - 確保継続時間 = 接続 |
|
既存 |
- |
既存 |
確保済 |
T14 |
トランザクション2をコミットする |
|
既存 |
- |
削除済 |
確保済 |
T15 |
セッション1を終了する |
|
既存 |
- |
- |
確保済 |
T16 |
トランザクション3を開始する |
|
既存 |
- |
- |
確保済 |
T17 |
アプリケーション・データを処理する |
- |
既存 |
- |
- |
確保済 |
T18 |
トランザクション3をコミットする |
|
既存 |
- |
- |
確保済 |
T19 |
接続を終了する |
- |
削除済 |
- |
- |
確保解除 |
関連項目:
|
メモリーのインスタンスは、インスタンスのトップレベル・メモリー・チャンク、NULLインジケータ構造体のトップレベル・メモリー、およびオプションとして多数の2次メモリー・チャンクで構成されています。たとえば、次のようなDEPARTMENT
という行の型を想定します。
CREATE TYPE department AS OBJECT ( dep_name varchar2(20), budget number, manager person, /* person is an object type */ employees person_array ); /* varray of person objects */
このC表現は次のようになります。
struct department { OCIString * dep_name; OCINumber budget; struct person manager; OCIArray * employees; ); typedef struct department department;
DEPARTMENT
の各インスタンスには、dep_name
、budget
、manager
、employees
などのトップレベル属性が含まれたトップレベル・メモリー・チャンクがあります。dep_name
属性とemployees
属性は、実際にはそれ自体が追加メモリー(2次メモリー・チャンク)へのポインタです。2次メモリーは、埋込みインスタンスの実データを入れるために使用されます(employees
のVARRAYやdep_name
の文字列など)。
NULLインジケータ構造体のトップレベル・メモリーには、インスタンスのトップレベル・メモリー・チャンクにある属性のNULL状態が入っています。この例では、NULLインジケータ構造体のトップレベル・メモリーに、属性dep_name
、budget
、manager
のNULL状態と属性employees
のアトミックNULL状態が含まれています。
この項では、OCIアプリケーションでオブジェクト・キャッシュ内のオブジェクトのグラフをナビゲートする方法を説明します。
これまでの項に示した例では、アプリケーションで取り出したオブジェクトは単純なオブジェクトであり、その属性はすべてスカラー値でした。アプリケーションで、別のオブジェクトへのREF
である属性を持つオブジェクトを取り出す場合は、OCIコールを使用してオブジェクト・グラフを横断し、参照対象のインスタンスにアクセスできます。
たとえば、次の宣言でデータベースに新しい型を定義したとします。
CREATE TYPE person_t AS OBJECT ( name VARCHAR2(30), mother REF person_t, father REF person_t);
person_t
オブジェクトのオブジェクト表は、次の文を使用して作成します。
CREATE TABLE person_table OF person_t;
これでperson_t
型のインスタンスをこの型指定表に格納できます。person_t
の各インスタンスには、同じ表に格納される他の2つのオブジェクトへの参照が含まれています。NULL
参照は、親に関する情報がないことを表します。
オブジェクト・グラフは、オブジェクト・インスタンス間のREF
リンクを視覚的に表現したものです。たとえば、次のページの図14-2「person_tインスタンスのオブジェクト・グラフ」に、1オブジェクトから別のオブジェクトへのリンクを示すperson_t
インスタンスのオブジェクト・グラフを示します。円はオブジェクトを表し、矢印は他のオブジェクトへの参照を表しています。
この場合、各オブジェクトは、同一オブジェクトの他の2つのインスタンスへのリンクを持ちます。これ以外のケースも考えられます。オブジェクトがその他のオブジェクト型へのリンクを持つ場合もあります。その他の型のグラフも可能です。たとえば、オブジェクトの集合をリンク・リストとして実装する場合は、オブジェクト・グラフを単純な連鎖とみなすことができます。この場合、各オブジェクトは、リンク・リストの前にあるオブジェクトまたは次にあるオブジェクト(あるいはその両方)を参照します。
この章の前半で説明した方法を使用して、person_t
インスタンスへの参照を取り出し、次にそのインスタンスを確保できます。OCIには、1つのオブジェクトから別のオブジェクトへ参照をたどることでオブジェクト・グラフを横断できる機能があります。
たとえば、アプリケーションで前述のグラフのperson1
インスタンスをフェッチし、pers_1
として確保するとします。確保されると、アプリケーションでは、person1
のmotherインスタンスにアクセスし、2回目のPINオペレーションでpers_2
に確保することができます。
OCIObjectPin(env, err, pers_1->mother, OCI_PIN_ANY, OCI_DURATION_TRANS, OCI_LOCK_X, (OCIComplexObject *) 0, &pers_2);
この場合、2つ目のインスタンスを取り出すためのOCIのフェッチ操作は必要ありません。
アプリケーションでは、次に、person1
のfatherインスタンスを確保するか、person2
の参照リンクで操作できます。
注意: NULL または参照先がないREF を確保しようとすると、OCIObjectPin() コールがエラーになります。 |
この項では、OCIナビゲーショナル関数をまとめて簡単に説明します。関数は一般的な機能別にグループ化してあります。
これらの関数の使用方法については、この章の初めの項で説明しています。
ナビゲーショナル関数は、機能の種類によって異なる接頭辞を付けるネーミング計画に従っています。
OCICache*()
− キャッシュ操作の関数
OCIObject*()
− 個々のオブジェクトを操作する関数
オブジェクトの確保、確保解除または解放を行うには、次の関数を使用します。
表14-3 確保関数、解放関数および確保解除関数
関数 | 用途 |
---|---|
|
キャッシュの全インスタンスを解放します。 |
|
キャッシュまたは接続で永続オブジェクトの確保を解除します。 |
|
参照の配列を確保します。 |
|
スタンドアロン・インスタンスを解放し、確保解除します。 |
|
オブジェクトを確保します。 |
|
オブジェクトの確保を解除して確保カウントを0(ゼロ)にします。 |
|
継続時間を指定して表オブジェクトを確保します。 |
|
オブジェクトの確保を解除します。 |
アプリケーションでメタ属性の1つを変更してオブジェクトのマークまたはマーク解除を行うには、次の関数を使用します。
表14-5 マークおよびマーク解除関数
関数 | 用途 |
---|---|
|
|
|
オブジェクトに更新済/使用済のマークを付けます。 |
|
オブジェクトに削除済のマークを付けます。または、値インスタンスを削除します。 |
|
キャッシュ内の全オブジェクトをマーク解除します。 |
|
指定のオブジェクトを更新済にマークします。 |
|
|
型情報を型名に基づいて要求すると、OCIでは、その型の最新バージョンに対応する型記述子オブジェクト(TDO)を戻します。サーバーとオブジェクト・キャッシュ間は同期化されていません。したがって、オブジェクト・キャッシュ内のTDOは、更新されていない可能性があります。
オブジェクトの確保時に、イメージのバージョンがTDOのバージョンと異なっている場合があります。この場合は、エラーが発生します。アプリケーションを停止するか、TDOをリフレッシュしてオブジェクトを再度確保します。アプリケーションを続行すると、アプリケーションが失敗する可能性があります。これは、イメージとTDOのバージョンが同じであっても、アプリケーションで定義されているオブジェクト構造体(つまり、C構造体)に新しい型のバージョンとの互換性があるという保証ができないためです。サーバー内の型の属性が削除されている場合は、特にこの可能性があります。
したがって、型の構造体を変更した場合は、変更された型のヘッダー・ファイルを再生成し、アプリケーションを変更し、再コンパイルおよび再リンクしてから、プログラムを再実行する必要があります。
Oracle XML DBでは、XMLType
データ型を使用したXMLインスタンスの格納および操作をサポートしています。これらのXMLインスタンスにアクセスするには、OCIをC DOM API for XMLと併用します。
アプリケーション・プログラムでは、サーバー・ハンドル、文ハンドルなどの通常のOCIハンドルを初期化し、その後XMLコンテキストを初期化する必要があります。アプリケーション・プログラムでは、バックエンドでXMLインスタンスを操作したり、クライアント側で新規インスタンスを作成できます。初期化されたXMLコンテキストは、すべてのC DOM関数で使用できます。
Oracle XML DBに格納されたXMLデータは、C DOM構造体xmldocnode
によってクライアント側でアクセスできます。この構造体は、OCI文のXML値のバインド、定義および操作に使用できます。
関連項目:
|
XMLコンテキストは、すべてのC DOM API関数における必須パラメータです。この不透明なコンテキストは、データ・エンコーディング、エラー・メッセージ言語などに関連する情報をカプセル化します。このコンテキストの内容は、XDKおよびOracle XML DBアプリケーションでは異なります。
Oracle XML DBの場合、XMLコンテキストを初期化および解放するための2つのOCI関数が提供されています。
xmlctx *OCIXmlDbInitXmlCtx (OCIEnv *envhp, OCISvcCtx *svchp, OCIError *errhp, ocixmldbparam *params, ub4 num_params); void OCIXmlDbFreeXmlCtx (xmlctx *xctx);
サーバー上のXMLデータは、OCI文のコールによって操作できます。他のオブジェクト・インスタンスと同様に、xmldocnode
を使用してXMLType
の値をバインドおよび定義できます。サーバーからXMLデータを選択するには、OCI文を使用します。このデータは直接C DOM関数で使用できます。同様に、値を直接SQL文にバインドしなおすことができます。
XMLコンテキストを初期化および終了するには、それぞれOCIXmlDbInitXmlCtx()
関数およびO
CIXmlDbFreeXmlCtx()
関数を使用します。ヘッダー・ファイルocixmldb.h
は統合C APIとともに使用されます。
次のテスト例の断片は、C APIによる操作の実行方法を示しています。
#ifndef S_ORACLE #include <s.h> #endif #ifndef ORATYPES_ORACLE #include <oratypes.h> #endif #ifndef XML_ORACLE #include <xml.h> #endif #ifndef OCIXML_ORACLE #include <ocixmldb.h> #endif #ifndef OCI_ORACLE #include <oci.h> #endif #include <string.h> typedef struct test_ctx { OCIEnv *envhp; OCIError *errhp; OCISvcCtx *svchp; OCIStmt *stmthp; OCIServer *srvhp; OCIDuration dur; OCISession *sesshp; oratext *username; oratext *password; } test_ctx; ... void main() { test_ctx temp_ctx; test_ctx *ctx = &temp_ctx; OCIType *xmltdo = (OCIType *) 0; xmldocnode *doc = (xmldocnode *)0; ocixmldbparam params[1]; xmlnode *quux, *foo, *foo_data, *top; xmlerr err; sword status = 0; xmlctx *xctx; ... /* Initialize envhp, svchp, errhp, dur, stmthp */ ... /* Get an xml context */ params[0].name_ocixmldbparam = XCTXINIT_OCIDUR; params[0].value_ocixmldbparam = &ctx->dur; xctx = OCIXmlDbInitXmlCtx(ctx->envhp, ctx->svchp, ctx->errhp, params, 1); /* Do unified C API operations next */ ... /* Free the statement handle using OCIHandleFree() */ ... /* Free the allocations associated with the context */ OCIXmlDbFreeXmlCtx(xctx); /* Free envhp, svchp, errhp, stmthp */ ... }
中間層とクライアント層では、バイナリのXML形式でXMLを生成、消費および処理できます。Cアプリケーションでは、XML DBリポジトリからデータをフェッチし、DOMを使用してXMLの更新を実行し、データベースに格納します。または、クライアント上で作成または入力されたXML文書に対して、XSLT、XQueryおよび他のユーティリティを使用できます。その後、出力XMLはXML DBに保存されます。
クライアント・アプリケーションは、バイナリのXML文書のエンコードまたはデコード中にトークン定義、XMLスキーマおよびDTDをフェッチするために、メタデータ・リポジトリ(通常はバックエンド・データベース)への接続(メタデータ接続)を必要とします。
リポジトリ・コンテキストは、専用接続または接続プールを使用して初期化されます。リポジトリ・コンテキストから取得した接続を使用して、トークン定義やXMLスキーマなどのメタデータがフェッチされます。これに対して、アプリケーションには、データベースとの間でデータ(XMLデータなど)の通常の転送に使用するデータ接続もあります。リポジトリ・コンテキストは、(1つ以上の)データ接続に明示的に関連付けられています。データ接続を使用してデータベースとの間でXMLデータが読書きされる場合、基礎となるエンコードまたはデコード操作中に、該当するリポジトリ・コンテキストがアクセスされます。 必要に応じて、リポジトリからメタデータをフェッチするためにメタデータ接続が使用されます。
Cアプリケーションでは、OCIを使用してデータベースに存在する永続XMLにアクセスし、統合XML C APIを使用してフェッチしたXMLデータを操作できます。
クライアント・アプリケーションでは、次のステップを実行します。
通常のOCIハンドル(OCIEnv
、OCISvcCtx
、OCIError
など)を作成します。
バイナリのXMLメタデータをフェッチするためにリポジトリ・コンテキストを1つ以上作成します。
リポジトリ・コンテキストをデータ接続に関連付けます。
変数をSELECT/INSERT/UPDATE文にバインドおよび定義します(xmldocnode
)。
SELECT/INSERT/UPDATE文を実行して、XML文書をフェッチおよび格納します。この時点で、クライアントOCIライブラリがデータベース・バックエンドと相互作用し、必要なXMLスキーマ、DTD、トークン定義などをフェッチします。
統合C APIを使用してXMLデータ(DOM)を操作します。
OCIBinXmlReposCtx
は、リポジトリ・コンテキストのデータ構造です。クライアント・アプリケーションでは、メタデータ・リポジトリに接続情報を提供することで、このコンテキストを作成します。アプリケーションでは、複数のリポジトリ・コンテキストを作成して複数のトークン・リポジトリに接続できます。リポジトリ・コンテキストは、データ接続(OCISvcCtx
)に明示的に関連付けられます。システムがデータ接続との間でデータをエンコードまたはデコードするためにメタデータをフェッチする必要がある場合は、該当するメタデータにアクセスします。
アプリケーションでは、OCIEnv
ごとに1つリポジトリ・コンテキストを作成することをお薦めします。これにより、マルチスレッド・アプリケーションの同時実行性が向上します。
リポジトリ・コンテキストは、専用OCI接続またはOCI接続プールから作成できます。
OCIBinXmlCreateReposCtxFromConn()
では、指定した専用OCI接続を使用してリポジトリ・コンテキストが作成されます。OCI接続はメタデータ・アクセスにのみ使用され、アプリケーションで他のシナリオに使用されることはありません。この接続へのアクセスはシリアル化されることにも注意してください。つまり、同じ接続を使用する複数のスレッドはシリアル化されます。拡張性を考慮し、次の項で説明するようにアプリケーションで接続プールを使用してリポジトリ・コンテキストを作成することをお薦めします。
注意: データに使用するのと同じ接続を渡すことも可能です。ただし、この場合、クライアント・システムがSELECT/INSERTのような別の操作の一部を実行する間にメタデータ・リポジトリに接続すると、特定の状況でエラーになる可能性があります。
OCIBinXmlCreateReposCtxFromCPool()
では、接続プールからリポジトリ・コンテキストが作成されます。アプリケーションからバックエンド・リポジトリにアクセスする間に、プール内の使用可能接続が使用されます。さらに、この接続はメタデータ操作の完了直後にプールに解放されます。マルチスレッド・アプリケーションのシナリオには、接続プールを使用することをお薦めします。様々なスレッドがプール内の異なる接続を使用し、完了直後に解放できます。このアプローチでは、少数の物理接続で高度な拡張性と同時実行性が実現できます。
OCIBinXmlSetReposCtxForConn()
では、リポジトリ・コンテキストがOCISvcCtx
*
により記述されたデータ接続に関連付けられます。複数のデータ接続で同じリポジトリ・コンテキストを共有できますが、リポジトリへのアクセスがシリアル化される可能性があります(専用接続ベースの場合)。システムがエンコードまたはデコード操作のためにメタデータをフェッチする必要がある場合は、OCIEnv
、OCISvcCtx
のペアから該当するリポジトリ接続を参照し、それを使用して必要なメタデータをフェッチします。