ホーム > 使った

C++Builder : FireDACでSQLiteに接続する

概要

スタンドアロンのアプリケーションでも、少しデータの件数が多かったりデータ構造が複雑だったりすると、データをDBで管理したくなってくる。しかし、そのために――わざわざDBサーバまでインストールしたくない、とにかくアプリケーションから簡単に利用したい、他の言語やプラットフォームからもアクセスしたい――というようなときに便利なのがSQLiteである。

というわけで、ここではC++BuilderでFireDACを用いてSQLiteへ接続する流れを取り上げる。

環境

Windows7 Professional SP1
C++Builder XE6 Professional + DataBasePack
SQLite 3.8.8.2 win32 x86
DB Browser for SQLite 3.5.0

SQLiteとは

利点

  • フリーでSQL92にほぼ準拠
  • DBのデータが1ファイルのみでバイトオーダに依存しない(再配布が楽)
  • DLLも1ファイルのみ(サーバ管理が不要、ライブラリとして利用できる)
  • DBの編集ツールがCUI・GUIともに揃っている(メンテが楽)
  • 他言語やスマホなどからも利用できる(再利用が楽)

その上で

  • FireDACを用いると少ないコードでアプリケーションを作成できる(実装が楽)

欠点

  • ストアドプロシージャに非対応
  • 更新時にファイル全体がロックされるため頻繁にアクセスされるアプリケーションには不向き
  • 大規模アプリケーションには不向き

しかし相応の規模でさえなければ、もうDBはMariaDBとSQLiteでいいような...

SQLiteのDBを作成する

本家のダウンロードページから「Precompiled Binaries for Windows」をx86またはx64で入手する。ここでは sqlite-dll-win32-x86-3080802.zipをダウンロードして解凍しdllをD:\sqlite3.dllなどに配置する。

なお、DBを操作するシェルを使いたい場合などは同様に入手しておく。シェルからSQLを発行してもよいが、ここでは以下のDB Browser for SQLiteを使用する。というわけで、同ツールをダウンロードしてインストールする。

DB Browser for SQLiteを起動したら以下のようにDBとテーブルを作成する。まず[New Database]をクリックしてD:\sample.dbなどと指定。次いで表示されるTableの新規作成ダイアログを以下のように作成して[OK]ボタンをクリックする。(SQL文の併記される辺りが何気に便利。)

テーブル作成1

これで器ができたので、[Open Database]をクリックして[Browse Data]タブをクリックすればテーブルを参照できる。そこで[New Record]ボタンをクリックしてnameフィールドに値をセット後、[Write Changes]ボタンをクリックしてデータを保存する。ちなみにSQLiteで使用する文字コードはUTF8(BOMなし)となる。

テーブル作成2

これでDB側の準備は完了。もちろんC++Builderからデータエクスプローラを使ったり、アプリケーション中からSQL文を発行することも可能である。

FireDACでSQLiteに接続する

C++Builderを起動したら [ファイル]-[新規作成]-[VCLフォームアプリケーション]で、フォームに以下のコンポーネントを張り付ける。

FireDAC > TFDConnection (接続管理)
FireDAC Links > TFDPhysSQLiteDriverLink (DLLリンク)
FireDAC UI > TFDGUIxWaitCursor (待機カーソル)

上記3つは必須。後はアプリケーション次第だが、とりあえずここでは以下を使う。

FireDAC > TFDQuery (クエリベースのデータセット)
DataAccess > TDataSource (データセットとUIの紐づけ)
DataControls > TDBGrid (UI、ここではテーブル状のDBGrid)
DataControls > TDBMemo (UI、nameフィールド用)
Standard > TButton (ボタン)

コンポーネント群1

そして、各コンポーネントのプロパティ類を以下でセットする。ここでは触れないが、当然ながら実行時の指定も可能である。

FDConnection1

DriverNameプロパティにSQLiteを指定

LoginPromptプロパティをFalseに指定 (Trueだと接続時にユーザ名とパスワードを入力するダイアログが表示される)

コンポーネントを右クリックして[接続エディタ]を開いたらDatabaseパラメータに先ほど作成したsample.dbのパスを指定

FDConnection設定1

FDConnection設定2

FDPhysSQLiteDriverLink1

VendorLibプロパティにsqlite3.dllのパスを指定

FDPhysSQLiteDriverLink設定

FDGUIxWaitCursor1

特になし

FDQuery1

ConnectionプロパティにFDConnection1を指定
SQLプロパティに「SELECT * FROM customer」などと指定

FDQuery設定

DataSource1

DataSetプロパティにTFDQuery1を指定

DBGrid1

DataSourceプロパティにDataSource1を指定

DBMemo1

DataSourceプロパティにDataSource1を指定
DataFieldプロパティにnameフィールドを指定

DBMemo設定

Button1

ボタンをダブルクリックしてOnClickイベントハンドラに以下のコードを追加。ちなみにConnectedやActiveプロパティは設計時にも変更できるので、DBGridに特定のフィールドだけ表示させたいような場合に便利。

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    try
    {
        FDConnection1->Connected = true;
        FDQuery1->Active = true;
    }
    catch(Exception& e)
    {
        ShowMessage("Exception raised : " + e.Message);
    }
}

これで[F9]してアプリケーションを実行したらButton1をクリックするとDBに接続し、クエリの結果がDBGridとDBMemoに表示される。DBGridで2列目を選ぶとDBMemoの内容が対象レコードのnameフィールドの値に変更される。

実行1

レコードの追加と更新

とこれだけでは何なので、ついでにレコードの追加と更新までやってみる。

先に既存のコンポーネントで以下を変更する。

まず、TFDConnectionの接続エディタでLockingModeとSynchronousをNormalにする。(私の環境ではこれを変更せずパフォーマンス優先だと「Database is locked」でエラーになった。)

TFDConnection設定3

また、TFDQueryのCacheedUpdatesプロパティをTrueにする。(TrueにしないとApplyUpdates()が使えない。)

それからボタンを2つ追加して、それぞれのOnClickイベントハンドラに以下を記述する。

コンポーネント群2

void __fastcall TForm1::Button2Click(TObject *Sender)
{
    FDQuery1->Edit();
    FDQuery1->Append();  // レコードの追加
    FDQuery1->Post();  // 更新ログの登録
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button3Click(TObject *Sender)
{
    FDQuery1->Edit();
    FDConnection1->StartTransaction();  // トランザクション開始
    int err = FDQuery1->ApplyUpdates();  // 更新の適用
    if(err == 0)
    {
        FDQuery1->CommitUpdates();  // 更新ログのクリア
        FDConnection1->Commit()  // トランザクションの終了;  
    }
    else
    {
        FDConnection1->Rollback();  // トランザクションの取消
    }
}

リビルドして実行したら、Button1クリック後に、Button2でレコードを追加してDBTextに何か書いてButton3をクリックすると結果が保存される。または、任意のレコードのDBTextを書き換えてButton3をクリックしても結果が保存される。

実行2

その他、データセットに対する主な操作として、レコードの絞り込みはSQL文またはFilterプロパティ、レコードの削除はDelete()、変更の取消はCancelUpdates()、コピーはCloneCursor()などのメソッドでできる。また、First()、Next()、Last()などでデータセット内を移動し、各レコードのフィールド値にはFieldByName("フィールド名")->AsStringなどでアクセスできる。

なお、DBGrid上でSQLiteのTEXT型が(WIDEMEMO)となっているのは、TFDQueryを右クリックして[フィールドエディタ]で表示内容を制御したいフィールドを移動後、該当フィールドのオブジェクトインスペクタにあるOnGetTextイベントハンドラに以下のように記述すると、直接DBGridのセルに文字列を表示できる。逆にいうと、DBの文字列データ型がCHAR(M)でないと、文字列を直接表示できない。

void __fastcall TForm1::FDQuerynameGetText(TField *Sender, UnicodeString &Text, bool DisplayText)
{
    Text = Sender->AsString;
}

終わりに

正直なところRAD Studio(Delphi, C++Builder)からDBにアクセスする手段は数が多く、大昔のBDEとか、自前で持つDBMSであるInterbaseも含めて、もっと開発者に対してすっきりさせてほしいところではある。私は今までDBアクセスにはdbExpressを使ってきたが、dbExpressは単方向という最大の特徴が、良くも悪くもメリットでありデメリットでもあると感じていた。(高速かもしれないが、TClientDataSetやTDataSetProviderとセットで常にキャッシュを意識しているイメージ。)その点、シンプルさという意味でFireDACは悪くないと思う。

ちなみにSQLiteの本家からソースをダウンロードして.h、.c(これまた一つずつ)をプロジェクトに追加してコンパイルすれば、低レベルにSQLiteを操作することもできる。VC++やCygwin用のmakefileはあってもbcc用は見捨てられていることの多い昨今、オリジナルのソースコードをそのまま流用できるのは貴重だと思う。但し、FireDAC側の.hppと競合するので同時には使用できないし、コード量を激減できるので開発効率からいってもFireDACを使うべきだと思う。

参考文献