プライマリ・コンテンツに移動
Oracle® C++ Call Interfaceプログラマーズ・ガイド
12c リリース1 (12.1)
B72464-02
  目次へ移動
目次
索引へ移動
索引

前
 
次
 

3 C++によるOracle Databaseへのアクセス

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

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

データベースへの接続

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

環境の作成と終了

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

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

Environment *env = Environment::createEnvironment();

createxxx()メソッドで作成した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()コールを実行する必要があります。

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

  • スレッド環境での実行(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);

プラガブル・データベースのサポート

マルチテナント・アーキテクチャにより、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 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では、接続とタグのペアが正確であるかどうかは確認されません。同じタグが異なるクライアント側のプロパティで使用されていないかを確認する必要があります。

  • 接続をプールに解放して戻す前に、アプリケーションですべてのオープン・トランザクションをコミットまたはロールバックする必要があります。そうしない場合、接続が解放されるときに、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 同種のステートレスな接続プールの作成および使用方法

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

  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構成では、接続プールに常駐するデータベースはすべての構成済ノードで起動されることに注意してください。プールを停止しない場合、起動中の構成はインスタンスの再起動にわたって永続的となります。インスタンスが生成されると、プールは自動的に起動されます。

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

データベース常駐接続プーリングを使用する場合、接続クラスと接続純度を指定する必要があります。先行する接続状態により汚染されていてはならない接続をアプリケーションが要求する場合には、純度を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オブジェクトを作成するには、例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に示すように、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クラスの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に設定します。


    注意:

    setBinaryStreamMode()の場合、sizeパラメータは32KB (32,768バイト)に制限されています。

  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, 32768, 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 ClassgetCurrentStreamParam()メソッドまたはResultSet ClassgetCurrentStreamColumn()メソッドを使用します。表13-45に示すように、Stream Classstatus()メソッドは、ストリーム内に読取り可能なデータが存在する場合は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()メソッドを使用して、反復ごとに異なる行を変更するバッチ操作を実行します。

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()メソッドのコールは必要ありません。

例3-27のDML実行で、各反復によって影響を受けた行数を取得するには、setRowCountsOption()を使用して機能を有効にし、その後getDMLRowCounts()を使用して行数のベクターを戻します。影響を受けた行の合計数を取得するには、executeUpdate()の戻り値を使用するか、getUb8RowCount()をコールします。

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

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

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操作での増加した行数の使用

INSERTUPDATEおよび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 接続プーリングを使用しない文キャッシュ

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

  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-36 接続プーリングを使用する文キャッシュ

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

文キャッシュは、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に設定されます。