Delphiを使用したインストール済みアプリケーションのデータベースバージョン管理
-
05-07-2019 - |
質問
私は、新しいバージョンがリリースされたり、ユーザーが追加のモジュールをインストールすることを選択したときに、現場で独自のデータベース構造をアップグレードする必要がある多くのDelphiアプリケーションに取り組んでいます。アプリケーションはさまざまな組み込みデータベースを使用しています(現在DBISAMおよびJetですが、これは変更される可能性があります)。
これまで、各テーブルに保存できるユーザーバージョン番号を使用してDBISAMでこれを実行しました。追加の空のデータベースファイルセットを出荷し、起動時にFieldDefsを使用して各テーブルのバージョン番号を比較し、必要に応じてインストールされたテーブルを更新しました。これが機能している間、データベースのスペアコピーを出荷する必要があるのは面倒で、DBISAMの新しいバージョンではテーブルの再構築方法が変更されたため、とにかくこれを書き換える必要があります。
これを実装する方法は2つあります。バージョン番号をデータベースに保存する方法と、DDLスクリプトを使用して古いバージョンから新しいバージョンに移動する方法、またはデータベース構造の参照バージョンをアプリケーション内に保存して、参照をデータベースと比較する方法です起動時に、アプリケーションがデータベースをアップグレードするためのDDLコマンドを生成するようにします。
おそらく両方の部分を実装する必要があると思います。アプリケーションが起動するたびに参照構造とデータベースを比較するのは遅すぎます(遅すぎる)ため、ユーザーが古い構造を使用しているかどうかを検出するには、データベース構造のバージョン番号が必要です。ただし、データベースが過去に部分的に更新された可能性がある場合、またはユーザーが自分でデータベース構造を変更した可能性がある場合、事前に記述されたスクリプトを信頼して構造的なアップグレードを行うことができるかどうかはわかりません。実際の更新の参照差分。
質問の調査データベースのバージョン管理ツールをいくつか見つけましたが、それらはすべてSQL Serverを対象としているようで、実際のアプリケーションの外部に実装されています。アプリケーションに緊密に統合され、さまざまなデータベース要件に適応できるプロセスを探しています(さまざまなDDLの違いを処理するために、アダプター、カスタムの子孫クラス、またはイベントコードを記述する必要があることを知っています)データベース、それは私を気にしません)。
これを行うまたは失敗する既成の情報を誰もが知っていますか、誰もが考えていますか:
-
アプリケーション内に汎用リレーショナルデータベース構造の参照バージョンを保存する最良の方法。
-
実際のデータベースに対して参照を比較する最良の方法。
-
データベースを更新するDDLを生成する最良の方法。
解決
ここでブログの投稿方法について dbisamデータベースのバージョン管理および SQLサーバー。
重要な部分は次のとおりです。
dbisamはビューをサポートしていないため、 バージョン番号が(に沿って保存されます 他の情報の束と一緒に)iniで データベースディレクトリ内のファイル。
データモジュールがありますが、 TdmodCheckDatabase。これは すべてのテーブルのTdbisamTableコンポーネント データベース内。テーブルコンポーネント テーブル内のすべてのフィールドが含まれ、 テーブルがいつでも更新されます 変更されました。
データベースを変更するには、 次のプロセスが使用されました:
- アプリケーションのバージョン番号を上げる
- DBの変更を行い、テストします。
- TdmodCheckDatabaseの影響を受けるテーブルを更新します
- 必要に応じて(まれに)アップグレードクエリを追加します TdmodCheckDatabase。例えば。設定する 新しいフィールドの値、または新しいフィールドを追加する データ行。
- 提供されたデータベースを使用してCreateDatabaseユニットスクリプトを生成する ツール。
- 新しいデータベースに合わせてユニットテストを更新する
アプリケーションが実行されると、 次のプロセスを経て
- データベースが見つからない場合は、CreateDatabaseユニットを実行してから実行します ステップ3
- データベースのiniファイルから現在のバージョン番号を取得する
- 予想されるバージョン番号よりも小さい場合 CreateDatabaseを実行します(新しいテーブルを作成します) TdmodCheckDatabaseのすべてのテーブルコンポーネントをチェックします テーブルの変更を適用する 手動アップグレードスクリプトを実行します
- データベースのiniファイルのバージョン番号を更新します
コードサンプルは
class procedure TdmodCheckDatabase.UpgradeDatabase(databasePath: string; currentVersion, newVersion: integer);
var
module: TdmodCheckDatabase;
f: integer;
begin
module:= TdmodCheckDatabase.create(nil);
try
module.OpenDatabase( databasePath );
for f:= 0 to module.ComponentCount -1 do
begin
if module.Components[f] is TDBISAMTable then
begin
try
// if we need to upgrade table to dbisam 4
if currentVersion <= DB_VERSION_FOR_DBISAM4 then
TDBISAMTable(module.Components[f]).UpgradeTable;
module.UpgradeTable(TDBISAMTable(module.Components[f]));
except
// logging and error stuff removed
end;
end;
end;
for f:= currentVersion + 1 to newVersion do
module.RunUpgradeScripts(f);
module.sqlMakeIndexes.ExecSQL; // have to create additional indexes manually
finally
module.DBISAMDatabase1.Close;
module.free;
end;
end;
procedure TdmodCheckDatabase.UpgradeTable(table: TDBISAMTable);
var
fieldIndex: integer;
needsRestructure: boolean;
canonical: TField;
begin
needsRestructure:= false;
table.FieldDefs.Update;
// add any new fields to the FieldDefs
if table.FieldDefs.Count < table.FieldCount then
begin
for fieldIndex := table.FieldDefs.Count to table.Fields.Count -1 do
begin
table.FieldDefs.Add(fieldIndex + 1, table.Fields[fieldIndex].FieldName, table.Fields[fieldIndex].DataType, table.Fields[fieldIndex].Size, table.Fields[fieldIndex].Required);
end;
needsRestructure:= true;
end;
// make sure we have correct size for string fields
for fieldIndex := 0 to table.FieldDefs.Count -1 do
begin
if (table.FieldDefs[fieldIndex].DataType = ftString) then
begin
canonical:= table.FindField(table.FieldDefs[fieldIndex].Name);
if assigned(canonical) and (table.FieldDefs[fieldIndex].Size <> canonical.Size) then
begin
// field size has changed
needsRestructure:= true;
table.FieldDefs[fieldIndex].Size:= canonical.Size;
end;
end;
end;
if needsRestructure then
table.AlterTable(); // upgrades table using the new FieldDef values
end;
procedure TdmodCheckDatabase.RunUpgradeScripts(newVersion: integer);
begin
case newVersion of
3: sqlVersion3.ExecSQL;
9: sqlVersion9.ExecSQL;
11: begin // change to DBISAM 4
sqlVersion11a.ExecSQL;
sqlVersion11b.ExecSQL;
sqlVersion11c.ExecSQL;
sqlVersion11d.ExecSQL;
sqlVersion11e.ExecSQL;
end;
19: sqlVersion19.ExecSQL;
20: sqlVersion20.ExecSQL;
end;
end;
他のヒント
同様のストーリーはこちら。 DBバージョン番号を「システム」テーブルに保存し、起動時に確認します。 (テーブル/フィールド/値が存在しない場合、そのビットを追加するのを忘れたのはバージョン0であることがわかります!)
開発中およびデータベースをアップグレードする必要がある場合は、作業を行うためのDDLスクリプトを作成し、正常に機能することを確認したら、アプリにテキストリソースとして追加します。
アップグレードが必要であるとアプリが判断すると、適切なリソースをロードして実行します。複数のバージョンをアップグレードする必要がある場合は、各スクリプトを順番に実行する必要があります。結局、ほんの数行のコードであることが判明しました。
主なポイントは、GUIベースのツールを使用してアドホックまたは「ランダム」な方法でテーブルを変更する代わりに、実際にDDLをすぐに作成することです。これにより、完全なアップグレードスクリプトを作成することが非常に簡単になります。構造の違いは必要ありません。
データベースにADOを使用しています。バージョン番号スキームも使用しますが、これは健全性チェックとしてのみ使用されます。 Connection.GetTableNamesとConnection.GetFieldNamesを使用して、<!> quot; master <!> quotを記述するXMLドキュメントに対する矛盾を識別するプログラムを開発しました。データベース。矛盾がある場合は、適切なSQLを構築して不足しているフィールドを作成します。追加のものをドロップしません。
次に、一意の名前で識別されるパッチのリストを含むdbpatchテーブルがあります。特定のパッチが欠落している場合、それらが適用され、適切なレコードがdbpatchテーブルに追加されます。ほとんどの場合、これは新しいストアドプロシージャ、フィールドのサイズ変更、またはインデックスです
また、ユーザーが古いバージョンのクライアントを使用できるようにするためにチェックされるmin-db-versionも維持します。<!> gt; = min-db- versionおよび<!> lt; = cur-db-version。
データベースにバージョン番号を保存し、アプリケーションにバージョン番号を保存します。データベース構造を変更する必要があるたびに、データベースの構造を更新するコードを作成し、アプリケーションのバージョン番号を増やします。アプリケーションが起動すると、番号が比較され、必要に応じてコードを実行してデータベース構造を更新し、 データベースのバージョン番号を更新します。したがって、データベースはアプリケーションで最新の状態になっています。私のコードは次のようなものです
if DBVersion < AppVersion then
begin
for i := DBVersion+1 to AppVersion do
UpdateStructure(i);
end
else
if DBVersion > AppVersion then
raise EWrongVersion.Create('Wrong application for this database');
UpdateStructureは、次のような必要なコードを実行するだけです。
procedure UpdateStructure(const aVersion : Integer);
begin
case aVersion of
1 : //some db code
2 : //some more db code
...
...
end;
UpdateDatabaseVersion(aVersion);
end;
実際に同じコードを使用してデータベースを最初から作成できます
CreateDatabase;
for i := 1 to AppVersion do
UpdateStructure(i);