4 オブジェクト・プログラミング

この章では、Oracle C++ Call Interface (OCCI)を使用してオブジェクト・リレーショナル・プログラミングを実装する方法を説明します。

この章には次のトピックが含まれます:

4.1 オブジェクト・プログラミングの概要

OCCIでは、連想スタイルとナビゲーション・スタイルのデータ・アクセスがサポートされています。通常、第三世代言語(3GL)のプログラムでは、リレーショナル・データベース表で編成された関連付けに基づいた連想アクセスを使用して、データベースに格納されたデータを操作します。連想アクセスでは、データはSQL文やPL/SQLプロシージャの実行によって操作されます。OCCIでは、データをクライアントに移送するためのコストをかけずに、アプリケーションがデータベース・サーバーに対してSQL文やPL/SQLプロシージャを実行できるようにして、オブジェクトへの連想アクセスをサポートしています。

OCCIを使用したオブジェクト指向のプログラムでは、このOCCIプログラミング・パラダイムの重要な側面の1つであるナビゲーショナル・アクセスも活用できます。オブジェクト間のオブジェクト関連は参照(REF)として実装されます。通常、ナビゲーショナル・アクセスを使用するオブジェクト・アプリケーションは、最初にオブジェクトへのREFを戻すSQL文を発行して、データベース・サーバーから1つ以上のオブジェクトを取り出します。次に、アプリケーションはこのREFを使用して関連オブジェクトをたどり、必要に応じて、その他の関連オブジェクト上で計算を実行します。ナビゲーショナル・アクセスでは、初期オブジェクト集合の参照をフェッチする以外、SQL文の実行は含まれません。OCCI APIをナビゲーショナル・アクセスで使用すると、アプリケーションはOracleオブジェクトに対して次の操作を実行できます。

  • オブジェクトの作成、アクセス、ロック、削除、コピーおよびフラッシュ

  • オブジェクトへの参照の取得およびその参照を介したナビゲート

この章では、いくつかの例を示して、永続オブジェクトの作成方法、オブジェクトのアクセス方法と変更方法、および変更内容をデータベース・サーバーにフラッシュする方法を説明します。また、ナビゲーショナル・アクセスおよび連想アクセスの2つのアプローチを使用してオブジェクトにアクセスする方法を説明します。

4.2 OCCIでのC++のオブジェクトの操作について

リレーショナルOCCIアプリケーションに関するプログラミング原理の多くは、オブジェクト・リレーショナル・アプリケーションにも適用されます。オブジェクト・リレーショナル・アプリケーションでは、標準OCCIコールを使用してデータベース接続を確立し、SQL文を処理します。その違いは、発行されるSQL文によってオブジェクト参照が取り出され、OCCIのオブジェクト関数でその参照を操作できることです。オブジェクトを(そのオブジェクト参照を使用せずに)値として直接操作することもできます。

Oracle型のインスタンスは、その存続期間に応じて永続オブジェクト一時オブジェクトに分類されます。永続オブジェクトのインスタンスは、オブジェクト識別子によって参照されるかどうかに応じて、さらにスタンドアロン・オブジェクト埋込みオブジェクトに分割できます。

このセクションのトピックは次のとおりです:

4.2.1 永続オブジェクトについて

永続オブジェクトとは、Oracleデータベースに格納されているオブジェクトのことです。このオブジェクトは、OCCIアプリケーションによってオブジェクト・キャッシュ内にフェッチし、変更できます。永続オブジェクトは、それにアクセスしているアプリケーションの存続期間を超えて存続できます。永続オブジェクトには、次の2種類があります。

  • スタンドアロン・インスタンスはデータベース表の行に格納され、それぞれに一意のオブジェクト識別子があります。OCCIアプリケーションでは、スタンドアロン・オブジェクトへの参照を取り出してオブジェクトを確保し、確保したオブジェクトからその他の関連オブジェクトにナビゲートできます。スタンドアロン・オブジェクトは、参照可能オブジェクトとも呼ばれます。

    永続オブジェクトを選択することもできますが、この場合にはオブジェクトを参照によりフェッチするのではなく、値でフェッチします。

  • 埋込みインスタンスはデータベース表の行に格納されず、別のオブジェクトの中に埋め込まれます。埋込みオブジェクトの例には、別のオブジェクトの属性であるオブジェクトや、データベースの表のオブジェクト列に存在するオブジェクトなどがあります。埋込みオブジェクトにはオブジェクト識別子がないため、OCCIアプリケーションで埋込みインスタンスへのREFを取得することはできません。

    埋込みオブジェクトは、参照不可能なオブジェクトまたは値インスタンスとも呼ばれます。埋込みオブジェクトはと呼ばれることもありますが、それをスカラー・データ値と混同しないでください。どちらの意味であるかは文脈で判断できます。

ユーザーは、参照を介してマテリアライズされた永続オブジェクトを明示的に削除する必要はありません。

ユーザーは、トランザクションがロールバックされたときに、アプリケーションによって作成された永続オブジェクトを削除する必要があります。

例4-1および例4-2のSQLの例では、この2種類の永続オブジェクトの違いを説明します。

例4-1 スタンドアロン・オブジェクトの作成

オブジェクト表person_tabに格納されるオブジェクトは、スタンドアロン・オブジェクトです。このオブジェクトにはオブジェクト識別子があるため、参照できます。したがって、OCCIアプリケーションで確保できます。

CREATE TYPE person_t AS OBJECT
   (name      varchar2(30),
    age       number(3));
CREATE TABLE person_tab OF person_t;

例4-2 埋込みオブジェクトの作成

department表のmanager列に格納されるオブジェクトは、埋込みオブジェクトです。このオブジェクトにはオブジェクト識別子がないため、参照できません。したがって、OCCIアプリケーションで確保できないため、確保を解除する必要もありません。これらは常に、オブジェクト・キャッシュに値によって取得されます。

CREATE TABLE department
   (deptno     number,
    deptname   varchar2(30),
    manager    person_t);

配列確保機能を使用すると、参照のベクターを1回のラウンドトリップで間接参照して、対応するオブジェクトのベクターを戻すことができます。新しいグローバル・メソッドであるpinVectorOfRefs()は、Refのベクターを取得して、PObjectのベクターを1回のラウンドトリップで移入することで、n-1回のラウンドトリップにおけるn-1個の参照の確保にかかるコストを削減します。

4.2.2 一時オブジェクトについて

一時オブジェクトは、オブジェクト型のインスタンスです。一時オブジェクトは、アプリケーションの存続期間を超えて存続できません。また、一時オブジェクトは常にアプリケーションで削除できます。

オブジェクト型トランスレータ(OTT)ユーティリティは、例4-3に示すように、各C++クラスに対して2つのoperator newメソッドを生成します 4-3:

例4-4に、一時オブジェクトの動的な作成方法を示します。一時オブジェクトを永続オブジェクトに変換することはできません。これらの役割はインスタンス化されたときに決定され、一時オブジェクトを削除してメモリーを解放するのはユーザーの役割です。

例4-5に示すように、一時オブジェクトはローカル変数としてスタック上に作成することもできます。後者の方法では、変数の有効範囲が終了した際に一時オブジェクトが破棄されることが保証されています。

関連項目:

例4-3 オブジェクト型トランスレータ・ユーティリティにおけるOperator new()の2つのメソッド

class Person : public PObject {
   ...
public:
   dvoid *operator new(size_t size);    // creates transient instance
   dvoid *operator new(size_t size, Connection &conn, string table);
                                        // creates persistent instance
}

例4-4 一時オブジェクトの動的な作成方法

Person *p = new Person();

例4-5 ローカル変数としての一時オブジェクトの作成方法

Person p;

4.2.3 値について

このマニュアルでは、は次のいずれかを意味します。

  • データベースの表の非オブジェクト列に格納されるスカラー値。OCCIアプリケーションでは、SQL文を発行してデータベースから値をフェッチできます。

  • 埋込み(参照不可能)オブジェクト。

どちらを指しているのかは文脈で判断できます。

参照可能オブジェクトは、確保するかわりにオブジェクト・キャッシュ内でSELECTすることができ、その場合は、参照によってフェッチせずに、オブジェクトを値によってフェッチします。

4.3 C++アプリケーションでのオブジェクトの表現について

OCCIアプリケーションでオブジェクト型を操作するには、データベースにそのオブジェクト型が存在している必要があります。通常は、CREATE TYPEなど、SQLのDDL文で型を作成します。

このセクションのトピックは次のとおりです:

4.3.1 永続オブジェクトと一時オブジェクトの作成

この項では、永続オブジェクトと一時オブジェクトの作成方法について説明します。

永続オブジェクトを作成する前に、環境を作成し、接続をオープンしておく必要があります。

永続オブジェクトは、次のいずれか1つが発生した場合にのみ、データベース内に作成されます。

  • トランザクションがコミットされた場合(Connection::commit())

  • オブジェクト・キャッシュがフラッシュされた場合(Connection::flushCache())

  • オブジェクト自体がフラッシュされた場合(PObject::flush())

例4-6は、データベース表addr_tabに永続オブジェクトaddrを作成する方法を示しています。

例4-7は、一時オブジェクトADDRESSのインスタンスの作成方法を示しています。

例4-6 永続オブジェクトの作成方法

CREATE TYPE ADDRESS AS OBJECT (
   state CHAR(2), 
   zip_code CHAR(5));
CREATE TABLE ADDR_TAB of ADDRESS;
ADDRESS *addr = new(conn, "ADDR_TAB") ADDRESS("CA", "94065");

例4-7 一時オブジェクトの作成方法

ADDRESS *addr_trans = new ADDRESS("MD", "94111");

4.3.2 OTTユーティリティによるオブジェクト表現の作成

C++アプリケーションでデータベースからオブジェクトのインスタンスを取り出す場合は、オブジェクトのクライアント側表現が必要です。オブジェクト型トランスレータ(OTT)ユーティリティでは、データベース・オブジェクト型のC++クラス表現を生成します。例4-8は、データベースでのカスタム型の宣言およびOTTユーティリティで生成される対応するC++クラスを示しています。

例4-8のこれらのクラス宣言は、OTTによって、名前を指定するヘッダー・ファイルに自動的に書き込まれます。このヘッダー・ファイルをアプリケーションのソース・ファイルにインクルードすると、オブジェクトにアクセスできます。PObject (およびPObjectから導出されたクラス)のインスタンスは、一時または永続インスタンスです。writeSQL()メソッドおよびreadSQL()メソッドは、オブジェクトのシリアライズのためにOCCIオブジェクト・キャッシュで内部的に使用され、OCCIクライアントでは使用および変更できません。

関連項目:

OTTユーティリティの詳細は、「オブジェクト型トランスレータ・ユーティリティ」を参照してください。

例4-8 データベースでのカスタム型の宣言方法

CREATE TYPE address AS OBJECT (state CHAR(2), zip_code CHAR(5));

OTTユーティリティは、次のC++クラスを生成します。

class ADDRESS : public PObject {

   protected:
      string state;
      string zip;

   public:
      void *operator new(size_t size); 
      void *operator new(size_t size, 
         const Connection* conn, 
         const string& table);
      string  getSQLTypeName() const;
      void getSQLTypeName(oracle::occi::Environment *env, void **schemaName,
                          unsigned int &schemaNameLen, void **typeName,
                          unsigned int &typeNameLen) const;
      ADDRESS(void *ctx) : PObject(ctx) { };
      static void *readSQL(void *ctx);
      virtual void readSQL(AnyData& stream);
      static void writeSQL(void *obj, void *ctx);
      virtual void writeSQL(AnyData& stream);
}

4.4 OCCIによるC++アプリケーションの開発について

この項では、基本的なOCCIオブジェクト・アプリケーションの開発に伴うステップを説明します。

このセクションのトピックは次のとおりです:

4.4.1 基本的なオブジェクト・プログラム構造体の開発

オブジェクトを使用したOCCIアプリケーションの基本構造は、リレーショナルOCCIアプリケーションと類似していますが、オブジェクト機能には違いがあります。OCCIオブジェクト・プログラムに伴うステップは、次のとおりです。

  1. Environmentを初期化します。OCCIプログラミング環境をオブジェクト・モードで初期化します。アプリケーションでは、ヘッダー・ファイルからデータベース・オブジェクトのC++クラス表現をインクルードする必要があります。「オブジェクト型トランスレータ・ユーティリティ」で説明しているように、オブジェクト型トランスレータ(OTT)ユーティリティを使用すると、このクラスを作成できます。

  2. 接続を確立します。環境ハンドルを使用して、データベース・サーバーとの接続を確立します。

  3. SQL文を準備します。これは、ローカル(クライアント側)のステップで、プレースホルダをバインドします。オブジェクト・リレーショナル・アプリケーションの場合、このSQL文はオブジェクトへの参照(REF)を戻します。

  4. オブジェクトにアクセスします。

    1. プリペアド文をデータベース・サーバーに関連付けて実行します。

    2. ナビゲーショナル・アクセスを使用して、データベース・サーバーからオブジェクト参照(REF)を取り出し、オブジェクトを確保します。その後、次の一部またはすべての操作を実行できます。

      • オブジェクトの属性を操作し、そのオブジェクトに使用済(変更済)のマークを付ける。

      • 別の1つのオブジェクト、または一連のオブジェクトへの参照をたどる。

      • 型および属性の情報にアクセスする。

      • 複合オブジェクト検索グラフをナビゲートする。

      • 変更したオブジェクトをデータベース・サーバーにフラッシュする。

    3. 連想アクセスを使用すると、SQLを使ってオブジェクト全体を値によりフェッチできます。また、埋込み(参照不可能)オブジェクトを選択することもできます。その後、次の一部またはすべての操作を実行できます。

      • 表に値を挿入する。

      • 既存の値を変更する。

  5. トランザクションをコミットします。このステップでは、変更されたすべてのオブジェクトがデータベース・サーバーに暗黙的に書き込まれ、変更がコミットされます。

  6. 文とハンドルを解放します。プリコンパイルされた文を再利用または再実行することはできません。

    関連項目:

4.4.2 基本的なオブジェクト操作のフローについて

図4-1は、アプリケーションがオブジェクトを操作する方法について、そのプログラム・ロジックを簡単に示しています。図を簡略にするために、必要なステップの一部が省略されています。

図4-1 基本的なオブジェクト操作のフロー

図4-1の説明が続きます
「図4-1 基本的なオブジェクト操作のフロー」の説明
4.4.2.1 オブジェクト・モードでのOCCIの初期化について

OCCIアプリケーションでオブジェクトにアクセスして操作する場合は、OCCIアプリケーションの最初のコールであるcreateEnvironment()メソッドのmodeパラメータに、OBJECTの値を指定する必要があります。modeにこの値を指定することで、アプリケーションでオブジェクトを操作することをOCCIに通知します。この通知により、次の重要事項が有効になります。

  • オブジェクト・ランタイム環境が確立されます。

  • オブジェクト・キャッシュが設定されます。

modeパラメータにOBJECTを設定しないと、オブジェクト関連関数を使用しても結果はエラーになります。

次のコード例は、OCCI環境の作成時にOBJECTモードを指定する方法を示しています。

Environment *env;
Connection *con;
Statement *stmt;

env = Environment::createEnvironment(Environment::OBJECT);
con = Connection(userName, password, connectString);

データベース・オブジェクトがオブジェクト・キャッシュ内にロードされたときに、アプリケーションでメモリーを割り当てる必要はありません。オブジェクト・キャッシュによって、データベース・オブジェクトに対する透過的かつ効率的なメモリー管理が行われます。データベース・オブジェクトはオブジェクト・キャッシュ内にロードされると、ホスト言語(C++)表現に透過的にマップされます。

オブジェクト・キャッシュにより、オブジェクト・キャッシュ内のオブジェクト・コピーと、対応するデータベース・オブジェクトとの間の関連付けが保持されます。commitが実行されると、オブジェクト・キャッシュ内のオブジェクト・コピーに加えた変更が、データベースに対して自動的に伝播されます。

また、オブジェクト・キャッシュでは、参照をオブジェクトにマッピングするための参照表がメンテナンスされます。アプリケーションでオブジェクトへの参照を間接参照したときに、対応するオブジェクトがオブジェクト・キャッシュ内にキャッシュされていない場合は、データベース・サーバーに対してデータベースからオブジェクトをフェッチし、オブジェクト・キャッシュ内にロードするように、要求が自動的に送信されます。その後、同じ参照を間接参照すると、オブジェクト・キャッシュ自体が間接参照の対象となり、データベース・サーバーとのラウンドトリップを伴わないため、処理が速くなります。

同じ参照に関するその後の間接参照は、ラウンドトリップなしに、キャッシュからフェッチされます。ただし、コミット直後に行われた間接参照操作の場合を除きます。この場合は、オブジェクトの最新コピーがサーバーから戻されます。この機能により、各トランザクションの後にデータベースの最新オブジェクトが確実にキャッシュされます。

さらに、オブジェクト・キャッシュでは、オブジェクト・キャッシュ内の各永続オブジェクトに対する確保カウントが保持されます。アプリケーションでオブジェクトへの参照を間接参照すると、オブジェクトの確保カウントが増加します。その後、同じオブジェクトへの参照を間接参照しても、確保カウントは変更されません。オブジェクトは、そのオブジェクトへの参照がスコープ外になるまで、オブジェクト・キャッシュ内に継続して確保され、OCCIクライアントによるアクセスが可能です。

確保カウントは、オブジェクトに対する参照カウントとして機能します。オブジェクトの確保カウントは、そのオブジェクトへの参照がなくなった場合にのみ0 (ゼロ)になり、その間このオブジェクトは、ガベージ・コレクションの対象となります。オブジェクト・キャッシュでは、最低使用頻度アルゴリズムによって、オブジェクト・キャッシュのサイズが管理されます。このアルゴリズムにより、オブジェクト・キャッシュが最大サイズに達すると、確保カウント0 (ゼロ)のオブジェクトは解放されます。

4.4.2.2 オブジェクトの確保について

オブジェクト・キャッシュがキャッシュ内のすべてのオブジェクトの確保カウントを自動的に追跡しているため、多くの場合、OCCIユーザーはオブジェクトの確保や確保解除を明示的に行う必要はありません。すでに説明したように、オブジェクト・キャッシュは参照がオブジェクトを示している場合に確保カウントを増分し、参照がスコープ外であるかオブジェクトをもはや示していない場合に確保カウントを減分します。

ただし、例外が1つあります。OCCIアプリケーションでRef<T>::ptr()メソッドを使用してオブジェクトへのポインタを取得している場合は、PObjectクラスのpinメソッドとunpinメソッドを使用してオブジェクト・キャッシュ内のオブジェクトの確保と確保解除を制御できます。

4.4.2.3 キャッシュ内のオブジェクトの操作について

オブジェクト・キャッシュではオブジェクト・コピーの内容は管理されず、オブジェクト・コピーが自動的にリフレッシュされることもありません。オブジェクト・コピーの妥当性と一貫性は、アプリケーションで確認する必要があります。

4.4.2.4 変更のオブジェクトへのフラッシュについて

オブジェクト・キャッシュ内のオブジェクト・コピーに変更を加えた場合は、必ずアプリケーションで、変更したオブジェクトをデータベースにフラッシュする必要があります。

オブジェクト・キャッシュ用のメモリーは、オブジェクトがオブジェクト・キャッシュ内にロードされるときに、必要に応じて割り当てられます。

クライアント側オブジェクト・キャッシュは、プログラムのプロセス領域に割り当てられます。このオブジェクト・キャッシュは、データベース・サーバーから取り出してアプリケーションで使用できるオブジェクト用のメモリーです。

OCCI環境をオブジェクト・モードで初期化すると、実際にオブジェクト・コールを使用するかどうかに関係なく、オブジェクト・キャッシュ用のメモリーを割り当てることになります。

OCCI環境ごとに、1つのオブジェクト・キャッシュのみが割り当てられます。1つの環境内で異なる接続を通じて取出しまたは作成されたオブジェクトは、すべて同じ物理オブジェクト・キャッシュを使用します。各接続には、それぞれ専用の論理オブジェクト・キャッシュがあります。

4.4.2.5 オブジェクトの削除について

参照を間接参照してキャッシュに取得したオブジェクトに対しては、明示的な削除を実行しないでください。このようなオブジェクトの場合は、最初に参照が間接参照されると確保カウントが増加し、参照がスコープ外になると確保カウントが減少します。オブジェクトの確保カウントが0 (ゼロ)になると(そのオブジェクトへのすべての参照がスコープ外であることを示す)、そのオブジェクトは自動的にガベージ・コレクションの対象となり、その後、キャッシュから削除されます。

new演算子をコールして作成された永続オブジェクトについては、トランザクションをコミットしない場合は、deleteをコールする必要があります。コミットする場合、オブジェクトはコミット後にガベージ・コレクションの対象となります。これは、newを使用してこのようなオブジェクトが作成された場合、確保カウントは0 (ゼロ)から始まるためです。ただし、オブジェクトは使用済であるため、キャッシュ内に残ります。コミット後は使用済でなくなるため、ガベージ・コレクションの対象となります。したがって、削除の必要はありません。

コミットが実行されない場合は、deleteを明示的にコールして、そのオブジェクトを破棄する必要があります。この操作は、そのオブジェクトへの参照がない場合に実行できます。一時オブジェクトの場合は、明示的に削除して、そのオブジェクトを破棄する必要があります。

永続オブジェクトに対してdelete演算子をコールすることはできません。マーク済または使用済ではない永続オブジェクトは、確保カウントが0 (ゼロ)になると、ガベージ・コレクタによって解放されます。ただし、一時オブジェクトの場合は、明示的に削除して、破棄する必要があります。

4.5 OCCIによるOracleへのC++アプリケーションの移行

この項では、OCCIを使用して既存のC++アプリケーションを移行する方法について説明します。

移行の手順は次のとおりです。

  1. オブジェクト・モデルおよびクラス階層の決定
  2. JDeveloper9iによるOracleオブジェクト・スキーマへのマップ
  3. Oracle Type TranslatorによるC++ヘッダー・ファイルの生成
  4. 新しいオブジェクト型定義の操作に必要な古いC++アクセス・クラスの変更
  5. 一時オブジェクトおよび永続オブジェクトを管理する機能の追加(必要な場合)

4.6 連想アクセスの概要

OCCIでは、SQLを使用してオブジェクトを取り出し、DML操作を実行できます。

このセクションのトピックは次のとおりです:

4.6.1 SQLによるオブジェクトへのアクセス

前項では、ナビゲーショナル・アクセスについて説明し、初期オブジェクト集合の参照をフェッチするためにのみSQLを使用しました。ここでは、SQLを使用してオブジェクトをフェッチする方法を説明します。

次の例は、ResultSet::getObject()メソッドを使用して連想アクセスでオブジェクトをフェッチし、その際に、SQLを使用してaddr_tab表から各オブジェクトを取得する方法を示しています。

string sel_addr_val = "SELECT VALUE(address) FROM ADDR_TAB address";

ResultSet *rs = stmt->executeQuery(sel_addr_val);

while (rs->next())
{
   ADDRESS *addr_val = rs->getObject(1); 
   cout << "state: " << addr_val->getState();
}

連想アクセスでフェッチされたオブジェクトは期限付きの値インスタンスで、一時オブジェクトと同様に振る舞います。markModified()flush()markDeleted()などのメソッドは、永続オブジェクトにのみ適用できます。

これらのオブジェクトに加えた変更は、データベースに反映されません。

戻されるオブジェクトは値インスタンスであるため、オブジェクト・ポインタを削除してメモリーを解放するのは、ユーザーの役割です。

4.6.2 値の挿入と変更

前項では、SQLを使用してオブジェクトにアクセスする方法を説明しました。OCCIでは、SQLを使用して、Statement::setObjectメソッド・インタフェースを通じてデータベース・サーバー内に新規オブジェクトを挿入したり、データベース・サーバー内の既存オブジェクトを変更することもできます。

次の例では、一時オブジェクトAddressを作成し、そのオブジェクトをデータベース表addr_tabに挿入しています。

ADDRESS *addr_val = new address("NV", "12563");  // new a transient instance
stmt->setSQL("INSERT INTO ADDR_TAB values(:1)");
stmt->setObject(1, addr_val);
stmt->execute();

4.7 ナビゲーショナル・アクセスの概要

ナビゲーショナル・アクセスを使用して、次の一連の操作を実行します。

このセクションのトピックは次のとおりです:

4.7.1 データベース・サーバーからのオブジェクト参照(REF)の取出し

アプリケーションでオブジェクトを操作するには、最初にデータベース・サーバーから1つ以上のオブジェクトを取り出す必要があります。取出しは、1つ以上のオブジェクトへの参照(REF)を戻すSQL文を発行して行います。

SQL文によって、データベースからREFではなく値インスタンスをフェッチすることもできます。

次のSQL文では、1つのオブジェクトaddressへのREFをデータベース表addr_tabから取り出しています。

string sel_addr = "SELECT REF(address) 
   FROM addr_tab address 
   WHERE zip_code = '94065'";

次のコード例は、問合せを実行し、結果セットからREFをフェッチする方法を示しています。

ResultSet *rs = stmt->executeQuery(sel_addr);
rs->next();
Ref<address> addr_ref = rs->getRef(1);

この時点で、オブジェクト参照を使用して、オブジェクトまたはデータベースからのオブジェクトにアクセスして操作できます。

関連項目:

SQL文の準備および実行の一般情報は、「SQLのDDL文とDML文の実行について」を参照してください

4.7.2 オブジェクトの確保

この項では、1度に1つのオブジェクトを確保する単純な操作の例を示します。複合オブジェクト検索を介した複数のオブジェクトの取出しの詳細は、「複合オブジェクト検索の概要」を参照してください。

フェッチを行うステップが完了すると、アプリケーションでは、オブジェクトへのREFを取得します。この時点では、まだ実際のオブジェクトは操作できません。オブジェクトを操作する前にオブジェクトを確保する必要があります。オブジェクトの確保によって、オブジェクトをオブジェクト・キャッシュ内にロードした後、オブジェクトの属性へのアクセスおよび変更、また1つのオブジェクトからその他のオブジェクトへの参照追跡が可能になります。アプリケーションでは、変更したオブジェクトをデータベース・サーバーに書き込む時期を制御することもできます。

OCCIでは、C++ポインタを間接参照するのと同じ方法でREFの間接参照のみが必要となります。REFを間接参照すると、オブジェクトはC++クラス・インスタンスとして透過的にマテリアライズされます。

前項のAddressクラスの例に続けて、ユーザーが次のメソッドを追加したとします。

string  Address::getState()
{
   return state;
}

このREFを間接参照し、オブジェクトの属性およびメソッドにアクセスするには、次のように記述します。

string state = addr_ref->getState();     // -> pins the object

最初に Ref<T> (addr_ref)が間接参照されたときに、オブジェクトが確保されます(つまり、オブジェクトがデータベース・サーバーからオブジェクト・キャッシュ内にロードされます)。オブジェクトが確保された後は、Ref<T>の演算子 ->の動作は、C++ポインタ (T *)と同じ動作になります。オブジェクトは、REF (addr_ref)がスコープ外となるまでオブジェクト・キャッシュ内に残留します。その後、ガベージ・コレクションの対象となります。

これでオブジェクトが確保され、アプリケーションでそのオブジェクトを変更できます。

4.7.3 オブジェクト属性の操作

オブジェクト属性の操作は、前項で示したようにオブジェクトにアクセスして行います。Addressクラスに、state属性を入力値に設定する次のユーザー定義メソッドがあるとします。

void Address::setState(string new_state)
{
   state = new_state;
}

次の例は、オブジェクトaddrのstate属性を変更する方法を示しています。

addr_ref->setState("PA");

前述のとおり、オブジェクトがオブジェクト・キャッシュ内にない場合は、Ref<T>の演算子->の最初の呼出しで、オブジェクトがロードされます。

4.7.4 オブジェクトのマークおよび変更のフラッシュについて

前項の例では、オブジェクトの属性を変更しました。この変更はクライアント側キャッシュのみに生じます。変更をデータベースに書き込むには、特定のプログラム上の手順を実装する必要があります。

4.7.5 オブジェクトへの変更済(使用済)のマーク付け

最初のステップでは、オブジェクトが変更されたことを示します。これは、オブジェクトに対してmarkModified()メソッド(PObjectの導出メソッド)をコールして行います。このメソッドは、オブジェクトに使用済 (変更済)のマークを付けます。

前の例に続いて、オブジェクト属性の操作後、次のようにaddr_refが参照するオブジェクトに使用済のマークを付けます。

addr_ref->markModified();

4.7.6 データベースへの変更の記録について

使用済フラグが設定されているオブジェクトは、変更をデータベースに記録するために、データベース・サーバーにフラッシュする必要があります。これは、次の3つの方法で実行できます。

  • 使用済とマークされた1つのオブジェクトをフラッシュするには、PObjectの導出メソッドであるflushメソッドをコールします。

  • オブジェクト・キャッシュ全体をフラッシュするには、Connection::flushCache()メソッドを使用します。この場合、OCCIはオブジェクト・キャッシュによって保持されている使用済リストを横断して、使用済オブジェクトをすべてフラッシュします。

  • トランザクションをコミットするには、Connection::commit()メソッドをコールします。この場合も、使用済リストを横断して、オブジェクトをデータベース・サーバーにフラッシュします。この使用済リストには、新しく作成された永続オブジェクトが含まれています。

4.7.7 オブジェクト・キャッシュでのガベージ・コレクション

オブジェクト・キャッシュには、次の2つの重要な関連パラメータがあります。

  • 最大キャッシュ・サイズ率

  • 最適キャッシュ・サイズ

これらのパラメータは、キャッシュ・メモリー使用量のレベルを参照し、対象のオブジェクトをキャッシュで自動的にエージ・アウトしてメモリーを解放する時期を判断するのに役立ちます。

現在キャッシュ内にあるオブジェクトによるメモリー使用量が最大キャッシュ・サイズに達するかそれを超えると、キャッシュでは、確保カウントが0 (ゼロ)のマークされていないオブジェクトの解放(またはエージ・アウト)が自動的に開始されます。キャッシュでは、キャッシュ内のメモリー使用量が最適なサイズに達するまで、または解放の対象となるオブジェクトがなくなるまで、オブジェクトの解放を継続します。キャッシュは、指定の最大キャッシュ・サイズより大きくなる場合があります。

オブジェクトの最大キャッシュ・サイズ(バイト単位)は、次に示すように、最適キャッシュ・サイズ(optimal_size)に最大キャッシュ・サイズ率(max_size_percentage)を増分して計算されます。

Maximum cache size = optimal_size + optimal_size * max_size_percentage / 100;

最大キャッシュ・サイズ率のデフォルト値は10%です。最適キャッシュ・サイズのデフォルト値は8MBです。オーバーロードされたPObject::new()演算子を使用して永続オブジェクトが作成されている場合、新しく作成されたオブジェクトは使用済としてマークされ、その確保カウントは0 (ゼロ)になります。

これらのパラメータは、Environmentクラスの次のメンバー関数を使用して設定または取得できます。

  • void setCacheMaxSize(unsigned int maxSize);

  • unsigned int getCacheMaxSize() const;

  • void setCacheOptSize(unsigned int OptSize);

  • unsigned int getCacheOptSize() const;

オブジェクトの確保カウントがどのように参照カウントとして機能するか、および確保カウントが0 (ゼロ)の、マークされていないオブジェクトがどのようにガベージ・コレクションの対象となるかについては、「オブジェクトの確保について」で説明しています。新しく作成された永続オブジェクトの場合は、トランザクションのコミット後または異常終了後、およびオブジェクトの確保カウントが0 (ゼロ)の場合に、オブジェクトのマークが解除されます。このオブジェクトを参照しているものはないため、エージ・アウトの候補になります。

大量の文字列属性やコレクション属性を持つ複数のオブジェクトを処理していると、メモリーの大部分はC++ヒープから割当てが行われます。これは、OCCIがSTLを使用するためです。したがって、ガベージ・コレクションが起動する前に、メモリー使用量が高くなるのを避けるためにキャッシュ・サイズを小さな値に設定する必要があります。

関連項目:

詳細は、「OCCI Application Program Interface」を参照してください。

4.7.8 参照のトランザクション一貫性の確保について

前のセクションで説明したように、Ref<T>を初めて逆参照すると、オブジェクトがデータベース・サーバーからオブジェクト・キャッシュにロードされます。オブジェクトがロードされた後は、Ref<T>の演算子->の動作はC++ポインタと同じになり、キャッシュ内のオブジェクトのコピーにアクセス可能となります。ただし、トランザクションがコミットされるか中止されると、キャッシュ内のオブジェクトのコピーは他のクライアントから修正される可能性がありますので、もはや有効ではなくなります。したがって、トランザクションの終了後には、そのRef<T>が再び逆参照された際に、オブジェクト・キャッシュはそのオブジェクトがもはや有効ではないことを認識してデータベース・サーバーから最新のコピーをフェッチします。

4.8 複合オブジェクト検索の概要

ここまでの説明では、1度に1つのオブジェクトのみフェッチまたは確保する例を示しました。その場合、オブジェクトを取り出すためのデータベース・サーバー・ラウンドトリップが、確保操作ごとに別個に発生します。

オブジェクト指向のアプリケーションでは、多くの場合、問題をオブジェクト・グラフを形成する相互関連オブジェクトの集合としてモデル化します。これらのアプリケーションは、いくつかのイニシャル・オブジェクトの集合から始めて、これらのイニシャル・オブジェクトの参照を使用して残りのオブジェクトにアクセスすることで、オブジェクトを処理します。クライアント/サーバー設定では、これらのトラバースはそれぞれ、オブジェクトのフェッチにコストの高いネットワーク・ラウンドトリップを発生させることがあります。

複合オブジェクト検索(COR)を使用すると、このようなアプリケーションのパフォーマンスが向上する場合があります。これはプリフェッチ・メカニズムであり、アプリケーションでは、リンクされた一連のオブジェクトを1回のネットワーク・ラウンドトリップで取り出すための基準(内容と境界)を指定します。CORでは、プリフェッチされたオブジェクトを確保しません。オブジェクトはオブジェクト・キャッシュ内にフェッチされるため、後続の確保コールはローカル操作で行われます。

複合オブジェクトは、論理的に関連した複数のオブジェクトの集合で、1つのルート・オブジェクトと、指定したネスト・レベルに基づいてそれぞれプリフェッチされた一連のオブジェクトで構成されています。ルート・オブジェクトは、明示的にフェッチされるか、または確保されます。ネストレベルは、複合オブジェクトのルート・オブジェクトから指定のプリフェッチ・オブジェクトまで最短で横断した場合の参照の数です。

アプリケーションで複合オブジェクトを指定するには、複合オブジェクトの内容と境界を記述します。複合オブジェクトのフェッチは、環境のプリフェッチ制限、およびオブジェクトのプリフェッチに使用可能な、オブジェクト・キャッシュ内のメモリーの量によって制約されます。

複合オブジェクト検索を使用するとパフォーマンスのみが改善され、機能性は追加されません。したがって、使用しないことも可能です。

このセクションのトピックは次のとおりです:

4.8.1 複合オブジェクト検索

OCCIアプリケーションでCORを実行するには、適切な属性をRef<T>に設定した後、次のメソッドを使用して間接参照します。

// prefetch attributes of the specified type name up to the specified depth
Ref<T>::setPrefetch(const string &typeName, unsigned int depth);
// prefetch all the attribute types up to the specified depth.
Ref<T>::setPrefetch(unsigned int depth);

アプリケーションでは、REFを通して特定の深さまで到達できる(推移閉包)オブジェクトすべてをフェッチすることも選択できます。そのためには、目的の深さをレベル・パラメータとして設定する必要があります。前述の2つの例では、アプリケーションで(PO object REF, OCCI_MAX_PREFETCH_DEPTH)(PO object REF, 1)をそれぞれ指定して、必要なオブジェクトをプリフェッチすることもできます。この場合は多数の余分なフェッチが発生しますが、指定が簡単であり、データベース・サーバー・ラウンドトリップが1回のみで済みます。

この説明の例として、次の型宣言があるとします。

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_numberline_itemsVARRAYおよび2つの参照が含まれます。1つはcustomer型への参照、もう1つはpurchase_order型への参照であり、この型がリンク済リストとして実装されることを示しています。

複合オブジェクトをフェッチする場合は、アプリケーションで次の指定を行う必要があります。

  • 目的のルート・オブジェクトへの参照。

  • 複合オブジェクトの境界を指定するための1組以上の型および深さ情報。型情報はCORでたどるREF属性を指示し、ネスト・レベルはリンクをたどるレベル数を指示します。

前述のpurchase_orderオブジェクトの場合は、アプリケーションで次の指定を行う必要があります。

  • ルートpurchase_orderオブジェクトへの参照。

  • customerpurchase_orderまたはline_itemについての1組以上の型および深さ情報。

発注情報をプリフェッチするアプリケーションでは、発注書を発行した顧客の情報にアクセスする必要があります。単純なナビゲーションでは、2つのオブジェクトを取り出すためにデータベース・サーバーに2回アクセスする必要があります。

複合オブジェクト検索を使用すると、purchase_orderオブジェクトを確保するときにcustomerをプリフェッチできます。この場合、複合オブジェクトは、purchase_orderオブジェクトと参照するcustomerオブジェクトで構成されます。

前述の例で、発注情報と関連する顧客情報をプリフェッチする必要がある場合は、次に示すように、アプリケーションでpurchase_orderオブジェクトを指定し、customerはネスト・レベル1までたどるように指示します。

Ref<PURCHASE_ORDER> poref;
poref.setPrefetch("CUSTOMER",1);

アプリケーションで、purchase orderと、それに含まれるオブジェクト・グラフのすべてのオブジェクトをプリフェッチする必要がある場合、アプリケーションはpurchase_orderオブジェクトを指定し、customerおよびpurchase_orderは両方とも、最大のネスト・レベルまでたどるように次のように示します。

Ref<PURCHASE_ORDER> poref;
poref.setPrefetch("CUSTOMER", OCCI_MAX_PREFETCH_DEPTH);
poref.setPrefetch("PURCHASE_ORDER", OCCI_MAX_PREFETCH_DEPTH);

OCCI_MAX_PREFETCH_DEPTHは、ルート・オブジェクトから参照を通して到達できる指定の型のオブジェクトすべてのプリフェッチを指定します。

発注情報と発注情報に関連付けられた明細項目すべてをプリフェッチする必要がある場合は、次に示すように、アプリケーションでpurchase_orderオブジェクトを指定し、line_itemsは最大のネスト・レベルまでたどるように指示します。

Ref<PURCHASE_ORDER> poref;
poref.setPrefetch("LINE_ITEM", 1);

4.8.2 複合オブジェクトのプリフェッチについて

複合オブジェクトを指定してフェッチした後に行う、複合オブジェクトに含まれるオブジェクトのフェッチでは、これらのオブジェクトがすでにプリフェッチ済であり、オブジェクト・キャッシュ内にあるため、ネットワーク・ラウンドトリップは発生しません。プリフェッチするオブジェクトが多すぎると、オブジェクト・キャッシュがあふれる可能性があることに注意してください。あふれると、アプリケーションですでに確保している他のオブジェクトがキャッシュから押し出され、パフォーマンスの向上ではなく逆にパフォーマンスの低下につながることがあります。

プリフェッチ済のオブジェクトをすべて保持できるメモリーがオブジェクト・キャッシュにない場合は、オブジェクトの一部がプリフェッチされないことがあります。アプリケーションでは、後でそれらのオブジェクトにアクセスするときに、ネットワーク・ラウンドトリップが発生します。

プリフェッチ・オブジェクトすべてに、READまたはSELECT権限が必要です。複合オブジェクト内のオブジェクトに対するREADまたはSELECT権限がアプリケーションにない場合、そのオブジェクトはプリフェッチできません。

ConnectionクラスのグローバルなpinVectorOfRefs()メソッドを使用すると、1回のラウンドトリップでRefのベクター全体をオブジェクト・キャッシュにプリフェッチできます。このメソッドは、Refnサイズのベクターのラウンドトリップの回数をnから1に減らし、OUTパラメータ・ベクターを介して新しく確保されたオブジェクトを追跡します。

4.9 コレクションの操作

Oracleでは、可変長配列(順序付けられたコレクション)とネストした表(順序付けられていないコレクション)の2種類のコレクションをサポートしています。OCCIでは、この2種類のコレクションはいずれも標準テンプレート・ライブラリ(Standard Template Library: STL)のベクター・コンテナにマップされるため、強力、柔軟かつ高速なSTLベクターによるコレクション要素へのアクセスと操作が可能になります。例4-9は、VARRAYおよびVARRAY型の属性が含まれたオブジェクトを作成するSQLのDDL文と、結果としてOTTで生成されるC++宣言を示しています。

関連項目:

デモ・プログラムの完全なコード・リスト

例4-9 VARRAYコレクションの作成方法

CREATE TYPE ADDR_LIST AS VARRAY(3) OF REF ADDRESS;
CREATE TYPE PERSON AS OBJECT (name VARCHAR2(20), addr_l ADDR_LIST);

次に示すのは、OTTによって生成されたC++クラス宣言です。

class PERSON : public PObject
{
   protected:
      string name;
      vector< Ref< ADDRESS > > addr_1;

   public:
      void *operator new(size_t size); 
      void *operator new(size_t size,
      const Connection* conn,
      const string& table); 
      string  getSQLTypeName() const;
      void getSQLTypeName(oracle::occi::Environment *env, void **schemaName,
                          unsigned int &schemaNameLen, void **typeName,
                          unsigned int &typeNameLen) const;
      PERSON (void *ctx) : PObject(ctx) { };
      static void *readSQL(void *ctx);
      virtual void readSQL(AnyData& stream);
      static void writeSQL(void *obj, void *ctx);
      virtual void writeSQL(AnyData& stream);
}
この項には次のトピックが含まれます:

4.9.1 埋込みオブジェクトのフェッチ

使用しているアプリケーションで、埋込みオブジェクト・インスタンス(オブジェクト表でなく、通常の表の列に格納されているオブジェクト)をフェッチする必要がある場合、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);

OCCIアプリケーションで次のSQL文を発行します。

SELECT addr FROM clients
WHERE name='BEAR BYTE DATA MANAGEMENT';

この文は、clients表の埋込みオブジェクトaddressを戻します。アプリケーションでは、このオブジェクトの属性値を別の処理で使用できます。「連想アクセスの概要」の項で説明したように、アプリケーションで文を実行してオブジェクトをフェッチする必要があります。

4.9.2 Nullについて

データベース表で、行の中に値のない列がある場合、その列はNULLまたはNULLを含むと呼ばれます。オブジェクトには、次の2種類のNULLを適用できます。

  • オブジェクトの属性はすべて、NULL値を持つことができます。これはオブジェクトの属性の値が不明であることを意味します。

  • オブジェクトは、アトミックNULLにできます。したがって、オブジェクト全体の値は不明です。

アトミックNULLとは、オブジェクトが存在しないということとは異なります。アトミックNULLのオブジェクトは値が不明なだけで、存在しています。つまり、データを持たないオブジェクトとみなすことができます。

OCCIでは、オブジェクト属性のすべての型について、それぞれ対応するクラスが用意されています。たとえば、NUMBER属性型はNumberクラスにマップされ、REFRefAnyにマップされます。データ型を表す各OCCIクラスでは、次の2つのメソッドを使用できます。

  • isNull(): オブジェクトがNULLかどうかを戻します。

  • setNull(): オブジェクトをNULLに設定します。

同様に、これらのメソッドは、PObjectクラスからすべてのオブジェクトに継承され、オブジェクトのNULL情報のアクセスと自動設定を行うために使用されます。

4.10 オブジェクト参照の使用について

アプリケーションでは、OCCIのポインタや参照を使用して、オブジェクトの内容に柔軟にアクセスできます。OCCIでは、PObject::getRef()メソッドを使用して永続オブジェクトへの参照を戻すことができます。このコールは、永続オブジェクトに対してのみ有効です。

4.11 データベースからのオブジェクトの削除について

OCCIユーザーは、オーバーロードしたPObject::operator new()を使用して永続オブジェクトを生成できます。ただし、データベース・サーバーからオブジェクトを削除するには、Refref.markDelete()を直接コールすることをお薦めします。こうすることで、オブジェクトがクライアント・キャッシュに入るのを避けられます。オブジェクトがクライアント・キャッシュ内にある場合には、オブジェクトでobj.markDelete()をコールすることで削除できます。削除用にマークされたオブジェクトは、トランザクションがコミットされた時に完全に削除されます。

4.12 型の継承について

オブジェクトの型継承は、C++とJavaの継承との多くの類似点があります。既存のオブジェクト型のサブタイプとしてオブジェクト型を作成できます。サブタイプは、元のタイプであるスーパータイプのすべての属性とメソッド(メンバー関数とプロシージャ)を継承します。単一継承のみがサポートされています。オブジェクトは複数のスーパータイプを持つことはできません。サブタイプは、継承したものに新たな属性およびメソッドを追加できます。継承したあらゆるメソッドをオーバーライド(実装の再定義)することもできます。サブタイプは、スーパータイプの拡張(継承)を行います。

関連項目:

この項目の詳細は、『Oracle Databaseオブジェクト・リレーショナル開発者ガイド』を参照してください。

たとえば、Person_t型は、サブタイプStudent_tとサブタイプEmployee_tを所有できます。また、Student_tも独自のサブタイプPartTimeStudent_tを所有できます。サブタイプを所有するには、型宣言にフラグNOT FINALが必要です。デフォルトのフラグはFINALで、この型はサブタイプを所有できないことを意味します。

この章でこれまでに説明した型は、すべてFINALです。Oracle Databaseリリース8.1.7以前に開発されたアプリケーションの型は、すべてFINALです。FINALの型は、NOT FINALに変更できます。サブタイプを所有しないNOT FINAL型は、FINALに変更できます。次の例では、Person_ tNOT FINALとして宣言されています。

CREATE TYPE Person_t AS OBJECT
(  ssn NUMBER,
   name VARCAHR2(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));

関連項目:

サブタイプStudent_tは、PartTimeStudent_tなどの独自のサブタイプを所有できます。

CREATE TYPE PartTimeStuden_t UNDER Student_t ( numhours NUMBER) ;

4.12.1 代用性について

ポリモフィズムの利点の一部は、プロパティの代用性に由来しています。代用性によって、サブタイプに関する特定の知識を事前に必要とすることなく、あるサブタイプの値が本来そのスーパータイプのために書かれたコードから使用できるようになります。特殊化されたメソッドの内部では異なるメカニズムが使用されているかもしれませんが、その場合でも、サブタイプの値はスーパータイプの値のように周囲のコードに作用します。

インスタンスの代用性とは、サブタイプのオブジェクト値をスーパータイプで宣言されたコンテキスト内で使用できるようにする機能を指します。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))

4.12.2 NOT INSTANTIABLEの型とメソッドの宣言

型は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 INSTANTIABLE型のサブタイプは、スーパータイプの任意のNOT INSTANTIABLEメソッドをオーバーライドして、具体的な実装を行うことができます。NOT INSTANTIABLEメソッドが残っている場合、サブタイプも必ずNOT INSTANTIABLEとして宣言する必要があります。

NOT INSTANTIABLEサブタイプは、インスタンス化可能なスーパータイプの下に定義できます。NOT INSTANTIABLE型をFINALとして宣言することは無効なため、使用できません。

4.12.3 OCCIでの型の継承のサポートについて

次のコールは、型の継承をサポートしています。
4.12.3.1 Connection::getMetaData()について

このメソッドによって、継承された型に固有の情報が提供されます。継承された型のプロパティには、その他の属性が追加されています。たとえば、型のスーパータイプを取得できます。

4.12.3.2 バインド関数および定義関数について

StatementクラスのsetRef()setObject()およびsetVector()メソッドは、REF、オブジェクトおよびコレクションをそれぞれバインドするために使用します。これらの関数はすべて、REF、インスタンスおよびコレクション要素の代用性をサポートしています。同様に、データをフェッチするための対応するgetxxx()メソッドも代用性をサポートしています。

4.12.4 OTTによる型の継承のサポートについて

継承によるオブジェクトのクラス宣言は、そのクラスが親の型クラスから導出され、親のクラスにない属性に対応するフィールドのみが含まれていることを除くと、単純なオブジェクト宣言と似ています。この宣言の構造を例4-10に示します。

この構造では、変数はすべて単純なオブジェクトの場合と同じです。parentTypenameは親の型の名前、つまり、その型名が継承している型のクラス名です。

例4-10 OTTでの継承サポート

class <typename> : public <parentTypename> 
{
   protected:
      <OCCItype1> <attributename1>;
      ...
      <OCCItypen> <attributenamen>;

   public:
      void *operator new(size_t size); 
      void *operator new(size_t size, const Connection* conn, 
                          const string& table); 
      string  getSQLTypeName() const;
      void getSQLTypeName(oracle::occi::Environment *env, void **schemaName,
                          unsigned int &schemaNameLen, void **typeName,
                          unsigned int &typeNameLen) const;
      <typename> (void *ctx) : <parentTypename>(ctx) { };
      static void *readSQL(void *ctx);
      virtual void readSQL(AnyData& stream);
      static void writeSQL(void *obj, void *ctx);
      virtual void writeSQL(AnyData& stream);
}

4.13 サンプルOCCIアプリケーション

この項では、この章で説明した機能の一部を使用したサンプルOCCIアプリケーションを示します。

例4-11 サンプルOCCIアプリケーションのdemo2.sqlのリスト

drop table ADDR_TAB
/
drop table PERSON_TAB
/
drop type STUDENT
/
drop type PERSON
/
drop type ADDRESS_TAB
/
drop type ADDRESS
/
drop type FULLNAME
/
CREATE TYPE FULLNAME AS OBJECT (first_name CHAR(20), last_name CHAR(20))
/
CREATE TYPE ADDRESS AS OBJECT (state CHAR(20), zip CHAR(20)) 
/
CREATE TYPE ADDRESS_TAB  AS VARRAY(3) OF REF ADDRESS
/
CREATE TYPE PERSON AS OBJECT (id NUMBER, name FULLNAME,curr_addr REF ADDRESS, 
prev_addr_l ADDRESS_TAB) NOT FINAL
/
CREATE TYPE STUDENT UNDER  PERSON (school_name CHAR(20))
/
CREATE TABLE ADDR_TAB OF ADDRESS
/
CREATE TABLE PERSON_TAB OF PERSON
/

例4-12 サンプルOCCIアプリケーションのdemo2.typのリスト

TYPE FULLNAME GENERATE CFullName as MyFullName
TYPE ADDRESS GENERATE CAddress as MyAddress
TYPE PERSON GENERATE CPerson as MyPerson
TYPE STUDENT GENERATE CStudent as MyStudent

例4-13 サンプルOCCIアプリケーションのファイルを生成するOTTコマンドのリスト

OTTはユーザー名demousrを使用して接続を試行します。システムではパスワードが要求されます。

ott userid=demousr intype=demo2.typ code=cpp hfile=demo2.h
   cppfile=demo2.cpp mapfile=mappings.cpp attraccess=private

例4-14 サンプルOCCIアプリケーションのmappings.hのリスト

#ifndef MAPPINGS_ORACLE
# define MAPPINGS_ORACLE

#ifndef OCCI_ORACLE
# include <occi.h>
#endif

#ifndef DEMO2_ORACLE
# include "demo2.h"
#endif

void mappings(oracle::occi::Environment* envOCCI_);

#endif

例4-15 サンプルOCCIアプリケーションのmappings.cppのリスト

#ifndef MAPPINGS_ORACLE
# include "mappings.h"
#endif

void mappings(oracle::occi::Environment* envOCCI_)
{
  oracle::occi::Map *mapOCCI_ = envOCCI_->getMap();
  mapOCCI_->put("HR.FULLNAME", &CFullName::readSQL, &CFullName::writeSQL);
  mapOCCI_->put("HR.ADDRESS", &CAddress::readSQL, &CAddress::writeSQL);
  mapOCCI_->put("HR.PERSON", &CPerson::readSQL, &CPerson::writeSQL);
  mapOCCI_->put("HR.STUDENT", &CStudent::readSQL, &CStudent::writeSQL);
}

例4-16 サンプルOCCIアプリケーションのdemo2.hのリスト

#ifndef DEMO2_ORACLE
# define DEMO2_ORACLE

#ifndef OCCI_ORACLE
# include <occi.h>
#endif

using namespace std;
using namespace oracle::occi;

class MyFullName;
class MyAddress;
class MyPerson;
/*   Changes ended here */

/*  GENERATED DECLARATIONS FOR THE FULLNAME OBJECT TYPE. */
class CFullName : public oracle::occi::PObject {

private:
   OCCI_STD_NAMESPACE::string FIRST_NAME;
   OCCI_STD_NAMESPACE::string LAST_NAME;

public:   OCCI_STD_NAMESPACE::string getFirst_name() const;
   void setFirst_name(const OCCI_STD_NAMESPACE::string &value);
   OCCI_STD_NAMESPACE::string getLast_name() const;
   void setLast_name(const OCCI_STD_NAMESPACE::string &value);
   void *operator new(size_t size);
   void *operator new(size_t size, const oracle::occi::Connection * sess,
      const OCCI_STD_NAMESPACE::string& table);
   void *operator new(size_t, void *ctxOCCI_);
   void *operator new(size_t size, const oracle::occi::Connection *sess,
      const OCCI_STD_NAMESPACE::string &tableName, 
      const OCCI_STD_NAMESPACE::string &typeName,
      const OCCI_STD_NAMESPACE::string &tableSchema, 
      const OCCI_STD_NAMESPACE::string &typeSchema);
   string  getSQLTypeName() const;
   void getSQLTypeName(oracle::occi::Environment *env, void **schemaName,
      unsigned int &schemaNameLen, void **typeName,
      unsigned int &typeNameLen) const;
   CFullName();
   CFullName(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { };
   static void *readSQL(void *ctxOCCI_);
   virtual void readSQL(oracle::occi::AnyData& streamOCCI_);
   static void writeSQL(void *objOCCI_, void *ctxOCCI_);
   virtual void writeSQL(oracle::occi::AnyData& streamOCCI_);
   ~CFullName();
};

/* GENERATED DECLARATIONS FOR THE ADDRESS OBJECT TYPE. */ 
class CAddress : public oracle::occi::PObject {

private:
   OCCI_STD_NAMESPACE::string STATE;
   OCCI_STD_NAMESPACE::string ZIP;

public:
   OCCI_STD_NAMESPACE::string getState() const;
   void setState(const OCCI_STD_NAMESPACE::string &value);
   OCCI_STD_NAMESPACE::string getZip() const;
   void setZip(const OCCI_STD_NAMESPACE::string &value);
   void *operator new(size_t size);
   void *operator new(size_t size, const oracle::occi::Connection * sess,
      const OCCI_STD_NAMESPACE::string& table);
   void *operator new(size_t, void *ctxOCCI_);
   void *operator new(size_t size, const oracle::occi::Connection *sess,
      const OCCI_STD_NAMESPACE::string &tableName, 
      const OCCI_STD_NAMESPACE::string &typeName,
      const OCCI_STD_NAMESPACE::string &tableSchema, 
      const OCCI_STD_NAMESPACE::string &typeSchema);
   string  getSQLTypeName() const;
   void getSQLTypeName(oracle::occi::Environment *env, void **schemaName,
      unsigned int &schemaNameLen, void **typeName,
      unsigned int &typeNameLen) const;
   CAddress();
   CAddress(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { };
   static void *readSQL(void *ctxOCCI_);
   virtual void readSQL(oracle::occi::AnyData& streamOCCI_);
   static void writeSQL(void *objOCCI_, void *ctxOCCI_);
   virtual void writeSQL(oracle::occi::AnyData& streamOCCI_);
   ~CAddress();
};

/* GENERATED DECLARATIONS FOR THE PERSON OBJECT TYPE. */
class CPerson : public oracle::occi::PObject {

private:
   oracle::occi::Number ID;
   MyFullName * NAME;
   oracle::occi::Ref< MyAddress > CURR_ADDR;
   OCCI_STD_NAMESPACE::vector< oracle::occi::Ref< MyAddress > > PREV_ADDR_L;

public:   oracle::occi::Number getId() const;
   void setId(const oracle::occi::Number &value);
   MyFullName * getName() const;
   void setName(MyFullName * value);
   oracle::occi::Ref< MyAddress > getCurr_addr() const;
   void setCurr_addr(const oracle::occi::Ref< MyAddress > &value);
   OCCI_STD_NAMESPACE::vector<oracle::occi::Ref< MyAddress>>& 
      getPrev_addr_l();
   const OCCI_STD_NAMESPACE::vector<oracle::occi::Ref<MyAddress>>& 
      getPrev_addr_l() const;
   void setPrev_addr_l(const OCCI_STD_NAMESPACE::vector 
      <oracle::occi::Ref< MyAddress > > &value);
   void *operator new(size_t size);
   void *operator new(size_t size, const oracle::occi::Connection * sess,
      const OCCI_STD_NAMESPACE::string& table);
   void *operator new(size_t, void *ctxOCCI_);
   void *operator new(size_t size, const oracle::occi::Connection *sess,
      const OCCI_STD_NAMESPACE::string &tableName, 
      const OCCI_STD_NAMESPACE::string &typeName,
      const OCCI_STD_NAMESPACE::string &tableSchema, 
      const OCCI_STD_NAMESPACE::string &typeSchema);
   string  getSQLTypeName() const;
   void getSQLTypeName(oracle::occi::Environment *env, void **schemaName,
      unsigned int &schemaNameLen, void **typeName,
      unsigned int &typeNameLen) const;
   CPerson();
   CPerson(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { };
   static void *readSQL(void *ctxOCCI_);
   virtual void readSQL(oracle::occi::AnyData& streamOCCI_);
   static void writeSQL(void *objOCCI_, void *ctxOCCI_);
   virtual void writeSQL(oracle::occi::AnyData& streamOCCI_);
   ~CPerson();
};

/* GENERATED DECLARATIONS FOR THE STUDENT OBJECT TYPE. */
/*  changes to the generated file - declarations for the MyPerson class. */
class MyPerson : public CPerson {

public:
      MyPerson(Number id_i, MyFullName *name_i, const Ref<MyAddress>& addr_i) ;
      MyPerson(void *ctxOCCI_);
      void move(const Ref<MyAddress>& new_addr);
      void displayInfo();
      MyPerson();
};
/* changes  end here */

class CStudent : public MyPerson {
private:
   OCCI_STD_NAMESPACE::string SCHOOL_NAME;

public:
   OCCI_STD_NAMESPACE::string getSchool_name() const;
   void setSchool_name(const OCCI_STD_NAMESPACE::string &value);\
   void *operator new(size_t size);
   void *operator new(size_t size, const oracle::occi::Connection * sess,\
      const OCCI_STD_NAMESPACE::string& table);
   void *operator new(size_t, void *ctxOCCI_);
   void *operator new(size_t size, const oracle::occi::Connection *sess,
      const OCCI_STD_NAMESPACE::string &tableName, 
      const OCCI_STD_NAMESPACE::string &typeName,
      const OCCI_STD_NAMESPACE::string &tableSchema, 
      const OCCI_STD_NAMESPACE::string &typeSchema);
   string  getSQLTypeName() const;
   void getSQLTypeName(oracle::occi::Environment *env, void **schemaName,
      unsigned int &schemaNameLen, void **typeName,
      unsigned int &typeNameLen) const;
   CStudent();
   CStudent(void *ctxOCCI_) : MyPerson (ctxOCCI_) { };
   static void *readSQL(void *ctxOCCI_);
   virtual void readSQL(oracle::occi::AnyData& streamOCCI_);
   static void writeSQL(void *objOCCI_, void *ctxOCCI_);
   virtual void writeSQL(oracle::occi::AnyData& streamOCCI_);
   ~CStudent();
};

/*changes  made to the generated file */
/* declarations for the MyFullName class. */
class MyFullName : public  CFullName
{  public:
      MyFullName(string first_name, string last_name);
      void displayInfo();
      MyFullName(void *ctxOCCI_);
};

// declarations for the MyAddress class.
class MyAddress : public CAddress
{  public:
      MyAddress(string state_i, string zip_i);
      void displayInfo();
      MyAddress(void *ctxOCCI_);
};

class MyStudent : public CStudent
{
  public :
     MyStudent(void *ctxOCCI_) ;
};
/* changes end here */ 
#endif

例4-17 サンプルOCCIアプリケーションのdemo2.cppのリスト

#ifndef DEMO2_ORACLE
# include "demo2.h"
#endif

/* GENERATED METHOD IMPLEMENTATIONS FOR THE FULLNAME OBJECT TYPE. */
OCCI_STD_NAMESPACE::string CFullName::getFirst_name() const
{
  return FIRST_NAME;
}

void CFullName::setFirst_name(const OCCI_STD_NAMESPACE::string &value)
{
  FIRST_NAME = value;
}

OCCI_STD_NAMESPACE::string CFullName::getLast_name() const
{
  return LAST_NAME;
}

void CFullName::setLast_name(const OCCI_STD_NAMESPACE::string &value)
{
  LAST_NAME = value;
}

void *CFullName::operator new(size_t size)
{
  return oracle::occi::PObject::operator new(size);
}

void *CFullName::operator new(size_t size, const oracle::occi::Connection *
  sess,  const OCCI_STD_NAMESPACE::string& table)
{
  return oracle::occi::PObject::operator new(size, sess, table, 
            (char *) "HR.FULLNAME");
}

void *CFullName::operator new(size_t size, void *ctxOCCI_)
{
 return oracle::occi::PObject::operator new(size, ctxOCCI_);
}

void *CFullName::operator new(size_t size,
    const oracle::occi::Connection *sess,
    const OCCI_STD_NAMESPACE::string &tableName, 
    const OCCI_STD_NAMESPACE::string &typeName,
    const OCCI_STD_NAMESPACE::string &tableSchema, 
    const OCCI_STD_NAMESPACE::string &typeSchema)
{
  return oracle::occi::PObject::operator new(size, sess, tableName,
        typeName, tableSchema, typeSchema);
}

OCCI_STD_NAMESPACE::string CFullName::getSQLTypeName() const
{ 
  return OCCI_STD_NAMESPACE::string("HR.FULLNAME");
}

void CFullName::getSQLTypeName(oracle::occi::Environment *env,
     void  **schemaName, unsigned int &schemaNameLen, void **typeName,
     unsigned int &typeNameLen) const
{
  PObject::getSQLTypeName(env, &CFullName::readSQL, schemaName,
        schemaNameLen, typeName, typeNameLen);
}

CFullName::CFullName()
{
}

void *CFullName::readSQL(void *ctxOCCI_)
{
  MyFullName *objOCCI_ = new(ctxOCCI_) MyFullName(ctxOCCI_);
  oracle::occi::AnyData streamOCCI_(ctxOCCI_);

  try
  {
    if (streamOCCI_.isNull())
      objOCCI_->setNull();
    else
      objOCCI_->readSQL(streamOCCI_);
  }
  catch (oracle::occi::SQLException& excep)
  {
    delete objOCCI_;
    excep.setErrorCtx(ctxOCCI_);
    return (void *)NULL;
  }
  return (void *)objOCCI_;
}

void CFullName::readSQL(oracle::occi::AnyData& streamOCCI_)
{
   FIRST_NAME = streamOCCI_.getString();
   LAST_NAME = streamOCCI_.getString();
}

void CFullName::writeSQL(void *objectOCCI_, void *ctxOCCI_){
  CFullName *objOCCI_ = (CFullName *) objectOCCI_;
  oracle::occi::AnyData streamOCCI_(ctxOCCI_);

  try
  {
    if (objOCCI_->isNull())
      streamOCCI_.setNull();
    else
      objOCCI_->writeSQL(streamOCCI_);
  }
  catch (oracle::occi::SQLException& excep)
  {
    excep.setErrorCtx(ctxOCCI_);
  }
  return;
}

void CFullName::writeSQL(oracle::occi::AnyData& streamOCCI_)
{
   streamOCCI_.setString(FIRST_NAME);
   streamOCCI_.setString(LAST_NAME);
}

CFullName::~CFullName()
{
  int i;
}

/* GENERATED METHOD IMPLEMENTATIONS FOR THE ADDRESS OBJECT TYPE. */
OCCI_STD_NAMESPACE::string CAddress::getState() const
{
  return STATE;
}

void CAddress::setState(const OCCI_STD_NAMESPACE::string &value)
{
  STATE = value;
}

OCCI_STD_NAMESPACE::string CAddress::getZip() const
{
  return ZIP;
}

void CAddress::setZip(const OCCI_STD_NAMESPACE::string &value)
{
  ZIP = value;
}

void *CAddress::operator new(size_t size)
{
  return oracle::occi::PObject::operator new(size);
}

void *CAddress::operator new(size_t size, 
                             const oracle::occi::Connection * sess,
                             const OCCI_STD_NAMESPACE::string& table)
{
  return oracle::occi::PObject::operator new(size, sess, table, 
            (char *) "HR.ADDRESS");
}

void *CAddress::operator new(size_t size, void *ctxOCCI_)
{
 return oracle::occi::PObject::operator new(size, ctxOCCI_);
}

void *CAddress::operator new(size_t size,
    const oracle::occi::Connection *sess,
    const OCCI_STD_NAMESPACE::string &tableName, 
    const OCCI_STD_NAMESPACE::string &typeName,
    const OCCI_STD_NAMESPACE::string &tableSchema, 
    const OCCI_STD_NAMESPACE::string &typeSchema)
{
  return oracle::occi::PObject::operator new(size, sess, tableName,
        typeName, tableSchema, typeSchema);
}

OCCI_STD_NAMESPACE::string CAddress::getSQLTypeName() const
{ 
  return OCCI_STD_NAMESPACE::string("HR.ADDRESS");
}

void CAddress::getSQLTypeName(oracle::occi::Environment *env, 
                              void **schemaName,
                              unsigned int &schemaNameLen, 
                              void **typeName, 
                              unsigned int &typeNameLen) const
{
  PObject::getSQLTypeName(env, &CAddress::readSQL, schemaName,
        schemaNameLen, typeName, typeNameLen);
}

CAddress::CAddress()
{
}

void *CAddress::readSQL(void *ctxOCCI_)
{
  MyAddress *objOCCI_ = new(ctxOCCI_) MyAddress(ctxOCCI_);
  oracle::occi::AnyData streamOCCI_(ctxOCCI_);

  try
  {
    if (streamOCCI_.isNull())
      objOCCI_->setNull();
    else
      objOCCI_->readSQL(streamOCCI_);
  }
  catch (oracle::occi::SQLException& excep)
  {
    delete objOCCI_;
    excep.setErrorCtx(ctxOCCI_);
    return (void *)NULL;
  }
  return (void *)objOCCI_;
}

void CAddress::readSQL(oracle::occi::AnyData& streamOCCI_)
{
   STATE = streamOCCI_.getString();
   ZIP = streamOCCI_.getString();
}

void CAddress::writeSQL(void *objectOCCI_, void *ctxOCCI_)
{
  CAddress *objOCCI_ = (CAddress *) objectOCCI_;
  oracle::occi::AnyData streamOCCI_(ctxOCCI_);

  try
  {
    if (objOCCI_->isNull())
      streamOCCI_.setNull();
    else
      objOCCI_->writeSQL(streamOCCI_);
  }
  catch (oracle::occi::SQLException& excep)
  {
    excep.setErrorCtx(ctxOCCI_);
  }
  return;
}

void CAddress::writeSQL(oracle::occi::AnyData& streamOCCI_)
{
   streamOCCI_.setString(STATE);
   streamOCCI_.setString(ZIP);
}

CAddress::~CAddress()
{
  int i;
}

/* GENERATED METHOD IMPLEMENTATIONS FOR THE PERSON OBJECT TYPE. */
oracle::occi::Number CPerson::getId() const
{
  return ID;
}

void CPerson::setId(const oracle::occi::Number &value)
{
  ID = value;
}

MyFullName * CPerson::getName() const
{
  return NAME;
}

void CPerson::setName(MyFullName * value)
{
  NAME = value;
}

oracle::occi::Ref< MyAddress > CPerson::getCurr_addr() const
{
  return CURR_ADDR;
}

void CPerson::setCurr_addr(const oracle::occi::Ref< MyAddress > &value)
{
  CURR_ADDR = value;
}

OCCI_STD_NAMESPACE::vector< oracle::occi::Ref< MyAddress > >&
   CPerson::getPrev_addr_l() 
{
  return PREV_ADDR_L;
}

const OCCI_STD_NAMESPACE::vector< oracle::occi::Ref< MyAddress > >&
  CPerson::getPrev_addr_l() const
{
  return PREV_ADDR_L;
}

void CPerson::setPrev_addr_l(const OCCI_STD_NAMESPACE::vector<
 oracle::occi::Ref< MyAddress > > &value)
{
  PREV_ADDR_L = value;
}
void *CPerson::operator new(size_t size)
{
  return oracle::occi::PObject::operator new(size);
}

void *CPerson::operator new(size_t size, 
                            const oracle::occi::Connection * sess,
                            const OCCI_STD_NAMESPACE::string& table)
{
  return oracle::occi::PObject::operator new(size, sess, table, 
            (char *) "HR.PERSON");
}

void *CPerson::operator new(size_t size, void *ctxOCCI_)
{
 return oracle::occi::PObject::operator new(size, ctxOCCI_);
}

void *CPerson::operator new(size_t size,
    const oracle::occi::Connection *sess,
    const OCCI_STD_NAMESPACE::string &tableName, 
    const OCCI_STD_NAMESPACE::string &typeName,
    const OCCI_STD_NAMESPACE::string &tableSchema, 
    const OCCI_STD_NAMESPACE::string &typeSchema)
{
  return oracle::occi::PObject::operator new(size, sess, tableName,
        typeName, tableSchema, typeSchema);
}

OCCI_STD_NAMESPACE::string CPerson::getSQLTypeName() const
{ 
  return OCCI_STD_NAMESPACE::string("HR.PERSON");
}

void CPerson::getSQLTypeName(oracle::occi::Environment *env, 
                             void **schemaName,
                             unsigned int &schemaNameLen, 
                             void **typeName, 
                             unsigned int &typeNameLen) const
{
  PObject::getSQLTypeName(env, &CPerson::readSQL, schemaName,
        schemaNameLen, typeName, typeNameLen);
}

CPerson::CPerson()
{
   NAME = (MyFullName *) 0;
}

void *CPerson::readSQL(void *ctxOCCI_)
{
  MyPerson *objOCCI_ = new(ctxOCCI_) MyPerson(ctxOCCI_);
  oracle::occi::AnyData streamOCCI_(ctxOCCI_);
  try
  {
    if (streamOCCI_.isNull())
      objOCCI_->setNull();
    else
      objOCCI_->readSQL(streamOCCI_);
  }
  catch (oracle::occi::SQLException& excep)
  {
    delete objOCCI_;
    excep.setErrorCtx(ctxOCCI_);
    return (void *)NULL;
  }
  return (void *)objOCCI_;
}

void CPerson::readSQL(oracle::occi::AnyData& streamOCCI_)
{
   ID = streamOCCI_.getNumber();
   NAME = (MyFullName *) streamOCCI_.getObject(&MyFullName::readSQL);
   CURR_ADDR = streamOCCI_.getRef();
   oracle::occi::getVectorOfRefs(streamOCCI_, PREV_ADDR_L);
}

void CPerson::writeSQL(void *objectOCCI_, void *ctxOCCI_)
{
  CPerson *objOCCI_ = (CPerson *) objectOCCI_;
  oracle::occi::AnyData streamOCCI_(ctxOCCI_);
  try
  {
    if (objOCCI_->isNull())
      streamOCCI_.setNull();
    else
      objOCCI_->writeSQL(streamOCCI_);
  }
  catch (oracle::occi::SQLException& excep)
  {
    excep.setErrorCtx(ctxOCCI_);
  }
  return;
}

void CPerson::writeSQL(oracle::occi::AnyData& streamOCCI_)
{
   streamOCCI_.setNumber(ID);
   streamOCCI_.setObject(NAME);
   streamOCCI_.setRef(CURR_ADDR);
   oracle::occi::setVectorOfRefs(streamOCCI_, PREV_ADDR_L);
}

CPerson::~CPerson()
{
  int i;
  delete NAME;
}

/* GENERATED METHOD IMPLEMENTATIONS FOR THE STUDENT OBJECT TYPE. */
OCCI_STD_NAMESPACE::string CStudent::getSchool_name() const
{
  return SCHOOL_NAME;
}

void CStudent::setSchool_name(const OCCI_STD_NAMESPACE::string &value)
{
  SCHOOL_NAME = value;
}

void *CStudent::operator new(size_t size)
{
  return oracle::occi::PObject::operator new(size);
}

void *CStudent::operator new(size_t size, 
                             const oracle::occi::Connection * sess,
                             const OCCI_STD_NAMESPACE::string& table)
{
  return oracle::occi::PObject::operator new(size, sess, table, 
            (char *) "HR.STUDENT");
}

void *CStudent::operator new(size_t size, void *ctxOCCI_)
{
 return oracle::occi::PObject::operator new(size, ctxOCCI_);
}

void *CStudent::operator new(size_t size,
    const oracle::occi::Connection *sess,
    const OCCI_STD_NAMESPACE::string &tableName, 
    const OCCI_STD_NAMESPACE::string &typeName,
    const OCCI_STD_NAMESPACE::string &tableSchema, 
    const OCCI_STD_NAMESPACE::string &typeSchema)
{
  return oracle::occi::PObject::operator new(size, sess, tableName,
        typeName, tableSchema, typeSchema);
}

OCCI_STD_NAMESPACE::string CStudent::getSQLTypeName() const
{ 
  return OCCI_STD_NAMESPACE::string("HR.STUDENT");
}

void CStudent::getSQLTypeName(oracle::occi::Environment *env, 
                              void **schemaName,
                              unsigned int &schemaNameLen,
                              void **typeName,
                              unsigned int &typeNameLen) const
{
  PObject::getSQLTypeName(env, &CStudent::readSQL, schemaName,
        schemaNameLen, typeName, typeNameLen);
}

CStudent::CStudent()
{
}
void *CStudent::readSQL(void *ctxOCCI_)
{
  MyStudent *objOCCI_ = new(ctxOCCI_) MyStudent(ctxOCCI_);
  oracle::occi::AnyData streamOCCI_(ctxOCCI_);

  try
  {
    if (streamOCCI_.isNull())
      objOCCI_->setNull();
    else
      objOCCI_->readSQL(streamOCCI_);
  }
  catch (oracle::occi::SQLException& excep)
  {
    delete objOCCI_;
    excep.setErrorCtx(ctxOCCI_);
    return (void *)NULL;
  }
  return (void *)objOCCI_;
}

void CStudent::readSQL(oracle::occi::AnyData& streamOCCI_)
{
   CPerson::readSQL(streamOCCI_);
   SCHOOL_NAME = streamOCCI_.getString();
}

void CStudent::writeSQL(void *objectOCCI_, void *ctxOCCI_)
{
  CStudent *objOCCI_ = (CStudent *) objectOCCI_;
  oracle::occi::AnyData streamOCCI_(ctxOCCI_);
  try
  {
    if (objOCCI_->isNull())
      streamOCCI_.setNull();
    else
      objOCCI_->writeSQL(streamOCCI_);
  }
  catch (oracle::occi::SQLException& excep)
  {
    excep.setErrorCtx(ctxOCCI_);
  }
  return;
}

void CStudent::writeSQL(oracle::occi::AnyData& streamOCCI_)
{
   CPerson::writeSQL(streamOCCI_);
   streamOCCI_.setString(SCHOOL_NAME);
}

CStudent::~CStudent()
{
  int i;
}

OTTによって、FULL_NAMEADDRESSPERSONおよびPFGRFDENTクラスの宣言がdemo2.hに生成されると想定します。次のサンプルOCCIアプリケーションは、例4-12demo2.typファイルで指定された、OTTによって生成されたクラスを拡張し、ユーザー定義のメソッドをいくつか追加します。これらのクラス宣言は、正しくコンパイルするために、demo2.hに組み込まれています。

例4-18 サンプルOCCIアプリケーションのmyDemo.hのリスト

#ifndef MYDEMO_ORACLE
#define MYDEMO_ORACLE

#include <string>

#ifndef DEMO2_ORACLE
#include <demo2.h>
#endif

using namespace std;
using namespace oracle::occi;

// declarations for the MyFullName class.
class MyFullName : public  CFullName
{  public:
      MyFullName(string first_name, string last_name);
      void displayInfo();
};

// declarations for the MyAddress class.
class MyAddress : public CAddress 
{  public:
      MyAddress(string state_i, string zip_i);
      void displayInfo();
};

// declarations for the MyPerson class.
class MyPerson : public CPerson
{  public:
      MyPerson(Number id_i, MyFullname *name_i, 
               const Ref<MyAddress>& addr_i);
      void move(const Ref<MyAddress>& new_addr);
      void displayInfo();
};

#endif

例4-19 サンプルOCCIアプリケーションのmyDemo.cppのリスト

#ifndef DEMO2_ORACLE
#include <demo2.h>
#endif

using namespace std;

/* initialize MyFullName */
MyFullName::MyFullName(string first_name,string last_name)
{
 setFirst_name(first_name);
 setLast_name(last_name);
}

/* display all the information in MyFullName */
void MyFullName::displayInfo()
{
   cout << "FIRST NAME is" << getFirst_name() << endl;
   cout << "LAST NAME is" << getLast_name() << endl;
}

MyFullName::MyFullName(void *ctxOCCI_):CFullName(ctxOCCI_)
{
}

/* METHOD IMPLEMENTATIONS FOR MyAddress CLASS. */

/* initialize MyAddress */
MyAddress::MyAddress(string state_i, string zip_i)
{
  setState(state_i); 
  setZip(zip_i);
}

/* display all the information in MyAddress */
void MyAddress::displayInfo()
{
   cout << "STATE is" << getState() << endl;
   cout << "ZIP is" << getZip() << endl;
}

MyAddress::MyAddress(void *ctxOCCI_) :CAddress(ctxOCCI_)
{
}

/* METHOD IMPLEMENTATIONS FOR MyPerson CLASS. */

/* initialize MyPerson */
MyPerson::MyPerson(Number id_i, MyFullName* name_i, 
           const Ref<MyAddress>& addr_i)
{
  setId(id_i);
  setName(name_i);
  setCurr_addr(addr_i);
}

MyPerson::MyPerson(void *ctxOCCI_) :CPerson(ctxOCCI_)
{
}

/* move Person from curr_addr to new_addr */ 
void MyPerson::move(const Ref<MyAddress>& new_addr)
{
   // append curr_addr to the vector //
   getPrev_addr_l().push_back(getCurr_addr());  
   setCurr_addr(new_addr);

   // mark the object as dirty
   this->markModified();
}

/*  display all the information of MyPerson */
void MyPerson::displayInfo()
{
   cout << "ID is" << (int)getId() << endl;
   getName()->displayInfo();

   // de-referencing the Ref attribute using -> operator
   getCurr_addr()->displayInfo();
   cout << "Prev Addr List: " << endl;
   for (int i = 0; i < getPrev_addr_l().size(); i++)
   {  
      // access the collection elements using [] operator
      (getPrev_addr_l())[i]->displayInfo();
   }
}

MyPerson::MyPerson()
{
}

MyStudent::MyStudent(void *ctxOCCI_) : CStudent(ctxOCCI_)
{
}

例4-20 サンプルOCCIアプリケーションのmain.cppのリスト

#ifndef DEMO2_ORACLE
#include <demo2.h>
#endif

#ifndef MAPPINGS_ORACLE
#include <mappings.h>
#endif

#include <iostream>
using namespace std;
using namespace::oracle;

int main()
{
   Environment *env = Environment::createEnvironment(Environment::OBJECT);
   mappings(env);

   try {
     Connection *conn = Connection("HR", "password");

    /* Call the OTT generated function to register the mappings */
    /* create a persistent object of type ADDRESS in the database table, 
       ADDR_TAB */
    MyAddress *addr1 = new(conn, "ADDR_TAB") MyAddress("CA", "94065");
    conn->commit();

    Statement *st = conn->createStatement("select ref(a) from addr_tab a");
   ResultSet *rs = st->executeQuery();
   Ref<MyAddress> r1;
   if ( rs->next())
      r1 = rs->getRef(1);
   st->closeResultSet(rs);
   conn->terminateStatement(st);

   MyFullName * name1 = new MyFullName("Joe", "Black");

   /* create a persistent object of type Person in the database table 
      PERSON_TAB */
   MyPerson *person1 = new(conn, "PERSON_TAB") MyPerson(1,name1,r1);
   conn->commit();

   /* selecting the inserted information */
   Statement *stmt = conn->createStatement();
   ResultSet *resultSet = 
        stmt->executeQuery("SELECT REF(a) from person_tab a where id = 1");

   if (resultSet->next())
   {
      Ref<MyPerson> joe_ref = (Ref<MyPerson>) resultSet->getRef(1);
      joe_ref->displayInfo();

      /* create a persistent object of type ADDRESS in the database table
         ADDR_TAB */
      MyAddress *new_addr1 = new(conn, "ADDR_TAB") MyAddress("PA", "92140");
      joe_ref->move(new_addr1->getRef());
      joe_ref->displayInfo();
   }

   /* commit the transaction which results in the newly created object
       new_addr and the dirty object joe to be flushed to the server.
       Note that joe was marked dirty in move(). */
   conn->commit();

   conn->terminateStatement(stmt);
   env->terminateConnection(conn);
 }

 catch ( exception &x)

 {
  cout << x.what () << endl;
 }
   Environment::terminateEnvironment(env);
   return 0;
}