この章では、オブジェクトをOracle Databaseで操作するためのOCIの機能を紹介します。また、OCIのオブジェクト・ナビゲーショナル関数コールについても説明します。
この章は、次の項目で構成されています。
OCIによって、アプリケーションは、スカラー値、コレクション、任意のオブジェクト型のインスタンスなど、Oracle Database内のすべてのデータ型にアクセスできます。次のデータ型があります。
Oracle Databaseのオブジェクト機能を十分に活用するには、ほとんどのアプリケーションでは単にオブジェクトにアクセスするのみでは十分ではありません。アプリケーションでは、オブジェクトを取り出した後、そのオブジェクトから他のオブジェクトへ参照を通してナビゲートする必要があります。OCIはそのための機能を提供します。OCIのオブジェクト・ナビゲーショナル・コールを使用すると、アプリケーションはオブジェクトに対して次の関数を実行できます。
オブジェクトの作成、アクセス、ロック、削除、コピーおよびフラッシュ
オブジェクトとそのメタオブジェクトへの参照の取得
オブジェクト属性の値の動的な取得および設定
OCIナビゲーショナル・コールについては、「OCIオブジェクト・アプリケーションの開発について」でより詳しく説明しています。
また、OCIは、Oracleのデータベースに格納されている型情報にアクセスする機能も備えています。アプリケーションでは、OCIDescribeAny()関数を使用して、データベースに格納されている型に関するほとんどの情報(メソッド、属性、型メタデータなど)にアクセスできます。
関連項目:
OCIDescribeAny()
の詳細は、「スキーマ・メタデータの記述」を参照してください
Oracle Databaseオブジェクトを操作するアプリケーションでは、オブジェクトをホスト言語形式で表現する手段が必要です。Oracle Databaseには、Object Type Translator (OTT)というユーティリティがあり、データベース内の型定義をCの構造体宣言に変換できます。宣言はヘッダー・ファイルに格納され、それをOCIアプリケーションに組み込むことができます。
型定義がCで表現されている場合、属性の型は特別なC変数型にマップされます。OCIには、アプリケーションによるこれらのデータ型の操作とそれによるオブジェクトの属性の操作を可能にする、一連のデータ型マッピング関数および操作関数が含まれています。
関連項目:
関数の詳細は、「OCIのオブジェクト・リレーショナル・データ型」を参照してください
オブジェクトに関する用語は混同することがあります。この章の残りの部分では、オブジェクトおよびインスタンスという用語の両方が、データベースに格納されているか、またはオブジェクト・キャッシュ内にあるオブジェクトのことを示しています。
リレーショナルOCIアプリケーションを制御するプログラミング原理の多くは、オブジェクト・リレーショナル・アプリケーションにも適用されます。
オブジェクト・リレーショナル・アプリケーションでは、標準OCIコールを使用してデータベース接続を確立し、SQL文を処理します。その違いは、発行したSQL文でオブジェクト参照を取り出し、その参照をOCIのオブジェクト関数で操作できる点にあります。オブジェクトは、値インスタンスとして(オブジェクト参照を使用せずに)直接操作することもできます。
オブジェクトを使用するOCIアプリケーションの基本的な構造体は、本質的にはリレーショナルOCIアプリケーションと同じです。
ここでは、基本的なオブジェクト機能についての追加情報とともに、そのモデルをもう一度示します。
OCIプログラミング環境を初期化します。オブジェクト・モードで環境を初期化する必要があります。
アプリケーションでは、ヘッダー・ファイルにデータベース・オブジェクトのC構造体表現が含まれている必要があります。これらの構造体はプログラマであれば作成できますが、より簡単な方法として、Object Type Translator (OTT)でも生成できます。
必要なハンドルを割り当て、サーバーとの接続を確立します。
SQL文を準備して実行します。これは、ローカル・ (クライアント側)ステップで、プレースホルダのバインドと出力変数の定義を行います。オブジェクト・リレーショナル・アプリケーションの場合、このSQL文はオブジェクトへの参照(REF
)を戻します。
注意:
オブジェクトは参照(REF
)のみでなく、オブジェクト全体をフェッチすることもできます。参照可能なオブジェクトを選択する場合は、それを確保するのではなく、そのオブジェクトを値によって取得します。参照不可能なオブジェクトを選択することもできます。
プリペアド文をデータベース・サーバーに関連付けて実行します。
戻された結果をフェッチします。
オブジェクト・リレーショナル・アプリケーションでは、このステップでREF
を取り出し、参照対象のオブジェクトを確保します。オブジェクトを確保すると、アプリケーションでは次の操作の一部またはすべてを実行できます。
オブジェクトの属性を操作し、それに使用済(変更済)のマークを設定します。
別の1個のオブジェクトまたは一連のオブジェクトへのREF
をたどります。
型および属性の情報にアクセスします。
複合オブジェクト検索グラフをナビゲートします。
変更したオブジェクトをサーバーにフラッシュします。
トランザクションをコミットします。このステップでは暗黙的に、変更されたすべてのオブジェクトをサーバーにフラッシュし、変更をコミットします。
再利用しない文とハンドルを解放するか、プリペアド文を再実行します。
この章の残りの項で、こららの手順について詳しく説明します。
関連項目:
「埋込みオブジェクトのフェッチ」では、オブジェクト全体のフェッチについて説明しています
OCIを使用したサーバーへの接続、SQL文の処理およびハンドルの割当ての詳細は「OCIプログラミングの基本」を、OCIリレーショナル関数の詳細は「Oracle Database Access C API」を参照してください
OTTの詳細は、「Cアプリケーションでのオブジェクトの表現について」および「OCIでのObject Type Translatorの使用」を参照してください
Oracle型のインスタンスは、その存続期間に応じて永続オブジェクトと一時オブジェクトに分類されます。
永続オブジェクトのインスタンスは、オブジェクト識別子による参照が可能かどうかによって、スタンドアロン・オブジェクトと埋込みオブジェクトに分割されます。
注意:
このマニュアルでは、オブジェクトとインスタンスという用語は代替可能に使用されます。
永続オブジェクトは、Oracle Databaseに格納されているオブジェクトです。永続オブジェクトは、OCIアプリケーションによってオブジェクト・キャッシュにフェッチされ、変更されます。永続オブジェクトは、それにアクセスしているアプリケーションの存続期間を超えて存続できます。作成された永続オブジェクトは、明示的に削除されるまでデータベースに残留します。永続オブジェクトには次の2種類があります。
スタンドアロン・インスタンスは、オブジェクト表の行に格納され、それぞれが一意のオブジェクト識別子を持っています。OCIアプリケーションでは、スタンドアロン・インスタンスへのREF
を取り出してオブジェクトを確保し、確保したオブジェクトからその他の関連オブジェクトにナビゲートできます。スタンドアロン・オブジェクトは、参照可能オブジェクトとも呼ばれます。
参照可能オブジェクトの選択も可能で、その場合はREF
をフェッチするかわりに、オブジェクトを値によってフェッチします。
埋込みインスタンスは、オブジェクト表の行として格納されません。埋込みインスタンスは、他の構造体の中に埋め込まれます。埋込みオブジェクトの例は、別のオブジェクトの属性であるオブジェクトや、データベースの表のオブジェクト列に存在するインスタンスなどです。埋込みインスタンスにはオブジェクト識別子がないため、OCIアプリケーションでは、埋込みインスタンスへのREF
を取得できません。
埋込みオブジェクトは、参照不可能なオブジェクトまたは値インスタンスとも呼ばれます。埋込みオブジェクトは値と呼ばれることもありますが、それをスカラー・データ値と混同しないでください。どちらの意味であるかは文脈で判断できます。
例11-1と例11-2では、これら2種類の永続オブジェクトの違いを説明するSQLの例を示しています。
オブジェクト表person_tab
に格納されるオブジェクトは、スタンドアロン・インスタンスです。このオブジェクトはオブジェクト識別子を持ち、参照可能です。したがって、OCIアプリケーションで確保できます。
department
表のmanager
列に格納されるオブジェクトは、埋込みオブジェクトです。このオブジェクトは、オブジェクト識別子を持たず、参照可能でもありません。つまり、OCIアプリケーション内には確保できず、確保解除する必要もありません。常に、値によってオブジェクト・キャッシュに取り込まれます。
例11-1 スタンドアロン・オブジェクトのSQL定義
CREATE TYPE person_t AS OBJECT (name varchar2(30), age number(3)); CREATE TABLE person_tab OF person_t;
例11-2 埋込みオブジェクトのSQL定義
CREATE TABLE department (deptno number, deptname varchar2(30), manager person_t);
一時オブジェクトは一時インスタンスであり、アプリケーションの存続期間を超えて存続せず、サーバーに格納またはフラッシュできません。
一時オブジェクトはいつでもアプリケーションで削除できます。
アプリケーションでは、OCIObjectNew()
関数を使用して一時オブジェクトを作成し、計算のための一時的な値を格納することがよくあります。一時オブジェクトを永続オブジェクトに変換することはできません。オブジェクトのロールは、インスタンス化されたときに決定されます。
関連項目:
OCIObjectNew()
の使用方法の詳細は、「オブジェクトの作成について」を参照してください
この項では、基本的なOCIオブジェクト・アプリケーションの開発に伴うステップを説明します。
図11-1は、アプリケーションがオブジェクトを操作する方法について、そのプログラム・ロジックのフローを簡単に示しています。図を簡略にするために、必要なステップの一部が省略されています。この図にある各ステップについては、後の項で説明します。
関連項目:
OCIアプリケーションでオブジェクト型を操作するには、データベースにそのオブジェクト型が存在している必要があります。
通常は、CREATE
TYPE
など、SQLのDDL文で型を作成します。
型定義DDLコマンドがOracle Databaseで処理されると、型定義は型記述子オブジェクト(TDO)としてデータ・ディクショナリに格納されます。
アプリケーションでデータベースからオブジェクト・タイプのインスタンスを取り出す場合は、クライアント側のオブジェクト表現を使用する必要があります。C言語のプログラムでは、オブジェクト型の表現はstructです。OCIオブジェクト・アプリケーションには、各オブジェクト型構造体に対応するNULL
インジケータ構造体があります。
Oracle Databaseには、データベースのオブジェクト型の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入力パラメータによって決定されます。このヘッダー・ファイルをアプリケーションのコード・ファイルにインクルードすると、オブジェクトにアクセスできます。
関連項目:
アプリケーション・プログラマが、オブジェクトのキャッシュによって生成されるデフォルトの構造体以外のオブジェクト表現を利用する場合は、「オブジェクト・キャッシュおよびメモリー管理」を参照してください
OTTの詳細は、「OCIでのObject Type Translatorの使用」を参照してください
OCIオブジェクト・コールで採用されている構造体宣言で使用しているこれらの変数型のデータを操作するOCI関数のサブセットの詳細は、「OCIのオブジェクト・リレーショナル・データ型」を参照してください
OCIアプリケーションでオブジェクトにアクセスして操作する場合は、OCIアプリケーションの最初のOCIコールであるOCIEnvCreate()
コールのmode
パラメータに、OCI_OBJECT
の値を指定する必要があります。mode
にこの値を指定して、アプリケーションでオブジェクトを操作することをOCIライブラリに通知します。
この通知には次の重要な効果があります。
オブジェクト・ランタイム環境を確立します。
オブジェクト・キャッシュを設定します。
オブジェクト・キャッシュ用のメモリーは、オブジェクトがキャッシュ内にロードされたときに、必要に応じて割り当てられます。
OCIEnvCreate()
またはOCIEnvNlsCreate()
のmodeパラメータにOCI_OBJECT
を設定しないと、オブジェクト関連関数を使用しても結果はエラーになります。
クライアント側のオブジェクト・キャッシュは、プログラムのプロセス領域に割り当てられます。このキャッシュは、サーバーから取り出してアプリケーションで使用できるオブジェクトのメモリーです。
注意:
OCI環境をオブジェクト・モードで初期化すると、実際にオブジェクト・コールを使用するかどうかに関係なく、オブジェクト・キャッシュ用のメモリーを割り当てることになります。
関連項目:
オブジェクト・キャッシュの詳細は、「OCIのオブジェクトに関する高度なトピック」を参照してください
OCI環境が適切に初期化されると、アプリケーションはサーバーに接続できるようになります。この接続は、「OCIプログラミング・ステップ」で説明されている標準のOCI接続コールによって確立されます。これらのコールを使用する場合、このアプリケーションはオブジェクトにアクセスしているため、追加の考慮事項は必要ありません。
OCI環境ごとに、1つのオブジェクト・キャッシュが割り当てられます。1つの環境内で異なる接続を通じて取出しまたは作成されたオブジェクトは、すべて同じ物理的なオブジェクト・キャッシュを使用します。各接続には、それぞれ専用の論理的なオブジェクト・キャッシュがあります。
オブジェクトを操作するには、アプリケーションでまずサーバーから1つ以上のオブジェクトを取り出す必要があります。
これは、1つ以上のオブジェクトへのREF
を戻すSQL文を発行して行います。
注意:
SQL文によって、データベースからREF
ではなく、埋込みオブジェクトをフェッチすることもできます。
次の例では、アプリケーションでテキスト・ブロックが宣言されていて、ここには、実行時に入力変数(:emp_num
)として渡される特定の従業員番号を指定したときに、データベース内の従業員のオブジェクト表(emp_tab
)から1つの従業員オブジェクトへのREF
を取り出すように設計されたSQL文が格納されています。
text *selemp = (text *) "SELECT REF(e) FROM emp_tab e WHERE empno = :emp_num";
アプリケーションでは、次のようにリレーショナルSQL文の操作と同じ方法で、この文を準備し処理します。
これで、オブジェクト参照を使用して、オブジェクトまたはデータベースからのオブジェクトにアクセスして操作できます。
関連項目:
詳細は、「埋込みオブジェクトのフェッチ」を参照してください
SQL文の準備と実行の一般情報は、「OCIプログラミング・ステップ」を参照してください
REF
変数のバインドおよび定義の具体的な情報は、「OCIでの拡張バインド操作」および「OCIでの拡張定義操作」を参照してください
REF
の取出しおよびその確保を示すコード例については、Oracleのインストールに付属のデモンストレーション・プログラムを参照してください。詳細は、「OCIデモ・プログラム」を参照してください。
ステップ3で示した従業員オブジェクト参照の宣言の使用方法は、「Cアプリケーションでのオブジェクトの表現について」を参照してください
オブジェクトの確保では、オブジェクト・インスタンスをオブジェクト・キャッシュにロードすることにより、必要に応じて、インスタンスの属性へのアクセスおよび変更や、1オブジェクトからその他のオブジェクトへの参照追跡を可能にします。
フェッチを行うステップが完了すると、アプリケーションでは、オブジェクトへのREF
、つまりポインタを取得します。この時点ではまだ実際のオブジェクトは操作できません。オブジェクトを操作する前にオブジェクトを確保する必要があります。さらにアプリケーションでは、変更したオブジェクトをいつサーバーに書き込むか制御することもできます。
注意:
この項では、一度に1つのオブジェクトを確保する単純な操作の例を示します。複合オブジェクト検索を介した複数オブジェクトの取出しの詳細は、「複合オブジェクト検索」を参照してください。
アプリケーションでオブジェクトを確保するには、関数OCIObjectPin()
をコールします。この関数のパラメータでは、オブジェクトの確保オプション、確保時間およびロック・オプションを指定できます。
例11-3は、前の「サーバーからのオブジェクト参照の取出し」の項でアプリケーションにより取り出された従業員参照に対する確保操作を説明するサンプル・コードです。
この例の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アプリケーションでは、そのオブジェクトを変更できます。この単純な例では、オブジェクトは他のオブジェクトへの参照を含んでいません。
この項には次のトピックが含まれます。「配列確保」。
例11-3 オブジェクトの確保
if (OCIObjectPin(env, err, emp1_ref, (OCIComplexObject *) 0, OCI_PIN_ANY, OCI_DURATION_TRANS, OCI_LOCK_X, &emp1) != OCI_SUCCESS) process_error(err);
関連項目:
1つのインスタンスから別のインスタンスへのナビゲーションの例は、「単純なオブジェクト・ナビゲーション」を参照してください
確保オプションOCI_PIN_ANY
の詳細は、「オブジェクト・コピーの確保について」を参照してください
確保継続時間OCI_DURATION_TRANS
の詳細は、「オブジェクト継続時間」を参照してください
ロック・オプションOCI_LOCK_X
の詳細は、「更新するためのオブジェクトのロックについて」を参照してください
OCIアプリケーションでは、OCIObjectArrayPin()をコールすることによって、参照の配列にはオブジェクトの配列を確保できます。参照は、異なる種類のオブジェクトを指し示すことができます。この関数によって、異なる表から異なる型のオブジェクトを1回のネットワーク・ラウンドトリップでフェッチできます。
これでオブジェクトが確保され、OCIアプリケーションでは、その属性を変更できます。
OCIには、オブジェクト型構造体のデータ型を操作する一連の関数である、OCIデータ型マッピング関数および操作関数があります。
注意:
オブジェクト・キャッシュ内に確保されているオブジェクトに対して変更を加えた場合、影響を受けるのはそのオブジェクトのコピー(インスタンス)のみで、データベース内の元のオブジェクトは影響を受けません。アプリケーションで行った変更をデータベースに反映するには、変更内容をサーバーにフラッシュまたはコミットする必要があります。
たとえば、従業員給与を増額できるよう前項で従業員オブジェクトを確保したとします。また、この会社では、入社して180日間未満の従業員には、年間昇給率が案分比例されるとします。
この例の場合、従業員の入社日にアクセスし、現在の日付の180日前より前か後かをチェックする必要があります。その計算を基盤として、従業員の給与を$5000 (180日より前)または$3000 (180日より後)分増額します。例11-4のサンプル・コードでは、このプロセスを説明しています。
データ型マッピング関数および操作関数で操作できるのは特定のいくつかのデータ型であり、int
型などのその他の型は、適切なOCI型に変換しなければ計算で使用できないことに注意してください。
例11-4は、値をパラメータとしてOCIデータ型マッピング関数および操作関数に渡す前に、どのようにOCIデータ型(OCIDate
、OCINumber
など)に変換する必要があるかを示しています。
例11-4 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のオブジェクト・リレーショナル・データ型」を参照してください
オブジェクトへの変更をデータベースに確実に書き込むために、アプリケーションではオブジェクトにマークを設定してフラッシュする特定のステップを実行する必要があります。
例11-4では、1つのオブジェクト・インスタンスの1つの属性が変更されました。ただし、この時点では変更はクライアント側のオブジェクト・キャッシュにのみ存在しています。変更をデータベースに確実に書き込むために、アプリケーションで特定のステップを実行する必要があります。
最初のステップでは、オブジェクトが変更されたことを示します。これは、OCIObjectMarkUpdate()
関数を使用して行います。この関数は、オブジェクトに使用済(変更済)のマークを付けます。
使用済フラグが設定されているオブジェクトは、変更をデータベースに記録するために、サーバーにフラッシュする必要があります。次の3つの方法でこれを実行します。
単一の使用済オブジェクトは、OCIObjectFlush()
をコールしてフラッシュします。
OCICacheFlush()
を使用して、キャッシュ全体をフラッシュします。この場合、OCIはキャッシュで管理されている使用済リストを横断して、使用済オブジェクトをサーバーにフラッシュします。
OCICacheFlush()
をコールして、トランザクションをコミットします。この方法でも、使用済リストを横断して使用済オブジェクトをサーバーにフラッシュします。
フラッシュ操作は、キャッシュの永続オブジェクトのみに作用します。一時オブジェクトはサーバーにフラッシュされません。
オブジェクトをサーバーにフラッシュすることで、データベースのトリガーをアクティブにすることができます。実際、アプリケーションでオブジェクトを明示的にフラッシュし、サーバー側のトリガーを起動することが必要な場合があります。
関連項目:
OCITransCommit()
の詳細は、「OCIでのトランザクションのサポート」を参照してください
一時オブジェクトと永続オブジェクトの詳細は、「オブジェクトの作成について」を参照してください
オブジェクトのメタ属性(dirtyなど)の表示およびチェックの詳細は、「オブジェクトのメタ属性」を参照してください
アプリケーションは、埋込みオブジェクト・インスタンスをフェッチする必要があります。
使用しているアプリケーションで、埋込みオブジェクト・インスタンス(オブジェクト表でなく、通常の表の列に格納されているオブジェクト)をフェッチする必要がある場合、「サーバーからのオブジェクト参照の取出し」で説明されている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
を戻します。アプリケーションでは、このオブジェクトの属性値を別の処理で使用できます。
アプリケーションでは、「OCIプログラミングの基本」で説明したリレーショナルSQL文の操作と同じ方法でこの文を作成し、処理します。
OCIStmtPrepare2()
を使用してアプリケーション要求を準備します。
1つ以上の適切なバインド・コールを使用して、入力変数をバインドします。
address
インスタンスを受け取る出力変数を定義します。「Cアプリケーションでのオブジェクトの表現について」で説明するとおり、OTTで生成されたオブジェクト型のC構造体表現を使用します。
addr1 *address; /* variable of the address struct type */
出力変数の定義時に、定義コールのdty
データ型パラメータを、名前付きデータ型のデータ型定数、SQLT_NTYに設定します。
OCIStmtExecute()
で文を実行します。
OCIStmtFetch2()
を使用して、結果のインスタンスをaddr1
にフェッチします。
次に、「オブジェクト属性の操作」で説明しているように、インスタンスの属性にアクセスするか、あるいはインスタンスを別のSQL文の入力パラメータとして渡すことができます。
注意:
埋込みインスタンスに対する変更内容は、SQLのUPDATE
文の実行によってのみ永続的にできます。
関連項目:
SQL文の準備と実行の詳細は、「OCIプログラミング・ステップ」を参照してください
オブジェクトのメタ属性には、オブジェクトの状態に関する情報を、アプリケーションまたはオブジェクト・キャッシュに提供できるフラグの役割があります。
たとえば、オブジェクトのメタ属性の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
の場合、そのオブジェクトはビューであり、そうでない場合はビューではありません。指定されたオブジェクトが一時インスタンスまたは値インスタンスを指し示している場合は、エラーが戻されます。property
引数はboolean
型にしてください。
ビューが仮想表であるのと同様に、オブジェクト・ビューは仮想オブジェクト表です。ビューの各行はオブジェクトであるため、メソッドのコール、ドット表記法を使用したオブジェクト属性へのアクセスおよびオブジェクトを指すREF
の作成が可能です。
関連項目:
継続期間の詳細は、「オブジェクト継続時間」を参照してください
表11-2に示すように、OCIには、アプリケーションでこれらの属性を直接的または間接的に設定あるいはチェックできる関数が提供あります。
表11-2 設定およびチェック関数
メタ属性 | 設定に使用する関数 | チェックに使用する関数 |
---|---|---|
NULL |
<なし> |
|
存在 |
<なし> |
|
ロック |
||
使用済 |
一時オブジェクトには、永続的属性はありません。表11-3では、一時属性を示しています。
表11-3 一時メタ属性
一時メタ属性 | 意味 |
---|---|
既存 |
オブジェクトが存在しているか |
確保済 |
オブジェクトはアプリケーションによってアクセスされるか |
使用済 |
オブジェクトに使用済のマークが設定されているか |
NULL |
インスタンスのNULL情報。 |
割当て時間 |
「オブジェクト継続時間」を参照してください。 |
確保継続時間 |
「オブジェクト継続時間」を参照してください。 |
複合オブジェクトには、そのルート・オブジェクトと、指定したネスト・レベルに基づいてそれぞれプリフェッチされた論理的に関連する一連のオブジェクトが含まれます。
例11-3と例11-4では、一度に1つのインスタンスのみがフェッチまたは確保されました。これらの場合、オブジェクトを取り出すためのサーバー・ラウンドとリップが、確保操作ごとに必要でした。
オブジェクト指向のアプリケーションでは、その問題点を、オブジェクト・グラフを形成する相互関連オブジェクトの集合としてモデル化する場合があります。アプリケーションは、いくつかのイニシャル・オブジェクトの集合から始めて、これらのイニシャル・オブジェクトの参照を使用して残りのオブジェクトを横断することにより、オブジェクトを処理します。クライアント/サーバー設定では、各トラバースがオブジェクトをフェッチするために、ネットワーク・ラウンドトリップを引き起こします。
オブジェクトに対するアプリケーションのパフォーマンスは、複合オブジェクト検索(COR)で向上します。これはプリフェッチ・メカニズムであり、アプリケーションでは、このメカニズムを使用して、リンクされた一連のオブジェクトを1回の操作で取り出すための基準を指定します。
注意:
後述するように、プリフェッチ・オブジェクトがすべて確保されているとはかぎりません。それらはオブジェクト・キャッシュ内にフェッチされるため、後続の確保コールはローカル操作で行われます。
複合オブジェクトは、論理的に関連した複数のオブジェクトの集合で、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回のみですみます。
複合オブジェクトを指定してフェッチした後に行う、複合オブジェクトに含まれるオブジェクトのフェッチでは、これらのオブジェクトがすでにプリフェッチ済であり、オブジェクト・キャッシュ内にあるため、ネットワーク・ラウンドトリップは発生しません。プリフェッチするオブジェクトが多すぎると、オブジェクト・キャッシュがあふれる可能性があることを考慮します。あふれると、アプリケーションですでに確保している他のオブジェクトがキャッシュから押し出され、パフォーマンスの向上ではなく逆にパフォーマンスの低下につながることがあります。
注意:
プリフェッチ・オブジェクトすべてを保持するためのメモリーがキャッシュにない場合は、オブジェクトの一部がプリフェッチされない場合があります。アプリケーションでは、後でそれらのオブジェクトにアクセスするとき、ネットワーク・ラウンドトリップが発生します。
プリフェッチ・オブジェクトすべてに、READ
またはSELECT
権限が必要です。複合オブジェクト内のオブジェクトに対するREAD
または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_TYPE_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
の配列を作成し、別のラウンドトリップで複合オブジェクト全体をロックできます。
例11-5では、複合オブジェクト検索を行うためにアプリケーション・プログラムを修正する方法を説明します。
発注情報とそれに関連した明細項目を表示するアプリケーションを考えてみます。太字のコードでこの処理を実行します。残りのコードでは、プリフェッチの複合オブジェクト検索を使用するため、アプリケーションのパフォーマンスが向上します。
例11-5 OCIでの複合オブジェクト検索の使用
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
インスタンスは参照不可能です。SQLインタフェースを使用できるのは、manager
列インスタンスをフェッチする場合にかぎります。ただし、employee_t
にREF
属性がある場合は、OCIコールを使用してREF
をナビゲートできます。
オブジェクト・キャッシュ内の各オブジェクトには、関連する確保カウントがあります。
確保カウントは、オブジェクトに並行アクセスするコード・モジュールの数を表します。オブジェクトを初めてキャッシュに確保したときに、確保カウントが1に設定されます。複合オブジェクト検索でプリフェッチ・オブジェクトをオブジェクト・キャッシュに入れた時点では、オブジェクトの確保カウントは0 (ゼロ)です。
確保済のオブジェクトを確保することができます。そうすると、確保カウントが1増えます。プロセスでオブジェクトの使用が終了したときは、 OCIObjectUnpin()
を使用して確保解除を行う必要があります。このコールによって確保カウントが1減ります。
オブジェクトの確保カウントが0 (ゼロ)になった場合、必要があればオブジェクトをキャッシュから出し、オブジェクトが占めているメモリー領域を解放できます。
オブジェクトの確保カウントは、OCIObjectPinCountReset()
をコールして、明示的に0 (ゼロ)に設定できます。
アプリケーションではOCICacheUnpin()
をコールして、キャッシュ内にある特定の接続に関連した全オブジェクトを確保解除できます。
関連項目:
確保カウントが0 (ゼロ)のオブジェクトがキャッシュから削除される場合の条件と、キャッシュからエージ・アウトされるオブジェクトの詳細は、「オブジェクト・コピーの解放について」を参照してください
オブジェクトまたはキャッシュ全体の明示的なフラッシュの詳細は、「オブジェクトのマークおよび変更のフラッシュについて」を参照してください
データベース表で、行の中に値のない列がある場合、その列はNULL
またはNULL
を含むと呼ばれます。
オブジェクトには、次の2種類のNULL
を適用できます。
オブジェクトの属性はすべて、NULL
値を持つことができます。これはオブジェクトの属性の値が不明であることを意味します。
オブジェクトのインスタンスがアトミックNULLである場合、オブジェクト全体の値が不明であることを意味します。
アトミックNULLとは、オブジェクトが存在しないということではありません。アトミックNULL
のインスタンスは、値が不明なだけで、存在しています。つまり、データを持たないオブジェクトとみなすことができます。
OCIでオブジェクトを操作する場合は、アプリケーションで使用する各オブジェクト型のNULLインジケータ構造体を定義できます。そのために必要な作業は、通常、OTTで生成したNULL
インジケータ構造体を構造体宣言とともに組み込むことのみです。OTTの出力ヘッダー・ファイルを組み込むと、アプリケーションでNULL
インジケータ構造体を使用できるようになります。
各型に対するNULL
インジケータ構造体の内容は、アトミックNULL
インジケータ(OCIInd
型)とインスタンスの各属性のNULL
インジケータです。型にオブジェクト属性がある場合、NULL
インジケータ構造体には、その属性のNULL
インジケータ構造体が含まれます。例11-6では、対応するNULL
インジケータ構造体とともに型のC表現を示しています。
注意:
person_ind
のdependentsAge
フィールドは、VARRAY (person
のdependentsAge
フィールド)全体がアトミックNULL
かどうかを示します。dependentsAge
フィールドの個々の要素のNULL
情報は、OCICollGetElem()
のコールのelemind
パラメータを通して取り出せます。同様に、person_ind
のprevAddr
フィールドは、NESTED TABLE (person
のprevAddr
フィールド)全体がアトミックNULL
かどうかを示します。prevAddr
フィールドの個々の要素のNULL
情報は、OCICollGetElem()
のコールのelemind
パラメータを通して取り出せます。
オブジェクト型インスタンスの場合は、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;
例11-6 型のC表現とそれらに対応するNULLインジケータ構造体
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;
関連項目:
OTTが生成するNULL
インジケータ構造体の詳細は、「OCIでのObject Type Translatorの使用」を参照してください
OCIアプリケーションでは、OCIObjectNew()
を使用してあらゆるオブジェクトを作成できます。
アプリケーションで永続オブジェクトを作成するには、新規オブジェクトを挿入するオブジェクト表を指定してください。この値は、OCIObjectPinTable()
をコールして取り出し、table
パラメータで渡します。一時オブジェクトを作成するには、作成するオブジェクトの型に対する型記述子オブジェクト(OCIDescribeAny()
をコールして取出し)のみを渡す必要があります。
また、typecode
パラメータに適切な値を渡し、OCIObjectNew()
を使用して、スカラーのインスタンス(REF
、LOB、文字列、ロー、数値、日付など)およびコレクション(VARRAYやNESTED TABLEなど)を作成できます。
この項には次のトピックが含まれます。「新規オブジェクトの属性値」。
デフォルトでは、新規作成されたオブジェクトのすべての属性に NULL
値があります。属性データを初期化した後で、対応する各属性のNULL
ステータスをNULL
以外に変更する必要があります。
オブジェクトの作成時に、属性をNULL
以外の値に設定できます。これは、OCIAttrSet()を使用して環境ハンドルのOCI_ATTR_OBJECT_NEWNOTNULL
属性をTRUE
に設定することによって実行できます。その後、この属性をFALSE
に設定すると、このモードを無効にできます。
OCI_ATTR_OBJECT_NEWNOTNULL
がTRUE
に設定されている場合は、OCIObjectNew()によってNULL
以外のオブジェクトが作成されます。オブジェクトの属性には、表11-4に示したデフォルト値があり、対応するNULL
インジケータは、NOT NULL
に設定されています。
表11-4 新しいオブジェクトの属性値
属性の型 | デフォルト値 |
---|---|
REF |
オブジェクトに |
DATE |
Oracle Databaseで扱える最古の日付で、01-JAN-4712 BCE (ユリウス日の第1日)の午前0時。 |
ANSI DATE |
Oracle Databaseで扱える最古の日付で、01-JAN-4712 BCE (ユリウス日の第1日)。 |
TIMESTAMP |
Oracle Databaseで扱える最古の日時で、01-JAN-4712 BCE (ユリウス日の第1日)の午前0時。 |
TIMESTAMP WITH TIME ZONE |
Oracle Databaseで扱える最古の日時で、UTC (0:0)タイム・ゾーンで、01-JAN-4712 BCE (ユリウス日の第1日)の午前0時。 |
TIMESTAMP WITH LOCAL TIME ZONE |
Oracle Databaseで扱える最古の日時で、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には、オブジェクトに対するポインタを指定すると、オブジェクトの型記述子オブジェクト(TDO)に対する参照を戻すOCIObjectGetProperty()関数があります。
システム生成オブジェクト識別子(OID)を備えたオブジェクト表に基づいて永続オブジェクトが生成されると、OCIObjectGetObjectRef()を使用してただちにこのオブジェクトへの参照を取得できます。ただし、永続オブジェクトがオブジェクト・ビューまたは主キー・ベースのOIDを備えたオブジェクト表に基づいている場合、主キーに属するすべての属性を設定してからでなければ参照は取得できません。
アプリケーションで、OCIObjectNew()コールを使用して、オブジェクト・ビュー、または主キー・ベースのオブジェクト識別子(OID)を備えたオブジェクト表に基づいたオブジェクトを作成できます。このようなビューおよび表のオブジェクト識別子は属性値に基づいているため、アプリケーションでOCIObjectSetAttr()を使用して、主キーに属するすべての属性を設定する必要があります。属性値を設定すると、アプリケーションで、OCIObjectGetObjectRef()をコールすることによって、属性値に基づいたオブジェクト参照を取得できます。
この処理には、次のステップが必要です。
例11-7 オブジェクト・ビューに対する新規オブジェクトの作成
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 objects to server */ ... } /* end function */
例11-7では、このプロセスを実装して、HR
スキーマ内のemp_view
オブジェクト・ビューに対して新しいオブジェクトを作成する方法を示します。
他のOCIアプリケーションと同じです。
アプリケーションでオブジェクトを使用するかどうかにかかわらず、OCIアプリケーションでのエラー処理は同じです。
関連項目:
関数のリターン・コードとエラー・メッセージの詳細は、「OCIでのエラー処理」を参照してください
オブジェクトの型継承には、C++およびJavaでの継承と類似した点が多くあります。
オブジェクト型は、既存のオブジェクト型のサブタイプとして作成できます。サブタイプは、元の型であるスーパータイプのすべての属性とメソッド(メンバー関数とプロシージャ)を継承します。サポートされているのは1回の継承のみで、オブジェクトは複数のスーパータイプを持つことはできません。スーパータイプは、新規属性とメソッドを継承する属性とメソッドに追加します。また、継承されたメソッドのいずれかをオーバーライド(実装の再定義)することもできます。サブタイプは、そのスーパータイプを拡張(つまり、元から継承)します。
たとえば、Person_t
型は、サブタイプStudent_t
とサブタイプEmployee_t
を所有できます。さらに、Student_t
も独自のサブタイプPartTimeStudent_t
を所有できます。サブタイプを所有するには、型宣言にフラグNOT
FINAL
が必要です。デフォルトのフラグはFINAL
で、この型はサブタイプを所有できないことを意味します。
この章でこれまで説明した型は、すべてFINAL
です。Oracle Databaseリリース9.0より前に開発されたアプリケーションの型は、すべて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により生成される構造体については、「OTTでの型の継承のサポート」を参照してください。
このサブタイプStudent_t
は、PartTimeStudent_t
などの独自のサブタイプを所有できます。
CREATE TYPE PartTimeStudent_t UNDER Student_t ( numhours NUMBER) ;
関連項目:
型の継承の詳細は、『Oracle Databaseオブジェクト・リレーショナル開発者ガイド』を参照してください
ポリモフィズムのメリットは、一部代入性プロパティから引き出されます。代入性により、必要になるサブタイプに関する特別な知識がなくても、元々スーパータイプに対して記述されたコードにサブタイプの値を使用させることができます。サブタイプの値は、スーパータイプの値と同様に、メソッドの特殊化の範囲内で別のメカニズムを使用する可能性がある場合でも、前後のコードに対して作用します。
インスタンスの代入性とは、サブタイプのオブジェクト値を、スーパータイプで宣言されたコンテキスト内で使用できるようにする機能を指します。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
として宣言することは無効なため、使用できません。
型の継承をサポートするコールを示します。
OCIDescribeAny()関数は、継承された型固有の情報を提供します。継承された型のプロパティにはその他の属性が追加されています。たとえば、型のスーパータイプを取得できます。
関連項目:
既存のスキーマおよびサブスキーマ・オブジェクトの記述にOCIDescribeAny()を使用できる属性については、表6-7および表6-9を参照してください。
OCIのバインド関数は、REF
、インスタンスおよびコレクション要素の代入性をサポートしています(スーパータイプがある場合は、サブタイプのインスタンスを渡すことができます)。
すべての型のチェックと変換は、サーバー側で実行されるため、OCIのバインド・インタフェースに対する変更はありません。
OCIの定義関数も、代入性をサポートしています(サブタイプのインスタンスをスーパータイプの保持を宣言した定義変数にフェッチできます)。ただし、この場合、サブタイプのインスタンスを保持するために、システムによるメモリーのサイズ変更が必要になることがあります。
注意:
この場合、クライアント・プログラムでは、オブジェクト・キャッシュから割り当てられた(したがって、サイズ変更が可能な)オブジェクトを使用する必要があります。
値が多相になる可能性がある場合、クライアントでは、(スタック上に割り当てられた)構造体を定義変数として使用しないでください。
関連項目:
バインド処理と定義処理の詳細は、「OCIのオブジェクト・リレーショナル・データ型」を参照してください
OCIObjectGetTypeRef()関数は、入力オブジェクトに最も特有の型のTDOのREF
を戻します。この操作では、ユーザーに最も特有な型に対する権限がない場合、エラーが戻されます。
OCIObjectCopy()関数は、ソース・インスタンスの内容をターゲット・インスタンスにコピーします。ソース・インスタンスとターゲット・インスタンスは、同じ型にしてください。スーパータイプとサブタイプ間でコピーすることはできません。
同様に、tdo
引数は、ソースおよびターゲット・オブジェクトと同じオブジェクト・タイプを記述する必要があり、ソースおよびターゲット・オブジェクトのスーパータイプまたはサブタイプを参照しないようにする必要があります。
入力要素として、宣言された型のサブタイプのインスタンスを使用できます。コレクションの型がPerson_t
の場合は、OCICollAssignElem()関数を使用して、Employee_t
インスタンスをコレクションの要素として割り当てることができます。
入力要素として、宣言された型のサブタイプのインスタンスを使用できます。コレクションの型がPerson_t
の場合は、OCICollAppend()関数を使用して、コレクションにEmployee_t
インスタンスを追加できます。
戻されるコレクション要素は、宣言された型のサブタイプのインスタンスの場合があります。コレクションの型がPerson_t
の場合は、OCICollGetElem()関数を使用して、コレクションにEmployee_t
インスタンスなどの要素へのポインタを取得します。
Object Type Translator (OTT)では、継承される属性を'_super'というカプセル化された構造体内で最初に宣言し、その後に新しい属性を宣言することで、オブジェクトの型の継承をサポートします。
これを実行する理由は、Cでは型の継承がサポートされていないためです。
関連項目:
例および説明は、「OTTによる型の継承のサポート」を参照してください
型属性の追加、削除および変更がサポートされています。この概念は、型進化と呼ばれます。
これについては、『Oracle Databaseオブジェクト・リレーショナル開発者ガイド』を参照してください。
OCIDescribeAny()
は、入力オブジェクトの型がOCI_OTYPE_NAME
で、記述済のオブジェクトの型がOCI_PTYPE_TYPE
の場合、つまり、OCIDescribeAny()
に入力する名前が型名である場合、要求された型の最新バージョンの情報を戻します。
関連項目:
「OCITypeArrayByName()」および「OCITypeByName()」。型情報にアクセスするには、これらの関数以外に、OCIDescribeAny()
を使用します。
型進化のオブジェクト・キャッシュへの影響は、「型の変更とオブジェクト・キャッシュ」を参照してください