Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2017年01月21日] [更新 2017年02月25日] Delphi サンプルプログラム集
564_Delphi 10.1 Berlin Starter で JET データベース
動作確認等 Windows 7 U64(SP2) + Delphi 10.1 Berlin Starter
zeoslib-code-0-3961-branches-testing-7.2
564_ZeosLib_DelphiBerlin.zip [15,731 KB]  2017年02月25日版 (EXE 同梱)







ZeosLib を使用して Delphi 10.1 Berlin Starter でデータベース接続


2016年09月27日、Delphi 10.1 Berlin の Starter Edition が無料で入手可能になりました。
Delphi の Starter エディションにはデータベースの機能は実装されていませんが、ZeosLib というコンポーネントをインストールするとデータペースに接続できるようになります。

昔の Delphi には BDE というコンポーネントが実装されていました。ZeosLib は BDE と同じように各種のデータベースへの接続の仲介をするソフトウェアです。このようなソフトウェアはミドルウェアと呼ばれています。
データベースには、そのデータベース専用のミドルウェアが用意されているのが一般的です。ZeosLib は汎用的なミドルウェアということになります。

この ZeosLib を無料の Delphi 10.1 Berlin Starter にインストールして、簡単なデータベースの接続テストをしてみました。ただし、主に JET エンジンデータベースの接続だけのテストです。



[備考 1]
インストールの記事の画像は Windows 10 のものです。

[備考 2]
Delphi 10.1 Berlin Starter を入手して、データベースや SQL の学習目的等も含めて、新規にデータベースを構築する場合、バージョン依存等の制限がある JET データベースエンジンを使用する必然性は全くありません。





01_Delphi 10.1 Berlin Starter で使用可能な ZeosLib を入手


以下のウェブページにリンクがあります。そのリンク先のページの記事の中に、Sourceforge のダウンロードページへのリンクがあります。


図1
関係ページへのリンク

今回は Sourceforge の以下のページから [Download Snapshot] をクリックしてダウンロードしたファイルを使用しました。ダウンロードしたのは[zeoslib-code-0-3951-branches-testing-7.2.zip] という名前のファイルです (構成は変わっている可能性があります)。


図2
ZeosLib のダウンロード

以下は ZeosLib の Portal サイトと各バージョンのダウンロードのリンクです。


[備考 1]
ZeosLib は、いくつかのディストリビューション (配布ファイル) が存在します。
ディストリビューションによっては、Delphi のバージョンよってインストールできなかったり、インストールの手順やライブラリのパスの設定も異なる場合があります。

[備考 2]
ダウンロードして解凍展開したファイルを、システム関係のフォルダや Program Files 等の階層下に置かないようにします。それらのフォルダの階層下にあるパッケージやコンポーネントをインストールしようとすると、アクセス権の関係で失敗する場合があります。インストールに失敗しても、必ずしもエラーが発生するとは限りません。

[備考 3]
ダウンロードしたファイルの [documentation] フォルダ内にあるファイルは、doxygen というツールを使用して、ソースコードからドキュメントを自動生成するための設定ファイル類です。作成手順が doxygen_readme.txt に書いてあります。
古い ZeosLib のものでよければ、生成済みのファイルが以下から入手できます。doxygen があるファイルをダウンロードします。解凍した index.html をブラウザで表示します。

[備考 4]
Delphi 7 等、古いバージョンの Delphi には、今回ダウンロードしたファイルではインストールができないようです。以下のファイルを直接ダウンロードしてください。ダウンロードした古いバージョンの ZEOSLIB 7.1.2 であれば、ZeosDbo.bpg を開いて、次項の説明と同じ手順で Delphi 7 にインストールできることを確認しています。 現在一般的に普及しているデータベースは、テキスト、文字列をユニコードで扱います。Delphi 7 は非ユニコード版です。そのことを意識しておく必要があります。
当サイトの掲示板である喫茶XRAYに、Delphi 7, Delphi 2005, Delphi 2007 へのインストールとその結果の記事があります。参考にしてください。




02_ZeosLib をインストール


ダウンロードしたら、適当なフォルダに解凍します。
インストールは、パッケージ形式で提供されている通常のコンポーネントと同じです。下図で示すように、以下の手順で行います。
  • (1) [packages] [DelphiXE10.1] フォルダ内の [ProjectGroup24.groupproj] を IDE で開く
  • (2) 一番上のパッケージ以下の全てのパッケージをビルドする
  • (3) [ZComponentDesign240.bpl] をインストールする
  • (4) [ZComponentDesign240.bpl] を上書き保存して IDE を閉じる
インストールされたコンポーネント類は、コンポーネントパレットに [Zeos Access] というタブが追加されてそこに登録されます。


図3
パッケージグループを IDE で開く
  • この図ではドラッグ & ドロップでパッケージグループのファイルを開こうとしている
図4
全てのパッケージをビルド
  • 一番上のパッケージを選択して、メニューの [ここから下を全てビルド] で実行可能
図5
ZComponentDesign240.bpl をインストールして上書き保存する
図6
登録コンポーネント類
  • [Zeos Access] タブに登録される

インストールが終了すると、下図のダイアログが現れます。
ダイアログを閉じたら、ZComponentDesign240.bpl を上書き保存して IDE を閉じます。インストールはこれで終了です。上書き保存を忘れると、IDE を閉じる時に確認のダイアログが現れます。



図7
インストールされたコンポーネント群





03_ソースコードのフォルダにライブラリのパスを通す


インストールが終了したら、[src] フォルダとその階層下の全てのフォルダに対して、ライブラリのパスを通しておきます。
フォルダの参照ダイアログを表示しなくても、[src] までは同じなので、パスの入力欄の値を変更して追加できます。フォルダの参照ダイアログを表示してフォルダを選択する場合は、入力欄の末尾を src にしてから表示すると [src] フォルダを表示してくれます。


図8
ライブラリのパスの設定




04_ZeosLib のアンインストール


インストールした ZeosLib のパッケージをアンインストールするには、
  • (1) [packages] [DelphiXE10.1] フォルダ内の [ProjectGroup24.groupproj] を IDE で開く
  • (2) [ZComponentDesign240.bpl] を [アンインストール] する
  • (3) 一番上のパッケージ以下の全てのパッケージをクリーンアップする

という手順で行います。操作はメニューから行えます。上の説明図を参考にしてください。
ZComponentDesign240.bpl (ファイル名は ZComponentDesign.dpk) をアンインストールしただけだとコンパイル済みパッケージのファイルがディスク上に残ったままとなります。

アンインストールしても、コンポーネントパレットの [Zeos Access] は削除されません。
[ツール] [オプション] ダイアログの [コンポーネントツールバー] で表示される [ページ] で選択して削除します。
ライブラリのパスの設定も削除しておきます。

このアンインストールの手順は、パッケージ形式で提供されているコンポーネントのアンインストールの一般的な手順です。Delphi 7 等にはこのアンインストールの項目はありません。





05_ZeosLib で JET エンジンデータベースに接続 ( TZTable )


データベースの機能が実装されていない Starter エディションでも、ZeosLib をインストールすると ADO を介したデータベースの接続ができます。これ以降はそのテストです。

本サンプルは、Micorsoft Access 98 / 2000 が使用している JET エンジンデータベースに接続します。接続用のプロバイダとして Microsoft.Jet.OLEDB.4.0 を指定します。サンプルでは、TZTable を使用して添付のデータベースの Testtbl という名前の表のデータを表示します。

TZTable はデータベースの表のデータを全て表示します。したがって、レコード数が少ない表のデータの表示に利用することになります。


[備考]
TZConnection.Database はプロバイダの接続文字列です。接続文字列は、当サイトの ADO 関係のサシンプルとほとんど同じです。関係する記事のコードも参考にしてください。ZeosLib で ADO の機能を利用するには、TZConnection1.Protocol の値を ado にします。



図9
設計時画面
  • TDBGrid は
    [Data Controls] に、
    TDataSource は
    [Data Access] にある
図10
実行画面
  • 添付の TestMDB.mdb の表のデータ

リスト1
TTable を使用して JET エンジンデータベースに接続
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Data.DB,
  Vcl.Grids, Vcl.DBGrids, Vcl.StdCtrls, ZAbstractRODataset, ZAbstractDataset,
  ZAbstractTable, ZDataset, ZAbstractConnection, ZConnection;

type
  TForm1 = class(TForm)
    DBGrid1: TDBGrid;
    Button1: TButton;
    DataSource1: TDataSource;
    ZConnection1: TZConnection;
    ZTable1: TZTable;
    procedure Button1Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  //TDataSource,TDBGridの設定
  DataSource1.DataSet := ZTable1;
  DBGrid1.DataSource  := DataSource1;
end;

//=============================================================================
//  フォームを閉じる時の処理
//=============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  ZTable1.Active := False;
  ZConnection1.Disconnect;
end;

//=============================================================================
//  ZeosLibを使用してJETデータベースに接続
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LConnectStr : string;
begin
  //念のため接続を切断してから設定
  ZConnection1.Disconnect;
  ZConnection1.LoginPrompt := False;

  //接続文字列の設定
  LConnectStr := 'Provider=Microsoft.Jet.OLEDB.4.0'
               + ';Data Source=' + ExpandFileName('..\TestMDB.mdb')
               + ';Persist Security Info=False';

  ZConnection1.Database := LConnectStr;
  ZConnection1.Protocol := 'ado';
  //データベースへ接続
  ZConnection1.Connect;

  //TZTableの設定
  ZTable1.Connection := ZConnection1;
  ZTable1.TableName  := 'Testtbl';
  //テーブルをアクティブにする
  ZTable1.Active     := True;
end;

end.




06_ZeosLib で JET エンジンデータベースに接続 ( TZQuery )


上と同じデータベースの表のレコードを、TZQuery を使用して TDBGrid に表示するサンプルです。
接続文字列は上のサンプルと全く同じです。
データベースから、特定の条件に一致するデータ (レコード) を取得するにはクエリを使用します。検索条件は SQL 文で設定します。一般的に、クライアント (端末ユーザ) が操作するデータは、SQL 文を使用したクエリの結果を使用します。


図11
設計時画面
図12
実行画面
  • 添付の TestMDB.mdb データベースの表データ

リスト2
TZQuery を使用して JET エンジンデータベースに接続
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Data.DB,
  Vcl.Grids, Vcl.DBGrids, Vcl.StdCtrls, ZAbstractRODataset, ZAbstractDataset,
  ZAbstractTable, ZDataset, ZAbstractConnection, ZConnection;

type
  TForm1 = class(TForm)
    DBGrid1: TDBGrid;
    Button1: TButton;
    DataSource1: TDataSource;
    ZConnection1: TZConnection;
    ZQuery1: TZQuery;
    procedure Button1Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  //TDataSource,TDBGridの設定
  DataSource1.DataSet := ZQuery1;
  DBGrid1.DataSource  := DataSource1;
end;

//=============================================================================
//  フォームを閉じる時の処理
//=============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  ZQuery1.Close;
  ZConnection1.Disconnect;
end;

//=============================================================================
//  ZeosLibを使用してJETデータベースに接続
//  Micorosoft Access 98 が使用しているJETエンジンが対象
//
//  JETエンジンのデータベースは1つのファイルに複数の表を格納する
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LConnectStr : string;
begin
  //念のため接続を切断してから設定
  ZQuery1.Close;
  ZConnection1.Disconnect;
  ZConnection1.LoginPrompt := False;

  //接続文字列の設定
  LConnectStr := 'Provider=Microsoft.Jet.OLEDB.4.0'
               + ';Data Source=' + ExpandFileName('..\TestMDB.mdb')
               + ';Persist Security Info=False';

  ZConnection1.Database := LConnectStr;
  ZConnection1.Protocol := 'ado';
  //データベースへ接続
  ZConnection1.Connect;

  //TZQueryの設定
  ZQuery1.Connection := ZConnection1;
  ZQuery1.Close;
  ZQuery1.SQL.Clear;
  ZQuery1.SQL.Add('select * from Testtbl');

  //クエリの実行
  ZQuery1.Open;
end;

end.




07_ZeosLib で Microsoft Acces 2007 / 2010 用のJET に接続


上のサンプルコードでは、Microsoft Access 2007 / 2010 用のデータベースファイルは扱えません。
それらを扱うには、プロバイダの設定を本サンプルのように変更します。

Microsoft Office 2010 がインストールされている環境であれば、本サンプルは動作しますが、それ以外の環境で使用する場合は、以下の最初のリンクのページの [ダウンロード] からファイルを入手してインストールしてください。
[ダウンロード] のページには 2 つのファイルあります。X64 がない AccessDatabaseEngine.exe をダウンロードします。X64 が付いているファイルは、64 ビットのアプリ用です。Delphi 10.1 Berlin Starter では 64 ビットのアプリは作成できません。
Microsoft Office 2013 以降の環境については確認していません。



[備考]
プロバイダの設定が Microsoft.ACE.OLEDB.12.0 では、Microsoft Access 98 /2000 の JET データベースにも接続できます。


図13
設計時画面
図14
実行画面
  • 添付の TEST2007.mdb データベースの表データ

リスト3
ZeosLib で Microsoft Acces 2007 / 2010 用のJET に接続
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Data.DB,
  Vcl.Grids, Vcl.DBGrids, Vcl.StdCtrls, ZAbstractRODataset, ZAbstractDataset,
  ZAbstractTable, ZDataset, ZAbstractConnection, ZConnection;

type
  TForm1 = class(TForm)
    DBGrid1: TDBGrid;
    Button1: TButton;
    DataSource1: TDataSource;
    ZConnection1: TZConnection;
    ZQuery1: TZQuery;
    procedure Button1Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  //TDataSource,TDBGridの設定
  DataSource1.DataSet := ZQuery1;
  DBGrid1.DataSource  := DataSource1;
end;

//=============================================================================
//  フォームを閉じる時の処理
//=============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  ZQuery1.Close;
  ZConnection1.Disconnect;
end;

//=============================================================================
//  ZeosLibを使用してJETデータベースに接続
//  Micorosoft Access 2007/2010 が使用しているJETエンジンが対象
//
//  JETエンジンのデータベースは1つのファイルに複数の表を格納する
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LConnectStr : string;
begin
  //念のため接続を切断してから設定
  ZQuery1.Close;
  ZConnection1.Disconnect;

  //接続文字列の設定
  //パスワードの設定あり
  LConnectStr := 'Provider=Microsoft.ACE.OLEDB.12.0'
               + ';Jet OLEDB:Database Password=123'
               + ';Data Source=' + ExpandFileName('..\TEST2007.mdb')
               + ';Persist Security Info=False';

  ZConnection1.Database := LConnectStr;
  ZConnection1.Protocol := 'ado';
  //データベースへ接続
  ZConnection1.Connect;

  //TZQueryの設定
  //日本語の表名は[]で括る
  //日本語の項目名は[]または"(ダブルクォーテーション)で括る
  ZQuery1.Connection := ZConnection1;
  ZQuery1.Close;
  ZQuery1.SQL.Clear;
  ZQuery1.SQL.Add('select * from [食品カロリ表] order by [番号]');

  //クエリを開く
  ZQuery1.Open;
end;

end.




08_ZeosLib で Paradox データベースに接続


Paradox のデータベースに接続する例です。
初期の Delphi には Paradox のデータが添付されていて、テストには重宝しました。新規に Paradox のデータベースを扱うことはないと思いますが。ZeosLib をインストールすれば接続が可能であることを確認してみました。

実行時に「照合順序が正しくありません」」のエラーが発生した場合は、以下の記事にしたがって、レジストリの値を変更してくださしい。
本サシンプルは、Windows Vista 以降の 32 ビットの Windows では、管理者権限で実行しないと例外が発生して動作しないことがあるようです。原因については不明です。
また、全ての Paradox のデータベースの表が扱える保証はありません。早期に Paradox から他のデータベースに移行すべきでしょう。



図15
設計時画面
図16
実行画面
  • 添付の Paradox データベースの表データ

本サンプルでは TZConnection.UseMetadata の値を False にしています。
UseMetadata のデフォルト値は True です。True のままだと Windows 10 で実行した場合、正常にデータが取得できません。下図のようになってしまいます。



図17
UseMetadata の値をデフォルトの True で実行した場合

リスト4
ZeosLib で Paradox データベースに接続
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, DB, ZAbstractRODataset, ZAbstractDataset, ZDataset,
  ZAbstractConnection, ZConnection, StdCtrls, Grids, DBGrids;

type
  TForm1 = class(TForm)
    DBGrid1: TDBGrid;
    Button1: TButton;
    DataSource1: TDataSource;
    ZConnection1: TZConnection;
    ZQuery1: TZQuery;
    OpenDialog1: TOpenDialog;
    procedure Button1Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  ZCompatibility;

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  //TDataSource,TDBGridの設定
  DataSource1.DataSet := ZQuery1;
  DBGrid1.DataSource := DataSource1;
end;

//=============================================================================
//  フォームを閉じる時の処理
//=============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  ZQuery1.Close;
  ZConnection1.Disconnect;
end;

//=============================================================================
//  ZeosLibを使用してParadoxデータベースに接続
//
//  Paradoxはフォルダがデータベース名
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LPdxDBName    : string;
  LPdxTableName : string;
  LConnectStr   : string;
begin
  if not OpenDialog1.Execute then exit;


  OpenDialog1.InitialDir := ExtractFileDir(OpenDialog1.FileName);
  LPdxDBName    := OpenDialog1.InitialDir;
  LPdxTableName := ChangeFileExt(ExtractFileName(OpenDialog1.FileName), '');

  //接続を切断してから設定
  ZQuery1.Close;
  ZConnection1.Disconnect;
  ZConnection1.LoginPrompt := False;

  //接続文字列の設定
  LConnectStr := 'Provider=Microsoft.Jet.OLEDB.4.0'
               + ';Data Source=' + LPdxDBName
               + ';Extended Properties=Paradox 5.x'
               + ';Persist Security Info=False';

  ZConnection1.Database := LConnectStr;
  ZConnection1.Protocol := 'ado';

  //UseMetadataをFalseにしないとWindows10で正常にデータを取得できない
  ZConnection1.UseMetadata := False;

  ZConnection1.Properties.Clear;
  ZConnection1.Properties.Add('AutoEncodeStrings=ON');
  ZConnection1.Properties.Add('controls_cp=CP_UTF16');

  //データベースへ接続
  ZConnection1.Connect;

  //TZQueryの設定
  ZQuery1.Connection := ZConnection1;
  ZQuery1.Close;
  ZQuery1.SQL.Clear;
  ZQuery1.SQL.Add('select * from ' + LPdxTableName);

  //クエリを開く
  ZQuery1.Open;
end;

end.


上のコードのように、ado の機能を使用して Paradox のデータベースに接続する場合、BLOB 型のデータは読み込んでくれないようです。エラーは発生しませんが、画像等の BLOB 型のフィールドを無視してしまうようです。

もし、ZeosLib を使用して SQLite 等、他のデータベースに移行するのであれば、Paradox の BLOB 型のデータは以下のようにして代入できます。このコードは、Paradox のデータベースを BDE 経由で接続しているものと仮定しています。


  ZQuery1.ParamByName('Val').AsBlob := TBlobField(Table1.FieldByName('Graphic')).Value;


メモリストリームを使用する場合は、次のようにしても代入できます。


var
  LStream : TMemoryStream;
begin
  LStream := TMemoryStream.Create;
  try
    ...

    TBlobField(Table1.FieldByName('Graphic')).SaveToStream(LStream);

    LStream.Position := 0;
    ZQuery1.ParamByName('Val').LoadFromStream(LStream, ftBlob);
    ...
  finally
    FreeAndNil(LStream);
  end;
  ...
end;




09_ZeosLib におけるトランザクション処理


トランザクションを使用すると、複数の INSERT や UPDATE の操作による更新クエリの結果をまとめて更新することができます。また、POST メソッドでの実行結果や TDBGrid での編集の結果等も元に戻すことができます。トランザクションの機能は、データベースの実務には欠かせない技術です。

ZeosLib でトランザクションの機能を使用するには、TZConnection のトランザクション分離レベルの値を READ COMMITTED にします。
本サンプルは、この動作確認用のテストです。トランザクションを開始すると、読み込んだ表の「店舗名」という項目値を全て、TEdit に入力した値に置き換えます。ロールバックを実行すると、変更前の値に戻るのが確認できます。



[備考 1]
トランザクション分離レベルの推奨値は tiRepeatableRead です。テストに使用した JET データベースはこの分離レベルをサポートしていません。そこで tiReadCommitted を使用しています。
SQLite データベースでは tiRepeatableRead が使用できます。


図18
設計時の画面
図19
実行開始時の画面
  • 表のデータを TDBGrid に表示
図20
トランザクション開始
  • 更新クエリを実行しているので、データが更新される
  • 確定はしていない
図21
ロールバック
  • 変更前のデータに戻る (ロールバックされる)

リスト5
ZeosLib におけるトランザクション処理
TZConnection.TransactIsolationLevel の値を tiReadCommitted にする
implementation

uses
  ZDbcIntfs;

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//
//  tiReadCommitted等の定数名の使用にはusesにZDbcIntfsが必要
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  LConnectStr : string;
begin
  //TDBGridからは編集できないようにする
  DBGrid1.ReadOnly := True;

  //TDataSource,TDBGridの設定
  DataSource1.DataSet := ZQuery1;
  DBGrid1.DataSource  := DataSource1;

  ZQuery1.Close;
  ZConnection1.Disconnect;
  ZConnection1.LoginPrompt := False;

  //接続文字列の設定
  LConnectStr := 'Provider=Microsoft.Jet.OLEDB.4.0'
               + ';Data Source=' + ExpandFileName('..\TestMDB.mdb')
               + ';Persist Security Info=False';

  ZConnection1.Database := LConnectStr;
  ZConnection1.Protocol := 'ado';

  //トランザクションの分離レベルの設定
  //ZeosLibの推奨はtiRepeatableReadであるが未サポートの警告が表示される
  ZConnection1.TransactIsolationLevel := tiReadCommitted;
  //データベースへ接続
  ZConnection1.Connect;

  ZQuery1.Connection := ZConnection1;
  OpenQuery;
end;

//=============================================================================
//  フォームを閉じる時の処理
//=============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if ZConnection1.InTransaction then begin
    ZConnection1.Rollback;
  end;
  ZQuery1.Close;
  ZConnection1.Disconnect;
end;

//-----------------------------------------------------------------------------
//  表のデータを表示するためのクエリの実行
//-----------------------------------------------------------------------------
procedure TForm1.OpenQuery;
begin
  //TZQueryの設定
  ZQuery1.Close;
  ZQuery1.SQL.Clear;
  ZQuery1.SQL.Add('select * from Testtbl');

  //クエリの実行
  ZQuery1.Open;
end;

//=============================================================================
//  トランザクション開始
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  if not ZConnection1.Connected then exit;
  if ZConnection1.InTransaction then exit;
  if ZQuery1.IsEmpty then exit;

  ZConnection1.StartTransaction;
  try
    //別のTZQueryを使用して更新クエリを実行
    ZQuery2.Close;
    ZQuery2.Connection := ZConnection1;

    ZQuery2.SQL.Clear;
    ZQuery2.SQL.Text := 'UPDATE Testtbl SET [店舗名] ="' + Edit1.Text + '";';
    ZQuery2.ExecSQL;

    //実際にはここでコミットを実行することが多い
  except
    //失敗した場合
    //実際には何らかの表示等が必要だろう
    ZConnection1.Rollback;
  end;

  //確認表示
  OpenQuery;
end;

//=============================================================================
//  コミット(データを確定する)
//  コミット後は、データを元に戻すことはできない
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
  if not ZConnection1.InTransaction then exit;

  try
    ZConnection1.Commit;
  except
    //失敗した場合
    //実際には何らかの表示等が必要だろう
    ZConnection1.Rollback;
  end;
end;

//=============================================================================
//  ロールバック
//  トランザクション開始前のデータに戻す
//  元に戻せるのはトランザクション中に変更したデータ
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
begin
  if not ZConnection1.InTransaction then exit;

  //ロールバック
  ZConnection1.Rollback;

  //確認表示
  OpenQuery;
end;




10_ZeosLib における SQL 文のパラメータ設定方法 - ParamByName


SQL 文のパラメータは、多くの場合 ParamByName 等で設定できます。
TZQuery の場合、Params.ParamByName (または TZQuery.Params.ParamByName) で設定するには、以下のように、TZConnection の PrepareSQL プロパティの値を True にしないとパラメータが正しく設定できません。


// TZQuery.ParamByName(またはTZQuery.Params.ParamByName)を使用する場合
// プロパティの設定は以下でも可能
//  TZConnection.Properties.Add('PrepareSQL=True');

  //TZConnectionのPrepareSQLプロパティの値をTrueにする
  TZConnection.Properties.Values['PrepareSQL'] := 'True';
  //パラメータのチェックありにする
  TZQuery.ParamCheck := True;


TZConnection の PrepareSQL プロパティの値をデフォルトの False のままでパラメータを設定したい場合は、TZQuery.ParamCheck の値を False にして、パラメータのチェック機能を無効にします。実際のパラメータの設定は TZQuery.Params.CreateParam メソッドを使用します。
TZQuery.ParamCheck の値を False にした場合は、TZConnection の PrepareSQLプロパティの値に関係なく TZQuery.Params.CreateParam メソッドでパラメータを設定できます。


// PrepareSQLプロパティの値をデフォルトのFalseでパラメータを設定す場合
// 実際のパラメータの設定方法はサンプルコードを参照

  //パラメータのチェックなしにする(デフォルトはTrue)
  TZQuery.ParamCheck := False;


本サンプルでは、更新クリエを実行して、指定の項目の値を全て別の値にします。
また、比較のために FieldByName を使用して、項目に直接値を代入して Post メソッドで更新するコードも追加しておきました。この時、CachedUpdates の値を True にしてキャッシュを有効にしています。キャッシュを使用すると更新の処理が高速になりますが、データを確定するには ApplyUpdatesメソッドを実行する必要があります。



[備考]
本サンプルでは、どれかのボタンをクリックすると TZQuery.CachedUpdates の値が True になったままとなります。したがって、ボタンクリック後に TDBGrid でデータを編集しても、表示は更新されますが確定はされません。


図22
設計時の画面
図23
実行開始時の画面
  • 表のデータを TDBGrid に表示
  • カラムの幅と表示の順番を、手動で変更している
図24
指定項目の値を変更
  • カラムの幅と表示の順番は、手動で変更している

POST メソッドでデータを更新しようとすると、下図のようなエラーが発生することがあります。この場合、最初のレコードだけが更新され、その次のレコードでこのエラーが発生します。このエラーが発生すると、TDBGrid でのレコードの移動やデータの更新もできなくなります。

このエラーは、プライマリキーが存在しない表に対して、他のクライアントがデータを操作したり、トランザクションや更新クエリを実行した後の操作内容によって発生します。
一般的に、データベースの表にはプライマリキー (プライマリインデックス) が必要です。データベースは内部で、このプライマリキーによって、レコードを識別します。また、適切なインデックスが設定されていないと高速な処理は望めません。


0 record(s) updated. Only one record should have been updated.


図25
プライマリキーの設定がない表の場合
  • 常に発生するというわけではない
  • 表に対してどんな操作しをしたかによる

プライマリインデックス (プライマリキー) は、例えば次のような SQL 文を実行すると設定することができます。既に設定済みの場合はエラーが発生します。


ALTER TABLE [食品カロリ表] ADD PRIMARY KEY (番号);


図26
プライマリキーが設定済みの場合

リスト6
ZeosLib における SQL 文のパラメータ設定方法
TZQuery.CachedUpdates を使用したコード
implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  LConnectStr : string;
begin
  //TDataSource,TDBGridの設定
  DataSource1.DataSet := ZQuery1;
  DBGrid1.DataSource  := DataSource1;

  ZQuery1.Close;
  ZConnection1.Disconnect;
  ZConnection1.LoginPrompt := False;

  //接続文字列の設定
  LConnectStr := 'Provider=Microsoft.Jet.OLEDB.4.0'
               + ';Data Source=' + ExpandFileName('..\TestMDB.mdb')
               + ';Persist Security Info=False';

  ZConnection1.Database := LConnectStr;
  ZConnection1.Protocol := 'ado';

  //データベースへ接続
  ZConnection1.Connect;

  ZQuery1.Connection := ZConnection1;
  OpenQuery;
end;

//=============================================================================
//  フォームを閉じる時の処理
//=============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  ZQuery1.Close;
  ZQuery2.Close;
  ZConnection1.Disconnect;
end;

//-----------------------------------------------------------------------------
//  表のデータを表示するためのクエリの実行
//-----------------------------------------------------------------------------
procedure TForm1.OpenQuery;
begin
  //TZQueryの設定
  ZQuery1.Close;
  ZQuery1.SQL.Clear;
  ZQuery1.SQL.Add('select * from Testtbl');

  //クエリの実行
  ZQuery1.Open;
end;

//=============================================================================
//  SQL文のパラメータの設定方法
//  TZQuery.ParamByName(またはTZQuery.Params.ParamByName)を使用する場合
//
//  TZConnectionのPrepareSQLプロパティの値をTrueにする
//  TZQuery.ParamCheckの値はTrueとする
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  try
    //別のTZQueryを使用して更新クエリを実行
    ZQuery2.Close;
    ZQuery2.CachedUpdates := True;
    ZQuery2.Connection := ZConnection1;
    ZQuery2.SQL.Clear;

    //TZConnectionのPrepareSQLプロパティの値をTrueにする
    ZConnection1.Properties.Clear;
    ZConnection1.Properties.Add('PrepareSQL=True');
    //パラメータのチェックありにする
    ZQuery2.ParamCheck := True;

    //SQL文
    ZQuery2.SQL.Text := 'UPDATE Testtbl SET [店舗名] = :SHOP, [請求日] = :DATE;';

    //クエリのパラメータの設定
    ZQuery2.ParamByName('SHOP').AsString   := Edit1.Text;
    ZQuery2.ParamByName('DATE').AsDateTime := StrToDateTime('2010/10/15');

    //更新クエリの実行
    ZQuery2.ExecSQL;
  except
  end;

  //確認表示
  OpenQuery;
end;

//=============================================================================
//  SQL文のパラメータの設定方法
//  PrepareSQLプロパティの値をデフォルトのFalseでパラメータを設定すする場合
//  TZQuery.Params.CreateParamメソッドを使用する方法
//
//  TZQuery.ParamCheckの値をFalseとする(Trueがデフォルト値)
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
  try
    //別のTZQueryを使用して更新クエリを実行
    ZQuery2.Close;
    ZQuery2.Connection := ZConnection1;

    //パラメータのチェックなしにする
    ZQuery2.SQL.Clear;
    ZQuery2.CachedUpdates := True;
    ZQuery2.ParamCheck := False;

    //SQL文
    ZQuery2.SQL.Text := 'UPDATE Testtbl SET [店舗名] = :SHOP, [請求日] = :DATE;';

    //クエリのパラメータの設定
    ZQuery2.Params.CreateParam(ftString, 'SHOP', ptInput).Value := Edit1.Text;
    ZQuery2.Params.CreateParam(ftDateTime,'DATE', ptInput).Value := Now;

    //更新クエリの実行
    ZQuery2.ExecSQL;
  except
  end;

  //確認表示
  OpenQuery;
end;

//=============================================================================
//  SQL文のパラメータの設定方法
//  PrepareSQLプロパティの値をデフォルトのFalseでパラメータを設定すする場合
//  TZQuery.Params.CreateParamメソッドを使用する方法
//  TParamの変数を使用する例
//
//  TZQuery.ParamCheckの値をFalseとする(Trueがデフォルト値)
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LSQLParam : TParam;
begin
  try
    //別のTZQueryを使用して更新クエリを実行
    ZQuery2.Close;
    ZQuery2.CachedUpdates := True;
    ZQuery2.Connection := ZConnection1;

    //パラメータのチェックなしにする
    ZQuery2.SQL.Clear;
    ZQuery2.ParamCheck := False;

    //SQL文
    ZQuery2.SQL.Text := 'UPDATE Testtbl SET [店舗名] = :SHOP, [請求日] = :DATE;';

    LSQLParam := ZQuery2.Params.CreateParam(ftString, 'SHOP', ptInputOutput);
    LSQLParam.AsString := Edit1.Text;

    //クエリのパラメータの設定
    LSQLParam := ZQuery2.Params.CreateParam(ftDateTime,'DATE', ptInput);
    LSQLParam.AsDateTime := Now;

    //更新クエリの実行
    ZQuery2.ExecSQL;
  except
  end;

  //確認表示
  OpenQuery;
end;

//=============================================================================
//  FieldByNameでレコード単位で値を代入するコード
//  上の2つのコードと結果は同じになる
//
//  プライマリインデックスがない表では正常に動作しない場合がある
//=============================================================================
procedure TForm1.Button4Click(Sender: TObject);
var
  LDateTime : TDateTime;
begin
  LDateTime := StrToDate('2010/10/05');

  ZQuery1.DisableControls;
  ZQuery1.CachedUpdates := True;
  try
    ZQuery1.First;
    while not ZQuery1.Eof do begin
      //編集モードにする
      ZQuery1.Edit;
      ZQuery1.FieldByName('店舗名').AsString   := Edit1.Text;
      ZQuery1.FieldByName('請求日').AsDateTime := LDateTime;
      ZQuery1.Post;
      ZQuery1.Next;
    end;
    //CachedUpdatesをTrueにした時はApplyUpdatesを実行しないと確定されない
    //CachedUpdatesがFalseの時は不要
    ZQuery1.ApplyUpdates;
  except
    //確認のためにエラーの再生成
    raise;
  end;

  ZQuery1.First;
  ZQuery1.EnableControls;
end;




11_ZeosLib で接続可能なデータベースの一覧


本ページのサンプルは、ADO の機能を使用したデータベースへの接続ですが、ZeosLib で接続可能なデータベースは、TZConnection.GetProtocolNames メソッド、あるいは IZCollection を使用して以下のようなコードでも 取得することができます。



図27
設計時の画面
  • TZSQLMetadata, TZConnection コンポを配置
図28
実行結果
  • ZeosLib で接続可能なデータベースのリスト

リスト7
ZeosLib で接続可能なデータベースの一覧
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, DB, ZAbstractRODataset, ZSqlMetadata, ZAbstractConnection,
  ZConnection;

type
  TForm1 = class(TForm)
    Button1: TButton;
    ListBox1: TListBox;
    ZSQLMetadata1: TZSQLMetadata;
    Button2: TButton;
    ZConnection1: TZConnection;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  Types,
  ZClasses,
  ZDbcIntfs;

{$R *.dfm}

//=============================================================================
//  ZeosLibで接続可能なデータベースの一覧
//  これはProtocolプロパティの一覧
//
//  フォームにTZSQLMetadataを配置して、
//  usesにTypes,ZClasses,ZDbcIntfsが必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LDrivers   : ZClasses.IZCollection;
  LProtocols : Types.TStringDynArray;
  i          : Integer;
  j          : Integer;
begin
  ListBox1.Items.Clear;
  ListBox1.Items.BeginUpdate;
  LDrivers := ZDbcIntfs.DriverManager.GetDrivers;

  for i := 0 to LDrivers.Count - 1 do begin
    LProtocols := (LDrivers.Items[i] as IZDriver).GetSupportedProtocols;
    for j := 0 to High(LProtocols) do begin
      ListBox1.Items.Add(LProtocols[j]);
    end;
  end;
  ListBox1.Sorted := True;
  ListBox1.Items.EndUpdate;
end;

//=============================================================================
//  ZeosLibで接続可能なデータベースの一覧
//  これはProtocolプロパティの一覧
//  TZConnection.GetProtocolNamesを使用する方法
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
  ZConnection1.GetProtocolNames(ListBox1.Items);
end;

end.




12_ZeosLib を使用した Paradox データベースのメタデータ取得


従来は、BDE を使用して Paradox データベースに接続するのが一般的でした。ADO あるいは ZeosLib を使用して ADO の機能を使用すると、本ページのサンプルのように BDE なしで Paradox データベースに接続することができます。しかし、実際にやってみると、いくつかの問題があります。

一般に、異種の、異なる種類のデータベースのデータの移行は INSERT INTO 文で SELECT 文は使用できませんが、レコード単位での処理は可能です。その他、プライマリーキー等の設定も必要となってきます。そのためにはそれらの値を知る必要があります。TZConnection1.GetTableNames 等、個別の取得用メソッドもありますが、プライマリキー等の、いわゆるメタデータは、TZSQLMetadata コンポーネントを使用しても取得できます。

本サンプルは、Paradox データベースの各種メタデータを取得して表示します。Button1 をクリックするとファイルの選択ダイアログを表示します。Paradox のデータベースのファイル、これは表のファイルに相当しますが、それを選択すると、そのデータベースのフォルダ内の情報を取得します。
下のコードでは、取得するメタデータのタイプを、コンボボックスに設定した値から選択するようになっていますが、直接値を指定する場合は以下のようにします。


  TSQLMetadata.MetadataType := mdTables;


メタデータのタイプには以下の値が指定できます。


mdProcedures
mdProcedureColumns
mdTables
mdSchemas
mdCatalogs
mdTableTypes
mdColumns
mdColumnPrivileges
mdTablePrivileges
mdTablePrivileges
mdTablePrivileges
mdBestRowIdentifier
mdVersionColumns
mdPrimaryKeys
mdImportedKeys
mdExportedKeys
mdCrossReference
mdTypeInfo
mdTriggers
mdIndexInfo
mdSequences
mdUserDefinedTypes


[備考]
BDE + Paradox から他のデータベースに移行したい場合、BDE が利用可能な環境であれば、本項のような方法を使う必要はありません。
Paradox を BDE で接続して、BDE の機能を使用して情報を取得できます。


図29
設計時の画面
  • TZSQLMetadata コンポーネントを配置

下図は、メタデータのタイプを mdTables とした場合です。Delphi 6 Pro に添付されている Paradox のデータベースの表名のリストとなっています。



図30
表名 (テーブル名) の一覧

下図は、添付の Paradox データベースのプライマリキーの一覧です。添付の Paradox のデータベースは表が 2 つありますが、1 つはプライマリキーを設定していません。したがって、結果は 1 つだけとなっています。



図31
プライマリキーの一覧

リスト8
ZeosLib を使用した Paradox データベースのメタデータ取得
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, DB, ZAbstractRODataset, ZSqlMetadata, ZAbstractConnection,
  ZConnection, StdCtrls, Grids, DBGrids;

type
  TForm1 = class(TForm)
    DBGrid1: TDBGrid;
    Button1: TButton;
    DataSource1: TDataSource;
    ZConnection1: TZConnection;
    OpenDialog1: TOpenDialog;
    ZSQLMetadata1: TZSQLMetadata;
    ComboBox1: TComboBox;
    procedure Button1Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormShow(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  TypInfo;

{$R *.dfm}

//=============================================================================
//  フォーム表示開始時の処理
//  TComboBoxに列挙型のTZMetadataTypeの値を文字列でセット
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
var
  LpInfo : PTypeInfo;
  LpData : PTypeData;
  i     : Integer;
begin
  ComboBox1.Items.Clear;
  ComboBox1.Items.BeginUpdate;

  LpInfo := TypeInfo(TZMetadataType);

  //列挙型であれば調査
  if LpInfo^.Kind = tkEnumeration then begin
    LpData := GetTypeData(LpInfo);
    for i := LpData^.MinValue to LpData^.MaxValue do begin
      ComboBox1.Items.Add(GetEnumName(LpInfo, i));
    end;
  end;
  ComboBox1.Sorted := True;
  ComboBox1.Items.EndUpdate;

  ComboBox1.Text := 'mdTables';
end;

//=============================================================================
//  フォームを閉じる時の処理
//=============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  ZSQLMetadata1.Close;
  ZConnection1.Disconnect;
end;

//=============================================================================
//  ZeosLibを使用したParadoxのメタデータ取得
//
//  Paradoxはフォルダがデータベース名
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LPdxDBName    : string;
  LConnectStr   : string;
  LMetadataType : TZMetadataType;
  LText         : string;
  i             : Integer;
begin
  OpenDialog1.Filter := 'Paradox DB File|*.db';
  if not OpenDialog1.Execute then exit;

  OpenDialog1.InitialDir := ExtractFilePath(OpenDialog1.FileName);
  LPdxDBName := OpenDialog1.InitialDir;


  //接続を切断してから設定
  ZSQLMetadata1.Close;
  ZConnection1.Disconnect;
  ZConnection1.LoginPrompt := False;

  //接続文字列の設定
  //Paradoxデータベースへの接続にはADOの機能を使用する
  LConnectStr := 'Provider=Microsoft.Jet.OLEDB.4.0'
               + ';Data Source=' + LPdxDBName
               + ';Extended Properties=Paradox 5.x'
               + ';Persist Security Info=False';

  ZConnection1.Database := LConnectStr;
  ZConnection1.Protocol := 'ado';

  ///メタデータを取得するのでUseMetadataをTrueにする
  //設定しなくてもデフォルト値はTrue
  ZConnection1.UseMetadata := True;

  //コードページ関係の設定
  ZConnection1.Properties.Clear;
  ZConnection1.Properties.Add('AutoEncodeStrings=ON');
  ZConnection1.Properties.Add('controls_cp=CP_UTF16');


  DataSource1.DataSet := ZSQLMetadata1;
  DBGrid1.DataSource  := DataSource1;

  ZSQLMetadata1.Connection := ZConnection1;

  //TComboBoxの値を列挙型のTZMetadataTypeの値に変換する
  LText := Trim(ComboBox1.Text);
  LMetadataType := TZMetadataType(GetEnumValue(TypeInfo(TZMetadataType), LText));
  ZSQLMetadata1.MetadataType := LMetadataType;

  ZSQLMetadata1.Open;

  //TDBGridのカラム幅の調整
  for i := 0 to DBGrid1.Columns.Count - 1 do begin
    if DBGrid1.Columns[i].Width > 100 then begin
      DBGrid1.Columns[i].Width := 100;
    end;
  end;
end;

end.




13_JET エンジンデータベースの制限等


JET データベースは 1 つのファイルです。このファイルサイズの上限は 2 GB です。
この制限容量までのデータが格納できるわけではありません。管理と作業用に内部で一定の領域を使用します。この制限を超えると、データの破壊という現象が発生する可能性があります。
ファイルサイズは、レコードの追加をしなくても、データ操作によって増加します。

一般的なデータベースも、OS のファイルシステムでの制限があります。32 ビット Windows の NTFS では 2 TB です。当サイトには SQLite のサンプルがありますが、現時点での SQLite での容量制限は 140 TB です。しかし、実際にはこの制限値までの使用はできないことになります。


JET データベースでもトランザクションが利用できますが、同時アクセスすると、ロック制御の不良で参照障害が発生することが報告されています。特に Windows Vista で採用された SMB2.x のキャッシュ処理により、発生する可能性が高くなっているようです。

マイクロソフトでは、JET エンジンのデータベースは、ローカルで使用する個人用のデータベースシステムとしての位置付けとしています。



[備考]
Microsoft Access はデータベースあるいはデータベースシステムの名前ではありません。
Microsoft Access は、JET というデータペースエンジンを含むデータベース操作用のアプリケーションソフトの名前です。
JET エンジンのデータベースのことを Microsoft Access のデータベース、あるいは Access のデータベースと言うことがあります。
マイクロソフトのオンラインの記事では、JET データベースエンジンと表記されていますから、JET エンジンを使用したデータベースは、正確には JET データベースエンジンを使用したデータベースということになります。




14_データベースの表名と項目名における日本語の使用


サンプルコード内にコメントとして書いておきましたが、データベースの表の名前や項目名 (フィールド名) に日本語を使用する時は注意が必要です。筆者が業務アプリの開発でデータベースの構築を始めた頃は、表名や項目名には日本語は使用しないというのが暗黙の常識事項でした。

表名や項目名には半角の英数字のみを使用し、表示の時に日本語にする方が確実であることは間違いありせん。SQL 文の記述も分かりやすく簡潔になります。その際、表名や項目名には空白文字を使用しないようにします。
表示名や表示の書式等は TZQuery の項目エディタのプロパティや TDBGrid 等のカラムエディタのプロパティで設定できます。