ヘッダーをスキップ
Oracle® C++ Call Interfaceプログラマーズ・ガイド,
11g リリース2 (11.2)
B72452-01
  目次へ移動
目次
索引へ移動
索引

前
 
次
 

3 リレーショナル・プログラミング

この章では、リレーショナル・データベースに格納されたデータを操作するための、Oracle C++ Call Interface(OCCI)を使用したC++アプリケーション開発の基本を説明します。

ここでは、次の項目について説明します。

データベースへの接続

アプリケーションをデータベースに接続する方法には、複数の異なるオプションがあります。

環境の作成と終了

OCCIの処理はすべて、Environmentクラスの内部で行われます。OCCI環境では、アプリケーション・モードおよびユーザー指定のメモリー管理関数が提供されます。例3-1に、OCCI環境の作成方法を示しています。

例3-1 OCCI環境の作成方法

Environment *env = Environment::createEnvironment();

createxxx()メソッドで作成したOCCIオブジェクト(接続、接続プール、文)はすべて、明示的に終了する必要があります。必要に応じて、OCCI環境も明示的に終了する必要があります。例3-2に、OCCI環境の終了方法を示しています。

例3-2 OCCI環境の終了方法

Environment::terminateEnvironment(env);

また、OCCI環境の有効範囲は、その環境の内部で作成するオブジェクト型(AgentBytesDateMessageIntervalDSIntervalYMSubscriptionおよびTimestamp)より大きく設定しておく必要があります。例3-3に示すように、この規則は、BFileBlobおよびClobオブジェクトには適用されません。

例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);

アプリケーションがグローバル・スコープ内のオブジェクト(静的変数またはグローバル変数など)にアクセスする必要がある場合、環境を終了する前にこれらのオブジェクトをNULLに設定する必要があります。前述の例で、bがグローバル変数の場合、terminateEnvironment()コールの前にb.setNull()コールを実行する必要があります。

createEnvironmnt()メソッドのモード・パラメータを使用すると、アプリケーションのモードを次のように指定できます。

  • スレッド環境での実行(THREADED_MUTEXEDまたはTHREADED_UNMUTEXED

  • オブジェクトの使用(OBJECT

これらのモードは、環境ごとに設定できます。

接続のオープンとクローズ

Environmentクラスは、Connectionオブジェクトを作成するためのファクトリ・クラスです。最初にEnvironmentインスタンスを作成し、それを使用してユーザーがcreateConnection()メソッドでデータベースに接続できるようにします。

例3-4では、環境インスタンスを作成し、それを使用してデータベース・ユーザーHRと適切なパスワードのデータベース接続を作成しています。

例3-4 環境およびデータベースへの接続の作成方法

Environment *env = Environment::createEnvironment();
Connection *conn = env->createConnection("HR", "password");

次のコード例で示すように、操作セッションの終了時に、terminateConnection()メソッドを使用して、接続を明示的にクローズする必要があります。また、OCCI環境も明示的に終了する必要があります。

Connectionインスタンスの中で作成または命名されたすべてのオブジェクト(RefBfileProducerConsumerなど)は、そのインスタンスの内部スコープ内で使用する必要があります。これらのオブジェクトのスコープは、Connectionが終了する前に明示的に終了する必要があります。例3-5に、接続および環境の終了方法を示しています。

例3-5 データベースへの接続および環境の終了方法

env->terminateConnection(conn);
Environment::terminateEnvironment(env);

接続プーリング

この項では、OCCIの接続プーリング機能の使用方法を説明します。この項は、次の項目で構成されています。

これらの2つの主な違いは、StatelessConnectionPoolが状態に依存しないアプリケーションで使用されることです。このようなアプリケーションで事前に認証された接続を使用すると、アプリケーションのパフォーマンスが向上します。

接続プールの使用

通常、中間層アプリケーションでは、多数のスレッドがデータベースに接続できる必要があります。各スレッドの継続時間は比較的短いため、すべてのスレッドに対してデータベースへの接続をオープンすると、接続の使用率およびパフォーマンスが低下する場合があります。

接続プーリング機能を使用すると、多数のスレッドで使用できる小さな接続セットがアプリケーションで作成できます。これにより、データベース・リソースを非常に効率的に活用できます。

接続プールの作成

接続プールを作成するには、例3-6に示すように、createConnectionPool()メソッドを使用します。

例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-6では、次のパラメータが使用されています。

  • poolUserName: 接続プールの所有者。

  • poolPassword: 接続プールにアクセスするためのパスワード。

  • connectString: 接続プールが関連付けられたデータベース・サーバーを指定するデータベース名。

  • minConn: 接続プールの作成時にオープンする最小接続数。

  • maxConn: 接続プールで維持できる最大接続数。接続プールで最大接続数がオープンされていて、すべての接続がビジー状態の場合、接続を要求したOCCIメソッドのコールは、setErrorOnBusy()が接続プールに対してコールされていないかぎり、接続を取得するまで待機します。

  • incrConn: すべての接続がビジー状態のときに、コールによって接続が要求された場合にオープンする追加の接続数。この増分は、オープン接続数の合計が、その接続プールでオープンできる最大接続数より少ない場合にのみ行われます。

例3-7は、接続プールの作成方法を示しています。

例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);

これらの属性は、すべて動的に構成できます。これによって、現行のロード(オープン接続数およびビジー接続数)を読み込み、これらの属性を適切にチューニングできる柔軟なアプリケーションを設計できます。また、setTimeOut()メソッドを使用すると、指定した時間を超過してアイドル状態になっている接続をタイムアウトすることもできます。OCCIでは、最適なオープン接続数を維持するために、アイドル状態の接続を定期的に終了します。

1つの環境に作成できる接続プールは1つに制限されているわけではありません。1つのOCCI環境に複数の接続プールを作成し、同一または異なるデータベースに接続できます。これは、ロード・バランシングが必要なアプリケーションで役に立ちます。

プロキシ接続

接続プールのユーザーに、他の接続のプロキシとして機能することを許可すると、その接続プールの接続を使用するデータベース・ユーザーは、ログイン・パスワードが一切不要になります。

プロキシ接続は、例3-8で示す2つのバージョンのcreateProxyConnection()メソッドをいずれか使用して作成できます。

例3-8 createProxyConnection()メソッド

ConnectionPool->createProxyConnection( 
   const string &username,
   Connection::ProxyType proxyType = Connection::PROXY_DEFAULT);

または

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がデータベース・ユーザー名を表していて、現在サポートされている唯一のプロキシ認証モードであることを示すために使用します。

ステートレスな接続プーリング

ステートレスな接続プーリングは、接続時間が短く、状態を考慮しないアプリケーションで使用するために設計されています。ステートレスな接続プーリングの主な利点は、時間のかかる接続および認証プロトコルの省略により、パフォーマンスが向上することです。

ステートレスな接続プールでは、複数のスレッドで使用可能な、ステートレスな認証済のデータベース接続のグループを作成し、維持します。スレッドによる接続の使用が終了すると、接続はプールに解放されます。使用可能な接続がない場合、新しい接続が生成されます。したがって、プール内の接続数は動的に増加します。

プール内の一部の接続に、特定のプロパティのタグを付加できます。デフォルト接続を要求し、グローバリゼーション・サポート設定などの属性を設定し、タグを付加してプールに戻すことが可能です。同一の属性を持つ接続が必要な場合、同一のタグを持つ接続を要求し、同一のタグを持つ、プール内の接続の1つを再利用できます。接続のタグは変更または再設定できます。

ステートレスな接続プーリング・インタフェースを介して、プロキシ接続も作成および維持できます。

ステートレスな接続プーリングは、接続を多重化することにより、中間層アプリケーションのスケーラビリティを向上します。ただし、StatelessConnectionPoolの接続は、ロング・トランザクションには使用しないでください。接続を長時間保持すると、同時実行性が低下します。


注意:

  • OCCIでは、接続とタグのペアが正確であるかどうかは確認されません。同じタグが異なるクライアント側のプロパティで使用されていないかを確認する必要があります。

  • 接続をプールに解放して戻す前に、アプリケーションですべてのオープン・トランザクションをコミットまたはロールバックする必要があります。そうしないと、接続の解放時にすべてのオープン・トランザクションが自動的にコミットされます。


ステートレスな接続プールには、次の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 同種のステートレスな接続プールの作成および使用方法

同種のステートレスな接続プールを作成するには、次の基本手順および疑似コード・コマンドを実行します。

  1. createStatelessConnectionPool()コールを使用して、EnvironmentHOMOGENEOUSモードで、ステートレスな接続プールを作成します。

    StatelessConnectionPool *scp = 
          env->createStatelessConnectionPool(
             username, passwd, connectString, maxCon, minCon, incrCon,
             StatelessConnectionPool::HOMOGENEOUS );
    
  2. getConnection()メソッドをコールして、新規または既存の接続をプールから取得します。

    Connection *conn=scp->getConnection(tag);
    

    このコールの実行中に、一致するタグを持つ接続がプールで検索されます。一致する接続が存在する場合、その接続がユーザーに戻されます。存在しない場合は、プールのユーザー名およびパスワードによって認証された、タグを持たない接続が返されます。

    または、getAnyTaggedConnection()コールによって接続を取得できます。一致するタグまたはNULLタグの接続が存在しない場合、一致しないタグを持つ接続が戻されます。ConnectionへのgetTag()コールを使用して、戻されたタグを検証する必要があります。

    Connection *conn=scp->getAnyTaggedConnection(tag);
    string tag=conn->getTag();
    
  3. 接続を使用します。

  4. releaseConnection()をコールし、接続を解放してStatelessConnectionPoolに戻します。

    scp->releaseConnection(conn, tag);
    

    空のタグ""は、Connectionのタグを解除します。

    getConnection()コールで同一のtagパラメータ値を使用して、StatelessConnectionPoolから接続を取得するよう選択できます。

    Connection *conn=scp->getConnection(tag);
    

    ConnectionStatelessConnectionPoolに戻すかわりに、terminateConnection()コールを使用して破棄することもできます。

    scp->terminateConnection(conn);
    
  5. Environmentオブジェクトに対するterminateStatelessConnectionPool()コールにより、プールを破棄します。

    env->terminateStatelessConnectionPool(scp);
    

例3-11 異種のステートレスな接続プールの作成および使用方法

異種のステートレスな接続プールを作成するには、次の基本手順および疑似コード・コマンドを実行します。

  1. createStatelessConnectionPool()コールを使用して、EnvironmentHETEROGENEOUSモードで、ステートレスな接続プールを作成します。

    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));
    
  2. 異種プール・オプション用にオーバーロードされたStatelessConnectionPoolgetConnection()メソッドをコールして、新規または既存の接続をプールから取得します。

    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();
    
  3. 接続を使用します。

  4. releaseConnection()をコールし、接続を解放してStatelessConnectionPoolに戻します。

    scp->releaseConnection(conn, tag);
    

    空のタグ""は、Connectionのタグを解除します。

    getConnection()コールで同一のtagパラメータ値を使用して、StatelessConnectionPoolから接続を取得するよう選択できます。

    Connection *conn=scp->getConnection(tag);
    

    ConnectionStatelessConnectionPoolに戻すかわりに、terminateConnection()コールを使用して破棄することもできます。

    scp->terminateConnection(conn);
    
  5. Environmentオブジェクトに対するterminateStatelessConnectionPool()コールにより、プールを破棄します。

    env->terminateStatelessConnectionPool(scp);
    

データベース常駐接続プーリング

企業レベルのアプリケーションは通常、データベースへの永続的な接続として実装されている大量の同時ユーザー・セッションを処理する必要があります。こうした接続の作成および管理のメモリー・オーバーヘッドは、データベースのパフォーマンスに対して重大な意味を持ちます。

データベース常駐接続プーリングは、大規模なアプリケーション接続を処理するために専用サーバーのプールを提供することで、永続的な接続が多すぎる問題を解決しており、これによってデータベースは何万もの同時接続に対応できます。したがって、データベース層のメモリー・フットプリントが大幅に削減され、データベース層および中間層の両方のスケーラビリティが向上します。データベース常駐接続プーリングは、中間層の接続プーリングに対応できない複数プロセス・アプリケーション・サーバーおよび複数の中間層を持つアーキテクチャ用に設計されています。

データベース常駐接続プーリングのアーキテクチャは、Oracle Databaseインスタンスに接続するためのデフォルトの専用モデルに密接に従っていますが、特定のサーバーを各接続に割り当てるオーバーヘッドはなくなります。サーバー層では、大部分の接続が指定時刻に非アクティブとなり、これらの各接続でメモリーが消費されます。このため、大量の接続をサポートするデータベース・システムは、使用可能なすべてのメモリーをすぐに使い果たしてしまう恐れがあります。データベース常駐接続プーリングでは、接続で専用サーバーを使用でき、専用サーバーによってOracleサーバー・プロセスとユーザー・セッションが結合されます。接続が非アクティブになると、接続のリソースがプールに戻され、類似した接続で使用可能になります。

包括的な接続プーリングが可能であるマルチスレッド中間層では、未使用の接続の問題が多少異なります。中間層の数が増えると、各中間層はデータベースへの複数の接続を個別に保持します。つまり、これらの接続は他の中間層と共有できません。かわりに接続プールをデータベースに置くと、類似したクライアント間で接続を共有できます。

データベース常駐接続プーリングでは、パスワードベース認証、文キャッシュ、タグ付けおよび高速アプリケーション通知がサポートされています。また、クライアント側のステートレスな接続プーリングをデータベース常駐接続プーリングと組み合せて使用できます。

データベース常駐接続プールからの接続を保持するクライアントは、バックグラウンドの接続ブローカ・プロセスに永続的に接続している点に注意してください。接続ブローカはプール機能を実装しており、専用サーバー・プロセスのプールに対するインバウンド・クライアント接続を多重化します。接続プールを使用しないクライアントは、かわりに専用サーバー・プロセスを使用します。


関連項目

  • データベース常駐接続プーリングのアーキテクチャの詳細は、『Oracle Database概要』を参照してください。

  • データベース常駐接続プーリングの構成の詳細は、『Oracle Database管理者ガイド』を参照してください。

  • DBMS_CONNECTION_POOLパッケージの詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。


データベース常駐接続プールの管理

データベース常駐接続プーリングを実装するには、最初にSYSDBA権限を持つユーザーがシステム上で有効化する必要があります。データベース常駐接続プールの開始および保持に必要なステップは、例3-12を参照してください。

例3-12 データベース常駐接続プールの管理方法

SYSDBA権限を持つユーザーは次のステップを実行する必要があります。

  1. データベースに接続します。

    SQLPLUS / AS SYSDBA
    
  2. (オプション)データベース常駐接続プールのパラメータを構成します。プールのデフォルト値は次のように設定されます。

    DBMS_CONNECTION_POOL.CONFIGURE_POOL( 'SYS_DEFAULT_CONNECTION_POOL', 
                                          MIN=>10,
                                          MAX=>200);
    
  3. (オプション)データベース常駐接続プールの特定のパラメータを、他のパラメータに影響を与えずに変更します。

    DBMS_CONNECTION_POOL.ALTER_PARAM(  'SYS_DEFAULT_CONNECTION_POOL', 
                                       'INACTIVITY_TIMEOUT', 
                                        10);
    
  4. 接続プールを起動します。このステップの後、接続プールは適格なすべてのクライアントで使用可能になります。

    DBMS_CONNECTION_POOL.START_POOL( 'SYS_DEFAULT_CONNECTION_POOL');
    
  5. (オプション)データベース常駐接続プールのパラメータを変更します。

    DBMS_CONNECTION_POOL.ALTER_PARAM( 'SYS_DEFAULT_CONNECTION_POOL', 
                                      'MAXSIZE', 
                                       20);
    
  6. (オプション)接続プールの構成をデフォルト値にリセットできます。

    DBMS_CONNECTION_POOL.RESTORE_DEFAULTS ( 'SYS_DEFAULT_CONNECTION_POOL');
    
  7. プールを停止します。プール情報は永続的である点に注意してください。プールを停止しても、プール名および構成パラメータは破棄されません。

    DBMS_CONNECTION_POOL.STOP_POOL();
    

Oracle RAC構成では、データベース常駐接続プールはすべての構成ノードで起動する点に注意してください。プールが停止しない場合、起動構成はインスタンスの再起動間も永続的です。プールはインスタンスの起動時に自動的に起動します。

データベース常駐接続プールの使用

データベース常駐接続プーリングを使用するには、Connectionクラスおよび接続の純正値を指定する必要があります。前回の接続状態の痕跡が残された可能性のない接続をアプリケーションが要求した場合、純正値をNEWと指定する必要があります。異なる地理ロケール設定のクライアントが同じデータベース・インスタンスを共有する場合、この方法をお薦めします。アプリケーションで以前使用された接続を使用できる場合、純正値をSELFに設定する必要があります。Connectionクラスおよび純正値の指定と組み合せてアプリケーション固有タグを使用し、必要な状態である、以前使用済の接続を選択できます。例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オブジェクトを作成するには、例3-15に示すように、ConnectionオブジェクトのcreateStatement()メソッドをコールします。

例3-15 文の作成方法

Statement *stmt = conn->createStatement();

SQLコマンドを実行する文オブジェクトの作成

Statementオブジェクトの作成後、Statementオブジェクトのexecute()executeUpdate()executeArrayUpdate()またはexecuteQuery()メソッドをコールして、SQLコマンドを実行します。これらのメソッドは、次の目的に使用されます。

データベース表の作成

例3-16に、executeUpdate()メソッドを使用したデータベース表の作成方法を示しています。

例3-16 executeUpdate()メソッドを使用したデータベース表の作成方法

stmt->executeUpdate("CREATE TABLE shopping_basket
   (item_number VARCHAR2(30), quantity NUMBER(3))");

データベース表への値の挿入

同様に、executeUpdate()メソッドを呼び出して、例3-17で示すように、SQLのINSERT文を実行できます。

例3-17 executeUpdate()メソッドを使用したレコードの追加方法

stmt->executeUpdate("INSERT INTO shopping_basket
   VALUES('MANGO', 3)");

executeUpdate()メソッドは、SQL文によって影響を受けた行数を戻します。


関連項目

$ORACLE_HOME/rdbms/demoのコード例で、表の行に対する挿入、選択、更新および削除操作の実行方法の実例を参照してください。

文オブジェクトの再利用

Statementオブジェクトを再利用すると、SQL文を複数回実行できます。複数のパラメータで同じ文を繰り返し実行するには、例3-18に示すように、StatementオブジェクトのsetSQL()メソッドを使用して文を指定します。

例3-18 setSQL()メソッドを使用したSQL文の指定方法

stmt->setSQL("INSERT INTO shopping_basket VALUES(:1,:2)");

この指定によって、同じINSERT文を必要な回数だけ実行できます。後で別のSQL文を実行する場合は、例3-19に示すように、文オブジェクトを再設定します。

例3-19 setSQL()メソッドを使用したSQL文の再設定方法

stmt->setSQL("SELECT * FROM shopping_basket WHERE quantity >= :1");

setSQL()メソッドを使用することにより、OCCIの文オブジェクトとそれに関連付けられたリソースが、不必要に割り当てられたり解放されることがなくなります。現行の文オブジェクトの内容を任意の時点で取り出すには、getSQL()メソッドを使用します。

文オブジェクトの終了

例3-20に示すように、terminateStatement()メソッドを使用してStatementオブジェクトを明示的に終了し、割当てを解除する必要があります。

例3-20 terminateStatement()メソッドを使用した文の終了方法

Connection::conn->terminateStatement(Statement *stmt);

OCCI環境で使用するSQL文の種類

OCCI環境では、次の3種類のSQL文を使用します。

Statementクラスのメソッドは、すべての文、パラメータ化された文およびコール可能文の各文に適用可能なメソッドに分類されます。標準的な文はパラメータ化された文のスーパーセットで、パラメータ化された文はコール可能文のスーパーセットです。

標準的な文

例3-16および例3-17のどちらも、文の値を明示的に定義する必要がある標準的なを示しています。例3-16で、CREATE TABLE文では表の名前(shopping_basket)を指定しています。例3-17で、INSERT文では表に挿入する値('MANGO', 3)を定めています。

パラメータ化された文

文の入力変数にプレースホルダを設定すると、複数のパラメータを使用して同じ文を実行できます。これらの文は、ユーザーまたはプログラムからのパラメータ入力に対応できるため、パラメータ化された文と呼ばれます。

複数のパラメータでINSERT文を実行する場合は、例3-18に示すように、まずStatementオブジェクトのsetSQL()メソッドを使用して文を指定する必要があります。

次に、パラメータを指定するために、setxxx()をコールします。xxxは、パラメータの型を表します。例3-18で指定するように、文オブジェクトの値が"INSERT INTO shopping_basket VALUES(:1,:2)"である場合、例3-21のコードを使用し、setString()メソッドおよびsetInt()メソッドを呼び出して、これらの型の値を第1および第2パラメータに入力できます。さらにexecuteUpdate()メソッドを呼び出して新しい行を表に挿入できます。文オブジェクトを再利用するには、パラメータを再設定し、executeUpdate()メソッドをもう一度コールします。アプリケーションで同じ文を繰り返し実行している場合は、入力パラメータの型を変更すると、リバインド操作が開始されてアプリケーションのパフォーマンスに影響するため、入力パラメータの型の変更は避ける必要があります。

例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では、コール可能文は、他のSQL文を含むプロシージャのコールです。

指定した種類のフルーツの数を戻すプロシージャcountGroceries()をコールする場合、例3-22に示すように、まずStatementクラスのsetXXX()メソッドにより、PL/SQLストアド・プロシージャの入力パラメータを指定する必要があります。

例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に示すように、registerOutParam()メソッドをコールして、OUTパラメータの型とサイズを指定しておく必要があります。IN/OUTパラメータの場合、setXXX()メソッドを使用してパラメータを渡し、getXXX()メソッドを使用して結果を取得します。

例3-23 PL/SQLストアド・プロシージャのOUTパラメータの指定方法

stmt->registerOutParam(2, Type::OCCIINT, sizeof(quantity));
   // specify type and size of the second (OUT) parameter

次に、プロシージャをコールして文を実行します。

stmt->executeUpdate();           // call the procedure

最後に、適切なgetxxx()メソッドをコールして、出力パラメータを取得します。

quantity = stmt->getInt(2);     // get value 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ブロック内のコール可能文で使用できます。ストリームでサポートされているデータ型は、BLOBCLOBLONGLONG RAWRAWおよび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を参照してください。

  1. 適切なバインド・プレースホルダを使用して、SELECT/DMLまたはPL/SQL文を作成します。

  2. ストリーム・モードで使用する各バインドの位置に対し、StatementクラスsetBinaryStreamMode()またはsetCharacterStreamMode()メソッドをコールします。バインドの位置がPL/SQLのINまたはIN/OUT引数型の場合、このことを示すために、これらのメソッドの3引数バージョンをコールし、inArgパラメータをTRUEに設定します。

  3. 文を実行します。Statementクラスstatus()メソッドにより、NEEDS_STREAM_DATAが戻されます。

  4. StatementクラスgetStream()メソッドを使用してストリーム・オブジェクトを取得します。

  5. StreamクラスwriteBuffer()およびwriteLastBuffer()メソッドを使用してデータを書き込みます。

  6. StatementクラスcloseStream()メソッドを使用してストリームをクローズします。

  7. すべてのストリームがクローズされると、Statementクラスstatus()メソッドはUPDATE_COUNT_AVAILABLEなどの適切な値に変更されます。

例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, 100000, TRUE);

ストリーム・モードでのデータのフェッチ(PL/SQL)

ストリーム・モードでデータをフェッチするには、次の手順を実行し、例3-25を参照してください。

  1. 適切なバインド・プレースホルダを使用して、SELECT/DML文を作成します。

  2. ストリーム・モードから取得されたデータが入る各バインド位置に対し、StatementクラスsetBinaryStreamMode()またはsetCharacterStreamMode()メソッドをコールします。

  3. 文を実行します。Statementクラスstatus()メソッドにより、STREAM_DATA_AVAILABLEが戻されます。

  4. StatementクラスgetStream()メソッドを使用してストリーム・オブジェクトを取得します。

  5. StreamクラスreadBuffer()およびreadLastBuffer()メソッドを使用してデータを読み込みます。

  6. 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

ストリーム・モードでのデータのフェッチ(ResultSet)

「SQL問合せの実行」および例3-28で、ストリーム・インタフェースで結果セットを使用する方法を説明します。

複数のストリームの使用

複数の読込みおよび書込みストリームを使用する場合、別のストリームの読込みまたは書込みを実行する前に、その前のストリームの読込みまたは書込みが完了していることを確認する必要があります。ストリームの位置を判断するには、StatementクラスgetCurrentStreamParam()メソッド、またはResultSetクラスgetCurrentStreamColumn()メソッドを使用します。表13-45で説明しているように、Streamクラス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, 100000);
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()メソッドを使用して、反復ごとに異なる行を変更するバッチ操作を実行します。

INSERTUPDATEおよびDELETEの各操作を反復して実行するには、次の設定を行う必要があります。

  • 最大反復回数の設定

  • 可変長パラメータに対する最大パラメータ・サイズの設定

最大反復回数の設定

反復実行を行う場合は、最初にsetMaxIterations()メソッドをコールして、文に対して実行する最大反復回数を指定します。

Statement->setMaxIterations(int maxIterations);

現行の最大反復回数の設定を取得するには、getMaxIterations()メソッドをコールします。

最大パラメータ・サイズの設定

反復実行にstringBytesなどの可変長のデータ型が含まれている場合は、OCCIで最大バッファ・サイズの割当てができるように、次のように最大パラメータ・サイズを設定する必要があります。

Statement->setMaxParamSize(int parameterIndex, int maxParamSize);

NumberDate、あるいはsetDataBuffer()メソッドを使用するパラメータなど、固定長のデータ型については最大パラメータ・サイズを設定する必要はありません。

現行の最大パラメータ・サイズの設定を取得するには、getMaxParamSize()メソッドをコールします。

反復操作の実行

最大反復回数および(必要に応じて)最大パラメータ・サイズの設定後は、例3-27に示すように、パラメータ化された文を使用した反復実行を簡単に行うことができます。

反復実行は、標準的な文またはパラメータ化された文のいずれかによるINSERTUPDATEおよびDELETE操作にのみ使用できるように設計されています。コール可能文および問合せには使用できません。反復間でのデータ型の変更はできません。たとえば、パラメータ1setInt()を使用した場合、後続の反復の同じパラメータにはsetString()を使用できません。

例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

例に示したように、最後の反復を除く各反復の後にaddIteration()メソッドをコールし、その後executeUpdate()メソッドを呼び出します。2番目の行を挿入しない場合は、addIteration()メソッドや後続のsetxxx()メソッドのコールは必要ありません。

SQL問合せの実行

SQL問合せを使用すると、アプリケーションは指定した制約に基づいて、データベースから情報を要求できます。結果セットが問合せによって返されます。

結果セットの使用

データベース問合せを実行すると、問合せ結果は、結果セットと呼ばれる行セットに格納されます。OCCIでは、SQLのSELECT文は、StatementクラスのexecuteQueryメソッドによって実行されます。このメソッドは、問合せ結果を表すResultSetオブジェクトを戻します。

ResultSet *rs = stmt->executeQuery("SELECT * FROM basket_tab");

結果セットのデータを取得した後は、そのデータに対して操作を実行できます。たとえば、この表の内容を出力するとします。次のコード例に示すように、ResultSetnext()メソッドを使用してデータをフェッチし、getxxx()メソッドを使用して結果セットの個々の列を取り出します。

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文のいずれかであること

影響を受けた行数を取得するには、次の文を発行します。

Statement->getUpdateCount();

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クラスの読込み可能なストリームと同様に機能します。

トランザクションのコミット

SQLのDML文はすべて、トランザクションのコンテキスト内で実行されます。DML文によって行われたアプリケーションへの変更は、トランザクションのコミットによって永続的に変更されるか、ロールバックの実行によって取り消されます。SQLのCOMMIT文およびROLLBACK文は、executeUpdate()メソッドで実行できますが、Connection::commit()メソッドおよびConnection::rollback()メソッドをコールすることもできます。

DMLで行った変更を即時にコミットする場合は、次の文を発行して、Statementクラスの自動コミット・モードをオンにできます。

Statement::setAutoCommit(TRUE);

自動コミットを有効にした後は、変更を行うたびに自動的に永続的な変更となります。これは、各実行の直後にコミットを発行するのと同じです。

デフォルトのモードに戻るには、次の文を発行して自動コミットをオフにします。

Statement::setAutoCommit(FALSE);

文キャッシュ

文キャッシュ機能により、セッション内の文のキャッシュが確立および管理されます。サーバー側で準備したカーソルを効率的に使用し、文の解析の繰返しを省略することにより、アプリケーションのパフォーマンスおよびスケーラビリティを改善します。

文キャッシュは接続およびセッション・プーリングとともに使用でき、また接続プーリングなしでも使用できます。一般的な使用例は、例3-29および例3-30を参照してください。

例3-29 接続プーリングを使用しない文キャッシュ

次の手順および疑似コードで、接続プールを使用しない文キャッシュ機能の実装方法を説明します。

  1. Environmentオブジェクトに対してcreateConnection()コールを実行し、Connectionを作成します。

    Connection *conn = env->createConnection(
          username, password, connecstr);
    
  2. setStmtCacheSize()コールで0(ゼロ)以外のsizeパラメータを使用して、Connectionオブジェクトで文キャッシュを使用可能にします。

    conn->setStmtCacheSize(10);
    

    この後、getStmtCacheSize()をコールすることにより、キャッシュのサイズが決定されます。また、setStmtCacheSize()コールにより、文キャッシュのサイズが変更され、sizeパラメータを0(ゼロ)に設定すると文キャッシュが使用禁止になります。

  3. ConnectionオブジェクトへのcreateStatement()コールにより、Statementを作成します。キャッシュ内に存在する場合はStatementは戻され、そうでない場合はNULLタグを持つ新しいStatementが作成されます。

    Statement *stmt = conn->createStatement(sql);
    

    事前にキャッシュされたタグ付き文を取り出すには、createStatement()メソッドの代替形式を使用します。

    Statement *stmt = conn->createStatement(sql, tag);
    
  4. 文を使用してSQLコマンドを実行し、結果を取得します。

  5. 文をキャッシュに戻します。

    conn->terminateStatement(stmt, tag);
    

    この文をキャッシュしない場合、disableCaching()コールおよびterminateStatement()の代替形式を使用します。

    stmt->disableCaching();
    conn->terminateStatement(stmt);
    

    文がキャッシュされているかどうかを検証するには、Connectionオブジェクトに対してisCached()コールを発行します。

    解放時に文をタグ付けし、後で、同じタグを持つ別の文用に再利用することもできます。タグは、キャッシュを検索するために使用されます。タグ付けされていない文(つまりタグがNULLの文)は、タグ付けされている文の特別なケースです。2つの文のタグのみが異なり、一方のみがタグ付けされている場合は、これらの2つの文は異なるものとみなされます。

  6. 接続を終了します。

例3-30 接続プーリングを使用する文キャッシュ

次の手順および疑似コードで、接続プールを使用する文キャッシュ機能の実装方法を説明します。

文キャッシュは、setStmtCacheSize()コールの後に作成された接続でのみ使用できます。

文キャッシュは、プール・レベルで使用できない場合でも、プール内の個別の接続で実装できます。

  1. 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);
    
  2. setStmtCacheSize()コールで0(ゼロ)以外のsizeパラメータを使用して、ConnectionPool内のすべてのConnectionで文キャッシュを使用可能にします。

    conPool->setStmtCacheSize(10);
    

    この後、getStmtCacheSize()をコールすることにより、キャッシュのサイズが決定されます。また、setStmtCacheSize()コールにより、文キャッシュのサイズが変更され、sizeパラメータを0(ゼロ)に設定すると文キャッシュが使用禁止になります。

  3. ConnectionPoolオブジェクトへのcreateConnection()コールにより、プールからConnectionを取得します。キャッシュ内に存在する場合はStatementが戻され、そうでない場合はNULLタグを持つ新しいStatementが作成されます。

    Connection *conn = conPool->createConnection(username, password, connecstr);
    

    事前にキャッシュされたタグ付き文を取り出すには、createStatement()メソッドの代替形式を使用します。

    Statement *stmt = conn->createStatement(sql, tag);
    
  4. ConnectionオブジェクトへのcreateStatement()コールにより、Statementを作成します。キャッシュ内に存在する場合はStatementは戻され、そうでない場合はNULLタグを持つ新しいStatementが作成されます。

    Statement *stmt = conn->createStatement(sql);
    

    事前にキャッシュされたタグ付き文を取り出すには、createStatement()メソッドの代替形式を使用します。

    Statement *stmt = conn->createStatement(sql, tag);
    
  5. 文を使用してSQLコマンドを実行し、結果を取得します。

  6. 文をキャッシュに戻します。

    conn->terminateStatement(stmt, tag);
    

    この文をキャッシュしない場合、disableCaching()コールおよびterminateStatement()の代替形式を使用します。

    stmt->disableCaching();
    conn->terminateStatement(stmt);
    

    文がキャッシュされているかどうかを検証するには、Connectionオブジェクトに対してisCached()コールを発行します。

  7. 接続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例外も同様に処理されます。


関連項目


NULLおよび切捨てデータの処理

通常、OCCIでは、ResultSetクラスまたはStatementクラスのgetxxx()メソッドで取り出したデータ値がNULLまたは切捨てデータの場合は例外が発生しません。ただし、この動作は、setErrorOnNull()メソッドまたはsetErrorOnTruncate()メソッドをコールして変更できます。setErrorxxx()メソッドに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に設定されます。