BCBでSQLite3を簡単に利用する方法を模索。
探す
同じようなことを考えてた人のページを参考にします。
- SQLite DbExpress driver
- dbExpress用のドライバ。ダウンロードするには登録が必要です。
- A simple Delphi wrapper for SQLite 3
- Delphiで書かれたSQLite3のラッパー。
- Delphi SQLite Components
- Deplhi用コンポーネント。BCBで使えるか分かりません。
Simple Delphi Wrapperを使う
「Simple Delphi Wrapper」を使ってみることにする。
準備
- VCLアプリケーションのプロジェクトを新規作成。
- ダウンロードしたアーカイブからSQLite3.pasとSQLiteTable3.pasをコピーしてくる。
- プロジェクトにSQLite3.pasとSQLiteTable3.pasを追加する。
- sqlite3.dllを実行ファイルが作られるフォルダへコピー。
> implib sqlite3.lib .\Debug_Build\sqlite3.dll
- ソースのいずれかに(Project1.cppが分かりやすい)、sqlite3.libとのリンクを明示する。リンカのライブラリパスを設定するのも忘れずに。
#pragma link "sqlite3.lib"
- コンパイルしてエラーが出ないことを確認する。
- SQLiteTable3.hpp内からSQLite3.hppをインクルードする部分が「#include <SQLite3.hpp>」となっているので「#include "SQLite3.hpp"」と修正する。
ここでインポートライブラリ作成時のオプション指定がSQLite3を試すと異なっているのに気づいた方はスルドイ。BCCでコンパイルすると外部に出る関数名はアンダースコアが自動的に付いてしまう。WINAPIで装飾するとそれを防ぐことが出来るようだ。SQLite3を試すではSQLite3のヘッダを出来るだけ変更したくなかったので、インポートライブラリ側でアンダースコアを吸収している。
今回はDelphi側からのDLLの呼び出し定義でアンダースコア無しの関数名を要求しているために、インポートライブラリも標準のアンダースコア無し版を作成している。
メイン記述
というわけで作成したプログラムがコレ。基本的な検索・追加・更新・削除の動作を実装してます。
主な手順
- TSQLiteDatabaseオブジェクト生成。
- 検索の場合はGetTable()メソッドを使用し、結果をTSQLiteTableオブジェクトで受け取る。
- TSQLiteTableオブジェクトがある場合はdeleteで解放。
- TSQLiteDatabaseオブジェクトをdeleteで解放。
TSQLiteDatabaseクラス
プロパティ | 解説 |
---|---|
bool isTransactionOpen | トランザクション処理の途中の場合にtrue。 |
メソッド | 解説 |
TSQLiteTable *GetTable(const AnsiString SQL) | SQLに検索用SQL文を入れて呼び出す。検索の結果をTSQLiteTableオブジェクトを生成して返す。 |
void ExecSQL(const AnsiString SQL) | SQL文を実行する。 |
__int64 GetTableValue(const AnsiString SQL) | SQLによる検索結果の1行目1カラム目の値を整数値として返す。 |
AnsiString GetTableString(const AnsiString SQL) | SQLによる検索結果の1行目1カラム目の値を文字列として返す。 |
void UpdateBlob(const AnsiString SQL, Classes::TStream* BlobData) | SQL文の「?」の部分にBLOB型をバインドして実行する。 |
void BeginTransaction(void) | トランザクション開始。 |
void Commit(void) | コミットする。 |
void Rollback(void) | ロールバックする。 |
bool TableExists(AnsiString TableName) | 指定のテーブルが存在するか。存在する場合はtrue。 |
__int64 GetLastInsertRowID(void) | 一番最近に挿入した行番号を返す。 |
void SetTimeout(int Value) | タイムアウト時間(msec)を設定する。テーブルがロックされていた場合にSQLITE_BUSYを返すまでの待ち時間を設定する。 |
AnsiString version() | バージョン文字列を返す。 |
※__fastcall修飾子は省略してます。 |
TSQLiteTableクラス
プロパティ | 解説 |
---|---|
bool EOF | 現在の行が保有している行数を超えている場合にtrue。 |
bool BOF | 現在の行が先頭行の場合にtrue。 |
AnsiString Fields[unsigned I] | I番目のカラムのデータを文字列として返す。NULL値なら0文字の文字列を返す。 |
AnsiString FieldByName[AnsiString FieldName] | カラム名からデータを取得し、文字列として返す。NULL値なら0文字の文字列を返す。 |
int FieldIndex[AnsiString FieldName] | カラム名から何番目のカラムかを返す。 |
AnsiString Columns[int I] | I番目のカラムのカラム名を取得する。 |
unsigned ColCount | データのカラム数を返す。 |
unsigned RowCount | 保有しているデータの行数を返す。 |
unsigned Row | 現在の行を返す。 |
int Count | 保有しているデータの行数を返す。 |
int CountResult | 0番目のカラムの値をint型で返す。 |
メソッド | 解説 |
__int64 FieldAsInteger(unsigned I) | I番目のカラムのデータをInteger型として取得し__int64型で返す。 |
TMemoryStream* FieldAsBlob(unsigned I) | I番目のカラムのデータをBLOB型として取得しTMemoryStreamオブジェクトを生成・格納して返す。 |
AnsiString FieldAsBlobText(unsigned I) | I番目のカラムのデータをBLOB型として取得しAnsiString型で返す。 |
bool FieldIsNull(unsigned I) | I番目のカラムのデータがNULL値ならtrueを返す。 |
AnsiString FieldAsString(unsigned I) | I番目のカラムのデータをText型として取得しAnsiString型で返す。 |
double FieldAsDouble(unsigned I) | I番目のカラムのデータをNumeric型またはInteger型として取得しdouble型で返す。 |
bool Next(void) | 次の行へ進む。 |
bool Previous(void) | 前の行へ戻る。 |
※__fastcall修飾子は省略してます。 |
ソース
- 今回作成したプログラムのソース。
課題および注意点
integerフィールドからデータ取得時に、FieldAsInteger()メソッドを使ったのだが「Not an integer or numeric field」例外が発生してしまった。
これはTSQLiteTableのコンストラクタ内でフィールドの型を値化する処理において、型名を大文字でしかチェックしていないことに起因する。テーブル作成時に型名を小文字で'integer'などとしてしまうとSqlite3_ColumnDeclType()関数はそのまま小文字で返してしまう。比較は大文字の'INTEGER'としかしていないので、あまりものの'TEXT'と解釈されてしまう。
テーブル作成時に型名に小文字を使わないようにするか、この処理部分でUpperCase処理を行うようにして対処しなくてはならない。