この章では、オブジェクトをOracleデータベース・サーバーで操作するためのOCIの機能を紹介します。また、OCIのオブジェクト・ナビゲーショナル関数コールについても説明します。
この章は、次の項目で構成されています。
OCIによって、アプリケーションは、スカラー値、コレクション、任意のオブジェクト型のインスタンスなど、Oracleデータベース・サーバー内のすべてのデータ型にアクセスできます。次のデータ型があります。
Oracleサーバーのオブジェクト機能を十分に活用するには、ほとんどのアプリケーションでは単にオブジェクトにアクセスするのみでは十分ではありません。アプリケーションでは、オブジェクトを取り出した後、そのオブジェクトから他のオブジェクトへ参照を通してナビゲートする必要があります。OCIはそのための機能を提供します。OCIのオブジェクト・ナビゲーショナル・コールを使用すると、アプリケーションはオブジェクトに対して次の関数を実行できます。
オブジェクトの作成、アクセス、ロック、削除、コピーおよびフラッシュ
オブジェクトとそのメタオブジェクトへの参照の取得
オブジェクト属性の値の動的な取得および設定
OCIナビゲーショナル・コールについては、この章の後半で詳しく説明します。
また、OCIは、Oracleのデータベースに格納されている型情報にアクセスする機能も備えています。アプリケーションでは、OCIDescribeAny()関数を使用して、データベースに格納されている型に関するほとんどの情報(メソッド、属性、型メタデータなど)にアクセスできます。
Oracleオブジェクトを操作するアプリケーションでは、オブジェクトをホスト言語形式で表現する手段が必要です。Oracleには、Object Type Translator(OTT)というユーティリティがあります。このユーティリティを使用すると、データベース内の型定義をCの構造体宣言に変換できます。宣言はヘッダー・ファイルに格納され、それをOCIアプリケーションに組み込むことができます。
型定義がCで表現されている場合、属性の型は特別なC変数型にマップされます。OCIには、アプリケーションによるこれらのデータ型の操作とそれによるオブジェクトの属性の操作を可能にする、一連のデータ型マッピング関数および操作関数が含まれています。
オブジェクトに関する用語は混同することがあります。この章では、以降「オブジェクト」と「インスタンス」という用語は両方とも、データベースに格納されているオブジェクトか、オブジェクト・キャッシュにあるオブジェクトのいずれかを指します。
リレーショナルOCIアプリケーションを制御するプログラミング原理の多くは、オブジェクト・リレーショナル・アプリケーションにも適用されます。オブジェクト・リレーショナル・アプリケーションでは、標準OCIコールを使用してデータベース接続を確立し、SQL文を処理します。その違いは、発行したSQL文でオブジェクト参照を取り出し、その参照をOCIのオブジェクト関数で操作できる点にあります。オブジェクトは、値インスタンスとして(オブジェクト参照を使用せずに)直接操作することもできます。
オブジェクトを使用するOCIアプリケーションの基本的な構造体は、本質的にはリレーショナルOCIアプリケーションと同じです。これについては、「OCIプログラム構造」で説明しています。ここでは、基本的なオブジェクト機能についての追加情報とともに、そのモデルをもう一度示します。
OCIプログラミング環境を初期化します。環境は、オブジェクト・モードで初期化してください。
また、アプリケーションでは通常、ヘッダー・ファイルからデータベース・オブジェクトのC構造体の表現を組み込む必要があります。
|
関連項目: これらの構造体はプログラマであれば作成できますが、より簡単な方法として、第15章「OCIでのObject Type Translatorの使用」で説明するように、Object Type Translator(OTT)でも生成できます。 |
必要なハンドルを割り当て、サーバーとの接続を確立します。
SQL文を準備して実行します。これは、ローカル・(クライアント側)ステップで、プレースホルダのバインドと出力変数の定義を行います。オブジェクト・リレーショナル・アプリケーションの場合、このSQL文はオブジェクトへの参照(REF)を戻します。
|
注意: オブジェクトは参照( REF)のみでなく、オブジェクト全体をフェッチすることもできます。参照可能なオブジェクトを選択(SELECT)する場合は、それを確保するのではなく、そのオブジェクトを値によって取得します。参照不可能なオブジェクトを選択することもできます。この方法によるオブジェクト全体のフェッチについては、「埋込みオブジェクトのフェッチ」で説明しています。 |
プリコンパイルされたSQL文をデータベース・サーバーに関連付けて実行します。
戻された結果をフェッチします。
オブジェクト・リレーショナル・アプリケーションでは、このステップでREFを取り出し、参照対象のオブジェクトを確保します。オブジェクトを確保した後、次の一部またはすべてを実行します。
オブジェクトの属性を操作し、それに使用済のマークを設定します。
別の1個のオブジェクトまたは一連のオブジェクトへのREFをたどります。
型および属性の情報にアクセスします。
複合オブジェクト検索グラフをナビゲートします。
変更したオブジェクトをサーバーにフラッシュします。
トランザクションをコミットします。このステップでは暗黙的に、変更されたすべてのオブジェクトをサーバーにフラッシュし、変更をコミットします。
再利用しない文とハンドルを解放するか、プリコンパイルされたSQL文を再実行します。
この章の後の項で、すべてのステップを詳しく説明します。
|
関連項目:
|
Oracleの型のインスタンスは、その存続期間によって、永続オブジェクトと一時オブジェクトに分類されます。永続オブジェクトのインスタンスは、オブジェクト識別子による参照が可能かどうかによって、スタンドアロン・オブジェクトと埋込みオブジェクトに分割されます。
|
注意: このマニュアルでは、オブジェクトとインスタンスは同じ意味の用語です。 |
永続オブジェクトはOracleデータベースに格納されているオブジェクトです。永続オブジェクトは、OCIアプリケーションによってオブジェクト・キャッシュにフェッチされ、変更されます。永続オブジェクトは、それにアクセスしているアプリケーションの存続期間が過ぎても存続できます。作成された永続オブジェクトは、明示的に削除されるまでデータベースに残留します。永続オブジェクトには次の2種類があります。
スタンドアロン・インスタンス。オブジェクト表の行に格納され、それぞれが他と重複しないオブジェクト識別子を持っています。OCIアプリケーションでは、スタンドアロン・インスタンスへのREFを取り出してオブジェクトを確保し、確保したオブジェクトからその他の関連オブジェクトにナビゲートできます。スタンドアロン・オブジェクトは、参照可能オブジェクトとも呼ばれます。
参照可能オブジェクトの選択(SELECT)も可能で、その場合はREFをフェッチするかわりに、オブジェクトを値によってフェッチします。
埋込みインスタンスは、オブジェクト表の行として格納されません。埋込みインスタンスは、他の構造体の中に埋め込まれます。埋込みオブジェクトの例は、別のオブジェクトの属性であるオブジェクトや、データベースの表のオブジェクト列に存在するインスタンスなどです。埋込みインスタンスにはオブジェクト識別子がないため、OCIアプリケーションでは、埋込みインスタンスへのREFを取得できません。
埋込みオブジェクトは、参照不可能なオブジェクトまたは値インスタンスとも呼ばれます。埋込みオブジェクトは値と呼ばれることもありますが、それがスカラー・データ値と混同して使用されることはありません。どちらの意味であるかは文脈で判断できます。
次のSQLの例では、この2種類の永続オブジェクトの違いを説明します。
例1 スタンドアロン・オブジェクト
CREATE TYPE person_t AS OBJECT
(name varchar2(30),
age number(3));
CREATE TABLE person_tab OF person_t;
オブジェクト表person_tabに格納されるオブジェクトは、スタンドアロン・インスタンスです。このオブジェクトはオブジェクト識別子を持ち、参照可能です。したがって、OCIアプリケーションで確保できます。
例2 埋込みオブジェクト
CREATE TABLE department
(deptno number,
deptname varchar2(30),
manager person_t);
department表のmanager列に格納されるオブジェクトは、埋込みオブジェクトです。このオブジェクトはオブジェクト識別子を持たず、参照不可能です。つまり、OCIアプリケーションで確保することはできないため、確保を解除する必要もありません。このオブジェクトは常に、オブジェクト・キャッシュ内に値によって取り込まれます。
この項では、基本的なOCIオブジェクト・アプリケーションの開発に伴うステップを説明します。「基本的なオブジェクト・プログラム構造体」で説明している各ステップについて、さらに詳しく説明します。
次の図は、アプリケーションがオブジェクトを操作する方法について、そのプログラム・ロジックを簡単に示しています。図を簡略にするために、必要なステップの一部が省略されています。この図にある各ステップについては、後の項で説明します。
OCIアプリケーションでオブジェクト型を操作するには、データベースにそのオブジェクト型が存在している必要があります。通常は、CREATE TYPEなど、SQLのDDL文で型を作成します。
型定義DDLコマンドがOracleサーバーで処理されると、型定義は型記述子オブジェクト(TDO)としてデータ・ディクショナリに格納されます。
アプリケーションでデータベースからオブジェクトのインスタンスを取り出す場合は、クライアント側のオブジェクト表現を使用する必要があります。C言語のプログラムでは、オブジェクト型の表現はstructです。OCIオブジェクト・アプリケーションには、各オブジェクト型構造体に対応するNULLインジケータ構造体があります。
|
関連項目: アプリケーション・プログラマが、オブジェクトのキャッシュによって生成されるデフォルトの構造体以外のオブジェクト表現を利用する場合は、「オブジェクト・キャッシュおよびメモリー管理」を参照してください。 |
Oracleには、データベースのオブジェクト型のC構造体表現を生成するObject Type Translator(OTT)というユーティリティがあります。たとえば、次のように宣言した型がデータベースにあるとします。
CREATE TYPE emp_t AS OBJECT ( name VARCHAR2(30), empno NUMBER, deptno NUMBER, hiredate DATE, salary NUMBER);
OTTでは、次のC構造体と、対応するNULLインジケータ構造体が生成されます。
struct emp_t
{
OCIString * name;
OCINumber empno;
OCINumber deptno;
OCIDate hiredate;
OCINumber salary;
};
typedef struct emp_t emp_t
struct emp_t_ind
{
OCIInd _atomic;
OCIInd name;
OCIInd empno;
OCIInd deptno;
OCIInd hiredate;
OCIInd salary;
};
typedef struct emp_t_ind emp_t_ind;
この構造体宣言で使用している変数型は、OCIオブジェクト・コールで採用されている特殊な型です。OCI関数のサブセットでは、このデータ型のデータを操作します。
これらの構造体の宣言は自動的にヘッダー・ファイルに書き込まれます。このファイルの名前はOTT入力パラメータで判断されます。このヘッダー・ファイルをアプリケーションのコード・ファイルに組み込んで、オブジェクトにアクセスできます。
|
関連項目: これらの関数については、この章の後半に説明がありますが、第12章「OCIのオブジェクト・リレーショナル・データ型」でも詳しく説明します。
|
OCIアプリケーションでオブジェクトにアクセスして操作する場合は、OCIアプリケーションの最初のOCIコールであるOCIEnvCreate()コールのmodeパラメータに、OCI_OBJECTの値を指定する必要があります。modeにこの値を指定して、アプリケーションでオブジェクトを操作することをOCIライブラリに通知します。この通知には次の重要な効果があります。
オブジェクト・ランタイム環境の確立
オブジェクト・キャッシュの設定
オブジェクト・キャッシュ用のメモリーは、オブジェクトがキャッシュ内にロードされたときに、必要に応じて割り当てられます。
OCIInitialize()のmodeパラメータにOCI_OBJECTを設定しないと、オブジェクト関連関数を使用しても結果はエラーになります。
クライアント側のオブジェクト・キャッシュは、プログラムのプロセス領域に割り当てられます。このキャッシュは、サーバーから取り出してアプリケーションで使用できるオブジェクトのメモリーです。
|
注意: OCI環境をオブジェクト・モードで初期化すると、実際にオブジェクト・コールを使用するかどうかに関係なく、オブジェクト・キャッシュ用のメモリーを割り当てることになります。 |
アプリケーションでは、OCI環境を正しく初期化した後、サーバーに接続できます。これは標準OCI接続コールを介して行われます。詳細は、「OCIプログラミング・ステップ」を参照してください。標準OCI接続コールを使用すると、アプリケーションでオブジェクトにアクセスするための特別な配慮は必要ありません。
OCI環境ごとに、1つのオブジェクト・キャッシュのみ割り当てられます。1つの環境内で異なる接続を通じて取出しまたは作成されたオブジェクトは、すべて同じ物理的なオブジェクト・キャッシュを使用します。各接続には、それぞれ専用の論理的なオブジェクト・キャッシュがあります。
アプリケーションでオブジェクトを操作するには、最初にサーバーから1つ以上のオブジェクトを取り出す必要があります。取出しは、1つ以上のオブジェクトへのREFを戻すSQL文を発行して行います。
次の例では、テキスト・ブロックがアプリケーションで宣言されています。このテキスト・ブロックには、データベース内の従業員のオブジェクト表(emp_tab)から1つの従業員オブジェクトへのREFを取り出すように設計されたSQL文が格納されています。また、実行時に入力変数(:emp_num)として渡される特定の従業員番号が指定されています。
text *selemp = (text *) "SELECT REF(e)
FROM emp_tab e
WHERE empno = :emp_num";
アプリケーションでは、第2章「OCIプログラミングの基本」で説明したリレーショナルSQL文の操作と同じ方法でこの文を作成し、処理します。
OCIStmtPrepare()を使用してアプリケーション要求を準備します。
適切なバインド・コールを使用してホスト入力変数をバインドします。
従業員オブジェクト参照を受け取る出力変数を宣言し、準備します。ここでは、「Cアプリケーションでのオブジェクトの表現」で宣言した参照に類似した従業員オブジェクト参照を使用します。
OCIRef *emp1_ref = (OCIRef *) 0; /* reference to an employee object */
出力変数の定義時に、定義コールのdtyデータ型パラメータをSQLT_REF(REFのデータ型定数)に設定します。
OCIStmtExecute()で文を実行します。
OCIStmtFetch()を使用して、結果のREFをemp1_refにフェッチします。
これで、オブジェクト参照を使用して、オブジェクトまたはデータベースからのオブジェクトにアクセスして操作できます。
|
関連項目:
|
フェッチを行うステップが完了すると、アプリケーションでは、オブジェクトへのREF、つまりポインタを取得します。この時点ではまだ実際のオブジェクトは操作できません。オブジェクトを操作するには、そのオブジェクトの確保が必要です。オブジェクトの確保では、オブジェクト・インスタンスをオブジェクト・キャッシュにロードすることにより、必要に応じて、インスタンスの属性へのアクセスおよび変更や、1オブジェクトからその他のオブジェクトへの参照追跡を可能にします。さらにアプリケーションでは、変更したオブジェクトをいつサーバーに書き込むか制御することもできます。
アプリケーションでオブジェクトを確保するには、関数OCIObjectPin()をコールします。この関数のパラメータによって、オブジェクトのPINオプション、確保継続時間およびロック・オプションを指定できます。
次のコード例は、前の項で取り出した従業員参照のPINオペレーションを示しています。
if (OCIObjectPin(env, err, emp1_ref, (OCIComplexObject *) 0,
OCI_PIN_ANY,
OCI_DURATION_TRANS,
OCI_LOCK_X, &emp1) != OCI_SUCCESS)
process_error(err);
この例のprocess_error()はエラー処理関数を表しています。OCIObjectPin()関数で、OCI_SUCCESS以外の値が戻された場合、このエラー処理関数がコールされます。OCIObjectPin()関数のパラメータは次のとおりです。
envはOCI環境ハンドルです。
errはOCIエラー・ハンドルです。
emp1_refは、SQLによって取り出された参照です。
(OCIComplexObject *)0は、このPINオペレーションで複合オブジェクト検索を行わないことを示します。
OCI_PIN_ANYはPINオプションです。詳細は、「オブジェクト・コピーの確保」を参照してください。
OCI_DURATION_TRANSは確保継続時間です。詳細は、「オブジェクト継続時間」を参照してください。
OCI_LOCK_Xはロック・オプションです。詳細は、「更新するためのオブジェクトのロック」を参照してください。
emp1は出力パラメータで、確保したオブジェクトへのポインタを戻します。
これでオブジェクトが確保され、OCIアプリケーションでは、そのオブジェクトを変更できます。この単純な例では、オブジェクトは他のオブジェクトへの参照を含んでいません。
これでオブジェクトが確保され、OCIアプリケーションでは、その属性を変更できます。OCIには、オブジェクト型構造体のデータ型を操作する一連の関数である、OCIデータ型マッピング関数および操作関数があります。
|
注意: オブジェクト・キャッシュで確保したオブジェクトに対する変更内容は、オブジェクトのコピー(インスタンス)にのみ反映され、データベースにある元のオブジェクトには影響を与えません。アプリケーションで行った変更をデータベースに反映するには、変更内容をサーバーにフラッシュまたはコミットする必要があります。詳細は、「オブジェクトのマークおよび変更のフラッシュ」を参照してください。 |
たとえば、従業員給与を増額できるよう前項で従業員オブジェクトを確保したとします。また、この会社では、入社して180日間未満の従業員には、年間昇給率が案分比例されるとします。
この例の場合、従業員の入社日にアクセスし、現行の日付の180日前より前か後をチェックする必要があります。その計算を基盤として、従業員の給与を$5000(180日より前)または$3000(180日より後)分増額します。次のページのコード例では、このプロセスを示しています。
データ型マッピング関数および操作関数で操作できるのは特定のいくつかのデータ型であり、int型などのその他の型は、適切なOCI型に変換しなければ計算で使用できないことに注意してください。
/* assume that sysdate has been fetched into sys_date, a string. */
/* emp1 and emp1_ref are the same as in previous sections. */
/* err is the OCI error handle. */
/* NOTE: error handling code is not included in this example. */
sb4 num_days; /* the number of days between today and hiredate */
OCIDate curr_date; /* holds the current date for calculations */
int raise; /* holds the employee's raise amount before calculations */
OCINumber raise_num; /* holds employee's raise for calculations */
OCINumber new_sal; /* holds the employee's new salary */
/* convert date string to an OCIDate */
OCIDateFromText(err, (text *) sys_date, (ub4) strlen(sys_date), (text *)
NULL, (ub1) 0, (text *) NULL, (ub4) 0, &curr_date);
/* get number of days between hire date and today */
OCIDateDaysBetween(err, &curr_date, &emp1->hiredate, &num_days);
/* calculate raise based on number of days since hiredate */
if (num_days > 180)
raise = 5000;
else
raise = 3000;
/* convert raise value to an OCINumber */
OCINumberFromInt(err, (void *)&raise, (uword)sizeof(raise),
OCI_NUMBER_SIGNED, &raise_num);
/* add raise amount to salary */
OCINumberAdd(err, &raise_num, &emp1->salary, &new_sal);
OCINumberAssign(err, &new_sal, &emp1->salary);
この例は、値をパラメータとしてOCIデータ型マッピング関数および操作関数に渡す前に、どのようにOCIデータ型(OCIDateやOCINumberなど)に変換するかを示しています。
前の項の例では、オブジェクト・インスタンスの属性を変更しました。ただし、この時点では変更はクライアント側のオブジェクト・キャッシュにのみ存在しています。変更をデータベースに確実に書き込むために、アプリケーションで特定のステップを実行する必要があります。
最初のステップでは、オブジェクトが変更されたことを示します。これは、OCIObjectMarkUpdate()関数を使用して行います。この関数は、オブジェクトに使用済(変更あり)のマークを設定します。
使用済フラグが設定されているオブジェクトは、変更をデータベースに記録するために、サーバーにフラッシュする必要があります。次の3つの方法でこれを実行します。
単一の使用済オブジェクトは、OCIObjectFlush()をコールしてフラッシュします。
OCICacheFlush()を使用して、キャッシュ全体をフラッシュします。この場合、OCIはキャッシュで管理されている使用済リストを横断して、使用済オブジェクトをサーバーにフラッシュします。
OCITransCommit()をコールして、トランザクションをコミットします。この方法でも、使用済リストを横断してオブジェクトをサーバーにフラッシュします。
フラッシュ操作は、キャッシュの永続オブジェクトのみに作用します。一時オブジェクトはサーバーにフラッシュされません。
オブジェクトをサーバーにフラッシュすることで、データベースのトリガーをアクティブにすることができます。実際、アプリケーションでオブジェクトを明示的にフラッシュし、サーバー側のトリガーを起動することが必要な場合があります。
|
関連項目:
|
使用しているアプリケーションで、埋込みオブジェクト・インスタンス(オブジェクト表でなく、通常の表の列に格納されているオブジェクト)をフェッチする必要がある場合、「サーバーからのオブジェクト参照の取出し」で説明するREF検索機能は使用できません。埋込みインスタンスにはオブジェクト識別子がないため、埋込みインスタンスへのREFは取得できません。これは埋込みインスタンスがオブジェクト・ナビゲーションの基礎として機能しないことを意味します。しかし、アプリケーションで埋込みインスタンスをフェッチすることが必要な状況は数多くあります。
たとえば、address型が作成されたとします。
CREATE TYPE address AS OBJECT ( street1 varchar2(50), street2 varchar2(50), city varchar2(30), state char(2), zip number(5));
その型を、他の表で列のデータ型として使用することができます。
CREATE TABLE clients ( name varchar2(40), addr address);
OCIアプリケーションで次のSQL文を発行します。
SELECT addr FROM clients WHERE name='BEAR BYTE DATA MANAGEMENT'
この文は、clients表の埋込みオブジェクトaddressを戻します。アプリケーションでは、このオブジェクトの属性値を別の処理で使用できます。
アプリケーションでは、第2章「OCIプログラミングの基本」で説明したリレーショナルSQL文の操作と同じ方法でこの文を作成し、処理します。
OCIStmtPrepare()を使用してアプリケーション要求を準備します。
適切なバインド・コールを使用して入力変数をバインドします。
addressインスタンスを受け取る出力変数を定義します。「Cアプリケーションでのオブジェクトの表現」で説明するとおり、OTTで生成されたオブジェクト型のC構造体表現を使用します。
addr1 *address; /* variable of the address struct type */
出力変数を定義するときは、定義コールのデータ型パラメータdtyをSQLT_NTY(名前付きデータ型のデータ型定数)に設定する必要があります。
OCIStmtExecute()で文を実行します。
OCIStmtFetch()を使用して結果のインスタンスをaddr1にフェッチします。
次に、「オブジェクト属性の操作」で説明しているように、インスタンスの属性にアクセスするか、あるいはインスタンスを別のSQL文の入力パラメータとして渡すことができます。
|
注意: 埋込みインスタンスに対する変更内容は、SQLの UPDATE文の実行によってのみ永続的にできます。 |
オブジェクトのメタ属性には、オブジェクトの状態に関する情報を、アプリケーションまたはオブジェクト・キャッシュに提供できるフラグの役割があります。たとえば、オブジェクトのメタ属性の1つとして、オブジェクトがサーバーにフラッシュ済かどうかを示す属性があります。これは、アプリケーションでインスタンスの動作を制御するのに役立ちます。
永続オブジェクトのインスタンスと一時オブジェクトのインスタンスには、それぞれ異なるメタ属性があります。永続オブジェクトのメタ属性は、さらに永続メタ属性と一時メタ属性に分かれます。一時メタ属性は、インスタンスがメモリー内にあるときにのみ存在します。永続メタ属性は、サーバーに格納されているオブジェクトにも適用されます。
表11-1は、スタンドアロン永続オブジェクトのメタ属性を示しています。
表11-1 永続オブジェクトのメタ属性
| メタ属性 | 意味 |
|---|---|
|
既存 |
オブジェクトが存在しているか |
|
NULL |
インスタンスのNULL情報 |
|
ロック |
オブジェクトはロックされているか |
|
使用済 |
オブジェクトに使用済のマークが設定されているか |
|
確保済 |
オブジェクトは確保済か |
|
割当て時間 |
「オブジェクト継続時間」を参照してください。 |
|
確保継続時間 |
「オブジェクト継続時間」を参照してください。 |
|
注意: 埋込み永続オブジェクトの属性は、一時属性である「NULLかどうか」と「割当て時間」のみです。 |
OCIは、アプリケーションでオブジェクトのあらゆる属性のステータスをチェックできるOCIObjectGetProperty()関数を提供します。この関数の構文は次のとおりです。
sword OCIObjectGetProperty ( OCIEnv *envh,
OCIError *errh,
CONST void *obj,
OCIObjectPropId propertyId,
void *property,
ub4 *size );
propertyIdおよびpropertyパラメータは、あらゆるプロパティまたは属性に関する情報の取出しに使用されます。
異なるプロパティIDとそれに対応するproperty引数の型を次に示します。
このプロパティによって、指定されたオブジェクトが永続オブジェクト、一時オブジェクト、または値のインスタンスかのいずれであるかが識別されます。property引数はOCIObjectLifetime型の変数へのポインタにしてください。可能な値は、次のとおりです。
OCI_OBJECT_PERSISTENT
OCI_OBJECT_TRANSIENT
OCI_OBJECT_VALUE
このプロパティによって、オブジェクトが存在する表のスキーマ名が戻されます。指定されたオブジェクトが一時インスタンスまたは値を指し示している場合は、エラーが戻されます。スキーマ名の保持に入力バッファのサイズが足りない場合は、エラーが戻され、エラー・メッセージで必要なサイズが表示されます。成功した場合は、sizeによって、戻されたスキーマ名のサイズがバイト単位で戻されます。property引数は、text型の配列にしてください。コール元は、sizeをバイト単位で配列のサイズに設定する必要があります。
このプロパティによってそのオブジェクトが存在する表名が戻されます。指定されたオブジェクトが一時インスタンスまたは値を指し示している場合は、エラーが戻されます。表名の保持に入力バッファが足りない場合は、エラーが戻され、エラー・メッセージで必要なサイズが表示されます。成功した場合は、sizeによって、戻された表名のサイズがバイト単位で戻されます。property引数は、text型の配列にしてください。コール元では、sizeを配列のサイズにバイト単位で設定する必要があります。
このプロパティによってオブジェクトの確保継続時間が戻されます。指定されたオブジェクトが値のインスタンスを指し示している場合は、エラーが戻されます。property引数はOCIDuration型の変数へのポインタにしてください。次の値が有効です。
OCI_DURATION_SESSION
OCI_DURATION_TRANS
このプロパティによって、オブジェクトの割当て時間が戻されます。property引数はOCIDuration型の変数へのポインタにしてください。次の値が有効です。
OCI_DURATION_SESSION
OCI_DURATION_TRANS
このプロパティによってオブジェクトのロック状況が戻されます。可能なロック状況は、OCILockOptで計数値です。指定されたオブジェクトが一時インスタンスまたは値インスタンスを指し示している場合は、エラーが戻されます。property引数はOCILockOpt型の変数へのポインタにしてください。オブジェクトのロック状況は、OCIObjectIsLocked()関数をコールしても取り出せます。
このプロパティによって使用済ステータスが戻され、オブジェクトが新規のオブジェクトであるか、更新されたオブジェクトであるか、削除されたオブジェクトであるかが示されます。指定されたオブジェクトが一時インスタンスまたは値インスタンスを指し示している場合は、エラーが戻されます。property引数はOCIObjectMarkStatus型にしてください。次の値が有効です。
OCI_OBJECT_NEW
OCI_OBJECT_DELETED
OCI_OBJECT_UPDATED
次のマクロを使用してマーク状態をテストすることが可能です。
OCI_OBJECT_IS_UPDATED (flag)
OCI_OBJECT_IS_DELETED (flag)
OCI_OBJECT_IS_NEW (flag)
OCI_OBJECT_IS_DIRTY (flag)
このプロパティによって指定されたオブジェクトがオブジェクト・ビューであるかどうかが識別されます。戻されたプロパティ値がTRUEであれば、オブジェクトはビューであり、TRUEでない場合はオブジェクトはビューではありません。指定されたオブジェクトが一時インスタンスまたは値インスタンスを指し示している場合は、エラーが戻されます。このproperty引数はブール型にする必要があります。
ビューが仮想表であるのと同様に、オブジェクト・ビューは仮想オブジェクト表です。ビューの各行はオブジェクトであるため、メソッドのコール、ドット表記法を使用したオブジェクト属性へのアクセスおよびオブジェクトを指すREFの作成が可能です。
一時オブジェクトには次の一時属性があります。一時オブジェクトに永続属性はありません。
表11-3 一時メタ属性
| 一時メタ属性 | 意味 |
|---|---|
|
既存 |
オブジェクトが存在しているか |
|
確保済 |
オブジェクトはアプリケーションによってアクセスされるか |
|
使用済 |
オブジェクトに使用済のマークが設定されているか |
|
NULL |
インスタンスのNULL情報 |
|
割当て時間 |
「オブジェクト継続時間」を参照してください。 |
|
確保継続時間 |
「オブジェクト継続時間」を参照してください。 |
ここまでの例は、一度に1つのインスタンスのみのフェッチまたは確保を行う例でした。その場合、オブジェクトを取り出すためのサーバー・ラウンドトリップが、PINオペレーションごとに別個に発生します。
オブジェクト指向アプリケーションでは、相互に関連したオブジェクトの集合として問題をモデル化することがよくあります。これらのオブジェクトは、オブジェクトのグラフを形成します。アプリケーションでは、初期オブジェクト集合の一部からオブジェクトの処理を開始し、その一部のオブジェクトの参照を使用して残りのオブジェクトを横断します。クライアント/サーバー設定では、横断のたびにオブジェクトをフェッチするためのネットワーク・ラウンドトリップが発生し、非効率的です。
オブジェクトの処理時に、複合オブジェクト検索(COR)を使用すると、アプリケーションのパフォーマンスが向上する場合があります。これはプリフェッチ・メカニズムであり、アプリケーションでは、このメカニズムを使用して、リンクされた一連のオブジェクトを1回の操作で取り出すための基準を指定します。
|
注意: 後述するように、プリフェッチ・オブジェクトがすべて確保されているとはかぎりません。それらはオブジェクト・キャッシュ内にフェッチされるため、後続の確保コールはローカル操作で行われます。 |
複合オブジェクトは、論理的に関連した複数のオブジェクトの集合です。この集合は、ルート・オブジェクトと、指定したネスト・レベルに基づいてそれぞれプリフェッチされた一連のオブジェクトで構成されています。ルート・オブジェクトは、明示的にフェッチまたは確保されます。ネスト・レベルは、複合オブジェクトのルート・オブジェクトから指定のプリフェッチ・オブジェクトまで最短で横断した場合の参照の数です。
アプリケーションで複合オブジェクトを指定するときは、複合オブジェクトの内容と境界を記述します。複合オブジェクトのフェッチは、環境のプリフェッチ制限、つまり、複数オブジェクトのプリフェッチに使用可能なオブジェクト・キャッシュ内のメモリー容量によって制約されます。
|
注意: CORによって機能性が追加されるわけではなく、パフォーマンスが改善されるのみです。オプションとして使用してください。 |
この説明の例として、次の型宣言があるとします。
CREATE TYPE customer(...); CREATE TYPE line_item(...); CREATE TYPE line_item_varray as VARRAY(100) of REF line_item; CREATE TYPE purchase_order AS OBJECT ( po_number NUMBER, cust REF customer, related_orders REF purchase_order, line_items line_item_varray);
purchase_order型には、スカラー値po_number、VARRAYのline_itemsおよび2つの参照が含まれています。1つはcustomer型への参照、もう1つはpurchase_order型への参照であり、この型がリンク済リストとして実装されることを示しています。
複合オブジェクトをフェッチする場合は、アプリケーションで次の指定をする必要があります。
目的のルート・オブジェクトへのREF。
複合オブジェクトの境界を指定するための1組以上の型およびネストの情報。型情報は、CORでたどるREF属性を示し、ネスト・レベルはリンクをたどるレベル数を示します。
前述の発注情報オブジェクトの場合は、アプリケーションで次の指定をする必要があります。
ルート発注情報オブジェクトへのREF。
cust、related_ordersまたはline_itemsについての1組以上の型およびネストの情報。
発注情報をプリフェッチするアプリケーションでは、発注書を発行した顧客の情報にアクセスすることも十分考えられます。単純なナビゲーションでは、2つのオブジェクトをフェッチするためにサーバーに2回アクセスすることが必要です。複合オブジェクト検索によって、発注情報を確保するときに顧客をプリフェッチできます。この場合、複合オブジェクトは、発注情報オブジェクトと参照する顧客のオブジェクトで構成されます。
前の例では、アプリケーションはREFにpurchase_orderを指定し、cust REF属性をネスト・レベル1までたどるように指示します。
REF(PO object)
{(customer, 1)}
アプリケーションで、purchase_orderオブジェクトと、オブジェクト・グラフに含まれるすべてのオブジェクトをプリフェッチする必要がある場合、custおよびrelated_ordersは両方とも、最大のネスト・レベルまでたどるようにアプリケーションで指示します。
REF(PO object)
{(customer, UB4MAXVAL), (purchase_order, UB4MAXVAL)}
UB4MAXVALは、ルート・オブジェクトから参照を通して到達できる指定の型のオブジェクトすべてのプリフェッチを指定します。
アプリケーションで、POおよび関連する明細項目すべてをフェッチする場合は、次の指定をします。
REF(PO object)
{(line_item, 1)}
アプリケーションでは、REFを通して特定の深さまで到達できる(推移閉包)オブジェクトすべてをフェッチすることも選択できます。そのためには、目的の深さをレベル・パラメータとして設定する必要があります。前述の2つの例では、それぞれアプリケーションで(PO object REF, UB4MAXVAL)と(PO object REF, 1)を指定して、必要なオブジェクトをプリフェッチすることもできます。この場合は多数のフェッチが余計に発生しますが、指定が非常に簡単であり、サーバー・ラウンドトリップが1回のみですみます。
複合オブジェクトを指定してフェッチした後に行う、複合オブジェクトに含まれるオブジェクトのフェッチでは、ネットワーク・ラウンドトリップは発生しません。これは、オブジェクトがすでにプリフェッチ済であり、オブジェクト・キャッシュ内にあるためです。プリフェッチするオブジェクトが多すぎると、オブジェクト・キャッシュがあふれることがあるので注意が必要です。オブジェクト・キャッシュがあふれると、アプリケーションですでに確保している他のオブジェクトがキャッシュから押し出され、パフォーマンスの向上ではなく逆にパフォーマンスの低下につながることがあります。
|
注意: プリフェッチ・オブジェクトすべてを保持するためのメモリーがキャッシュにない場合は、オブジェクトの一部がプリフェッチされない場合があります。アプリケーションでは、後でそれらのオブジェクトにアクセスするとき、ネットワーク・ラウンドトリップが発生します。 |
プリフェッチ・オブジェクトすべてに、SELECT権限が必要です。複合オブジェクト内のオブジェクトに対するSELECT権限がアプリケーションにない場合、そのオブジェクトはプリフェッチできません。
複合オブジェクト検索(COR)によって、アプリケーションは、ルート・オブジェクトをフェッチするときに複合オブジェクトのプリフェッチができます。単純オブジェクトに対して使用するのと同じOCIObjectPin()関数に、複合オブジェクト指定を渡します。
アプリケーションでは、複合オブジェクト検索ハンドルを使用して、複合オブジェクト検索のパラメータを指定します。このハンドルは、OCIComplexObject型であり、その他のOCIハンドルと同じ方法で割り当てます。
複合オブジェクト検索ハンドルの内容は、複合オブジェクト検索記述子のリストです。複合オブジェクト検索記述子は、OCIComplexObjectComp型であり、その他のOCI記述子と同じ方法で割り当てます。
各COR記述子には、REF型とネスト・レベルが含まれています。REF型では、複合オブジェクトを組み立てるときにたどる参照の型を指定します。ネスト・レベルで、特定の型の参照をどこまでたどるかを指定します。ネスト・レベルの最大値には、整数値または定数UB4MAXVALを指定します。
アプリケーションでは、タイプ・パラメータとネスト・レベル・パラメータのCOR記述子を作成しないで、CORハンドルにネスト・レベルを指定することもできます。この場合、すべてのREFについて、CORハンドルに指定されたネスト・レベルまでたどります。また、要求があったときに個別にコレクション属性がフェッチされる(アウト・ライン)かどうかも、CORハンドルで指定できます。デフォルトでは、コレクション属性は、それを含むオブジェクトとともにフェッチされます(インライン)。
アプリケーションでCORハンドルの属性を設定するには、OCIAttrSet()を使用します。次の属性があります。
OCI_ATTR_COMPLEXOBJECT_LEVEL− ネスト・レベル
OCI_ATTR_COMPLEXOBJECT_COLL_OUTOFLINE− オブジェクト型のコレクション属性をアウト・ラインでフェッチ
アプリケーションで、OCIDescriptorAlloc()を使用してCOR記述子を割り当て、次の属性を設定できます。
OCI_ATTR_COMPLEXOBJECTCOMP_TYPE−REF型
OCI_ATTR_COMPLEXOBJECTCOMP_LEVEL− 前述の型の参照に使用するネスト・レベル
これらの属性を設定すると、アプリケーションではOCIParamSet()をコールして記述子を複合オブジェクト検索ハンドルに設定します。ハンドルにはハンドルの記述子の数を指定するOCI_ATTR_PARAM_COUNT属性があります。この属性はOCIAttrGet()で読み取ることができます。
ハンドルに記述子を挿入した後、ハンドルをOCIObjectPin()コールに渡してルート・オブジェクトを確保し、複合オブジェクトの残りのオブジェクトのプリフェッチを行います。
複合オブジェクト検索ハンドルと記述子は、必要がなくなったとき、明示的に解放する必要があります。
アプリケーションでは、ルート・オブジェクトのフェッチ時に複合オブジェクトを指定します。プリフェッチ・オブジェクトは、指定のルート・オブジェクトを基点とするオブジェクトのグラフに対して、幅優先横断を実行することによって取得されます。横断は、必要なオブジェクトがすべてプリフェッチされたとき、またはプリフェッチ・オブジェクトの合計サイズがプリフェッチ制限を超えたときに停止します。
複合オブジェクトをフェッチするためのインタフェースは、OCI確保インタフェースです。アプリケーションは、初期化済のCORハンドルをOCIObjectPin()に(またはハンドルの配列をOCIObjectArrayPin()に)渡して、ルート・オブジェクト、およびCORハンドルで指定したプリフェッチ・オブジェクトをフェッチすることができます。
sword OCIObjectPin ( OCIEnv *env,
OCIError *err,
OCIRef *object_ref,
OCIComplexObject *corhdl,
OCIPinOpt pin_option,
OCIDuration pin_duration,
OCILockOpt lock_option,
void **object );
sword OCIObjectArrayPin ( OCIEnv *env,
OCIError *err,
OCIRef **ref_array,
ub4 array_size,
OCIComplexObject **cor_array,
ub4 cor_array_size,
OCIPinOpt pin_option,
OCIDuration pin_duration,
OCILockOpt lock,
void **obj_array,
ub4 *pos );
CORの使用時には、次の点について留意してください。
NULLのCORハンドル引数は、デフォルトでルート・オブジェクトのみを確保します。
ルート・オブジェクト型でネスト・レベル0のCORハンドルは、ルート・オブジェクトのみをフェッチします。つまり、NULLのCORハンドルに相当します。
ロック・オプションは、ルート・オブジェクトのみに適用されます。
|
注意: プリフェッチ・オブジェクトにロック・オプションを指定するために、アプリケーションでは配列インタフェース( OCIObjectArrayPin())を使用して、複合オブジェクト内のオブジェクトすべてにアクセスし、REFの配列を作成し、別のラウンドトリップで複合オブジェクト全体をロックできます。 |
次の例では、複合オブジェクト検索を行うためにアプリケーション・プログラムを修正する方法を詳しく説明します。
発注情報とそれに関連した明細項目を表示するアプリケーションを考えてみます。太字のコードでこの処理を実行します。残りのコードでは、プリフェッチの複合オブジェクト検索を使用するため、アプリケーションのパフォーマンスが向上します。
OCIEnv *envhp; OCIError *errhp; OCIRef **liref; OCIRef *poref; OCIIter *itr; boolean eoc; purchase_order *po = (purchase_order *)0; line_item *li = (line_item *)0; OCISvcCtx *svchp; OCIComplexObject *corhp; OCIComplexObjectComp *cordp; OCIType *litdo; ub4 level = 0; /* get COR Handle */ OCIHandleAlloc((void *) envhp, (void **) &corhp, (ub4) OCI_HTYPE_COMPLEXOBJECT, 0, (void **)0); /* get COR descriptor for type line_item */ OCIDescriptorAlloc((void *) envhp, (void **) &cordp, (ub4) OCI_DTYPE_COMPLEXOBJECTCOMP, 0, (void **) 0); /* get type of line_item to set in COR descriptor */ OCITypeByName(envhp, errhp, svchp, (const text *) 0, (ub4) 0, (const text *) "LINE_ITEM", (ub4) strlen((const char *) "LINE_ITEM"), (text *) 0, (ub4) 0, OCI_DURATION_SESSION, OCI_TYPEGET_HEADER, &litdo); /* set line_item type in COR descriptor */ OCIAttrSet( (void *) cordp, (ub4) OCI_DTYPE_COMPLEXOBJECTCOMP, (void *) litdo, (ub4) sizeof(void *), (ub4) OCI_ATTR_COMPLEXOBJECTCOMP_TYPE, (OCIError *) errhp); level = 1; /* set depth level for line_item_varray in COR descriptor */ OCIAttrSet( (void *) cordp, (ub4) OCI_DTYPE_COMPLEXOBJECTCOMP, (void *) &level, (ub4) sizeof(ub4), (ub4) OCI_ATTR_COMPLEXOBJECTCOMP_TYPE_LEVEL, (OCIError *) errhp); /* put COR descriptor in COR handle */ OCIParamSet(corhp, OCI_HTYPE_COMPLEXOBJECT, errhp, cordp, OCI_DTYPE_COMPLEXOBJECTCOMP, 1); /* pin the purchase order */ OCIObjectPin(envhp, errhp, poref, corhp, OCI_PIN_LATEST, OCI_DURATION_SESSION, OCI_LOCK_NONE, (void **)&po); /* free COR descriptor and COR handle */ OCIDescriptorFree((void *) cordp, (ub4) OCI_DTYPE_COMPLEXOBJECTCOMP); OCIHandleFree((void *) corhp, (ub4) OCI_HTYPE_COMPLEXOBJECT); /* iterate and print line items for this purchase order */ OCIIterCreate(envhp, errhp, po->line_items, &itr); /* get first line item */ OCIIterNext(envhp, errhp, itr, (void **)&liref, (void **)0, &eoc); while (!eoc) /* not end of collection */ { /* pin line item */ OCIObjectPin(envhp, errhp, *liref, (void *)0, OCI_PIN_RECENT, OCI_DURATION_SESSION, OCI_LOCK_NONE, (void **)&li)); display_line_item(li); /* get next line item */ OCIIterNext(envhp, errhp, itr, (void **)&liref, (void **)0, &eoc); }
アプリケーションで複数オブジェクト(オブジェクト参照によって相互に関連)のグラフを操作する必要がある場合は、オブジェクトへのアクセスに、SQLインタフェースよりもOCIインタフェースを使用する方が効率的です。SQLインタフェースを使用してオブジェクトのグラフを取り出すには、複数のSELECT文の実行、つまり、複数のネットワーク・ラウンドトリップが必要になる場合があります。OCIによって提供される複合オブジェクト検索機能を使用すると、アプリケーションは1つのOCIObjectPin()コール内で複数のオブジェクトのグラフを取り出せます。
アプリケーションでオブジェクトのグラフを取り出し、それをユーザーとの対話を基に変更して、その変更をデータベース内に持続させる場合の更新について考えてみます。SQLインタフェースを使用する場合、アプリケーションではオブジェクトのグラフの更新に複数のUPDATE文を実行する必要があります。新しいオブジェクトの作成と既存のオブジェクトの削除が変更に含まれている場合は、対応するINSERT文およびDELETE文も実行する必要があります。さらに、アプリケーションでは、表名の変更状況など、INSERT文、UPDATE文またはDELETE文の実行に必要な、さらに多くの情報を記録する必要があります。
OCIのOCICacheFlush()関数を使用すると、アプリケーションですべての変更(オブジェクトの挿入、削除および更新)を単一の操作でフラッシュできます。OCIではすべてが記録されるので、アプリケーション側のコーディングが少なくてすみます。このように、オブジェクトのグラフの操作にOCIを使用すると、効率的なだけでなく使用しやすいインタフェースが提供されます。
たとえば、アプリケーションでREFを使用してオブジェクトをフェッチする必要がある場合を考えてみます。OCIの場合、このフェッチは、OCIObjectPin()コールを使用してそのオブジェクトを確保することで、実行されます。SQLインタフェースの場合、このフェッチは、SELECT文(たとえば、SELECT DEREF(ref) from tbl;)のREFを参照解除することで実行されます。同じREF(同じオブジェクトへの参照)が1つのトランザクション内で複数回参照解除されるような状況について考えてみます。OCI_PIN_RECENTオプションとともにOCIObjectPin()をコールすると、トランザクションに対してオブジェクトがサーバーから1度のみフェッチされ、同じREFで確保が繰り返し行われる結果、すでにキャッシュ内に確保されているオブジェクトへのポインタが戻されます。 SQLインタフェースの場合は、SELECT DEREF...文の実行のたびに、サーバーからオブジェクトがフェッチされるため、サーバーへのラウンドトリップが何度も行われ、同じオブジェクトが複数コピーされる結果となります。
最後に、アプリケーションで、参照不可能なオブジェクトをフェッチする必要がある場合について考えてみます。たとえば、次のとおりです。
CREATE TABLE department ( deptno number, deptname varchar2(30), manager employee_t );
manager列に格納されたemployee_tインスタンスは、参照不可能です。manager列インスタンスをフェッチするには、SQLインタフェースのみが使用できます。ただし、employee_tにREF属性がある場合は、OCIコールを使用してREFをナビゲートできます。
オブジェクト・キャッシュ内の各オブジェクトには、確保カウントが関連付けられています。本来、確保カウントはオブジェクトに並行アクセスするコード・モジュールの数を表します。オブジェクトを初めてキャッシュに確保したときに、確保カウントが1に設定されます。複合オブジェクト検索でプリフェッチ・オブジェクトをオブジェクト・キャッシュに入れた時点では、オブジェクトの確保カウントは0(ゼロ)です。
すでに確保されているオブジェクトを確保することができます。そうすると、確保カウントが1増えます。プロセスでオブジェクトの使用が終了したときは、OCIObjectUnpin()を使用して確保解除を行う必要があります。このコールによって確保カウントが1減ります。
オブジェクトの確保カウントが0(ゼロ)になった場合、必要があればオブジェクトをキャッシュから出し、オブジェクトが占めているメモリー領域を解放できます。
オブジェクトの確保カウントは、OCIObjectPinCountReset()をコールして、明示的に0(ゼロ)に設定できます。
アプリケーションではOCICacheUnpin()をコールして、キャッシュ内にある特定の接続に関連した全オブジェクトを確保解除できます。
|
関連項目:
|
データベース表で、行の中に値のない列がある場合、その列はNULLまたはNULLを含むと呼ばれます。オブジェクトには、次の2種類のNULLを適用できます。
オブジェクトの属性はすべて、NULL値を持つことができます。これはオブジェクトの属性の値が不明であることを意味します。
オブジェクトのインスタンスは、アトミックNULLにできます。これはオブジェクト全体の値が不明なことを意味します。
アトミックNULLとは、オブジェクトが存在しないということではありません。アトミックNULLのインスタンスは、値が不明なだけで、存在しています。つまり、データを持たないオブジェクトとみなすことができます。
OCIでオブジェクトを操作する場合は、アプリケーションで使用する各オブジェクト型のNULLインジケータ構造体を定義できます。そのために必要な作業は、通常、OTTで生成したNULLインジケータ構造体を構造体宣言とともに組み込むことのみです。OTTの出力ヘッダー・ファイルを組み込むと、アプリケーションでNULLインジケータ構造体を使用できるようになります。
各型に対するNULLインジケータ構造体の内容は、アトミックNULLインジケータ(OCIInd型)とインスタンスの各属性のNULLインジケータです。型にオブジェクト属性がある場合、NULLインジケータ構造体には、その属性のNULLインジケータ構造体が含まれます。次の例は、それぞれ対応するNULLインジケータ構造体を持つ型のC言語での表現方法を示しています。
struct address
{
OCINumber no;
OCIString *street;
OCIString *state;
OCIString *zip;
};
typedef struct address address;
struct address_ind
{
OCIInd _atomic;
OCIInd no;
OCIInd street;
OCIInd state;
OCIInd zip;
};
typedef struct address_ind address_ind;
struct person
{
OCIString *fname;
OCIString *lname;
OCINumber age;
OCIDate birthday;
OCIArray *dependentsAge;
OCITable *prevAddr;
OCIRaw *comment1;
OCILobLocator *comment2;
address addr;
OCIRef *spouse;
};
typedef struct person person;
struct person_ind
{
OCIInd _atomic;
OCIInd fname;
OCIInd lname;
OCIInd age;
OCIInd birthday;
OCIInd dependentsAge;
OCIInd prevAddr;
OCIInd comment1;
OCIInd comment2;
address_ind addr;
OCIInd spouse;
};
typedef struct person_ind person_ind;
オブジェクト型インスタンスの場合は、NULLインジケータ構造体の最初のフィールドがアトミックNULLインジケータで、残りのフィールドはレイアウトがオブジェクト型インスタンスの属性に似ている属性NULLインジケータです。
アプリケーションでは、アトミックNULLインジケータの値をチェックすることで、インスタンスがアトミックNULLかどうかをテストできます。また、他のインスタンスをチェックすることで、次のコード例で示すように、アプリケーションでその属性のNULL状態をテストできます。
person_ind *my_person_ind
if( my_person_ind -> _atomic == OCI_IND_NULL)
printf ("instance is atomically NULL\n");
else
if( my_person_ind -> fname == OCI_IND_NULL)
printf ("fname attribute is NULL\n");
前述の例では、アトミックNULLインジケータまたは属性NULLインジケータの値が事前定義の値OCI_IND_NULLと比較され、NULLかどうかがテストされます。この比較では、事前定義済の次の値が使用可能です。
OCI_IND_NOTNULL− 値がNULLでないことを示します。
OCI_IND_NULL− 値がNULLであることを示します。
OCI_IND_BADNULL− 囲みオブジェクト(親オブジェクト)がNULLであることを示します。この値はPL/SQLで使用され、INVALID_NULLとしても参照されます。たとえば、型インスタンスがNULLの場合、その属性はINVALID_NULLです。
OCIObjectGetInd()関数を使用して、オブジェクトのNULLインジケータ構造体を取り出します。
C構造体内の属性を更新する場合、その属性のNULLインジケータも設定する必要があります。
obj->attr1 = string1; OCIObjectGetInd(envhp, errhp, obj, &ind); ind->attr1 = OCI_IND_NOTNULL;
OCIアプリケーションでは、OCIObjectNew()を使用してあらゆるオブジェクトを作成できます。アプリケーションで永続オブジェクトを作成するには、新規オブジェクトを挿入するオブジェクト表を指定してください。この値は、OCIObjectPinTable()をコールして取り出し、tableパラメータで渡します。一時オブジェクトを作成するには、作成するオブジェクトの型に対する型記述子オブジェクト(OCIDescribeAny()をコールして取出し)のみを渡す必要があります。
また、 typecodeパラメータに適切な値を渡し、OCIObjectNew()を使用して、スカラーのインスタンス(REF、LOB、文字列、ロー、数値、日付など)およびコレクション(VARRAYやNESTED TABLEなど)を作成できます。
デフォルトでは、新規作成されたオブジェクトのすべての属性に NULL値があります。属性データを初期化した後で、対応する各属性のNULLステータスをNULL以外に変更する必要があります。
オブジェクトの作成時に、属性をNULL以外の値に設定できます。これは、OCIAttrSet()を使用して環境ハンドルのOCI_OBJECT_NEWNOTNULL属性をTRUEに設定することによって実行できます。その後、この属性をFALSEに設定することにより、このモードを無効にできます。
OCI_OBJECT_NEWNOTNULLがTRUEに設定されている場合は、OCIObjectNew()によってNULL以外のオブジェクトが作成されます。オブジェクトの属性には、次の表に示したデフォルト値があり、対応するNULLインジケータは、NOT NULLに設定されています。
表11-4 新しいオブジェクトの属性値
| 属性の型 | デフォルト値 |
|---|---|
REF |
オブジェクトにREF属性がある場合は、オブジェクトをフラッシュする前にこの属性を有効な |
DATE |
Oracleで扱える最古の日付。01-JAN-4712 BCE(ユリウス日の第1日)の午前0時。 |
ANSI DATE |
Oracleで扱える最古の日付。01-JAN-4712 BCE(ユリウス日の第1日)。 |
TIMESTAMP |
Oracleで扱える最古の日時。01-JAN-4712 BCE(ユリウス日の第1日)の午前0時。 |
TIMESTAMP WITH TIME ZONE |
Oracleで扱える最古の日時。UTC(0:0)タイム・ゾーンで、01-JAN-4712 BCE(ユリウス日の第1日)の午前0時。 |
TIMESTAMP WITH LOCAL TIME ZONE |
Oracleで扱える最古の日時。UTC(0:0)タイム・ゾーンで、01-JAN-4712 BCE(ユリウス日の第1日)の午前0時。 |
INTERVAL YEAR TO MONTH |
|
INTERVAL DAY TO SECOND |
|
FLOAT |
0. |
NUMBER |
0 |
DECIMAL |
0. |
RAW |
長さが0に設定されたロー・データ。注意: |
VARCHAR2, NVARCHAR2 |
長さが0で最初の文字が |
CHAR, NCHAR |
長さが0で最初の文字が |
VARCHAR |
長さが0で最初の文字が |
VARRAY |
要素が0のコレクション。 |
NESTED TABLE |
要素が0の表。 |
CLOB, NCLOB |
空の |
BLOB |
空の |
BFILE |
ディレクトリ・オブジェクトおよびファイル名を設定することによって、 |
OCIObjectFree()を使用して、OCIObjectNew()で割り当てたメモリーを解放できます。オブジェクト・インスタンスには、追加メモリー(2次メモリー・チャンク)へのポインタである属性を設定できます。
オブジェクトの解放とは、関連するNULLインジケータ構造体や2次メモリー・チャンクなど、オブジェクトに割り当てたすべてのメモリーの割当てを解除することです。2次メモリー・チャンクを明示的に解放したり、ポインタの再割当てをすることはできません。これは、メモリー・リークやメモリーの破損が発生する可能性があるためです。このプロシージャによって、一時オブジェクトが、存続期間内に削除されます(永続オブジェクトは削除されません)。アプリケーションでは、永続オブジェクトを削除するには、OCIObjectMarkDelete() を使用します。
アプリケーションでは、OCIObjectCopy()を使用して、1つのインスタンスを同じ型の別のインスタンスにコピーできます。
アプリケーションでは、OCIのオブジェクト拡張機能を利用して、オブジェクトの内容に対してポインタや参照による柔軟なアクセスができます。OCIでは、ポインタが示すオブジェクトに参照を戻すOCIObjectGetObjectRef()関数を提供しています。
また、アプリケーションで、オブジェクトの型情報へのアクセスを行う場合は、OCIではOCIObjectGetProperty()関数で、ポインタが示すオブジェクトの型記述子オブジェクト(TDO)に参照を戻します。
システム生成オブジェクト識別子(OID)を備えたオブジェクト表に基づいて永続オブジェクトが生成されると、OCIObjectGetObjectRef()を使用してただちにこのオブジェクトへの参照を取得できます。 ただし、永続オブジェクトがオブジェクト・ビューまたは主キー・ベースのOIDを備えたオブジェクト表に基づいている場合、主キーに属するすべての属性を設定してからでなければ参照は取得できません。
アプリケーションで、OCIObjectNew()コールを使用して、オブジェクト・ビュー、または主キー・ベースのオブジェクト識別子(OID)を備えたオブジェクト表に基づいたオブジェクトを作成できます。 このようなビューおよびテーブルのオブジェクト識別子は属性値に基づいているため、その後、アプリケーションでOCIObjectSetAttr()を使用して、主キーに属するすべての属性を設定する必要があります。 属性値を設定すると、アプリケーションで、OCIObjectGetObjectRef()をコールすることによって属性値に基づいたオブジェクト参照を取得できます。
この処理には、次のステップが必要です。
新しいオブジェクトの基になるオブジェクト・ビューまたはオブジェクト表を確保します。
ステップ1のPINオペレーションで取得した表またはビューへのハンドルを渡して、OCIObjectNew()を使用して新しいオブジェクトを作成します。
OCIObjectSetAttr()を使用して、オブジェクト属性の必要な値を入力します。 指定する値には、オブジェクト表またはオブジェクト・ビューのユーザー定義オブジェクト識別子を構成する属性も含む必要があります。
必要に応じて、OCIObjectGetObjectRef()を使用して主キーを基にしたオブジェクトへの参照を取得します。さらにオブジェクトを作成する場合は、ステップ2に戻ります。
新しく作成されたオブジェクトをサーバーにフラッシュします。
次のコード例では、このプロセスを実装して、HRスキーマ内のemp_viewオブジェクト・ビューに対して新しいオブジェクトを作成する方法を示します。
void object_view_new ()
{
void *table;
OCIRef *pkref;
void *object;
OCIType *emptdo;
...
/* Set up the service context, error handle and so on.. */
...
/* Pin the object view */
OCIObjectPinTable(envp,errorp,svctx, "HR", strlen("HR"), "EMP_VIEW",
strlen("EMP_VIEW"),(void *) 0, OCI_DURATION_SESSION, (void **) &table);
/* Create a new object instance */
OCIObjectNew(envp, errorp, svctx, OCI_TYPECODE_OBJECT,(OCIType *)emptdo, table,
OCI_DURATION_SESSION,FALSE,&object);
/* Populate the attributes of "object" */
OCIObjectSetAttr(...);
...
/* Allocate an object reference */
OCIObjectNew(envp, errorp, svctx, OCI_TYPECODE_REF, (OCIType *)0, (void *)0,
OCI_DURATION_SESSION,TRUE,&pkref);
/* Get the reference using OCIObjectGetObjectRef */
OCIObjectGetObjectRef(envp,errorp,object,pkref);
...
/* Flush new object(s) to server */
...
} /* end function */
オブジェクトの型の継承は、C++やJavaでの継承に類似している点が数多くあります。オブジェクト型は、既存のオブジェクト型のサブタイプとして作成できます。サブタイプは、元の型であるスーパータイプの属性とメソッド(メンバー関数とプロシージャ)をすべて継承します。単一の継承のみがサポートされます。つまり、1つのオブジェクトが複数のスーパータイプを所有することはできません。サブタイプが継承した属性やメソッドには、新しい属性やメソッドを追加できます。サブタイプが継承した任意のメソッドをオーバーライド(実装を再定義)することもできます。サブタイプは、そのスーパータイプの拡張(つまり、スーパータイプの継承)といえます。
たとえば、Person_t型は、サブタイプStudent_tとサブタイプEmployee_tを所有できます。さらに、Student_tも独自のサブタイプPartTimeStudent_tを所有できます。サブタイプを所有するには、型宣言にフラグNOT FINALが必要です。デフォルトのフラグはFINALで、この型はサブタイプを所有できないことを意味します。
この章でこれまで説明した型は、すべてFINALです。リリース1(9.0.1)より前に開発されたアプリケーションの型は、すべてFINALです。FINALの型は、NOT FINALに変更できます。サブタイプを所有しないNOT FINAL型は、FINALに変更できます。次の例では、Person_tがNOT FINALとして宣言されています。
CREATE TYPE Person_t AS OBJECT ( ssn NUMBER, name VARCHAR2(30), address VARCHAR2(100)) NOT FINAL;
サブタイプは、スーパータイプで宣言された属性とメソッドをすべて継承します。また、新しい属性やメソッドを宣言することもできます。その場合は、スーパータイプとは異なる名前を指定する必要があります。次のように、キーワードUNDERによって、スーパータイプが識別されます。
CREATE TYPE Student_t UNDER Person_t ( deptid NUMBER, major VARCHAR2(30)) NOT FINAL;
新しく宣言した属性deptidおよびmajorは、サブタイプStudent_tに所属しています。たとえば、サブタイプEmployee_tを次のように宣言します。
CREATE TYPE Employee_t UNDER Person_t ( empid NUMBER, mgr VARCHAR2(30));
この結果OTTによって生成される構造体については、次の項目を参照してください。
このサブタイプStudent_tは、PartTimeStudent_tなどの独自のサブタイプを所有できます。
CREATE TYPE PartTimeStudent_t UNDER Student_t ( numhours NUMBER) ;
ポリモフィズムの利点の一部は、プロパティの持つ代入性によるものです。代入性によって、サブタイプの値をスーパータイプ用に記述された元のコードで使用できます。この場合、サブタイプに関する事前の知識は特に必要ありません。サブタイプの値は、メソッドの特化範囲内で異なるメカニズムが使用されている場合でも、周辺のコードに対応してスーパータイプの値と同じように動作します。
インスタンスの代入性とは、サブタイプのオブジェクト値を、スーパータイプで宣言されたコンテキスト内で使用できるようにする機能を指します。REFの代入性とは、サブタイプへのREFを、スーパータイプへのREFで宣言されたコンテキスト内で使用できるようにする機能を指します。
REF型の属性は置換可能です。つまり、REF Tとして定義された属性は、Tのインスタンスまたはそのサブタイプの任意のインスタンスへのREFを保持できます。
オブジェクト型の属性は置換可能です。つまり、(オブジェクト)型Tとして定義された属性は、Tのインスタンスまたはそのサブタイプの任意のインスタンスを保持できます。
コレクションの要素の型は置換可能です。つまり、T型の要素を持つコレクションを定義した場合、そのコレクションは、T型のインスタンスとそのサブタイプの任意のインスタンスを保持できます。オブジェクト属性の代入性の例を次に示します。
CREATE TYPE Book_t AS OBJECT ( title VARCHAR2(30), author Person_t /* substitutable */);
したがって、Book_tインスタンスは、次のように、タイトル文字列とPerson_t(または、Person_tの任意のサブタイプ)インスタンスを指定することによって作成できます。
Book_t('My Oracle Experience',
Employee_t(12345, 'Joe', 'SF', 1111, NULL))
型はNOT INSTANTIABLEとして宣言できます。これは、その型に(デフォルトまたはユーザー定義の)コンストラクタがないことを意味します。したがって、この型のインスタンスは構成できません。通常、このような型は、インスタンス化可能なサブタイプの定義に使用されます。このプロパティの使用方法を次に示します。
CREATE TYPE Address_t AS OBJECT(...) NOT INSTANTIABLE NOT FINAL; CREATE TYPE USAddress_t UNDER Address_t(...); CREATE TYPE IntlAddress_t UNDER Address_t(...);
型のメソッドは、NOT INSTANTIABLEとして宣言できます。メソッドをNOT INSTANTIABLEとして宣言することは、型がそのメソッドを実装しないことを意味します。また、NOT INSTANTIABLEメソッドが含まれている型は、必ずNOT INSTANTIABLEとして宣言する必要があります。たとえば、次のようにします。
CREATE TYPE T AS OBJECT ( x NUMBER, NOT INSTANTIABLE MEMBER FUNCTION func1() RETURN NUMBER ) NOT INSTANTIABLE NOT FINAL;
NOT INSTANTIABLE型のサブタイプは、スーパータイプの任意のNOT INSTANTIABLEメソッドをオーバーライドして、具体的な実装を行うことができます。NOT INSTANTIABLEメソッドが残っている場合、サブタイプも必ずNOT INSTANTIABLEとして宣言する必要があります。
NOT INSTANTIABLEサブタイプは、インスタンス化可能なスーパータイプの下に定義できます。NOT INSTANTIABLE型をFINALとして宣言することは無効なため、使用できません。
次のコールは、型の継承をサポートしています。
OCIのバインド関数は、REF、インスタンスおよびコレクション要素の代入性をサポートしています(スーパータイプがある場合は、サブタイプのインスタンスを渡すことができます)。すべての型のチェックと変換は、サーバー側で実行されるため、OCIのバインド・インタフェースに対する変更はありません。
OCIの定義関数も、代入性をサポートしています(サブタイプのインスタンスをスーパータイプの保持を宣言した定義変数にフェッチできます)。ただし、この場合、サブタイプのインスタンスを保持するために、システムによるメモリーのサイズ変更が必要になることがあります。
|
注意: この場合、クライアント・プログラムでは、オブジェクト・キャッシュから割り当てられた(したがって、サイズ変更が可能な)オブジェクトを使用する必要があります。 |
値が多相になる可能性がある場合、クライアントでは、(スタック上に割り当てられた)構造体を定義変数として使用しないでください。
この関数は、ソース・インスタンスの内容をターゲット・インスタンスにコピーします。ソース・インスタンスとターゲット・インスタンスは、同じ型にしてください。スーパータイプとサブタイプ間でコピーすることはできません。
同様に、tdo引数は、ソースおよびターゲット・オブジェクトと同じオブジェクト・タイプを記述する必要があり、ソースおよびターゲット・オブジェクトのスーパータイプまたはサブタイプを参照しないようにする必要があります。
入力要素は、宣言された型のサブタイプのインスタンスにできます。コレクションの型がPerson_tの場合は、この関数を使用して、Employee_tインスタンスをコレクションの要素として割り当てることができます。