この章では、Oracle C++ Call Interface(OCCI)を使用してオブジェクト・リレーショナル・プログラミングを実装する方法を説明します。
ここでは、次の項目について説明します。
OCCIでは、連想スタイルとナビゲーション・スタイルのデータ・アクセスがサポートされています。通常、第三世代言語(3GL)のプログラムでは、リレーショナル・データベース表で編成された関連付けに基づいた連想アクセスを使用して、データベースに格納されたデータを操作します。連想アクセスでは、データはSQL文やPL/SQLプロシージャの実行によって操作されます。OCCIでは、データをクライアントに移送するためのコストをかけずに、アプリケーションがデータベース・サーバーに対してSQL文やPL/SQLプロシージャを実行できるようにして、オブジェクトへの連想アクセスをサポートしています。
OCCIを使用したオブジェクト指向のプログラムでは、このOCCIプログラミング・パラダイムの重要な側面の1つであるナビゲーショナル・アクセスも活用できます。オブジェクト間のオブジェクト関連は参照(REF)として実装されます。通常、ナビゲーショナル・アクセスを使用するオブジェクト・アプリケーションは、最初にオブジェクトへのREFを戻すSQL文を発行して、データベース・サーバーから1つ以上のオブジェクトを取り出します。次に、アプリケーションはこのREFを使用して関連オブジェクトをたどり、必要に応じて、その他の関連オブジェクト上で計算を実行します。ナビゲーショナル・アクセスでは、初期オブジェクト集合の参照をフェッチする以外、SQL文の実行は含まれません。OCCI APIをナビゲーショナル・アクセスで使用すると、アプリケーションはOracleオブジェクトに対して次の操作を実行できます。
オブジェクトの作成、アクセス、ロック、削除、コピーおよびフラッシュ
オブジェクトへの参照の取得およびその参照を介したナビゲート
この章では、いくつかの例を示して、永続オブジェクトの作成方法、オブジェクトのアクセス方法と変更方法、および変更内容をデータベース・サーバーにフラッシュする方法を説明します。また、ナビゲーショナル・アクセスおよび連想アクセスの2つのアプローチを使用してオブジェクトにアクセスする方法を説明します。
リレーショナルOCCIアプリケーションに関するプログラミング原理の多くは、オブジェクト・リレーショナル・アプリケーションにも適用されます。オブジェクト・リレーショナル・アプリケーションでは、標準OCCIコールを使用してデータベース接続を確立し、SQL文を処理します。その違いは、発行されるSQL文によってオブジェクト参照が取り出され、OCCIのオブジェクト関数でその参照を操作できることです。オブジェクトを(そのオブジェクト参照を使用せずに)値として直接操作することもできます。
Oracleの型のインスタンスは、その存続期間によって、永続オブジェクトと一時オブジェクトに分類されます。永続オブジェクトのインスタンスは、オブジェクト識別子によって参照されるかどうかに応じて、さらにスタンドアロン・オブジェクトと埋込みオブジェクトに分割できます。
永続オブジェクトとは、Oracleデータベースに格納されているオブジェクトのことです。このオブジェクトは、OCCIアプリケーションによってオブジェクト・キャッシュ内にフェッチし、変更できます。永続オブジェクトは、それにアクセスしているアプリケーションの存続期間を超えて存続できます。永続オブジェクトには、次の2種類があります。
スタンドアロン・インスタンスはデータベース表の行に格納され、それぞれに一意のオブジェクト識別子があります。OCCIアプリケーションでは、スタンドアロン・オブジェクトへの参照を取り出してオブジェクトを確保し、確保したオブジェクトからその他の関連オブジェクトにナビゲートできます。スタンドアロン・オブジェクトは、参照可能オブジェクトとも呼ばれます。
永続オブジェクトの選択も可能で、その場合はオブジェクトを参照でフェッチするかわりに、値でフェッチします。
埋込みインスタンスはデータベース表の行に格納されず、別のオブジェクトの中に埋め込まれます。埋込みオブジェクトの例には、別のオブジェクトの属性であるオブジェクトや、データベースの表のオブジェクト列に存在するオブジェクトなどがあります。埋込みオブジェクトにはオブジェクト識別子がないため、OCCIアプリケーションで埋込みインスタンスへのREFを取得することはできません。
埋込みオブジェクトは、参照不可能オブジェクトまたは値インスタンスとも呼ばれます。埋込みオブジェクトは値と呼ばれることもありますが、それをスカラー・データ値と混同しないでください。どちらの意味であるかは文脈で判断できます。
|
注意:
|
次のSQLの例では、この2種類の永続オブジェクトの違いを説明します。
次のコード例は、スタンドアロン・オブジェクトの作成方法を示しています。
CREATE TYPE person_t AS OBJECT
(name varchar2(30),
age number(3));
CREATE TABLE person_tab OF person_t;
オブジェクト表person_tabに格納されるオブジェクトは、スタンドアロン・オブジェクトです。このオブジェクトにはオブジェクト識別子があるため、参照できます。したがって、OCCIアプリケーションで確保できます。
次のコード例は、埋込みオブジェクトの作成方法を示しています。
CREATE TABLE department
(deptno number,
deptname varchar2(30),
manager person_t);
department表のmanager列に格納されるオブジェクトは、埋込みオブジェクトです。このオブジェクトにはオブジェクト識別子がないため、参照できません。つまり、OCCIアプリケーションで確保できないため、確保を解除する必要もありません。このオブジェクトは常に、オブジェクト・キャッシュ内に値で取り込まれます。
配列確保機能を使用すると、参照のベクターを1回のラウンドトリップで間接参照して、対応するオブジェクトのベクターを戻すことができます。新しいグローバル・メソッドであるpinVectorOfRefs()は、Refのベクターを取得して、PObjectのベクターを1回のラウンドトリップで移入することで、n-1回のラウンドトリップにおけるn-1個の参照の確保にかかるコストを削減します。
一時オブジェクトは、オブジェクト型のインスタンスです。一時オブジェクトは、アプリケーションの存続期間を超えて存続できません。また、一時オブジェクトは常にアプリケーションで削除できます。
オブジェクト型トランスレータ(OTT)ユーティリティは、例4-3「オブジェクト型トランスレータ・ユーティリティにおけるoperator newの2つのメソッド」に示すように、各C++クラスに対して2つのoperator newメソッドを生成します。
例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に、一時オブジェクトの動的な作成方法を示します。一時オブジェクトを永続オブジェクトに変換することはできません。これらの役割はインスタンス化されたときに決定され、一時オブジェクトを削除してメモリーを解放するのはユーザーの役割です。
例4-5に示すように、一時オブジェクトはローカル変数としてスタック上に作成することもできます。後者の方法では、変数の有効範囲が終了した際に一時オブジェクトが破棄されることが保証されています。
OCCIアプリケーションでオブジェクト型を操作するには、データベースにそのオブジェクト型が存在している必要があります。通常は、CREATE TYPEなど、SQLのDDL文で型を作成します。
次の各項では、永続オブジェクトと一時オブジェクトの作成方法について説明します。
永続オブジェクトを作成する前に、環境を作成し、接続をオープンしておく必要があります。次の例は、SQL文で作成したデータベース表addr_tabに永続オブジェクトaddrを作成する方法を示しています。
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");
永続オブジェクトは、次のいずれか1つが発生した場合にのみ、データべース内に作成されます。
トランザクションがコミットされた場合(Connection::commit())
オブジェクト・キャッシュがフラッシュされた場合(Connection::flushCache())
オブジェクト自体がフラッシュされた場合(PObject::flush())
C++アプリケーションでデータベースからオブジェクトのインスタンスを取り出す場合は、オブジェクトのクライアント側表現が必要です。オブジェクト型トランスレータ(OTT)ユーティリティでは、データベース・オブジェクト型のC++クラス表現を生成します。たとえば、次の型の宣言をデータベースに定義したとします。
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);
}
これらのクラス宣言は、OTTによって、名前を指定するヘッダー・ファイル(.h)に自動的に書き込まれます。このヘッダー・ファイルをアプリケーションのソース・ファイルにインクルードすると、オブジェクトにアクセスできます。PObject(およびPObjectから導出されたクラス)のインスタンスは、一時または永続インスタンスです。writeSQL()メソッドおよびreadSQL()メソッドは、オブジェクトのシリアライズのためにOCCIオブジェクト・キャッシュで内部的に使用され、OCCIクライアントでは使用および変更できません。
この項では、基本的なOCCIオブジェクト・アプリケーションの開発に伴うステップを説明します。
オブジェクトを使用したOCCIアプリケーションの基本構造は、リレーショナルOCCIアプリケーションと類似していますが、オブジェクト機能には違いがあります。OCCIオブジェクト・プログラムに伴うステップは、次のとおりです。
Environmentを初期化します。OCCIプログラミング環境をオブジェクト・モードで初期化します。アプリケーションでは通常、ヘッダー・ファイルからデータベース・オブジェクトのC++クラス表現をインクルードする必要があります。第8章「オブジェクト型トランスレータ・ユーティリティ」で説明しているように、オブジェクト型トランスレータ(OTT)ユーティリティを使用すると、このクラスを作成できます。
接続を確立します。環境ハンドルを使用して、データベース・サーバーとの接続を確立します。
SQL文を準備します。これは、ローカル(クライアント側)のステップで、プレースホルダをバインドします。オブジェクト・リレーショナル・アプリケーションの場合、このSQL文はオブジェクトへの参照(REF)を戻します。
オブジェクトにアクセスします。
準備した文をデータベース・サーバーに関連付けて実行します。
ナビゲーショナル・アクセスを使用して、データベース・サーバーからオブジェクト参照(REF)を取り出し、オブジェクトを確保します。その後、次の一部またはすべての操作を実行できます。
オブジェクトの属性を操作し、そのオブジェクトに使用済(変更済)のマークを付ける。
別の1つのオブジェクト、または一連のオブジェクトへの参照をたどる。
型および属性の情報にアクセスする。
複合オブジェクト検索グラフをナビゲートする。
変更したオブジェクトをデータベース・サーバーにフラッシュする。
連想アクセスを使用すると、SQLによってオブジェクト全体を値でフェッチできます。また、埋込み(参照不可能)オブジェクトを選択することもできます。その後、次の一部またはすべての操作を実行できます。
表に値を挿入する。
既存の値を変更する。
トランザクションをコミットします。このステップでは、変更されたすべてのオブジェクトがデータベース・サーバーに暗黙的に書き込まれ、変更がコミットされます。
文とハンドルを解放します。プリコンパイルされた文を再利用または再実行することはできません。
|
関連項目:
|
図4-1は、アプリケーションがオブジェクトを操作する方法について、そのプログラム・ロジックを簡単に示しています。図を簡略にするために、必要なステップの一部が省略されています。
次の各項では、図4-1の各ステップについて説明します。
OCCIアプリケーションでオブジェクトにアクセスして操作する場合は、OCCIアプリケーションの最初のコールであるcreateEnvironment()メソッドのmodeパラメータに、OBJECTの値を指定する必要があります。modeにこの値を指定することで、アプリケーションでオブジェクトを操作することをOCCIに通知します。この通知により、次の重要事項が有効になります。
オブジェクト・ランタイム環境が確立されます。
オブジェクト・キャッシュが設定されます。
|
注意: modeパラメータにOBJECTを設定しないと、オブジェクト関連関数を使用しても結果はエラーになります。 |
次のコード例は、OCCI環境の作成時にOBJECTモードを指定する方法を示しています。
Environment *env; Connection *con; Statement *stmt; env = Environment::createEnvironment(Environment::OBJECT); con = env->createConnection(userName, password, connectString);
データベース・オブジェクトがオブジェクト・キャッシュ内にロードされたときに、アプリケーションでメモリーを割り当てる必要はありません。オブジェクト・キャッシュによって、データベース・オブジェクトに対する透過的かつ効率的なメモリー管理が行われます。データベース・オブジェクトはオブジェクト・キャッシュ内にロードされると、ホスト言語(C++)表現に透過的にマップされます。
オブジェクト・キャッシュにより、オブジェクト・キャッシュ内のオブジェクト・コピーと、対応するデータベース・オブジェクトとの間の関連付けが保持されます。commitが実行されると、オブジェクト・キャッシュ内のオブジェクト・コピーに加えた変更が、データベースに対して自動的に伝播されます。
また、オブジェクト・キャッシュでは、参照をオブジェクトにマッピングするための参照表がメンテナンスされます。アプリケーションでオブジェクトへの参照を間接参照したときに、対応するオブジェクトがオブジェクト・キャッシュ内にキャッシュされていない場合は、データベース・サーバーに対してデータベースからオブジェクトをフェッチし、オブジェクト・キャッシュ内にロードするように、要求が自動的に送信されます。その後、同じ参照を間接参照すると、オブジェクト・キャッシュ自体が間接参照の対象となり、データベース・サーバーとのラウンドトリップを伴わないため、処理が速くなります。
同じ参照に関するその後の間接参照は、ラウンドトリップなしに、キャッシュからフェッチされます。ただし、コミット直後に行われた間接参照操作の場合を除きます。この場合は、オブジェクトの最新コピーがサーバーから戻されます。この機能により、各トランザクションの後にデータベースの最新オブジェクトが確実にキャッシュされます。
さらに、オブジェクト・キャッシュでは、オブジェクト・キャッシュ内の各永続オブジェクトに対する確保カウントが保持されます。アプリケーションでオブジェクトへの参照を間接参照すると、オブジェクトの確保カウントが増加します。その後、同じオブジェクトへの参照を間接参照しても、確保カウントは変更されません。オブジェクトは、そのオブジェクトへの参照がスコープ外になるまで、オブジェクト・キャッシュ内に継続して確保され、OCCIクライアントによるアクセスが可能です。
確保カウントは、オブジェクトに対する参照カウントとして機能します。オブジェクトの確保カウントは、そのオブジェクトへの参照がなくなった場合にのみ0(ゼロ)になり、その間このオブジェクトは、ガベージ・コレクションの対象となります。オブジェクト・キャッシュでは、最低使用頻度(LRU)アルゴリズムによって、オブジェクト・キャッシュのサイズが管理されます。このアルゴリズムにより、オブジェクト・キャッシュが最大サイズに達すると、確保カウント0(ゼロ)のオブジェクトは解放されます。
通常、OCCIユーザーは、オブジェクトの確保や確保解除を明示的に行う必要はありません。オブジェクト・キャッシュによって、キャッシュ内のすべてのオブジェクトの確保カウントが自動的に追跡され、記録されます。前述のとおり、オブジェクト・キャッシュは、参照がオブジェクトを指し示している場合は確保カウントを増やし、参照がスコープ外になったり、オブジェクトを指していない場合は確保カウントを減らします。
ただし、例外が1つあります。OCCIアプリケーションでRef<T>::ptr()メソッドを使用してオブジェクトへのポインタを取得している場合は、PObjectクラスのpinメソッドとunpinメソッドを使用してオブジェクト・キャッシュ内のオブジェクトの確保と確保解除を制御できます。
オブジェクト・キャッシュではオブジェクト・コピーの内容は管理されず、オブジェクト・コピーが自動的にリフレッシュされることもありません。オブジェクト・コピーの妥当性と一貫性は、アプリケーションで確認する必要があります。
オブジェクト・キャッシュ内のオブジェクト・コピーに変更を加えた場合は、必ずアプリケーションで、変更したオブジェクトをデータベースにフラッシュする必要があります。
オブジェクト・キャッシュ用のメモリーは、オブジェクトがオブジェクト・キャッシュ内にロードされるときに、必要に応じて割り当てられます。
クライアント側オブジェクト・キャッシュは、プログラムのプロセス領域に割り当てられます。このオブジェクト・キャッシュは、データベース・サーバーから取り出してアプリケーションで使用できるオブジェクト用のメモリーです。
|
注意: OCCI環境をオブジェクト・モードで初期化すると、実際にオブジェクト・コールを使用するかどうかに関係なく、オブジェクト・キャッシュ用のメモリーを割り当てることになります。 |
OCCI環境ごとに、1つのオブジェクト・キャッシュのみが割り当てられます。1つの環境内で異なる接続を通じて取出しまたは作成されたオブジェクトは、すべて同じ物理オブジェクト・キャッシュを使用します。各接続には、それぞれ専用の論理オブジェクト・キャッシュがあります。
参照を間接参照してキャッシュに取得したオブジェクトに対しては、明示的な削除を実行しないでください。このようなオブジェクトの場合は、最初に参照が間接参照されると確保カウントが増加し、参照がスコープ外になると確保カウントが減少します。オブジェクトの確保カウントが0(ゼロ)になると(そのオブジェクトへのすべての参照がスコープ外であることを示す)、そのオブジェクトは自動的にガベージ・コレクションの対象となり、その後、キャッシュから削除されます。
new演算子をコールして作成された永続オブジェクトについては、トランザクションをコミットしない場合は、deleteをコールする必要があります。コミットする場合、オブジェクトはコミット後にガベージ・コレクションの対象となります。これは、newを使用してこのようなオブジェクトが作成された場合、確保カウントは0(ゼロ)から始まるためです。ただし、オブジェクトは使用済であるため、キャッシュ内に残ります。コミット後は使用済でなくなるため、ガベージ・コレクションの対象となります。したがって、削除の必要はありません。
コミットが実行されない場合は、deleteを明示的にコールして、そのオブジェクトを破棄する必要があります。この操作は、そのオブジェクトへの参照がない場合のみ実行できます。一時オブジェクトの場合は、明示的に削除して、そのオブジェクトを破棄する必要があります。
永続オブジェクトに対してdelete演算子をコールすることはできません。マーク済または使用済ではない永続オブジェクトは、確保カウントが0(ゼロ)になると、ガベージ・コレクタによって解放されます。ただし、一時オブジェクトの場合は、明示的に削除して、破棄する必要があります。
この項では、OCCIを使用して既存のC++アプリケーションを移行する方法について説明します。
OCCIでは、SQLを使用してオブジェクトを取り出し、DML操作を実行できます。
|
関連項目: デモ・プログラムの完全なコード・リスト |
前項では、ナビゲーショナル・アクセスについて説明し、初期オブジェクト集合の参照をフェッチするためにのみ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()などのメソッドは、永続オブジェクトにのみ適用できます。
これらのオブジェクトに加えた変更は、データベースに反映されません。
戻されるオブジェクトは値インスタンスであるため、オブジェクト・ポインタを削除してメモリーを解放するのは、ユーザーの役割です。
前項では、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();
ナビゲーショナル・アクセスを使用して、次の一連の操作を実行します。
|
関連項目: デモ・プログラムの完全なコード・リスト |
アプリケーションでオブジェクトを操作するには、最初にデータベース・サーバーから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);
この時点で、オブジェクト参照を使用して、オブジェクトまたはデータベースからのオブジェクトにアクセスして操作できます。
フェッチを行うステップが完了すると、アプリケーションでは、オブジェクトへの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)がスコープ外となるまでオブジェクト・キャッシュ内に残留します。その後、ガベージ・コレクションの対象となります。
これでオブジェクトが確保され、アプリケーションでそのオブジェクトを変更できます。
オブジェクト属性の操作は、前項で示したようにオブジェクトにアクセスして行います。Addressクラスに、state属性を入力値に設定する次のユーザー定義メソッドがあるとします。
void Address::setState(string new_state)
{
state = new_state;
}
次の例は、オブジェクトaddrのstate属性を変更する方法を示しています。
addr_ref->setState("PA");
前述のとおり、オブジェクトがオブジェクト・キャッシュ内にない場合は、Ref<T>の演算子 ->の最初の呼出しで、オブジェクトがロードされます。
前項の例では、オブジェクトの属性を変更しました。ただし、この時点では変更はクライアント側キャッシュにのみ存在しています。変更をデータベースに確実に書き込むには、アプリケーションで特定のステップを実行する必要があります。
最初のステップでは、オブジェクトが変更されたことを示します。これは、オブジェクトに対してmarkModified()メソッド(PObjectの導出メソッド)をコールして行います。このメソッドは、オブジェクトに使用済(変更済)のマークを付けます。
前の例に続いて、オブジェクト属性の操作後、次のようにaddr_refが参照するオブジェクトに使用済のマークを付けます。
addr_ref->markModified();
使用済フラグが設定されているオブジェクトは、変更をデータベースに記録するために、データベース・サーバーにフラッシュする必要があります。これは、次の3つの方法で実行できます。
使用済とマークされた1つのオブジェクトをフラッシュするには、PObjectの導出メソッドであるflushメソッドをコールします。
オブジェクト・キャッシュ全体をフラッシュするには、Connection::flushCache()メソッドを使用します。この場合、OCCIはオブジェクト・キャッシュによって保持されている使用済リストを横断して、使用済オブジェクトをすべてフラッシュします。
トランザクションをコミットするには、Connection::commit()メソッドをコールします。この場合も、OCCIは使用済リストを横断して、オブジェクトをデータベース・サーバーにフラッシュします。この使用済リストには、新しく作成された永続オブジェクトが含まれています。
オブジェクト・キャッシュには、次の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を使用するためです。したがって、キャッシュ・サイズを小さい値に設定して、ガベージ・コレクションがアクティブになる前に多くのメモリーが使用されるのを回避する必要があります。
前の項で説明したとおり、最初にRef<T>を間接参照すると、オブジェクトはデータベース・サーバーからオブジェクト・キャッシュにロードされます。それ以降は、Ref<T>の演算子->の動作はC++ポインタの動作と同じになり、キャッシュ内のオブジェクト・コピーへのアクセスを提供します。ただし、トランザクションがコミットまたは異常終了すると、キャッシュ内のオブジェクト・コピーは無効になります。これは、他のクライアントによってその内容が変更されている可能性があるためです。したがって、トランザクションの終了後にRef<T>を再度間接参照すると、オブジェクト・キャッシュでは、オブジェクトがすでに無効であることが認識され、最新のコピーがデータベース・サーバーからフェッチされます。
ここまでの説明では、1度に1つのオブジェクトのみフェッチまたは確保する例を示しました。その場合、オブジェクトを取り出すためのデータベース・サーバー・ラウンドトリップが、確保操作ごとに別個に発生します。
オブジェクト指向アプリケーションでは、相互に関連したオブジェクトの集合として問題をモデル化することがよくあります。これらのオブジェクトは、オブジェクトのグラフを形成します。アプリケーションでは、初期オブジェクト集合の一部からオブジェクトの処理を開始し、その一部のオブジェクトの参照を使用して残りのオブジェクトを横断します。クライアント/サーバー設定では、横断のたびにオブジェクトをフェッチするためのネットワーク・ラウンドトリップが発生し、非効率的です。
このようなアプリケーションでは、複合オブジェクト検索(COR)を使用することによってパフォーマンスを向上できます。CORはプリフェッチ・メカニズムです。アプリケーションでは、このメカニズムを使用して、リンクされた一連のオブジェクトを1回のネットワーク・ラウンドトリップで取り出すための基準(内容と境界)を指定します。
|
注意: CORでは、プリフェッチされたオブジェクトを確保しません。オブジェクトはオブジェクト・キャッシュ内にフェッチされるため、後続の確保コールはローカル操作で行われます。 |
複合オブジェクトは、論理的に関連したオブジェクトの集合です。ルート・オブジェクトと、指定のネスト・レベルに基づいてそれぞれプリフェッチされる一連のオブジェクトで構成されています。ルート・オブジェクトは、明示的にフェッチまたは確保されます。ネスト・レベルは、複合オブジェクトのルート・オブジェクトから指定のプリフェッチ・オブジェクトまでを最短で横断した場合の参照の数です。
アプリケーションで複合オブジェクトを指定するには、複合オブジェクトの内容と境界を記述します。複合オブジェクトのフェッチは、環境のプリフェッチ制限、およびオブジェクト・キャッシュ内で使用可能なメモリーの量によって制約されます。
|
注意: 複合オブジェクト検索を使用するとパフォーマンスのみが改善され、機能性は追加されません。したがって、使用しないことも可能です。 |
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_number、line_itemsのVARRAYおよび2つの参照が含まれます。1つはcustomer型への参照、もう1つはpurchase_order型への参照であり、この型がリンク済リストとして実装されることを示しています。
複合オブジェクトをフェッチする場合は、アプリケーションで次の指定を行う必要があります。
目的のルート・オブジェクトへの参照。
複合オブジェクトの境界を指定するための1組以上の型および深さ情報。型情報はCORでたどるREF属性を指示し、ネスト・レベルはリンクをたどるレベル数を指示します。
前述のpurchase_orderオブジェクトの場合は、アプリケーションで次の指定を行う必要があります。
ルートpurchase_orderオブジェクトへの参照。
customer、purchase_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オブジェクトを指定し、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);
複合オブジェクトを指定してフェッチした後に行う、複合オブジェクトに含まれるオブジェクトのフェッチでは、ネットワーク・ラウンドトリップは発生しません。これは、オブジェクトがすでにプリフェッチ済であり、オブジェクト・キャッシュ内にあるためです。プリフェッチするオブジェクトが多すぎると、オブジェクト・キャッシュがあふれることがあるので注意が必要です。オブジェクト・キャッシュがあふれると、アプリケーションがすでに確保している他のオブジェクトがキャッシュから押し出され、パフォーマンスの向上ではなく逆にパフォーマンスの低下につながることがあります。
|
注意: プリフェッチ済のオブジェクトをすべて保持できるメモリーがオブジェクト・キャッシュにない場合は、オブジェクトの一部がプリフェッチされないことがあります。アプリケーションでは、後でそれらのオブジェクトにアクセスするときに、ネットワーク・ラウンドトリップが発生します。 |
すべてのプリフェッチ・オブジェクトに対して、SELECT権限が必要です。複合オブジェクト内のオブジェクトに対するSELECT権限がアプリケーションにない場合、そのオブジェクトはフェッチできません。
ConnectionクラスのグローバルなpinVectorOfRefs()メソッドを使用すると、1回のラウンドトリップでRefのベクター全体をオブジェクト・キャッシュにプリフェッチできます。このメソッドは、Refのnサイズのベクターのラウンドトリップの回数をnから1に減らし、OUTパラメータ・ベクターを介して新しく確保されたオブジェクトを追跡します。
Oracleでは、可変長配列(順序付けられたコレクション)とネストした表(順序付けられていないコレクション)の2種類のコレクションをサポートしています。OCCIでは、この2種類のコレクションはいずれも標準テンプレート・ライブラリ(Standard Template Library: STL)のベクター・コンテナにマップされるため、強力、柔軟かつ高速なSTLベクターによるコレクション要素へのアクセスと操作が可能になります。次の例は、VARRAYおよびVARRAY型の属性が含まれたオブジェクトを作成するSQLのDDL文を示しています。
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);
}
|
関連項目: デモ・プログラムの完全なコード・リスト |
使用しているアプリケーションで、埋込みオブジェクト・インスタンス(オブジェクト表でなく、通常の表の列に格納されているオブジェクト)をフェッチする必要がある場合、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を戻します。アプリケーションでは、このオブジェクトの属性値を別の処理で使用できます。「連想アクセスの概要」の項で説明したように、アプリケーションで文を実行してオブジェクトをフェッチする必要があります。
データベース表で、行の中に値のない列がある場合、その列はNULLである、またはNULLを含む、と呼ばれます。オブジェクトには、次の2種類のNULLを適用できます。
オブジェクトの属性はすべて、NULL値を持つことができます。これはオブジェクトの属性の値が不明であることを意味します。
アトミックNULLとは、オブジェクトが存在しないということではありません。アトミックNULLのオブジェクトは値が不明なだけで、存在しています。つまり、データを持たないオブジェクトとみなすことができます。
OCCIでは、オブジェクト属性のすべての型について、それぞれ対応するクラスが用意されています。たとえば、NUMBER属性型はNumberクラスにマップされ、REFはRefAnyにマップされます。データ型を表す各OCCIクラスでは、次の2つのメソッドを使用できます。
isNull(): オブジェクトがNULLかどうかを戻します。
setNull(): オブジェクトをNULLに設定します。
同様に、これらのメソッドは、PObjectクラスからすべてのオブジェクトに継承され、オブジェクトのNULL情報のアクセスと自動設定を行うために使用されます。
アプリケーションでは、OCCIのポインタや参照を使用して、オブジェクトの内容に柔軟にアクセスできます。OCCIでは、PObject::getRef()メソッドを使用して永続オブジェクトへの参照を戻すことができます。このコールは、永続オブジェクトに対してのみ有効です。
OCCIユーザーは、オーバーロードされたPObject::operator new()を使用して永続オブジェクトを作成できます。ただし、データベース・サーバーからオブジェクトを削除するには、Refでref.markDelete()を直接コールします。これにより、オブジェクトがクライアント・キャッシュに入るのを回避できます。オブジェクトがクライアント・キャッシュ内にすでにある場合は、オブジェクトでobj.markDelete()をコールすることでオブジェクトを削除できます。1度トランザクションがコミットされると、削除マークが設定されたオブジェクトは永続的に削除されます。
オブジェクトの型の継承は、C++やJavaでの継承に類似している点が多くあります。オブジェクト型は、既存のオブジェクト型のサブタイプとして作成できます。サブタイプは、元の型であるスーパータイプの属性およびメソッド(メンバー関数およびプロシージャ)をすべて継承するといえます。単一の継承のみがサポートされます。つまり、1つのオブジェクトが複数のスーパータイプを所有することはできません。継承した属性やメソッドには、新しい属性やメソッドを追加できます。継承した任意のメソッドをオーバーライド(実装を再定義)することもできます。サブタイプは、そのスーパータイプの拡張(つまり、スーパータイプの継承)といえます。
たとえば、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_tがNOT 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) ;
ポリモフィズムの利点の一部は、プロパティが持つ代用性によるものです。代用性によって、サブタイプの値をスーパータイプ用に記述された元のコードで使用できます。この場合、サブタイプに関する事前の知識は特に必要ありません。サブタイプの値は、メソッドが特化されて異なるメカニズムが使用されている場合でも、周辺のコードに対応してスーパータイプの値と同じように振る舞います。
インスタンスの代用性とは、サブタイプのオブジェクト値をスーパータイプで宣言されたコンテキスト内で使用できるようにする機能を指します。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 INSTANTIABLE型のサブタイプは、スーパータイプの任意のNOT INSTANTIABLEメソッドをオーバーライドして、具体的な実装を行うことができます。NOT INSTANTIABLEメソッドが残っている場合、サブタイプも必ずNOT INSTANTIABLEとして宣言する必要があります。
NOT INSTANTIABLEサブタイプは、インスタンス化可能なスーパータイプの下に定義できます。NOT INSTANTIABLE型をFINALとして宣言することは無効なため、使用できません。
次のコールは、型の継承をサポートしています。
継承によるオブジェクトのクラス宣言は、そのクラスが親の型クラスから導出され、親のクラスにない属性に対応するフィールドのみが含まれていることを除くと、単純なオブジェクト宣言と似ています。この宣言の構造を例4-8に示します。
例4-8 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);
}
この構造では、変数はすべて単純なオブジェクトの場合と同じです。parentTypenameは親の型の名前、つまり、その型名が継承している型のクラス名です。
この項では、この章で説明した機能の一部を使用したサンプルOCCIアプリケーションを示します。
例4-9 サンプル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-10 サンプル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-11 サンプルOCCIアプリケーションのファイルを生成するOTTコマンドのリスト
OTTはユーザー名demousrを使用して接続を試行します。システムではパスワードが要求されます。
ott userid=demousr intype=demo2.typ code=cpp hfile=demo2.h cppfile=demo2.cpp mapfile= mappings.cpp attraccess=private
例4-12 サンプル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-13 サンプル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-14 サンプルOCCIアプリケーションのdemo2.hのリスト
#ifndef DEMO2_ORACLE
# define DEMO2_ORACLE
#ifndef OCCI_ORACLE
# include <occi.h>
#endif
/* Make the foll changes to the generated file */
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-15 サンプル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_NAME、ADDRESS、PERSONおよびPFGRFDENTクラスの宣言がdemo2.hに生成されると想定します。次のサンプルOCCIアプリケーションは、例4-10のdemo2.typファイルで指定された、OTTによって生成されたクラスを拡張し、ユーザー定義のメソッドをいくつか追加します。これらのクラス宣言は、正しくコンパイルするために、demo2.hに組み込まれています。
例4-16 サンプル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-17 サンプルOCCIアプリケーションのmyDemo.cppのリスト
#ifndef DEMO2_ORACLE
#include <demo2.h>
#endif
/* 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-18 サンプルOCCIアプリケーションのmain.cppのリスト
#ifndef DEMO2_ORACLE
#include <demo2.h>
#endif
#ifndef MAPPINGS_ORACLE
#include <mappings.h>
#endif
#include <iostream>
using namespace std;
int main()
{
Environment *env = Environment::createEnvironment(Environment::OBJECT);
mappings(env);
try {
Connec tion *conn = env->createConnection("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;
}