3 C++によるOracle Databaseへのアクセス
この章では、リレーショナル・データベースに格納されたデータを操作するための、Oracle C++ Call Interface (OCCI)を使用したC++アプリケーション開発の基本を説明します。
この章には次のトピックが含まれます:
データベースへの接続について
アプリケーションをデータベースに接続する方法には、複数の異なるオプションがあります。
環境の作成と終了
OCCIの処理はすべて、Environment
クラスの内部で行われます。OCCI環境では、アプリケーション・モードおよびユーザー指定のメモリー管理関数が提供されます。例3-1に、OCCI環境の作成方法を示しています。
create
xxx
()
メソッドで作成したOCCIオブジェクト(接続、接続プール、文)はすべて、明示的に終了する必要があります。必要に応じて、この環境も明示的に終了する必要があります。例3-2に、OCCI環境の終了方法を示しています。
また、OCCI環境の有効範囲は、その環境の内部で作成するオブジェクト型(Agent
、Bytes
、Date
、Message
、IntervalDS
、IntervalYM
、Subscription
およびTimestamp
)より大きく設定しておく必要があります。例3-3に示すように、この規則は、BFile
、Blob
およびClob
オブジェクトには適用されません。
アプリケーションがグローバル・スコープ内のオブジェクト(静的変数またはグローバル変数など)にアクセスする必要がある場合、環境を終了する前にこれらのオブジェクトをNULL
に設定する必要があります。前述の例で、b
がグローバル変数の場合、terminateEnvironment()
コールの前にb.setNull()
コールを実行する必要があります。
createEnvironment()
メソッドのモード・パラメータを使用すると、アプリケーションのモードを次のように指定できます。
-
スレッド環境での実行(
THREADED_MUTEXED
またはTHREADED_UNMUTEXED
) -
オブジェクトの使用(
OBJECT
)
これらのモードは、環境ごとに設定できます。
例3-1 OCCI環境の作成方法
Environment *env = Environment::createEnvironment();
例3-2 OCCI環境の終了方法
Environment::terminateEnvironment(env);
例3-3 Blobオブジェクトにおける環境の有効範囲の使用方法
const string userName = "HR"; const string password = "password"; const string connectString = ""; Environment *env = Environment::createEnvironment(); { Connection *conn = env->createConnection( userName, password, connectString); Statement *stmt = conn->createStatement( "SELECT blobcol FROM mytable"); ResultSet *rs = stmt->executeQuery(); rs->next(); Blob b = rs->getBlob(1); cout << "Length of BLOB : " << b.length(); ... stmt->closeResultSet(rs); conn->terminateStatement(stmt); env->terminateConnection(conn); } Environment::terminateEnvironment(env);
接続のオープンとクローズ
Environment
クラスは、Connection
オブジェクトを作成するためのファクトリ・クラスです。最初にEnvironment
インスタンスを作成し、それを使用してユーザーがcreateConnection()
メソッドでデータベースに接続できるようにします。
例3-4では、環境インスタンスを作成し、それを使用してデータベース・ユーザーHR
と適切なパスワードのデータベース接続を作成しています。
次のコード例で示すように、操作セッションの終了時に、terminateConnection()
メソッドを使用して、接続を明示的にクローズする必要があります。また、OCCI環境も明示的に終了する必要があります。
Connection
インスタンスの中で作成または命名されたすべてのオブジェクト(Ref
、Bfile
、Producer
、Consumer
など)は、そのインスタンスの内部スコープ内で使用する必要があります。これらのオブジェクトのスコープは、Connection
が終了する前に明示的に終了する必要があります。例3-5に、接続および環境の終了方法を示しています。
プラガブル・データベースのサポートについて
マルチテナント・アーキテクチャにより、Oracle Databaseにスキーマ、スキーマ・オブジェクト、および非スキーマ・オブジェクトのポータブル・コレクションを含めることができ、これらはOracleクライアントに個別のデータベースとして表示されます。マルチテナント・コンテナ・データベース(CDB)は、1つ以上のユーザー作成のプラガブル・データベース(PDB)を含むOracle Databaseです。
OCCIクライアントは、プラガブル・データベース・プロパティが関連PDBに設定されているサービスを使用して、PDBに接続できます。
関連項目:
PDBおよび様々なPDBに接続するサービスの構成の詳細は、Oracle Database管理者ガイドを参照してください。
関連項目:
PDBの操作時の制限の詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください。
接続プーリングについて
この項では、OCCIの接続プーリング機能の使用方法を説明します。この項は、次の項目で構成されています。
これらの2つの主な違いは、StatelessConnectionPool
が状態に依存しないアプリケーションで使用されることです。このようなアプリケーションで事前に認証された接続を使用すると、アプリケーションのパフォーマンスが向上します。
接続プールの使用について
通常、中間層アプリケーションでは、多数のスレッドがデータベースに接続できる必要があります。各スレッドの継続時間は比較的短いため、すべてのスレッドに対してデータベースへの接続をオープンすると、接続の使用率およびパフォーマンスが低下する場合があります。
接続プーリング機能を使用すると、多数のスレッドで使用できる小さな接続セットがアプリケーションで作成できます。これにより、データベース・リソースを非常に効率的に活用できます。
接続プールの作成
接続プールを作成するには、例3-6に示すように、createConnectionPool()メソッドを使用します。
例3-6では、次のパラメータが使用されています。
-
poolUserName
: 接続プールの所有者。 -
poolPassword
: 接続プールにアクセスするためのパスワード。 -
connectString
: 接続プールが関連付けられたデータベース・サーバーを指定するデータベース名。 -
minConn
: 接続プールの作成時にオープンする最小接続数。 -
maxConn
: 接続プールで維持できる最大接続数。接続プールで最大接続数がオープンされていて、すべての接続がビジー状態の場合、接続を要求したOCCIメソッドのコールは、setErrorOnBusy()
が接続プールに対してコールされていないかぎり、接続を取得するまで待機します。 -
incrConn
: すべての接続がビジー状態のときに、コールによって接続が要求された場合にオープンする追加の接続数。この増分は、オープン接続数の合計が、その接続プールでオープンできる最大接続数より少ない場合にのみ行われます。
例3-7は、接続プールの作成方法を示しています。
これらの属性は、すべて動的に構成できます。これによって、現行のロード(オープン接続数およびビジー接続数)を読み取り、これらの属性を適切にチューニングできる柔軟なアプリケーションを設計できます。また、setTimeOut()
メソッドを使用すると、指定した時間を超過してアイドル状態になっている接続をタイムアウトすることもできます。OCCIでは、最適なオープン接続数を維持するために、アイドル状態の接続を定期的に終了します。
1つの環境に作成できる接続プールは1つに制限されているわけではありません。1つのOCCI環境に複数の接続プールを作成し、同一または異なるデータベースに接続できます。これは、ロード・バランシングが必要なアプリケーションで役に立ちます。
例3-6 createConnectionPool()メソッド
virtual ConnectionPool* createConnectionPool( const string &poolUserName, const string &poolPassword, const string &connectString ="", unsigned int minConn =0, unsigned int maxConn =1, unsigned int incrConn =1) = 0;
例3-7 接続プールの作成方法
const string connectString = ""; unsigned int maxConn = 5; unsigned int minConn = 3; unsigned int incrConn = 2; ConnectionPool *connPool = env->createConnectionPool( poolUserName, poolPassword, connectString, minConn, maxConn, incrConn);
プロキシ接続の作成
接続プールのユーザーに、他の接続のプロキシとして機能することを許可すると、その接続プールの接続を使用するデータベース・ユーザーは、ログイン・パスワードが一切不要になります。
プロキシ接続は、例3-8で示す2つのバージョンのcreateProxyConnection()メソッドをいずれか使用して作成できます。
または
ConnectionPool->createProxyConnection( const string &username, string roles[], int numRoles, Connection::ProxyType proxyType = Connection::PROXY_DEFAULT);
前述のメソッド例では、次のパラメータが使用されています。
-
roles[ ]
: このロールの配列は、プロキシ接続がクライアントに対してアクティブになった後でアクティブにするロールのリストを指定します。 -
Connection::ProxyType proxyType = Connection::PROXY_DEFAULT
: 列挙パラメータConnection::ProxyType
には、プロキシ認証を実行する様々な方法を表す定数がリストされます。PROXY_DEFAULT
は、name
がデータベース・ユーザー名を表していて、現在サポートされている唯一のプロキシ認証モードであることを示すために使用します。
例3-8 createProxyConnection()メソッド
ConnectionPool->createProxyConnection( const string &username, Connection::ProxyType proxyType = Connection::PROXY_DEFAULT);
ステートレスな接続プーリングの使用
ステートレスな接続プーリングは、接続時間が短く、状態を考慮しないアプリケーションで使用するために設計されています。ステートレスな接続プーリングの主な利点は、時間のかかる接続および認証プロトコルの省略により、パフォーマンスが向上することです。
ステートレスな接続プールでは、複数のスレッドで使用可能な、ステートレスな認証済のデータベース接続のグループを作成し、維持します。スレッドによる接続の使用が終了すると、接続はプールに解放されます。使用可能な接続がない場合、新しい接続が生成されます。したがって、プール内の接続数は動的に増加します。
プール内の一部の接続に、特定のプロパティのタグを付加できます。デフォルト接続を要求し、グローバリゼーション・サポート設定などの属性を設定し、タグを付加してプールに戻すことが可能です。同一の属性を持つ接続が必要な場合、同一のタグを持つ接続を要求し、同一のタグを持つ、プール内の接続の1つを再利用できます。接続のタグは変更または再設定できます。
ステートレスな接続プーリング・インタフェースを介して、プロキシ接続も作成および維持できます。
ステートレスな接続プーリングによって接続が多重化され、中間層アプリケーションのスケーラビリティが向上します。ただし、接続を長時間保持すると平行性が低下するため、StatelessConnectionPool
による接続は長時間トランザクションには使用しないでください。
注意:
-
OCCIでは、接続とタグのペアが正確であるかどうかは確認されません。同じタグが異なるクライアント側のプロパティで使用されていないかを確認する必要があります。
-
接続をプールに解放して戻す前に、アプリケーションですべてのオープン・トランザクションをコミットまたはロールバックする必要があります。そうしない場合、接続が解放されるときに、Oracleでは自動的にあらゆるオープン・トランザクションがコミットされます。
ステートレスな接続プールには、次の2種類があります。
-
同種のプールでは、プールの作成時に指定したユーザー名とパスワードを使用して、すべての接続が認証されます。したがって、すべての接続の認証コンテキストは同じです。同種のプールでは、プロキシ接続は使用できません。
-
異種プールでは、異なる接続を認証するには、異なるユーザー名を使用します。また、異種プールでプロキシ接続を定義できるのは、サーバー上でプロキシ接続の作成に必要な権限が付与されている場合です。また、異種プールでは外部認証がサポートされます。
例3-9は、接続プールの基本的な使用例を示しています。例3-10に、同種のステートレスな接続プールの作成および使用例を、例3-11に、異種の接続プールの使用例を示します。
例3-9 StatelessConnectionPoolの使用方法
プール・サイズは動的であるため、ユーザー要件の変化に応じて指定の最大接続数の範囲内で設定されます。次のパラメータを使用してステートレスな接続プールが作成されているとします。
-
minConn = 5
-
incrConn = 2
-
maxConn = 10
プールの作成時に5つの接続がオープンされます。
-
openConn = 5
get[AnyTagged][Proxy]Connection()
メソッドを使用して、ユーザーは5つのオープン接続をすべて使用します。
-
openConn = 5
-
busyConn = 5
ユーザーが別の接続を必要とする場合、プールは新しく2
つの接続をオープンし、その一方をユーザーに戻します。
-
openConn = 7
-
busyConn = 6
プールできる接続数の上限は、プールの作成時に指定したmaxConn
です。
ユーザーは、プール作成後にsetPoolSize()
メソッドをコールしてプール・パラメータを変更することもできます。
異種プールが作成されている場合、incrConn
引数とminConn
引数は無視されます。
例3-10 同種のステートレスな接続プールの作成および使用方法
同種のステートレスな接続プールを作成するには、次の基本手順および疑似コード・コマンドを実行します。
-
createStatelessConnectionPool()コールを使用して、
Environment
のHOMOGENEOUS
モードで、ステートレスな接続プールを作成します。StatelessConnectionPool *scp = env->createStatelessConnectionPool( username, passwd, connectString, maxCon, minCon, incrCon, StatelessConnectionPool::HOMOGENEOUS );
-
getConnection()メソッドをコールして、新規または既存の接続をプールから取得します。
Connection *conn=scp->getConnection(tag);
このコールの実行中に、一致するタグを持つ接続がプールで検索されます。一致する接続が存在する場合、その接続がユーザーに戻されます。存在しない場合は、プールのユーザー名およびパスワードによって認証された、タグを持たない接続が返されます。
または、getAnyTaggedConnection()コールによって接続を取得できます。一致するタグまたは
NULL
タグの接続が存在しない場合、一致しないタグを持つ接続が戻されます。Connection
へのgetTag()コールを使用して、戻されたタグを検証する必要があります。Connection *conn=scp->getAnyTaggedConnection(tag); string tag=conn->getTag();
-
接続を使用します。
-
releaseConnection()をコールし、接続を解放して
StatelessConnectionPool
に戻します。scp->releaseConnection(conn, tag);
空のタグ
""
は、Connection
のタグを解除します。getConnection()コールで同一の
tag
パラメータ値を使用して、StatelessConnectionPool
から接続を取得するよう選択できます。Connection *conn=scp->getConnection(tag);
Connection
はStatelessConnectionPool
に戻すかわりに、terminateConnection()コールを使用して破棄することもできます。scp->terminateConnection(conn);
-
Environment
オブジェクトに対するterminateStatelessConnectionPool()コールにより、プールを破棄します。env->terminateStatelessConnectionPool(scp);
例3-11 異種のステートレスな接続プールの作成および使用方法
異種のステートレスな接続プールを作成するには、次の基本手順および疑似コード・コマンドを実行します。
-
createStatelessConnectionPool()コールを使用して、
Environment
のHETEROGENEOUS
モードで、ステートレスな接続プールを作成します。StatelessConnectionPool *scp = env->createStatelessConnectionPool( username, passwd, connectString, maxCon, minCon, incrCon, StatelessConnectionPool::HETEROGENEOUS);
外部認証を有効にする場合は、createStatelessConnectionPool()コールで
USES_EXT_AUTH
モードもアクティブにする必要があります。StatelessConnectionPool *scp = env->createStatelessConnectionPool( username, passwd, connectString, maxCon, minCon, incrCon, StatelessConnectionPool::PoolType( StatelessConnectionPool::USES_EXT_AUTH| StatelessConnectionPool::HETEROGENEOUS));
-
異種プール・オプション用にオーバーロードされた
StatelessConnectionPool
のgetConnection()メソッドをコールして、新規または既存の接続をプールから取得します。Connection *conn=scp->getConnection(username, passwd, tag);
このコールの実行中に、一致するタグを持つ接続が異種プールで検索されます。一致する接続が存在する場合、その接続がユーザーに戻されます。存在しない場合、
NULL
タグを持つ、適切に認証されたタグなしの接続が戻されます。または、異種プール用にオーバーロードされたgetAnyTaggedConnection()コールによって接続を取得できます。一致するタグまたは
NULL
タグの接続が存在しない場合、一致しないタグを持つ接続が戻されます。Connection
へのgetTag()コールを使用して、戻されたタグを検証する必要があります。Connection *conn=scp->getAnyTaggedConnection(username, passwd, tag); string tag=conn->getTag();
また、
StatelessConnectionPool
に対するgetProxyConnection()またはgetAnyTaggedProxyConnection()のコールにより、プロキシ接続も使用できます。Connection *pconn = scp->getProxyConnection(proxyName, roles{}, nuRoles, tag, proxyType); Connection *pconn = scp->getAnyTaggedProxyConnection( proxyName, tag, proxyType);
プールで外部認証がサポートされる場合は、次のgetConnection()コールを使用します。
Connection *conn=scp->getConnection();
-
接続を使用します。
-
releaseConnection()をコールし、接続を解放して
StatelessConnectionPool
に戻します。scp->releaseConnection(conn, tag);
空のタグ
""
は、Connection
のタグを解除します。getConnection()コールで同一の
tag
パラメータ値を使用して、StatelessConnectionPool
から接続を取得するよう選択できます。Connection *conn=scp->getConnection(tag);
Connection
はStatelessConnectionPool
に戻すかわりに、terminateConnection()コールを使用して破棄することもできます。scp->terminateConnection(conn);
-
Environment
オブジェクトに対する terminateStatelessConnectionPool()コールにより、プールを破棄します。env->terminateStatelessConnectionPool(scp);
データベース常駐接続プーリングの理解
企業レベルのアプリケーションは通常、データベースへの永続的な接続として実装されている大量の同時ユーザー・セッションを処理する必要があります。こうした接続の作成および管理のメモリー・オーバーヘッドは、データベースのパフォーマンスに対して重大な意味を持ちます。
データベース常駐接続プーリングは、大規模なアプリケーション接続を処理するために専用サーバーのプールを提供することで、永続的な接続が多すぎる問題を解決しているため、データベースは何万もの同時接続に対応できます。したがって、データベース層のメモリー・フットプリントが大幅に削減され、データベース層および中間層の両方のスケーラビリティが向上します。データベース常駐接続プーリングは、中間層の接続プーリングに対応できない複数プロセス・アプリケーション・サーバーおよび複数の中間層を持つアーキテクチャ用に設計されています。
データベース常駐接続プーリングのアーキテクチャは、Oracle Databaseインスタンスに接続するためのデフォルトの専用モデルに密接に従っていますが、特定のサーバーを各接続に割り当てるオーバーヘッドはなくなります。サーバー層では、大部分の接続が指定時刻に非アクティブとなり、これらの各接続でメモリーが消費されます。このため、大量の接続をサポートするデータベース・システムは、使用可能なすべてのメモリーをすぐに枯渇させてしまう恐れがあります。データベース常駐接続プーリングでは、接続で専用サーバーを使用でき、専用サーバーによってOracleサーバー・プロセスとユーザー・セッションが結合されます。接続が非アクティブになると、接続のリソースがプールに戻され、類似した接続で使用可能になります。
包括的接続プーリングが可能なマルチスレッド化された中間層においては、未使用接続の問題は少し異なります。中間層の数が増すにつれ、各中間層はデータベースに対する複数の接続をプライベートに保持します。これらの接続は、他の中間層と共有することができません。そのかわりに接続プールをデータベースを配置することにより、類似クライアント間で接続が共有できるようになります。
データベース常駐接続プーリングでは、パスワードベース認証、文キャッシュ、タグ付けおよび高速アプリケーション通知がサポートされています。また、クライアント側のステートレスな接続プーリングをデータベース常駐接続プーリングと組み合せて使用できます。
データベース常駐接続プールからの接続を保持するクライアントは、バックグラウンドの接続ブローカ・プロセスに永続的に接続している点に注意してください。接続ブローカはプール機能を実装しており、専用サーバー・プロセスのプールに対するインバウンド・クライアント接続を多重化します。接続プールを使用しないクライアントは、かわりに専用サーバー・プロセスを使用します。
関連項目:
-
データベース常駐接続プーリングのアーキテクチャの詳細は、『Oracle Database概要』を参照してください。
-
データベース常駐接続プーリングの構成の詳細は、『Oracle Database管理者ガイド』を参照してください。
-
DBMS_CONNECTION_POOL
パッケージの詳細は、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』を参照してください。
データベース常駐接続プールの管理
データベース常駐接続プーリングを実装するには、最初にSYSDBA
権限を持つユーザーがシステム上で有効化する必要があります。データベース常駐接続プールの開始および保持に必要なステップは、例3-12を参照してください。
Oracle RAC構成では、接続プールに常駐するデータベースはすべての構成済ノードで起動されることに注意してください。プールを停止しない場合、起動中の構成はインスタンスの再起動にわたって永続的となります。インスタンスが生成されると、プールは自動的に起動されます。
例3-12 データベース常駐接続プールの管理方法
SYSDBA
権限を持つユーザーは次のステップを実行する必要があります。
-
データベースへの接続。
SQLPLUS / AS SYSDBA
-
(オプション)データベース常駐接続プールのパラメータを構成します。プールのデフォルト値は次のように設定されます。
DBMS_CONNECTION_POOL.CONFIGURE_POOL( 'SYS_DEFAULT_CONNECTION_POOL', MIN=>10, MAX=>200);
-
(オプション)データベース常駐接続プールの特定のパラメータを、他のパラメータに影響を与えずに変更します。
DBMS_CONNECTION_POOL.ALTER_PARAM( 'SYS_DEFAULT_CONNECTION_POOL', 'INACTIVITY_TIMEOUT', 10);
-
接続プールを起動します。このステップの後、接続プールは適格なすべてのクライアントで使用可能になります。
DBMS_CONNECTION_POOL.START_POOL( 'SYS_DEFAULT_CONNECTION_POOL');
-
(オプション)データベース常駐接続プールのパラメータを変更します。
DBMS_CONNECTION_POOL.ALTER_PARAM( 'SYS_DEFAULT_CONNECTION_POOL', 'MAXSIZE', 20);
-
(オプション)接続プールの構成をデフォルト値にリセットできます。
DBMS_CONNECTION_POOL.RESTORE_DEFAULTS ( 'SYS_DEFAULT_CONNECTION_POOL');
-
プールを停止します。プール情報は永続的である点に注意してください。プールを停止しても、プール名および構成パラメータは破棄されません。
DBMS_CONNECTION_POOL.STOP_POOL();
データベース常駐接続プールの使用
データベース常駐接続プーリングを使用する場合、接続クラスと接続純度を指定する必要があります。先行する接続状態により汚染されていてはならない接続をアプリケーションが要求する場合には、純度をNEW
に指定する必要があります。地理的ロケール設定の異なるクライアントが同一のデータベース・インスタンスを共有する場合には、このアプローチをお薦めします。アプリケーションが過去に使われた接続を使用できる場合、純度はSELF
に設定する必要があります。接続クラスと純度指定を併用すると、さらにアプリケーション固有タグを使用して、希望する状態である使用済接続を選択することも可能です。例3-12に示すように、デフォルトの接続プール名はSYS_DEFAULT_CONNECTION_POOL
です。
この機能は、Connectionクラスおよび純正値を指定するパラメータを追加して、接続を取得するためのStatelessConnectionPoolクラスおよびEnvironmentクラスインタフェース(getConnection()
およびgetProxyConnection()
)をオーバーロードします。クライアント側接続プール外の接続要求はすべて、デフォルトの純正値がNEW
です。クライアント側接続プールの内部の接続要求は、デフォルトの純正値がSELF
となります。
例3-13 データベース常駐接続プールからの接続の取得方法
conn1 = env->createConnection (/*username */"hr", /*password*/ "password", /* database*/ "inst1_cmon", /* connection class */"TESTCC", /* purity */Connection::SELF); stmt1 = conn1->createStatement("select count(*) from emp"); rs=stmt1->executeQuery(); while (rs->next()) { int num = rs->getInt(1); sprintf((char *)tmp, "%d", num); cout << tmp << endl; } stmt1->closeResultSet(rs); conn1->terminateStatement(stmt1); env->terminateConnection(conn1);
例3-14 クライアント側プールおよびサーバー側プールの使用
StatelessConnectionPool *scPool; OCCIConnection *conn1, *conn2; scPool = env->createStatelessConnectionPool (poolUserName, poolPassword, connectString, maxConn, minConn, incrConn, StatelessConnectionPool::HOMOGENEOUS); conn1= scPool->getConnection( /* Connection class name */"TESTCC", /* Purity */ Connection::SELF); /* or, for proxy coonnections */ conn2= scPool->getProxyConnection(/* username*/ "HR_PROXY", /*Connection class */"TESTCC", /* Purity */Connection::SELF); /* or, for getting a tagged connection */ conn3 = scPool->getConnection(/*connection class */"TESTCC", /*purity*/ Connection::SELF, /*tag*/ "TESTTAG"); /* Releasing a tagged connection is done presently */ scPool->releaseConnection(conn3, "TESTTAG"); /* To specify purity as new */ conn4 = scPool->getConnection(/* connection class */"TESTCC",/* purity of new */ Connection::NEW); /* Get a connection using username and password */ conn5 = scPool->getConnection (username, password,"TESTCC", Connection::SELF); /* Using roles when asking for a connection */ conn6 = scPool->getProxyConnection (username, roles, nRoles,"TESTCC", Connection::SELF); ... /* The other code continues as is...writing for clarity */ ... stmt1=conn1->createStatement ("INSERT INTO emp values (:c1, :c2)"); stmt1->setInt(1, thrid); stmt1->setString(2, "Test"); int count = stmt1->executeUpdate (); conn1->commit(); conn1->terminateStatement(stmt1); /* Release the connection */ scPool->releaseConnection (conn1); ... env->terminateStatelessConnectionPool (scPool);
SQLのDDL文とDML文の実行について
SQLは、リレーショナル・データベースを操作するための業界標準の言語です。OCCIでは、Statement
クラスを使用してSQLコマンドを実行します。
Statementオブジェクトの作成
Statement
オブジェクトを作成するには、例3-15に示すように、Connection
オブジェクトのcreateStatement()
メソッドをコールします。
SQLコマンドを実行する文オブジェクトの作成
Statement
オブジェクトの作成後、Statement
オブジェクトのexecute()、executeUpdate()、executeArrayUpdate()またはexecuteQuery()メソッドをコールして、SQLコマンドを実行します。これらのメソッドは、次の目的に使用されます。
-
execute()は、不特定な種類の文をすべて実行します。
-
executeUpdate()は、DML文およびDDL文を実行します。
-
executeArrayUpdate()は、複数のDML文を実行します。
-
executeQuery()は、問合せを実行します。
データベース表の作成
例3-16に、executeUpdate()メソッドを使用したデータベース表の作成方法を示しています。
例3-16 executeUpdate()メソッドを使用したデータベース表の作成方法
stmt->executeUpdate("CREATE TABLE shopping_basket (item_number VARCHAR2(30), quantity NUMBER(3))");
データベース表への値の挿入
同様に、executeUpdate()メソッドを呼び出して、例3-17で示すように、SQLのINSERT
文を実行できます。
executeUpdate()メソッドは、SQL文によって影響を受けた行数を戻します。
関連項目:
$
ORACLE_HOME
/rdbms/demo
のコード例で、表の行に対する挿入、選択、更新および削除操作の実行方法の実例を参照してください。
例3-17 executeUpdate()メソッドを使用したレコードの追加方法
stmt->executeUpdate("INSERT INTO shopping_basket VALUES('MANGO', 3)");
文オブジェクトの再利用
Statement
オブジェクトを再利用すると、SQL文を複数回実行できます。複数のパラメータで同じ文を繰り返し実行するには、例3-18に示すように、Statement
オブジェクトのsetSQL()メソッドを使用して文を指定します。
この指定によって、同じINSERT
文を必要な回数だけ実行できます。後で別のSQL文を実行する場合は、例3-19に示すように、文オブジェクトを再設定します。
setSQL()メソッドを使用することにより、OCCIの文オブジェクトとそれに関連付けられたリソースが、不必要に割り当てられたり解放されることがなくなります。現行の文オブジェクトの内容を任意の時点で取り出すには、getSQL()メソッドを使用します。
例3-18 setSQL()メソッドを使用したSQL文の指定方法
stmt->setSQL("INSERT INTO shopping_basket VALUES(:1,:2)");
例3-19 setSQL()メソッドを使用したSQL文の再設定方法
stmt->setSQL("SELECT * FROM shopping_basket WHERE quantity >= :1");
文オブジェクトの終了
例3-20に示すように、terminateStatement()メソッドを使用してStatement
オブジェクトを明示的に終了し、割当てを解除する必要があります。
例3-20 terminateStatement()メソッドを使用した文の終了方法
Connection::conn->terminateStatement(Statement *stmt);
OCCI環境で使用するSQL文の種類について
OCCI環境では、次の3種類のSQL文を使用します。
-
標準的な文についてでは、SQLコマンドに値を指定します
-
パラメータ化された文の使用では、パラメータまたは変数を指定します
-
コール可能文の使用では、PL/SQLストアド・プロシージャおよびファンクションをコールします
Statementクラスのメソッドは、すべての文、パラメータ化された文およびコール可能文の各文に適用可能なメソッドに分類されます。標準的な文はパラメータ化された文のスーパーセットで、パラメータ化された文はコール可能文のスーパーセットです。
パラメータ化された文の使用
文の入力変数にプレースホルダを設定すると、複数のパラメータを使用して同じ文を実行できます。これらの文は、ユーザーまたはプログラムからのパラメータ入力に対応できるため、パラメータ化された文と呼ばれます。
複数のパラメータでINSERT
文を実行する場合は、例3-18に示すように、まずStatement
オブジェクトのsetSQL()メソッドを使用して文を指定する必要があります。
次に、set
xxx
()
メソッドをコールしてパラメータを指定します。ここで、xxx
はパラメータの型を表します。例3-18に示すように、Statementオブジェクトの値が"INSERT INTO shopping_basket VALUES(:1,:2)"
であるとします。例3-21のコードを使用すると、setString()メソッドおよびsetInt()メソッドを起動してこれらの型の値を第1と第2のパラメータに入力し、executeUpdate()メソッドを起動して新しい行を表に挿入できます。パラメータを再設定してexecuteUpdate()メソッドを再コールすることで、Statementオブジェクトを再利用できます。アプリケーションが同じ文を繰り返し実行する場合には、入力パラメータの型の変更を避ける必要があります。その理由は、これによってリバインド操作が起動してアプリケーションのパフォーマンスに影響を与えるためです。
例3-21 setxxx()メソッドを使用した個別の列値の設定方法
stmt->setString(1, "Banana"); // value for first parameter stmt->setInt(2, 5); // value for second parameter stmt->executeUpdate(); // execute statement ... stmt->setString(1, "Apple"); // value for first parameter stmt->setInt(2, 9); // value for second parameter stmt->executeUpdate(); // execute statement
コール可能文の使用
PL/SQLストアド・プロシージャとは、その名前が示すように、アプリケーションによる再利用のためにデータベース・サーバー上に格納されたプロシージャのことです。OCCIにおけるcallable statementとは、他のSQL文を含むプロシージャに対するコールです。
指定した種類のフルーツの数を戻すプロシージャcountGroceries()
をコールする場合、例3-22に示すように、まずStatement
クラスのset
XXX
()
メソッドにより、PL/SQLストアド・プロシージャの入力パラメータを指定する必要があります。
ただし、ストアド・プロシージャをコールする前に、例3-23に示すように、registerOutParam()メソッドをコールして、OUT
パラメータの型とサイズを指定しておく必要があります。IN/OUT
パラメータの場合、set
XXX
()
メソッドを使用してパラメータを渡し、get
XXX
()
メソッドを使用して結果を取得します。
次に、プロシージャをコールして文を実行します。
stmt->executeUpdate(); // call the procedure
最後に、適切なget
xxx()メソッドをコールして、出力パラメータを取得します。
quantity = stmt->getInt(2); // get value of the second (OUT) parameter
例3-22 PL/SQLストアド・プロシージャのINパラメータの指定方法
stmt->setSQL("BEGIN countGroceries(:1, :2); END:"); int quantity; stmt->setString(1, "Apple"); // specify the first (IN) parameter of procedure
例3-23 PL/SQLストアド・プロシージャのOUTパラメータの指定方法
stmt->registerOutParam(2, Type::OCCIINT, sizeof(quantity)); // specify type and size of the second (OUT) parameter
配列パラメータを使用するコール可能文の使用
コール可能文を通じて実行されるPL/SQLストアド・プロシージャには、値の配列をパラメータとして指定できます。配列内の要素の数とディメンションは、setDataBufferArray()
メソッドで指定します。
次に、setDataBufferArray()
メソッドの例を示します。
void setDataBufferArray( unsigned int paramIndex, void *buffer, Type type, ub4 arraySize, ub4 *arrayLength, sb4 elementSize, ub2 *elementLength, sb2 *ind = NULL, ub2 *rc = NULL);
前述のメソッド例では、次のパラメータが使用されています。
-
paramIndex
: パラメータ番号 -
buffer
: 値の配列を格納するデータ・バッファ。 -
Type
: データ・バッファ内のデータの型。 -
arraySize
: 配列の最大要素数。 -
arrayLength
: 配列の要素数。 -
elementSize
: 配列の現行要素のサイズ。 -
elementLength
: 長さの配列へのポインタ。elementLength[i]
は、配列内のi
番目の現在の長さです -
ind
: インジケータ情報 -
rc
: リターン・コード
ストリーム読取り/書込みについて
OCCIではストリーム・インタフェースがサポートされており、データを一連の小さな固まりに分割することで、非常に大きな列を挿入および取得できます。この方法により、クライアント側のメモリー要件が縮小されます。このストリーム・インタフェースは、SELECT
や様々なDMLコマンドなどのパラメータ化された文、およびPL/SQLブロック内のコール可能文で使用できます。ストリームでサポートされているデータ型は、BLOB
、CLOB
、LONG
、LONG RAW
、RAW
およびVARCHAR2
です。
ストリーム・データには、次の3種類があります。
-
SELECT
/DML文のバインド変数またはコール可能文のIN
引数に対応する書込み可能ストリーム -
SELECT
文のフェッチされた列値またはコール可能文のOUT
引数に対応する読取り可能ストリーム -
IN/OUT
バインド変数に対応する双方向ストリーム
Streamクラスのメソッドは、ストリーム・インタフェースをサポートします。
StatementクラスのgetStream()メソッドは、DMLおよびコール可能文の読取りおよび書込みをサポートするストリーム・オブジェクトを戻します。
-
書込みの場合、データをバインド変数に、または
IN
引数またはIN/OUT
引数に渡します。 -
読取りの場合、データを
OUT
またはIN/OUT
引数からフェッチします。
ResultSetクラスのgetStream()メソッドは、データの読取りに使用できるストリーム・オブジェクトを戻します。
これらのクラスのstatus()
メソッドにより、ストリーム操作のステータスが決定されます。
ストリーム・モードでのデータのバインド(SELECT/DMLおよびPL/SQL)
データをストリーム・モードでバインドするには、次の手順を実行し、例3-24を参照してください。
例3-24 ストリーム・モードでのデータのバインド
Statement *stmt = conn->createStatement( "Insert Into testtab(longcol) values (:1)"); //longcol is LONG type column stmt->setCharacterStreamMode(1, 100000); stmt->executeUpdate(); Stream *instream = stmt->getStream(1); char buffer[1000]; instream->writeBuffer(buffer, len); //write data instream->writeLastBuffer(buffer, len); //repeat stmt->closeStream(instream); //stmt->status() is //UPDATE_COUNT_AVAILABLE Statement *stmt = conn->createStatement("BEGIN testproc(:1); END;"); //if the argument type to testproc is IN or IN/OUT then pass TRUE to //setCharacterStreamMode or setBinaryStreamMode stmt->setBinaryStreamMode(1, 32768, TRUE);
ストリーム・モードでのデータのフェッチ(PL/SQL)
ストリーム・モードでデータをフェッチするには、次の手順を実行し、例3-25を参照してください。
- 適切なバインド・プレースホルダを使用して、
SELECT
/DML文を作成します。 - ストリーム・モードから取得されたデータが入る各バインド位置に対し、StatementクラスのsetBinaryStreamMode()またはsetCharacterStreamMode()メソッドをコールします。
- 文を実行します。Statementクラスのstatus()メソッドにより、
STREAM_DATA_AVAILABLE
が戻されます。 - StatementクラスのgetStream()メソッドを使用してストリーム・オブジェクトを取得します。
- StreamクラスのreadBuffer()およびreadLastBuffer()メソッドを使用してデータを読み取ります。
- StatementクラスのcloseStream()メソッドを使用してストリームをクローズします。
例3-25 PL/SQLを使用したストリーム・モードでのデータのフェッチ
Statement *stmt = conn->createStatement("BEGIN testproc(:1); END;"); //argument 1 is OUT type stmt->setCharacterStreamMode(1, 100000); stmt->execute(); Stream *outarg = stmt->getStream(1); //use Stream::readBuffer/readLastBuffer to read data
複数のストリームの使用
複数の読取りストリームおよび書込みストリームを処理する必要がある場合には、あるストリームの読取りや書込みが完了してから他のストリームの読取りまたは書込みを行う必要があります。ストリーム位置を決定するには、Statement ClassのgetCurrentStreamParam()メソッドまたはResultSet ClassのgetCurrentStreamColumn()メソッドを使用します。表13-45に示すように、Stream Classのstatus()メソッドは、ストリーム内に読取り可能なデータが存在する場合はREADY_FOR_READ
を返し、すべてのデータが読取り済の場合はINACTIVE
を返します。その後、アプリケーションは次のストリーム列を読取り可能となります。例3-26に2本の同時ストリームの読取りおよび書込みを行う方法を示します。これらのストリーム・インタフェースは、同一のStatement
オブジェクトおよびResultSet
オブジェクト内のsetDataBuffer()
メソッドとともには使用できません。
例3-26 複数のストリームの読取りおよび書込みの方法
Statement *stmt = conn->createStatement( "Insert into testtab(longcol1, longcol2) values (:1,:2)"); //longcol1 AND longcol2 are 2 columns inserted in streaming mode stmt->setBinaryStreamMode(1, 100000); stmt->setBinaryStreamMode(2, 32768); stmt->executeUpdate(); Stream *col1 = stmt->getStream(1); Stream *col2 = stmt->getStream(2); col1->writeBuffer(buffer, len); //first stream ... //complete writing coll stream col1->writeLastBuffer(buffer, len); //finish first stream and move to col2 col2->writeBuffer(buffer, len); //second stream //reading multiple streams stmt = conn->createStatement("select longcol1, longcol2 from testtab"); ResultSet *rs = stmt->executeQuery(); rs->setBinaryStreamMode(1, 100000); rs->setBinaryStreamMode(2, 100000); while (rs->next()) { Stream *s1 = rs->getStream(1) while (s1->status() == Stream::READY_FOR_READ) { s1->readBuffer(buffer,size); //process } //first streaming column done rs->closeStream(s1); //move onto next column. rs->getCurrentStreamColumn() returns 2 Stream *s2 = rs->getStream(2) while (s2->status() == Stream::READY_FOR_READ) { s2->readBuffer(buffer,size); //process } //close the stream rs->closeStream(s2); }
行変更の反復について
executeUpdate
メソッドは各行に対して繰り返し発行できますが、OCCIには、1回のネットワーク・ラウンドトリップで複数行に対してデータを送信する効率的なメカニズムがあります。Statement
クラスのaddIteration()
メソッドを使用して、反復ごとに異なる行を変更するバッチ操作を実行します。
INSERT
、UPDATE
およびDELETE
の各操作を反復して実行するには、次の設定を行う必要があります。
-
最大反復回数の設定
-
可変長パラメータに対する最大パラメータ・サイズの設定
最大反復回数の設定
反復実行を行う場合は、最初にsetMaxIterations()
メソッドをコールして、文に対して実行する最大反復回数を指定します。
Statement->setMaxIterations(int maxIterations);
現行の最大反復回数の設定を取得するには、getMaxIterations()
メソッドをコールします。
最大パラメータ・サイズの設定
反復実行にstring
やBytes
などの可変長のデータ型が含まれている場合は、OCCIで最大バッファ・サイズの割当てができるように、次のように最大パラメータ・サイズを設定する必要があります。
Statement->setMaxParamSize(int parameterIndex, int maxParamSize);
Number
やDate
、あるいはsetDataBuffer()
メソッドを使用するパラメータなど、固定長のデータ型については最大パラメータ・サイズを設定する必要はありません。
現行の最大パラメータ・サイズの設定を取得するには、getMaxParamSize()
メソッドをコールします。
反復操作の実行
最大反復回数および(必要に応じて)最大パラメータ・サイズの設定後は、例3-27に示すように、パラメータ化された文を使用した反復実行を簡単に行うことができます。
反復実行は、標準的な文またはパラメータ化された文のいずれかによるINSERT
、UPDATE
およびDELETE
操作にのみ使用できるように設計されています。コール可能文および問合せには使用できません。反復間でのデータ型の変更はできません。たとえば、パラメータ 1
にsetInt()
を使用した場合、後続の反復の同じパラメータにはsetString()
を使用できません
例に示したように、最後の反復を除く各反復の後にaddIteration()
メソッドをコールし、その後executeUpdate()
メソッドを呼び出します。2番目の行を挿入しない場合は、addIteration()
メソッドや後続のset
xxx()
メソッドのコールは必要ありません。
例3-27のDML実行で、各反復によって影響を受けた行数を取得するには、setRowCountsOption()を使用して機能を有効にし、その後getDMLRowCounts()を使用して行数のベクターを戻します。影響を受けた行の合計数を取得するには、executeUpdate()の戻り値を使用するか、getUb8RowCount()をコールします。
例3-27 反復操作の実行方法
stmt->setSQL("INSERT INTO basket_tab VALUES(:1, :2)"); stmt->setString(1, "Apples"); // value for first parameter of first row stmt->setInt(2, 6); // value for second parameter of first row stmt->addIteration(); // add the iteration stmt->setString(1, "Oranges"); // value for first parameter of second row stmt->setInt(1, 4); // value for second parameter of second row stmt->executeUpdate(); // execute statement
SQL問合せの実行について
SQL問合せを使用すると、アプリケーションは指定した制約に基づいて、データベースから情報を要求できます。結果セットが問合せによって返されます。
結果セットの使用
データベース問合せを実行すると、問合せ結果は、結果セットと呼ばれる行セットに格納されます。OCCIでは、SQLのSELECT
文は、Statement
クラスのexecuteQuery
メソッドによって実行されます。このメソッドは、問合せ結果を表すResultSet
オブジェクトを戻します。
ResultSet *rs = stmt->executeQuery("SELECT * FROM basket_tab");
結果セットのデータを取得した後は、そのデータに対して操作を実行できます。たとえば、この表の内容を出力するとします。次のコード例に示すように、ResultSet
のnext()
メソッドを使用してデータをフェッチし、get
xxx()
メソッドを使用して結果セットの個々の列を取り出します。
cout << "The basket has:" << endl; while (rs->next()) { string fruit = rs->getString(1); // get the first column as string int quantity = rs->getInt(2); // get the second column as int cout << quantity << " " << fruit << endl; }
ResultSet
クラスのnext()
およびstatus()
メソッドは、Status
(定義は表13-38を参照)を返します。
データが現在行に対して使用可能な場合、ステータスはDATA_AVAILABLE
となります。すべてのデータが読み取られると、ステータスはEND_OF_FETCH
に変更されます。読み取る出力ストリームがある場合は、そのストリーム・データがすべて正常に読み取られるまで、ステータスはSTREAM_DATA_AVAILABLE
となります。
例3-28は、ストリーム・データを結果セットへフェッチする方法を示しています。基本情報は、「ストリーム読取り/書込みについて」の項を参照してください。
例3-28 ResultSetを使用したストリーム・モードでのデータのフェッチ
char buffer[4096]; ResultSet *rs = stmt->executeQuery ("SELECT col1, col2 FROM tab1 WHERE col1 = 11"); rs->setCharacterStreamMode(2, 10000); while (rs->next ()) { unsigned int length = 0; unsigned int size = 500; Stream *stream = rs->getStream (2); while (stream->status () == Stream::READY_FOR_READ) { length += stream->readBuffer (buffer +length, size); } cout << "Read " << length << " bytes into the buffer" << endl; }
問合せの指定について
IN
バインド変数を問合せで使用すると、問合せのWHERE
句に制約を指定できます。たとえば、次のプログラムは、最小数量4
を持つ項目のみを出力します。
stmt->setSQL("SELECT * FROM basket_tab WHERE quantity >= :1"); int minimumQuantity = 4; stmt->setInt(1, minimumQuantity); // set first parameter ResultSet *rs = stmt->executeQuery(); cout << "The basket has:" << endl; while (rs->next()) cout << rs->getInt(2) << " " << rs->getString(1) << endl;
プリフェッチ・カウントの設定によるパフォーマンスの最適化について
ResultSet
メソッドでは1度に1行のデータを取り出しますが、サーバーからの実際のデータ・フェッチでは、問合せ対象の行ごとにネットワーク・ラウンドトリップを発生させる必要はありません。パフォーマンスを最大にするために、サーバーに対する各ラウンドトリップでプリフェッチする行数を設定できます。
setPrefetchRowCount(
)
メソッドでプリフェッチされる行数を設定するか、setPrefetchMemorySize()
メソッドでプリフェッチに使用するメモリ・サイズを設定することで行います。
この属性を両方とも設定すると、指定したメモリー制限に先に到達しないかぎり、指定した行数がプリフェッチされます。指定したメモリー制限に先に到達した場合は、setPrefetchMemorySize()
メソッドのコールで定義したメモリー領域に適合する行数が戻ります。
デフォルトではプリフェッチはオンになっており、データベースでは常に1行余分にフェッチします。プリフェッチをオフにするには、プリフェッチする行数とメモリー・サイズの両方を0
(ゼロ)に設定します。
LONG
、LOBまたは不透明型の列(XMLType
など)が問合せの一部である場合、プリフェッチは無効になります。
文の動的実行について
DML操作が必要であるとわかっている場合は、executeUpdate
メソッドを使用します。同様に、問合せが必要であるとわかっている場合はexecuteQuery()
メソッドを使用します。
アプリケーションで動的イベントを使用する必要がある場合、実行時にどの文の実行が必要であるかを確認できません。このため、OCCIには、execute()
メソッドが用意されています。execute()
メソッドを呼び出すと、次のいずれかのステータスが戻されます。
execute()
メソッドの呼出しで、これらのステータスのいずれかが戻されますが、status
メソッドを使用して文を「調べる」ことができます。
Statement stmt = conn->createStatement(); Statement::Status status = stmt->status(); // status is UNPREPARED stmt->setSQL("select * from emp"); status = stmt->status(); // status is PREPARED
SQL文字列で文オブジェクトを作成した場合は、PREPARED
状態で作成されます。たとえば、次のようにします。
Statement stmt = conn->createStatement("insert into foo(id) values(99)"); Statement::Status status = stmt->status();// status is PREPARED status = stmt->execute(); // status is UPDATE_COUNT_AVAILABLE
別のSQL文をStatementに設定すると、ステータスがPREPARED
に変更されます。たとえば、次のようにします。
stmt->setSQL("select * from emp"); // status is PREPARED status = stmt->execute(); // status is RESULT_SET_AVAILABLE
この項には次のトピックが含まれます: 「文のステータスの定義について」。
文のステータスの定義について
この項では、文オブジェクトに関連付けられているStatus
の取りうる値について説明します。
UNPREPARED
setSQL()
メソッドで文オブジェクトにSQL文字列の属性を指定していない場合、その文はUNPREPARED
状態になります。
Statement stmt = conn->createStatement(); Statement::Status status = stmt->status(); // status is UNPREPARED
PREPARED
SQL文字列でStatementを作成した場合は、PREPARED
状態で作成されます。たとえば、次のようにします。
Statement stmt = conn->createStatement("INSERT INTO demo_tab(id) VALUES(99)"); Statement::Status status = stmt->status(); // status is PREPARED
別のSQL文をStatement
に設定した場合、ステータスはPREPARED
に変更されます。たとえば、次のようにします。
status = stmt->execute(); // status is UPDATE_COUNT_AVAILABLE stmt->setSQL("SELECT * FROM demo_tab"); // status is PREPARED
RESULT_SET_AVAILABLE
RESULT_SET_AVAILABLE
ステータスは、適切に作成された問合せが実行済で、結果セットを通じて結果にアクセスできることを示します。
文オブジェクトを問合せに設定すると、ステータスはPREPARED
になります。問合せを実行すると、その文のステータスはRESULT_SET_AVAILABLE
に変更されます。たとえば、次のようにします。
stmt->setSQL("SELECT * from EMP"); // status is PREPARED status = stmt->execute(); // status is RESULT_SET_AVAILABLE
結果セットのデータにアクセスするには、次の文を発行します。
ResultSet *rs = Statement->getResultSet();
UPDATE_COUNT_AVAILABLE
PREPARED
状態のDDL文またはDML文を実行すると、その状態がUPDATE_COUNT_AVAILABLE
に変更されます。次にコード例を示します。
Statement stmt = conn->createStatement("INSERT INTO demo_tab(id) VALUES(99)"); Statemnt::Status status = stmt->status(); // status is PREPARED status = stmt->execute(); // status is UPDATE_COUNT_AVAILABLE
このステータスは、文の実行によって影響を受けた行数を示します。また、次のことを示しています。
-
その文に入力ストリームまたは出力ストリームが含まれていないこと
-
その文が問合せではなく、DDL文またはDML文のいずれかであること
影響を受けた行数を取得するには、次の文を発行します。
stmt->getUb8RowCount();
DDL文の結果は、更新カウント0
(ゼロ)になります。同様に、一致条件のいずれも満たさない更新の場合、結果はカウント0
(ゼロ)になります。このような場合は、実行された文の種類をレポートされたステータスから判断することはできません。
NEEDS_STREAM_DATA
書き込まれる出力ストリームがある場合は、すべてのストリーム・データが完全に書き込まれるまで実行は完了しません。このような場合、ステータスはNEEDS_STREAM_DATA
に変更され、ストリームを書き込む必要があることを示します。ストリームの書込み後、status()メソッドをコールして、さらに書き込むストリーム・データがあるかどうか、つまり実行が完了したかどうかを調べます。
複数のストリーム・パラメータが文に含まれている場合は、getCurrentStreamParam()メソッドを使用して書込みが必要なパラメータを検出します。
反復または配列実行の場合は、getCurrentStreamIteration()メソッドによって、データを書き込む対象の反復を識別します。
ストリーム・データがすべて処理されると、ステータスはRESULT_SET_AVAILABLE
またはUPDATE_COUNT_AVAILABLE
に変更されます。
STREAM_DATA_AVAILABLE
このステータスは、実行の完了前に、アプリケーションで一部のストリーム・データをOUT
パラメータまたはIN/OUT
パラメータに読み取る必要があることを示しています。ストリームの読取り後、status
メソッドをコールして、さらに読み取るストリーム・データがあるかどうか、つまり実行が完了したかどうかを調べます。
複数のストリーム・パラメータが文に含まれている場合は、getCurrentStreamParam()
メソッドを使用して読取りが必要なパラメータを検出します。
反復または配列実行の場合は、getCurrentStreamIteration()
メソッドによって、データを読み取る対象の反復を識別します。
ストリーム・データがすべて処理されると、ステータスはUPDATE_COUNT_REMOVE_AVAILABLE
に変更されます。
ResultSet
クラスにも読取り可能なストリームがあり、Statement
クラスの読取り可能なストリームと同様に機能します。
増加した行数と追加されたエラー・コード範囲のデータ型の使用について
Oracle Database Release 12c以降、Oracle C++ Call Interfaceでは、増加した行数と追加されたエラー・コード範囲のデータ型をサポートしています。増加した行数を戻すメソッドは、StatementクラスのgetUb8RowCount()です。
これには、2つの利点があります。
-
UB4MAXVAL行より多くの行数に影響を及ぼす文を実行するアプリケーションは、影響を受けた行数の正確な値を表示できます。
-
Oracle Databaseリリース12c以降、Oracle Databaseは正確に、より新しいエラー・コード(
ORA-65535
以降のコード)をアプリケーション・クライアントに戻すことができます。古いクライアントには、エラー・コードのオーバーフローを示す情報メッセージが戻されます。
この項では、次の項目について説明します。
SELECT操作での増加した行数の使用
getUb8RowCount()メソッドは、SELECT
文の実行後、処理した行数をub8
型として戻します。この項の例に、様々なSELECT
文のシナリオでgetUb8RowCount()を使用する方法を示しています。
-
例3-29の最も単純なシナリオでは、影響を受けた行数はフェッチされた数と同じです。
-
プリフェッチ・オプションが設定されている場合、例3-30で示すように、これにはプリフェッチされた行数が含まれています。
-
例3-31の配列フェッチ・メカニズムを使用する場合、setDataBuffer()インタフェースを起動することによって、getUb8RowCount()は、プリフェッチ・オプションと関係なく、フェッチされた合計行数をユーザー・バッファに戻します。
例3-29 getUb8RowCount()を使用するSELECT (単純なシナリオ)
影響を受けた行数はすでにフェッチされている行数です。
oraub8 largeRowCount = 0; Statement *stmt = conn->createStatement("SELECT salary FROM employees"); ResultSet *rs = stmt->executeQuery (); rs->next(); largeRowCount = stmt->getUb8RowCount();
例3-30 getUb8RowCount()を使用するSELECT (プリフェッチを使用)
ここで、影響を受けた行数は、前の反復でフェッチされた行数とnext()
のコールでプリフェッチした行数を足した行数と同じです。
oraub8 largeRowCount = 0; Statement *stmt = conn->createStatement("SELECT salary FROM employees"); stmt -> setPrefetchRowCount(prefetch_count); ResultSet *rs = stmt->executeQuery (); rs->next(); largeRowCount = stmt->getUb8RowCount();
例3-31 getUb8RowCount()を使用するSELECT (配列フェッチとプリフェッチを使用)
ここで、影響を受けた行数(largeRowCount
の値)は、前の反復でユーザー・バッファにフェッチされた行数とnext(max)
またはnext()
のコールでフェッチされた行数を足した行数です。これは、プリフェッチの値とは関係がありません。
oraub8 largeRowCount = 0; Statement *stmt=conn->createStatement("SELECT col1 FROM table1"); int max = 20; int prefetch_count = 10; ub2 lengthC1[max]; ub4 c1[max]; for (i = 0; i < max; ++i) { c1[i] = 0; lengthC1[i] = sizeof (c1[i]); } stmt -> setPrefetchRowCount(prefetch_count); ResultSet *rs = stmt->executeQuery(); rs->setDataBuffer (1, c1, OCCIINT, sizeof (ub4), lengthC1); rs->next(max); largeRowCount = stmt->getUb8RowCount();
INSERT、UPDATEおよびDELETE操作での増加した行数の使用
INSERT
、UPDATE
およびDELETE
文の場合、getUb8RowCount()メソッドは、直前の文で処理された行数を戻します。
例3-32 getUb8RowCount()を使用するINSERT (単純なシナリオ)
largeRowCount
の値は挿入された行数、すなわち1
です。
oraub8 largeRowCount = 0; Statement *stmt = conn->createStatement("INSERT INTO table1 values (:1)"); stmt->setNumber(1, 100); stmt->executeUpdate(); largeRowCount = stmt->getUb8RowCount();
例3-33 getUb8RowCount()を使用するINSERT (反復を使用)
ここで、largeRowCount
の値はmax
と同じです。
int max; oraub8 largeRowCount = 0; Statement *stmt=conn->createStatement("INSERT INTO table1 values (:1)"); stmt->setMaxIterations (max); for(i = 0; i < max-1; i++) { stmt->setNumber(1, 100); stmt->addIteration (); } stmt->setNumber(1, 100); stmt->executeUpdate(); largeRowCount = stmt->getUb8RowCount();
例3-34 getUb8RowCount()を使用するUPDATE
ここで、largeRowCount
の値は更新された行数です。
oraub8 largeRowCount = 0; Statement *stmt=conn->createStatement( "UPDATE table1 SET COL1 = COL1+100 WHERE COL1=:1"); stmt->setNumber(1, 200); stmt->executeUpdate(); largeRowCount = stmt->getUb8RowCount();
トランザクションのコミットについて
SQLのDML文はすべて、トランザクションのコンテキスト内で実行されます。これらの文によって行われたアプリケーションへの変更は、トランザクションのコミットによって永続的に変更されるか、ロールバックの実行によって取り消されます。SQLのCOMMIT
文およびROLLBACK
文は、executeUpdate()
メソッドで実行できますが、Connection::commit()
メソッドおよびConnection::rollback()
メソッドをコールすることもできます。
DMLで行った変更を即時にコミットする場合は、次の文を発行して、Statement
クラスの自動コミット・モードをオンにできます。
Statement::setAutoCommit(TRUE);
自動コミットを有効にした後は、変更を行うたびに自動的に永続的な変更となります。これは、各実行の直後にコミットを発行するのと同じです。
デフォルトのモードに戻るには、次の文を発行して自動コミットをオフにします。
Statement::setAutoCommit(FALSE);
文キャッシュ
文キャッシュ機能により、セッション内の文のキャッシュが確立および管理されます。サーバー側で準備したカーソルを効率的に使用し、文の解析の繰返しを省略することにより、アプリケーションのパフォーマンスおよびスケーラビリティを改善します。
文キャッシュは接続およびセッション・プーリングとともに使用でき、また接続プーリングなしでも使用できます。一般的な使用例は、例3-35および例3-36を参照してください。
例3-35 接続プーリングを使用しない文キャッシュ
次の手順および疑似コードで、接続プールを使用しない文キャッシュ機能の実装方法を説明します。
-
Environment
オブジェクトに対してcreateConnection()コールを実行し、Connection
を作成します。Connection *conn = env->createConnection( username, password, connecstr);
-
setStmtCacheSize()コールで0 (ゼロ)以外の
size
パラメータを使用して、Connection
オブジェクトで文キャッシュを使用可能にします。conn->setStmtCacheSize(10);
この後、getStmtCacheSize()をコールすることにより、キャッシュのサイズが決定されます。また、setStmtCacheSize()コールにより、文キャッシュのサイズが変更され、
size
パラメータを0 (ゼロ)に設定すると文キャッシュが使用禁止になります。 -
Connection
オブジェクトへのcreateStatement()コールにより、Statement
を作成します。キャッシュ内に存在する場合はStatement
は戻され、そうでない場合はNULL
タグを持つ新しいStatement
が作成されます。Statement *stmt = conn->createStatement(sql);
事前にキャッシュされたタグ付き文を取り出すには、createStatement()メソッドの代替形式を使用します。
Statement *stmt = conn->createStatement(sql, tag);
-
文を使用してSQLコマンドを実行し、結果を取得します。
-
文をキャッシュに戻します。
conn->terminateStatement(stmt, tag);
この文をキャッシュしない場合、disableCaching()コールおよびterminateStatement()の代替形式を使用します。
stmt->disableCaching(); conn->terminateStatement(stmt);
文がキャッシュされているかどうかを検証するには、
Connection
オブジェクトに対してisCached()コールを発行します。解放時に文をタグ付けし、後で、同じタグを持つ別の文用に再利用することもできます。タグは、キャッシュを検索するために使用します。タグ付けされていない文(つまりタグが
NULL
の文)は、タグ付けされている文の特別なケースです。2つの文のタグのみが異なり、一方のみがタグ付けされている場合は、これらの2つの文は異なるものとみなされます。 -
接続を終了します。
例3-36 接続プーリングを使用する文キャッシュ
次の手順および疑似コードで、接続プールを使用する文キャッシュ機能の実装方法を説明します。
文キャッシュは、setStmtCacheSize()
コールの後に作成された接続でのみ使用できます。
文キャッシュは、プール・レベルで使用できない場合でも、プール内の個別の接続で実装できます。
-
Environment
オブジェクトのcreateConnectionPool()コールを実行し、ConnectionPool
を作成します。ConnectionPool *conPool = env->createConnectionPool( username, password, connecstr, minConn, maxConn, incrConn);
StatelessConnectionPool
を使用する場合は、かわりにcreateStatelessConnectionPool()をコールします。これ以後の操作は、ConnectionPool
およびStatelessConnectionPool
オブジェクトと同じです。Stateless ConnectionPool *conPool = env->createStatelessConnectionPool( username, password, connecstr, minConn, maxConn, incrConn, mode);
-
setStmtCacheSize()コールで0 (ゼロ)以外の
size
パラメータを使用して、ConnectionPool
内のすべてのConnection
で文キャッシュを使用可能にします。conPool->setStmtCacheSize(10);
この後、getStmtCacheSize()をコールすることにより、キャッシュのサイズが決定されます。また、setStmtCacheSize()コールにより、文キャッシュのサイズが変更され、
size
パラメータを0 (ゼロ)に設定すると文キャッシュが使用禁止になります。 -
ConnectionPool
オブジェクトへのcreateConnection()コールにより、プールからConnection
を取得します。キャッシュ内に存在する場合はStatement
が戻され、そうでない場合はNULL
タグを持つ新しいStatement
が作成されます。Connection *conn = conPool->createConnection(username, password, connecstr);
事前にキャッシュされたタグ付き文を取り出すには、createStatement()メソッドの代替形式を使用します。
Statement *stmt = conn->createStatement(sql, tag);
-
Connection
オブジェクトへのcreateStatement()コールにより、Statement
を作成します。キャッシュ内に存在する場合はStatement
は戻され、そうでない場合はNULL
タグを持つ新しいStatement
が作成されます。Statement *stmt = conn->createStatement(sql);
事前にキャッシュされたタグ付き文を取り出すには、createStatement()メソッドの代替形式を使用します。
Statement *stmt = conn->createStatement(sql, tag);
-
文を使用してSQLコマンドを実行し、結果を取得します。
-
文をキャッシュに戻します。
conn->terminateStatement(stmt, tag);
この文をキャッシュしない場合、disableCaching()コールおよびterminateStatement()の代替形式を使用します。
stmt->disableCaching(); conn->terminateStatement(stmt);
文がキャッシュされているかどうかを検証するには、
Connection
オブジェクトに対してisCached()コールを発行します。 -
接続terminateConnection()を解放します。
conPool->terminateConnection(conn);
例外の処理について
各OCCIメソッドでは、メソッドが成功しなかった場合、例外を生成できます。この例外は、SQLException
型です。OCCIではC++の標準テンプレート・ライブラリ(Standard Template Library: STL)を使用するため、STLで発生した例外はOCCIメソッドでも発生します。
STL例外は、標準の例外クラスから導出されます。exception::what()
メソッドは、エラー・テキストへのポインタを戻します。このエラー・テキストは、catchブロック内では有効であることが保証されます。
SQLException
クラスには、Oracle固有のエラー番号とメッセージが格納されます。このクラスは、標準の例外クラスから導出されるため、exception::what()
メソッドを使用してエラー・テキストを取得することもできます。
また、SQLException
クラスには、エラー情報を取得できる2つのメソッドがあります。getErrorCode()
メソッドはOracleエラー番号を戻します。exception::what()
が戻すエラー・テキストと同じものを、getMessage()
メソッドでも取得できます。getMessage()
メソッドは、STL文字列を戻すため、他のSTL文字列と同様にコピーできます。
エラー処理の方針に従って、OCCI例外を標準の例外とは別に処理するか、または両者を区別しないで処理するかを選択できます。
OCCI例外と標準の例外を区別しない場合、catchブロックは次のようになります。
catch (exception &excp) { cerr << excp.what() << endl; }
OCCI例外を標準の例外とは別に処理する場合、catchブロックは次のようになります。
catch (SQLException &sqlExcp) { cerr <<sqlExcp.getErrorCode << ": " << sqlExcp.getErrorMessage() << endl; } catch (exception &excp) { cerr << excp.what() << endl; }
前述のcatchブロックでは、SQL例外が最初のブロックで捕捉され、非SQL例外が2番目のブロックで捕捉されます。この2つのブロックの順序を逆にすると、SQL例外は捕捉されません。SQLException
は標準の例外から導出され、標準のexception catchブロックではSQL例外も同様に処理されます。
関連項目:
-
バッチ更新の際に発生したエラーを処理するための特別な機能の詳細は、「C++アプリケーションのパフォーマンスの最適化」の「行変更の反復」の項を参照してください。
-
Oracleエラー・メッセージの詳細は、『Oracle Databaseエラー・メッセージ』を参照してください。
この項には、次のトピックが含まれます: 「NULLおよび切捨てデータの処理について」。
NULLおよび切捨てデータの処理について
通常、OCCIでは、ResultSet
クラスまたはStatement
クラスのget
xxx
()
メソッドで取り出したデータ値がNULL
または切捨てデータの場合は例外が発生しません。ただし、この動作は、setErrorOnNull()
メソッドまたはsetErrorOnTruncate()
メソッドをコールして変更できます。setError
xxx
()
メソッドにcauseException=TRUE
を指定してコールすると、データ値がNULL
または切捨てデータの場合に、SQLException
が呼び出されます。
デフォルトの動作では、SQLException
の呼出しを行いません。列またはパラメータの値がNULL
であることもあります。これは、ResultSet
またはStatement
オブジェクトに対してisNull()
をコールするとTRUE
が返されることでわかります。
rs->isNull(columnIndex); stmt->isNull(paramIndex);
列またはパラメータの値は、切り捨てられている場合も、TRUE
となります。切り捨てられているかどうかは、ResultSet
またはStatement
オブジェクトに対してisTruncated()
をコールすることで確認できます。
rs->isTruncated(columnIndex); stmt->isTruncated(paramIndex);
setDataBuffer()
メソッドおよびsetDataBufferArray()
メソッドを使用して取り出されたデータの場合、例外処理の動作は、表3-1、表3-2および表3-3に示したインジケータ変数とリターン・コード変数の有無によって制御されます。
表3-1 通常データ: 非NULLまたは切捨てなし
リターン・コード | インジケータ - 戻さない場合 | インジケータ - 戻す場合 |
---|---|---|
戻さない場合 |
error = 0 |
error = 0 indicator = 0 |
戻す場合 |
error = 0 return code = 0 |
error = 0 indicator = 0 return code = 0 |
表3-2 NULLデータ
リターン・コード | インジケータ - 戻さない場合 | インジケータ - 戻す場合 |
---|---|---|
戻さない場合 |
SQLException error = 1405 |
error = 0 indicator = -1 |
戻す場合 |
SQLException error = 1405 return code = 1405 |
error = 0 indicator = -1 return code = 1405 |
表3-3 切捨てデータ
リターン・コード | インジケータ - 戻さない場合 | インジケータ - 戻す場合 |
---|---|---|
戻さない場合 |
SQLException error = 1406 |
SQLException error = 1406 indicator = data_len |
戻す場合 |
error = 24345 return code = 1405 |
error = 24345 indicator = data_len return code = 1406 |
表3-3では、data_len
は、長さがSB2MAXVAL
以下であった場合に切り捨てられたデータの実際の長さです。この値を超過する場合は、インジケータが-2
に設定されます。