文化の日の前日、11月2日(水)に、エンバカデロテクノロジーズ 飯田橋セミナールームにおいて「Delphi / C++Builder 業務アプリケーション 刷新実践法」セミナーを実施いたしました。 その第2セッション 「BDEを使った業務アプリを移行ツールで最新化」にてデモとしてお見せした内容について、BDEを使用したプロジェクトからFireDACへ移行するスタディケースとして、ブログに記載しておきます。
###デモ&スタディ内容の基本となる資料
デモ&スタディ内容の基本となる資料は Embarcadero DocWiki の以下のページにあります。
- [FireDAC への BDE アプリケーションの移行] (http://docwiki.embarcadero.com/RADStudio/Berlin/ja/FireDAC_への_BDE_アプリケーションの移行)
上記の資料の中でデモとしてお見せしたBDEを使ったプロジェクトをFireDACのコンポーネントへ変換して動作させる部分はこちらに記載されています。
ここでは、「BDE アプリケーションの移行(FireDAC)」に記載されている例を実際に試してスタディしていただくにあたり、Delphi / C++Builder / RAD Studio 10.1 Berlin (以降 RAD Studio、とだけ記載します) を使用して行う場合の手順や、追加情報、さらなるチップスなども合わせてご紹介してまいります。また移行セミナーでは有料ツールの「Delphi Parser」(https://delphiparser.com/) を使用しました。このブログでは、RAD Studioに付属の無料変換ツール「reFind」を使用したケースを記載しておきます。
BDEを使ったソースコードをFireDACへと移行することのメリットや、移行の容易さなどの全体的な話について、ご興味のある方は別のブログエントリのこちら「コード資産を生かして移行 BDE から FireDAC へ」が参考になるかと思いますので、ご覧ください
使用開発環境として下記のいずれかをインストールしておくことが必要です。
- Delphi / C++Builder / RAD Studio 10.1 BerlinProfessiona Edition 以上
- InterBase XE7インストール&起動済
それでは、DocWiki内のステップ番号に対応するようにステップ番号を記載しながらBDEからFireDACへ移行する手順を説明してまいります。
###[ステップ 1. スタディに使用するプロジェクトの用意]
デモ・スタディに使用するBDEを使ったソースコード・プロジェクトは、RAD Studioをインストールしていればデフォルトで以下のフォルダにて提供されています。
- C:\Users\Public\Documents\Embarcadero\Studio\18.0\Samples\Object Pascal\Database\FireDAC\Tool\reFind\BDE2FDMigration\Demo
この「Demo]フォルダをわかりやすい任意の場所にコピーしておいてください。(例えばD:ドライブ直下などに)
###[ステップ 2. reFind を使って BDEのコンポーネントをFireDACへと変換]
reFindは Interface 宣言部、Implement 記述部、dfm内にあるコンポーネントオブジェクトの名称やプロパティまで、ほぼ網羅してBDEコンポーネントを対応するFireDACコンポーネント、プロパティに変換してくれます。
ご存じ[cmd]で起動するコマンドプロンプトを立ち上げ、先ほどコピーした[Demo]フォルダ・ディレクトリににcdコマンドで移動しておいてください。
ここで reFindを実行します。 デモファイル群がデフォルトパスにあるのであれば下の記述をそのまま使えばOKです。
"C:\Program Files (x86)\Embarcadero\Studio\18.0\bin\reFind.exe" *.pas *.dfm /X:"C:\Users\Public\Documents\Embarcadero\Studio\18.0\Samples\Object Pascal\Database\FireDAC\Tool\reFind\BDE2FDMigration\FireDAC_Migrate_BDE.txt"
上の記述は [reFindのあるパス、 ファイルフィルタ、/Xオプションでルールファイルのファイル指定] という文構成で実行したものであります。使用法とオプションの説明などの詳しくはreFind のヘルプ "/?" をご参照ください。
###[ステップ 3.データベース接続定義の移行、作成]
コード・プロジェクト内のBDE関連の記述をFireDAC向けに変換したら、次の手順にはいります。
BDEで使用していたデータベースは、BDEアドミニストレーターで接続定義を作って接続していましたが、FireDACにおいても同様の接続定義を行うトコロはあります。「FireDACエクスプローラ」がそれです。下記の手順で起動して設定しましょう。
- RAD Studioを起動
- RAD Studioの上部ツールバー内の[ツール(T)] - [FireDAC エクスプローラー(F)]をクリックして「FireDAC エクスプローラー」を起動
- FireDAC エクスプローラー ウインドウのツールバーから[ファイル(F)] - [新規作成(N)] - [Connection Definition Ctrl+C]をクリック
- 左側の「オブジェクトエクスプローラ」に新規の「ConnectionDef1」が作られますので任意の接続定義名を付けます。ここでは「MASTSQL」と設定しておきます。
- MASTSQLのパラメーターには下記の設定をそれぞれ行ってください。
- DriverID=IB
- Protocol=TCPIP
- Server=127.0.0.1
- DataBase=C:\Users\Public\Documents\Embarcadero\Studio\18.0\Samples\Data\MASTSQL.GDB
- User_Name=sysdba
- SQLDialect=3
- CharacterSet=UTF8
- ExtendedMetadata=True
設定できたらFireDAC エクスプローラー ウインドウのツールバーから[編集(E)] - [適用(A)]をクリックします。
その後、[ファイル(F)] - [上書き保存(S)]をクリックして保存しておきます。
設定後のパラメータ等
[補足]: 元のBDEを使ったソースプロジェクトは、Paradox(デスクトップデータベース)を使っているプロジェクトとしてつくられています。FireDAC はデスクトップデータベースのParadoxをサポートしていませんので、ほかのデータベースへ移行する必要があります。今回のスタディでは、すでにParadoxデータベースをInterBaseに移行してあるものが用意してあります。それが、上記のDataBase」に設定していあるパス:「C:\Users\Public\Documents\Embarcadero\Studio\18.0\Samples\Data\MASTSQL.GDB」です。※DocWikiにはこの移行済InterBaseのデータベースについて記述はないのですが、上記のように、サンプルとして存在しています。)
FireDAC接続設定ができたら、一度RAD Studioを終了しておきます。
###[ステップ 4. FireDACで別建てになったドライバやWaitingカーソルの設定]
ステップ 2 で変換を行った後のDemoフォルダ内のプロジェクトを起動します。プロジェクトファイル「mastapp.dproj」をダブルクリックします。
Docwikiではuses に FireDAC.Phys.IB, FireDAC.VCLUI.Wait,を追記するようになっていますが、コンポーネントとしてドラッグ&ドロップすればOKです。mainフォームもしくは、DataMod データモジュールのどこでもよいのでInterBaseドライバとして「TFDPhysIBDriverLink」と「TFDGUIxWaitCursor」コンポーネントをドロップしてください。さらに言えば、10.1 Berlinでは、TFDPhysIBDriverLinkはドロップしなくてもOKであります。 データベースにInterBaseを使っていれば、コンパイル時に必要ドライバとしてRAD Studioが自動でuses に 追加します。
###[ステップ 5. TFDConnection 接続設定の変更]
DocWikiではDataMod.dfmでのテキスト記述が記載されていますが、こちらもコンポーネント上のGUIから設定していただければOKです。
- RAD Studio 右側の「プロジェクトマネージャ」内の「DataMod.pas」をダブルクリックして開きます。もし、コードが表示されたら、下部中央に表示されているタブの「デザイン」を選んでフォームとビジュアルコンポーネントを表示します。
- Databaseコンポーネントをクリックします。
- 左下のオブジェクトインスペクタの「プロパティ」タブ内にある[ConnectionDef]にステップ3で記述した接続定義名[MASTSQL]を記載します。
- Databaseコンポーネントをダブルクリックして「FireDAC接続エディタ」を開きます。
- 「FireDAC接続エディタ」で[テスト(T)]をクリックして接続できるか確認してください。うまくいっていればログインダイアログが表示され、OKをクリックした後、「接続の確立が成功しました」と表示されるでしょう。 うまくいかないのであれば、おそらく、ステップ3 で実施した接続定義が間違っている可能性があります。今一度見直してください。(デフォルトのパスワードは masterkey に設定されています。お忘れでしたら追加しておいてください。)
###[ステップ 6. データベースマッピングを変更する]
ちょっとややこしいのがこのデータベースマッピングです。ややこしいのでまずはそのあたりの説明から。
BDEとFireDAC、ともにデータベースアクセスミドルウェアと言えますが、データベースから、クライアントドライバを通し、データとして持ってきて、どのデータ型にいれて渡してくれるか、というデータ型マッピングがBDEとFireDACでは少し異なります。
このあたりのFireDACでのマッピング方式について説明した資料がDocWikiにあります。下記のリンクです。
上記のリンク内ではOracleのデータベースを例に、Oracleデータベースでデータ定義「NUMBER」のものはFireDACでは「dtFmtBCD」データ型として扱っていることを説明しています。
一方、BDEではdtInt32やdtDoubleなどのデータ型として扱っていました。
これ以外にも、日付時間情報において、BDEではDateTime型として取り扱いますが、FireDACではDateTimeStamp型として取り扱う、という違いもあるようです。
さて、BDEを使っている古いプロジェクトで、さらにはプログラム上で永続フィールドを持っているケースでは、BDEの時と同様データの型を来るもんだと期待して待っている状態であります。 今回のように、データミドルウエアがBDEからFireDACに置き換わったケースでは、FireDACが渡そうとしたデータ型と受け取ろうとしている旧BDE時代に設定したデータ型が異なって、受けとれなくなってしまいます。
そこで、それを調整して解決してあげる方法がデータベースマッピングルールです。
つまりは、FireDACで、デフォルト設定のままではtFmtBCD やDateTimeStampで取り扱うデータを、BDEのルールに倣ってdtInt32やdtDouble, DateTime型で取り扱うように特別ルールを与えてあげるってわけです。そうすれば受け取るプログラムのほうも、昔のBDEの時と同じデータ型で渡されるようになるので、以前のコードのまま使えることになるというわけです。
余談ですが、マッピングをしない場合、受け取り側の永続フィールドの期待値をFireDACの取り扱うデータ型にあわせて変更してあげる必要があります。(永続フィールドの作り直し、永続フィールドに含まれる計算フィールドの再定義)
さて、DocWikiの手順にもどります。DocWikiではDataMod.DFMのエディタ内でテキスト記述していますが、ダイアログを使ってもうちょっと簡単に記載することができます。
- Databaseコンポーネントをダブルクリックして「FireDAC接続エディタ」を開きます。
- [オプションタブ]をクリックします。
- [データマッピングルール] 下の [継承したルールを無視] のチェックボックスをONにします。
- 下記の設定を行います。
- SourceDataType = dtFmtBCD
- TargetDataType = dtInt32
- PrecMin = 0
- PrecMax = 10
- ScaleMin = 0
- ScaleMax = 0
5.上記設定を入力したら[ルールの追加]をクリックして下記の設定をさらに追加します。
- SourceDataType = dtFmtBCD
- TargetDataType = dtDouble
6.同様に[ルールの追加]をクリックして下記の設定をさらに追加します。
- SourceDataType = dtDateTimeStamp
- TargetDataType = dtDateTime
上記の三つの設定を加えたときのデータマッピングルールは次のような状態になっているはずです。
※データマッピングルールの画面
7.[OK]を押して「FireDAC接続エディタ」を閉じます。
あとはDocWikiの記載通り FormatOptions.StrsTrim プロパティを False に設定します。
###[ステップ7. 不必要コードの削除]
ここはDocWiki通り、使われない以下のコードを削除します。
ここではローカルデータベースであるParadoxがもはや使われないのでその関連コードを消します。
- DataMod.pas 内の TMastData.UseLocalData メソッド
- Interface部内の宣言を削除
- Implementation部内の実装を削除
###[ステップ8. 新しい接続データベースに合わせたコードへの変更]
ここはDocWiki通り、新しいデータベース接続に合わせて以下のコードを変更します。
- Main.pas 内の TMainForm.ViewRemoteClick メソッドで、" (Local Interbase)" という文字列を " (InterBase)" に置き換えます。
- Main.pas 内の TMainForm.ViewMenuClick ハンドラ
- Interface部内の宣言を削除
- Implementation部内の実装 を削除
- Mainform - MainMenu - View - のOnClickイベントに紐づいている ViewMenuClickの記述をオブジェクトインスペクタから削除
- DataMod.pas 内の TMastData.DataDirectory メソッド
- Interface部内の宣言を削除
- Implementation部内の実装を削除
TMastData.UseRemoteDataについてのDocWiki内の記載で「Params.Values['DataBase'] := 'C:\\MastApp.GDB';」となっていますが、こちらは実際にデータベースのある場所を指定します。なので `Params.Values['DataBase'] := 'C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\18.0\\Samples\\Data\\MASTSQL.GDB';` となります。 また、下から2行目の「SetDatabaseConnectionDef('MASTSQL');」は、おそらく誤りで、 `SetDatabaseAlias('MASTSQL');` が正しいです。 private部で定義している `procedure SetDatabaseAlias(ConnectionDefName: string);` をなぜか違う名前で書いている様子。 ということで、Docwikiに記載されているコードを修正すると下記のコードとなります。これをUseRemoteDataにコピペ。
procedure TMastData.UseRemoteData;
var
Params: TStringList;
begin
{ See if the ConnectionDef exists. If not, add it. }
if not FDManager.IsConnectionDef('MASTSQL') then
begin
Params := TStringList.create;
try
Params.Values['Protocol'] := 'TCPIP';
Params.Values['Server'] := '127.0.0.1';
Params.Values['DataBase'] := 'C:\Users\Public\Documents\Embarcadero\Studio\18.0\Samples\Data\MASTSQL.GDB';
Params.Values['User_Name'] := 'sysdba';
Params.Values['SQLDialect'] := '3';
Params.Values['CharacterSet'] := 'UTF8';
Params.Values['ExtendedMetadata'] := 'True';
FDManager.AddConnectionDef('MASTSQL', 'IB', Params);
finally
Params.Free;
end;
end;
SetDatabaseAlias('MASTSQL');
end;
そして CustByLastInvQuery にあるSQL文の DESCENDING を DESC に変更しなさいと記載してありますが、今やどちらでも動きます。
####[ステップ 外 :その他]
上記までがDocWiki内の記載で、これでコンパイルすると、コンパイルも通り、ビルドも可能です。しかしいくつか問題も残っているようです。
- プログラム起動時に環境によっては'RPTSMITH.CON'が書き込めないとエラーを出す
- NewOrderボタンを押すとSQLエラーを出す
上記エラーについて、修正方法を記載しておきます。
■ プログラム起動時に環境によっては'RPTSMITH.CON'が書き込めないとエラーを出す
これはMain.PASにある UpdateRSConnect procedureにおいてデフォルトの設定ファイル場所に書き込もうとしたときに、パーミッションがないなどの理由でエラーとなっているようです。書き込み可能なパスを指定してあげることによって回避できます。
■ NewOrderボタンを押すとSQLエラーを出す
DataMod.pas内のOders (TFDTable)コンポーネントの永続フィールド「SalesPerson」が Emps (TFDTable)コンポーネントの計算フィールドである[Fullname]をlookupで参照しているのですが、参照しているFullnameが計算フィールドであるにもかかわらず、Fullnameフィールドとして存在するもののようにSQL文で指定しているのでエラーとなっているようです。
Oders コンポーネントをダブルクリックしフィールドリストを表示させたら、一番下の「salesperson」フィールドをクリックした後、RAD Studioの左下のオブジェクトインスペクタのプロパティタブから [FieldKind]プロパティの値を fkLookup から fkCalculated に変更してください。
このあたりのSQL周りや、FireDAC周りのバグを調査するために適したログを吐き出すコンポーネントも存在します。 そのログ取りコンポーネントの話はまた別の機会に記載します。
以上がBDEを使用したプロジェクトからFireDACへ移行するスタディケースになります。