5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

DelphiでFireDACを使う(コード編)

Last updated at Posted at 2019-09-03

データベースプログラミングというと、コントロールをフォーム配置してさぁ簡単、という話が多いのですが、それでは済まないケースの方が多いです。そういうときに、コードをゴリゴリ書いていくにはどうしたらいいか、私の経験から備忘録的にまとめてみました。

ちなみに開発環境は、すっかりユーザも少なくなったDelphi、データベースコンポーネントはFireDACです。Delphiの商品構成の見直しでFireDACがEnterprise版以上に制限されるそうですが、ローカルアクセスについては従来通り使えるようです。

話を簡単にするために、接続先のデータベースはSQLiteとします。また、必要な例外処理などは入れていないので、必要に応じてtry~exceptブロックで挟みます。

uses節にFireDACのユニットを追加

これは、何でもいいので1個、フォームにFireDACのコントロールを配置すれば自動的に追加されます。数が多いですが、適当にコピペして他のユニットにも持って行けます。

uses
 FireDAC.Stan.Intf, FireDAC.Stan.Option, FireDAC.Stan.Error, FireDAC.UI.Intf,
 FireDAC.Phys.Intf, FireDAC.Stan.Def, FireDAC.Stan.Pool, FireDAC.Stan.Async,
 FireDAC.Phys, FireDAC.Stan.Param, FireDAC.DatS, FireDAC.DApt.Intf,
 FireDAC.DApt, FireDAC.Comp.Client, FireDAC.Comp.DataSet,
 FireDAC.Stan.ExprFuncs, FireDAC.Phys.SQLite, FireDAC.VCLUI.Wait,
 FireDAC.Comp.UI;

TFDConnectionコントロールの生成と破棄

TFDConnectionは、データベースを使うための最も基本的なコントロールです。これを動的に生成し、接続するSQLiteデータベースを指定し、使い終わったら破棄します。一連の流れは、こんな感じです。

var
 connection: TFDConnection;
 ……

begin
 ……
 connection := TFDConnection.Create(AOwner);
 connection.DriverName := 'SQLite';
 connection.Params.Clear;
 connection.Params.Add('Database=database.db');
 connection.Params.Add('DriverID=SQLite');
 connection.Open;
 ……(ここで各種のデータベース操作を行う)
 connection.Close;
 connection.Free;
 ……

直接SQL文を実行

結果を返さないSQL文は、TFDConnectionオブジェクトから直接発行できます。下記はDROP文ですが、INSERT文、ALTER文、CREATE文なども同様です。

connection.ExecSQL('DROP TABLE IF EXISTS [table];');

結果を1個だけ返すSQL文も、TFDConnectionオブジェクトから直接発行できます。

result := connection.ExecSQLScalar('SELECT max(id) FROM [table]');

クエリセットを使う

SELECT文を発行して結果をもらうなどの場合には、TFDQueryコントロールを使います。安全のために、TFDConnectionオブジェクトのConnectedプロパティを参照し、接続されている場合のみ実行しています。

var
 query: TFDQuery;

begin
 ……
 if connection.Connected then begin
   query := TFDQuery.Create(connection);
   query.Connection := connection;
   ……(ここで各種のデータベース操作を行う)
   qyery.Free;
 end;
 ……

レコードを参照する

SELECT文を発行すれば、結果セットを取得できます。
FetchOptions.RecordCountModeプロパティにcmTotalを指定すると、RecordCountプロパティの返す値が常に結果セットの全レコード数になります。SQL.Textプロパティに実行したいSQL文を入れておくのですが、コロン(:)で始めた部分(下記では:id)はパラメータidになり、あとで値を設定できます。値を設定するのは、ParamByNameメソッドです。
Openメソッドでクエリをオープン、FetchAllメソッドで結果セットを全部取得します。
あとは、RecordCountプロパティの数だけループを回します。次のレコードに移るのはNextメソッドです。現在のレコードからの値の取り出しは、FieldByNameメソッドです。
これが基本的な形でしょう。

……
 query.FetchOptions.RecordCountMode := cmTotal;
 query.SQL.Text := 'SELECT * FROM [table] WHERE [id] = :id';
 query.ParamByName('id').AsInteger := id;
 query.Open;
 query.FetchAll;
 for i := 1 to query.RecordCount do begin
   strvalue := query.FieldByName('name').AsString;
   intvalue := query.FieldByName('birth').AsInteger;
   ……
   query.Next;
 end;
 query.Close;
 ……

レコードを挿入、削除する

値を返さないクエリも、TFDQueryオブジェクトに対して実行できます。下記のように、複数のテーブルに登録する場合などは、TFDConnectionオブジェクトのStartTransactionメソッドでトランザクションを開始し、無事終われば(例外に捕捉されなければ)Commitメソッドを実行します。
例外が発生すれば、RollbackメソッドでSQL文の実行をなかったことにして、Exceptionオブジェクトからの情報をデバッグ出力に流します。

……
 connection.StartTransaction;
 try
   query.ExecSQL('INSERT INTO [table1] VALUES(1, 2, 3);');
   query.ExecSQL('INSERT INTO [table2] VALUES(4, 5, 6);');
   query.ExecSQL('INSERT INTO [table3] VALUES(7, 8, 9);');
   connection.Commit;
 except
   on E: Exception do begin
     connection.Rollback;
     OutputDebugString(PWideChar(E.ClassName+', '+E.Message));
   end;
 end;

ざっと見てきましたが、すごい基本的な形なので、データ数の多寡などはまったく気にしていません。データ量に応じてFetchOptionsプロパティの値をコントロールしたり、AffectedRowsプロパティの値を参照してINSERT文の実行結果を確かめるなど必要でしょう。

機会があれば、データベースコンポーネントの方もやってみたいと思います。

5
3
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?