この章では、推奨されるTTClassesの使用方法について簡単に説明します。内容は次のとおりです。
TTClassesは様々な方法で使用が可能ですが、次の一般的な方法は成功実績があり、様々なアプリケーションで簡単に適用できます。
リアルタイム・アプリケーションでは、最適なパフォーマンスを得るために、準備済のSQL文を使用する必要があります。アプリケーションで使用されるすべてのSQL文について、文ごとに個別のTTCmdオブジェクトを使用して、アプリケーションの開始時に準備するのが理想的です。ODBCでは(したがってC++クラスでも)、文が特定の接続にバインドされるので、アプリケーションで使用されるすべての文の完全なセットが、TimesTen Databaseとのあらゆる接続に関連付けられるということがよく起こります。
最適な性能を簡単に実現する方法は、TTConnectionから導出されるアプリケーション固有のクラスを開発することです。 たとえば、XYZという名前のアプリケーションに対して、TTConnectionから導出したXYZConnectionという名前のクラスを作成できます。 XYZConnectionクラスには、アプリケーションで使用できる準備済のSQL文を表すプライベートTTCmdメンバーが含まれます。さらに、XYZConnectionクラスでは、このプライベートTTCmdメンバーを使用して、アプリケーション固有のデータベース機能を実装できる新しいパブリック・メソッドを提供します。
例3-1 接続クラスの定義
次に、自分の機能をTTConnectionから継承するクラスの例を示します。
class XYZConnection : public TTConnection {
private:
TTCmd updateData;
TTCmd insertData;
TTCmd queryData;
public:
XYZConnection();
~XYZConnection();
virtual void Connect (const char* connStr,TTStatus&);
void updateUser (TTStatus&);
void addUser (char* nameP, TTStatus&);
void queryUser (const char* nameP, int* valueP,
TTStatus&);
};
この例では、XYZConnectionオブジェクトが、アプリケーション固有の3つの操作(addUser()、updateUser()およびqueryUser())の実行に使用できるTimesTenへの接続となっています。これらの操作はアプリケーション固有のものです(預金残高の格納など)。 この3つのメソッドの実装では、アプリケーションの特定の機能を実装するために、TTCmd内で提供されるupdateData()、insertData()およびqueryData()が使用される可能性があります。
アプリケーションで使用するSQL文を準備させるために、XYZConnectionクラスはTTConnection基本クラスによって提供されるConnect()メソッドをオーバーロードします。 XYZConnection::Connect()メソッドは、データベース接続を確立するために、基本クラスのConnect()メソッドをコールし、さらに、TTCmdオブジェクトごとにPrepare()メソッドもコールして、後で使用するSQL文を準備させます。
例3-2 Connect()メソッドの定義
次の例に、XYZConnection::Connect()メソッドを示します。
void
XYZConnection::Connect(const char* connStr, TTStatus&
stat)
{
TTStatus stat2;
try {
TTConnection::Connect(connStr, stat);
updateData.Prepare(this,
"update mydata v
"set foo = ? where bar = ?",
stat);
insertData.Prepare(this,
"insert into mydata "
"values(?,0)", stat);
queryData.Prepare(this,
"select i from mydata where name "
" = ?", stat);
Commit(stat);
}
catch (TTStatus st) {
cerr << "Error in XYZConnection::Connect: " << st
<< endl;
Rollback(stat2);
}
return;
}
このConnect()メソッドによって、XYZConnectionが完全に操作可能になります。 Connect()がコールされた後に、アプリケーション固有のメソッドが完全に機能します。
このアプリケーション設計の方法は、TTConnectionPoolクラスの設計で非常に有効です。アプリケーションでは、XYZConnection型のオブジェクトを多数作成し、それらをTTConnectionPoolに追加できます。 アプリケーションは、TTConnectionPool::ConnectAll()をコールすることで、コード内の単一行において、プール内のすべての接続をデータベースに接続させるとともに、すべてのSQL文を準備させることができます。
このアプリケーション設計の方法によって、アプリケーションのデータベース・コンポーネントをアプリケーションの残りの部分から分離できます。 つまり、XYZConnectionクラスのみにデータベース固有のコードが含まれます。
このタイプの設計例は、TTClassesに付属するサンプル・プログラムのいくつかに見られます。 最も簡単な例は、sample.cppです。
他の構成が可能なことに注意してください。アプリケーションで使用されるSQL文が、アプリケーション自体にハードコードされるのではなく、データベース内の表にリストされるよう、このスキームをさらに拡張したユーザーもいます。このようにすれば、アプリケーションの変更ではなくデータベースの変更によって、データベースの機能の変更を実装できます。
TTClassesにはロギング機能があり、その機能を使用することで、実行中のTTClassesプログラムの有効なデバッグ情報をアプリケーションで取得することができます。TTClassesのロギングは、プロセス単位で実行されます。特定のプロセスに対してロギングを有効にし、そのプロセスに対して1つの出力ログのストリームを作成できます。デフォルトでは、TTClassesロギングは無効になっています。
TTClassesでは、様々なレベルのロギング情報をサポートしています。 各ログ・レベルでの出力については、例3-4を参照してください。
ログ・レベルWARNは、TTClassesアプリケーションの開発時に非常に役立ちます。 また、このログ・レベルではデータベースの問合せ計画が生成されるため、本番アプリケーションにも適している場合があります。
冗長度の高いログ・レベル(INFOおよびDEBUG)では大量のログ・データが生成され、アプリケーションのパフォーマンスに悪影響を与えるため注意が必要です。これらのログ・レベルは、本番環境で使用しないことをお薦めします。
TTClassesロギングではstdoutまたはstderrのいずれかに出力することもできますが、TTClassesログ・ファイルに直接書き込むことをお薦めします。 例3-3に、ログ・レベルWARNのTTClassesログ情報を/tmp/ttclasses.log出力ファイルに書き込む方法を示します。
例3-3 TTClassesログ情報の出力
ofstream output;
output.open("/tmp/ttclasses.log");
TTGlobal::setLogStream(output);
TTGlobal::setLogLevel(TTLog::TTLOG_WARN);
TTClassesを初めて使用する場合は、少し時間をかけてTTClassesのロギング機能を試してみてください。 sample.cppプログラムを変更して、様々なログ・レベルを使用できます。 これにより、ログ・レベルがERRORのときに出力されるエラーを確認したり、ログ・レベルがINFOやDEBUGのときに大量に生成されるログの容量を確認することができます。
TTGlobalクラスを使用してロギングを行う方法の詳細は、「TTGlobal」を参照してください。
トランザクション・ログAPI(XLA)は、次のような操作を実行するアプリケーションの実装を可能にする機能のセットです。
TimesTenのローカル・データ・ストア内の指定された表に対する変更の監視
これらの変更についてのリアルタイムな通知の受信
XLAの目的の1つに、トリガーに対する高性能な非同期の代替手段を提供することがあります。
XLAの詳細は、『Oracle TimesTen In-Memory Database C開発者ガイド』のXLAおよびTimesTenのイベント管理に関する説明を参照してください。
TimesTenでは、TTClassesのXLAデモが次の場所で提供されています。
install_dir/quickstart/sample_code/ttclasses/xla
「TimesTen TTClassesデモについて」を参照してください。
ttclassesディレクトリのREADMEファイルには、(他のTTClassesのデモと同様に)TTClassesのXLAデモの構築および実行についての説明が含まれています。
XLAは、データベース内の特定の表への変更通知を返すとともに、それらのデータベース変更のトランザクション境界に関する情報も返します。 この項では、トランザクション境界を使用しない例および使用する例を使用して、トランザクション境界のみでの更新(XLAアプリケーションでの一般的な要件)を確認する方法を示します。
例3-4 TTClassesのXLAプログラム
次の例に、TTClassesのXLAプログラムの一般的なメイン・ループを示します。
TTXlaPersistConnection conn; // XLA connection
TTXlaTableList list(&conn); // tables being monitored
ttXlaUpdateDesc_t ** arry; // ptr to returned XLA recs
TTStatus stat;
int records_fetched;
// ...
loop {
// fetch the updates
conn.fetchUpdatesWait(&arry, MAX_RECS_TO_FETCH,
&records_fetched, ...);
// Interpret the updates
for(j=0;j < records_fetched;j++){
ttXlaUpdateDesc_t *p;
p = arry[j];
list.HandleChange(p, NULL);
} // end for each record fetched
// periodically call ackUpdates()
if (some condition is reached) {
conn.ackUpdates(stat) ;
}
} // loop
HandleChange()メソッド内では、レコードがinsertか、updateか、deleteかに応じて、HandleInsert()、HandleUpdate()、HandleDelete()のいずれかの適切なメソッドがコールされます。
XLAレコードが特定のトランザクションにおける最後のレコードかどうかを示すフラグにユーザーがアクセスできるのは、HandleChange()内です。
したがって、この例のループでは、トランザクション境界に関する情報によってconn.ackUpdates()をコールする時点を変更できるように、HandleChange()メソッドがその情報をループに渡す方法はありません。
1つのトランザクションにつきレコードが数個しかない一般的な状況では、これは問題になりません。 fetchUpdatesWait()メソッドで最大1000個のレコードを返すようXLAに要求しても、通常は数個のレコードが返されるだけです。XLAはできるだけ速くレコードを返し、データベース内で膨大な数のトランザクションが発生していても、通常はXLAレコードを一度に数個ずつ、短時間で取り出すことができます。XLAレコードを一度に数個ずつ取り出す場合は、通常、XLAによって、最後に返されるレコードが確実にトランザクション境界上のレコードになります。
つまり、XLAに1000個のレコードを要求して、XLAが15個しか返さない場合は、15番目のレコードがほぼ確実にトランザクションの終了に当たります。
XLAによって、次のいずれかが保証されます。
完了したトランザクション(おそらくXLAレコードの単一バッチにおける複数のトランザクション)でレコードのバッチが終了すること
または、レコードのバッチに部分トランザクションが含まれ、同じバッチ内に完了したトランザクションがないこと、およびトランザクション境界に達するまで、その単一トランザクションの後続のXLAレコードのバッチが返されること
入念なXLAアプリケーションでは、XLAレコードのバッチにおける最後のレコードにトランザクション境界があるかどうか検証し、このトランザクション境界のみでackUpdates()をコールする必要があります。これは、操作に多数の行が関連する場合に特に重要です。データベースに対して挿入/更新/削除のバルク操作を実行した場合、XLAアプリケーションが1000個のレコードを要求すると、1000個のレコードすべて(または1000個未満)を受信する可能性があります。 XLAを介して最後に返されるレコードには、おそらくトランザクション終了フラグが設定されていません。実際、トランザクションによって10,000個のレコードが変更された場合、トランザクション境界に達するまでに1000個のXLAレコードのブロックを最低でも10個フェッチする必要があります。
ackUpdates()は相対的に負荷の高い操作であるため、トランザクション境界ごとにackUpdates()をコールすることはお薦めしません。入念なXLAアプリケーションでは、アプリケーション、システムまたはデータベースに障害が発生した場合、システムのリカバリ後にXLAブックマークがトランザクションの先頭に置かれるよう、確実にトランザクション境界のみでこのメソッドをコールする必要があります。
HandleChange()メソッドには、HandleChange()とメインXLAループとの間で情報の受渡しを可能にする第2のパラメータがあります。 例3-4と例3-5を比較してください(do_acknowledge設定およびHandleChange()コールの&do_acknowledgeパラメータ)。
例3-5 トランザクション境界を使用するTTClassesのXLAプログラム
この例では、ackUpdates()は、このXLAレコードのバッチがトランザクション境界にあることがdo_acknowledgeフラグによって示された場合にのみコールされます。
TTXlaPersistConnection conn; // XLA connection
TTXlaTableList list(&conn); // tables being monitored
ttXlaUpdateDesc_t ** arry; // ptr to returned XLA recs
TTStatus stat;
int records_fetched;
int do_acknowledge;
// ...
loop {
// fetch the updates
conn.fetchUpdatesWait(&arry, MAX_RECS_TO_FETCH,
&records_fetched, ...);
do_acknowledge = FALSE;
// Interpret the updates
for(j=0;j < records_fetched;j++){
ttXlaUpdateDesc_t *p;
p = arry[j];
list.HandleChange(p, &do_acknowledge);
} // end for each record fetched
// periodically call ackUpdates()
if (do_acknowledge == TRUE
/* and some other conditions ... */ ) {
conn.ackUpdates(stat) ;
}
} // loop
XLAメイン・ループのこの変更に加え、ttXlaUpdateDesc_tを使用するようにHandleChange()メソッドを上書きする必要があります。