2009-11-26 16 views
8

我正在開發一些Delphi應用程序,這些應用程序需要在新版本發佈時以及用戶選擇安裝附加模塊時在現場升級自己的數據庫結構。應用程序正在使用各種嵌入式數據庫(目前DBISAM和Jet,但這可能會改變)。在使用Delphi的已安裝應用程序中對數據庫進行版本控制

在過去,我已經使用DBISAM使用用戶版本號來完成此操作,而不是使用每個表可以存儲的數字。我發佈了一組額外的空數據庫文件,並在啓動時使用FieldDefs比較每個表的版本號,以便在必要時更新已安裝的表。雖然這工作,但我發現必須運送數據庫的備用副本並且DBISAM的較新版本改變了表格重構方法,所以我需要重寫這個。

我可以看到兩種實現方法:使用數據庫存儲版本號,並使用DDL腳本從舊版本獲取更新版本或在應用程序內存儲數據庫結構的引用版本,將引用與啓動數據庫,並讓應用程序生成DDL命令來升級數據庫。

我認爲我可能必須實現兩者的部分。每當應用程序啓動時(太慢),我都不希望應用程序將數據庫與引用結構區分開來,所以我需要一個數據庫結構版本號來檢測用戶是否使用過時結構。但是,我不確定在過去數據庫可能部分更新或用戶可能自己更改了數據庫結構時,我可以信任預先寫好的腳本進行結構升級,所以我傾向於使用實際更新的參考差異。

研究問題我找到了一些數據庫版本控制工具,但它們都似乎針對SQL Server,並且在實際應用程序之外實現。我正在尋找一個可以緊密集成到我的應用程序中的進程,並且可以適應不同的數據庫要求(我知道我必須編寫適配器,自定義後代類或事件代碼來處理各種DDL差異數據庫,這並沒有打擾我)。

有誰知道的東西掉,做這個或做不到這一點,沒有任何人有任何的想法貨架:

  1. 存儲的應用程序中的通用關係數據庫結構的參考版本的最好方法。

  2. 最好的方式來區分引用與實際數據庫。

  3. 生成DDL以更新數據庫的最佳方法。

回答

2

我有這裏的博客文章關於我如何做dbisam database versioningsql server

最重要的部分是:

由於DBISAM不支持視圖, 的版本號存儲(沿 了一堆其他信息)在INI文件 在數據庫目錄。

我有一個datamodule, TdmodCheckDatabase。這對數據庫中的每個表 都有一個 TdbisamTable組件。表格組件 包含表格中的所有字段,並且 在表格被 更改時更新。

爲了使數據庫的變化,使用了 以下過程:

  1. 增加應用
  2. 製作和測試數據庫變化的版本號。
  3. 更新TdmodCheckDatabase中的受影響表格
  4. 如有必要(很少)將進一步升級查詢添加到 TdmodCheckDatabase。例如。設置新字段的 值,或添加新的 數據行。
  5. 使用提供的數據庫 工具生成CreateDatabase單元腳本。
  6. 更新單元測試,以適應新的DB

當運行應用程序時,它會 通過以下過程

  1. 如果沒有數據庫被發現,然後運行的CreateDatabase單元,然後做 步驟3
  2. 從數據庫獲取當前版本號ini文件
  3. 如果它小於預期版本號,則 運行的CreateDatabase(創建任何新表) 入住TdmodCheckDatabase 每個表組件應用的任何表更改 運行任何手動升級腳本
  4. 更新數據庫中的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; 
+1

感謝您提供非常詳細和有用的答案。這將是一兩天,直到我可以消化所有。 – 2009-11-27 04:24:23

1

我所做的是在數據庫中存儲版本號,並在應用程序中存儲版本號。每次我需要更改數據庫結構時,都會創建一些代碼更新數據庫的結構,並增加應用程序中的版本號。當應用程序啓動時,它比較數字,如果需要運行一些代碼來更新數據庫結構更新數據庫的版本號。因此數據庫現在與應用程序保持同步。我的代碼是一樣的東西

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); 
4

類似的故事在這裏。 我們在一個「系統」表中存儲一個數據庫版本號並在啓動時檢查。 (如果表/字段/值不存在,那麼我們知道它是版本0,我們忘記添加該位!)

在開發過程中,當我們需要升級數據庫時,我們編寫一個DDL腳本來完成工作,並且一旦開始工作,它就會被添加爲應用程序的文本資源。

當應用程序確定需要升級時,它會加載相應的資源並運行它/它們。如果需要升級多個版本,則必須按順序運行每個腳本。原來只是最後幾行代碼。

最重要的是,不用使用基於GUI的工具以特設或「隨機」方式修改表格,我們實際上是直接寫DDL。這使得構建完整的升級腳本變得更容易。並不需要結構差異化。

2

我爲我的數據庫使用ADO。我也使用版本號方案,但只作爲一個完整性檢查。我有一個我開發的程序,它使用Connection.GetTableNames和Connection.GetFieldNames來識別描述「主」數據庫的XML文檔的任何差異。如果存在差異,那麼我將構建適當的SQL來創建缺少的字段。我從不放棄額外的。

然後我有一個dbpatch表,其中包含一個由唯一名稱標識的修補程序列表。如果缺少特定的修補程序,則會應用這些修補程序,並將相應的記錄添加到dbpatch表中。大多數情況下,這是新的存儲過程,或字段大小調整或索引

我還維護一個min-db版本,這也是因爲我允許用戶使用舊版本的客戶端,我只允許他們使用> = min-db-version的版本,並使用< = cur-db-version。

相關問題