2009-11-26 8 views
8

Sto lavorando a un certo numero di applicazioni Delphi che dovranno aggiornare le proprie strutture di database sul campo quando vengono rilasciate nuove versioni e quando gli utenti scelgono di installare moduli aggiuntivi. Le applicazioni utilizzano una varietà di database incorporati (DBISAM e Jet al momento, ma questo potrebbe cambiare).Controllo delle versioni del database nelle applicazioni installate usando Delphi

In passato l'ho fatto con DBISAM utilizzando i numeri di versione utente che possono essere memorizzati con ciascuna tabella. Ho spedito un set di file di database aggiuntivo e vuoto e, all'avvio, ho confrontato i numeri di versione di ogni tabella utilizzando FieldDefs per aggiornare la tabella installata, se necessario. Mentre questo funzionava, mi è sembrato maldestro dover spedire una copia di riserva del database e le versioni più recenti di DBISAM hanno cambiato la metodologia di ristrutturazione delle tabelle in modo che sia necessario riscriverlo comunque.

Sono in grado di vedere due modi di implementare questo: memorizzare un numero di versione con il database e utilizzare gli script DDL per passare da versioni precedenti a versioni più recenti o memorizzare una versione di riferimento della struttura del database all'interno dell'applicazione, confrontando il riferimento al database all'avvio e l'applicazione genera comandi DDL per aggiornare il database.

Penso che probabilmente dovrò implementare parti di entrambi. Non voglio che l'applicazione diff diff tra il database e la struttura di riferimento ogni volta che l'applicazione si avvia (troppo lento), quindi ho bisogno di un numero di versione della struttura del database per rilevare se l'utente sta utilizzando una struttura obsoleta. Tuttavia, non sono sicuro di potermi fidare degli script pre-scritti per eseguire l'aggiornamento strutturale quando il database potrebbe essere stato parzialmente aggiornato in passato o quando l'utente potrebbe aver cambiato la struttura del database, quindi sono propenso a utilizzare un diff di riferimento per l'aggiornamento effettivo.

Ricercare la domanda Ho trovato un paio di strumenti per il controllo delle versioni del database ma sembrano tutti indirizzati a SQL Server e sono implementati al di fuori dell'applicazione reale. Sto cercando un processo che sarebbe strettamente integrato nella mia applicazione e che potrebbe essere adattato ai diversi requisiti del database (so che dovrò scrivere adattatori, classi discendenti personalizzate o codice evento per gestire le differenze in DDL per vari database, che non mi infastidiscono).

Qualcuno sa di qualsiasi cosa al largo della piattaforma che fa questo o, in mancanza, qualcuno ha qualche idea su:

  1. Il modo migliore per conservare una versione di riferimento di una struttura di database relazionale generica all'interno di un'applicazione .

  2. Il modo migliore per diff il riferimento rispetto al database effettivo.

  3. Il modo migliore per generare DDL per aggiornare il database.

risposta

2

ho un post sul blog qui su come faccio dbisam database versioning e sql server.

Le parti importanti sono:

Perché DBISAM non supporta le viste, il numero di versione è memorizzato (insieme con un mucchio di altre informazioni) in un file ini nella directory del database.

Ho un datamodule, TdmodCheckDatabase. Questo ha un componente TdbisamTable per ogni tabella nel database. Il componente tabella contiene tutti i campi nella tabella e viene aggiornato ogni volta che la tabella è modificata.

Per apportare modifiche al database, è stato utilizzato il seguente processo :

  1. Aumentare il numero di versione nell'applicazione
  2. marca e il test di DB modifiche.
  3. aggiornare le tabelle interessate a TdmodCheckDatabase
  4. Se necessario (raramente) aggiungono ulteriore aggiornamento query per TdmodCheckDatabase. Per esempio. per impostare i valori di nuovi campi o per aggiungere nuove righe di dati .
  5. Generare uno script di unità CreateDatabase utilizzando gli strumenti di database forniti.
  6. test Aggiornamento unità adattandola al nuovo db

Quando l'applicazione viene eseguita, va attraverso il seguente processo

  1. Se non viene trovato alcun database, quindi eseguire unità CreateDatabase e poi fare passaggio 3
  2. Ottenere il numero di versione corrente dal file ini del database
  3. Se è inferiore al numero di versione previsto, quindi Esegui CreateDatabase (per creare eventuali nuove tabelle) Controllare tutti i componenti tavolo TdmodCheckDatabase Applicare qualsiasi tavolo cambia run eventuali script di aggiornamento manuale
  4. aggiornare il numero di versione nel file di database ini

Un esempio di codice è

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

Grazie per una risposta estremamente dettagliata e utile. Ci vorrà un giorno o due finché non riesco a digerire tutto. –

1

Quello che faccio è memorizzare un numero di versione nel database e un numero di versione nell'applicazione. Ogni volta che ho bisogno di modificare la struttura del database, creo un aggiornamento del codice della struttura del database e aumenti il ​​numero di versione nell'applicazione. All'avvio dell'applicazione, confronta, numeri e, se necessario, esegue un codice per aggiornare la struttura del database E aggiorna il numero di versione del database. Quindi il database è aggiornato con l'applicazione.Il mio codice è qualcosa di simile

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 esegue semplicemente il codice di qualcosa di necessario come:

procedure UpdateStructure(const aVersion : Integer); 
begin 
    case aVersion of 
    1 : //some db code 
    2 : //some more db code 
    ... 
    ... 
    end; 
    UpdateDatabaseVersion(aVersion); 
end; 

Si può effettivamente utilizzare lo stesso codice per creare il database da zero

CreateDatabase; 
for i := 1 to AppVersion do 
    UpdateStructure(i); 
4

Storia simile qui. Memorizziamo un numero di versione DB in una tabella 'sistema' e controlliamo all'avvio. (Se la tabella/campo/valore non esiste allora sappiamo che è la versione 0 dove abbiamo dimenticato di aggiungere quel bit!)

Durante lo sviluppo, come e quando è necessario aggiornare il database, scriviamo uno script DDL da fare il lavoro e, una volta felice che funzioni, viene aggiunto come risorsa testuale all'app.

Quando l'app determina che è necessario aggiornarlo carica le risorse appropriate e le esegue/le. Se è necessario aggiornare più versioni, è necessario eseguire ogni script in ordine. Risulta essere solo poche righe di codice alla fine.

Il punto principale è che invece di utilizzare gli strumenti basati su GUI per modificare le tabelle in modo ad-hoc o "casuale", in realtà scriviamo subito il DDL. Ciò rende molto più facile, quando sarà il momento, creare lo script di aggiornamento completo. E la struttura non è richiesta.

2

Sto utilizzando ADO per i miei database. Uso anche uno schema di numeri di versione, ma solo come controllo di integrità. Ho sviluppato un programma che utilizza Connection.GetTableNames e Connection.GetFieldNames per identificare eventuali discrepanze rispetto a un documento XML che descrive il database "master". Se c'è una discrepanza, quindi costruisco l'SQL appropriato per creare i campi mancanti. Non ne lascio mai più.

Ho quindi una tabella dbpatch, che contiene un elenco di patch identificate da un nome univoco. Se mancano patch specifiche, vengono applicate e il record appropriato viene aggiunto alla tabella dbpatch. Molto spesso si tratta di nuovi processi memorizzati, ridimensionamento del campo o indici

Inoltre, mantengo una versione min-db, anch'essa verificata poiché permetto agli utenti di utilizzare una versione precedente del client, li consento solo di usa una versione> = min-db-version e < = cur-db-version.