Pergunta

Eu estou trabalhando em uma série de aplicações Delphi que precisam atualizar suas próprias estruturas de banco de dados no campo quando novas versões são lançadas e quando os usuários optar por instalar módulos adicionais. Os aplicativos estão usando uma variedade de bancos de dados incorporados (DBISAM e Jet atualmente, mas isso pode mudar).

No passado, eu fiz isso com DBISAM usando os números de versão de usuário que podem ser armazenados com cada mesa. I enviado um extra, conjunto vazio de arquivos de banco de dados e, no arranque, comparou os números de versão de cada tabela usando os FieldDefs para atualizar a tabela instalado, se necessário. Enquanto isso funcionou Eu achei desajeitado ter que enviar uma cópia de reposição do banco de dados e versões mais recentes do DBISAM mudaram a tabela reestruturação metodologia para que eu vou precisar reescrever isso de qualquer maneira.

Eu posso ver duas formas de implementar esta: armazenar um número de versão com o banco de dados e usando scripts DDL para começar a partir de versões mais antigas para novas versões ou guardar uma versão de referência da estrutura de banco de dados dentro do aplicativo, comparando a referência para o banco de dados no start-up, e ter a aplicação gerar comandos DDL para atualizar o banco de dados.

Eu acho que eu provavelmente terá que implementar partes de ambos. Não vou desejar que o aplicativo diff o banco de dados contra a estrutura de referência cada vez que o aplicativo é iniciado (muito lento), então eu vou precisar um número de versão estrutura de banco de dados para detectar se o usuário está usando uma estrutura ultrapassada. No entanto, não estou certo de que posso confiar em scripts pré-escritas para fazer a atualização estrutural quando o banco de dados poderia ter sido parcialmente atualizado no passado ou quando o usuário pode ter-se alterado a estrutura de banco de dados, por isso estou inclinado a usar um diff de referência para a atualização real.

Pesquisando a pergunta que eu encontrei um par de ferramentas de banco de dados de versão, mas todos eles parecem direcionados para SQL Server e são implementados fora da aplicação real. Estou à procura de um processo que iria ser totalmente integrado em minha aplicação e que poderia ser adaptado a diferentes requisitos de banco de dados (eu sei que eu vou ter que adaptadores de gravação, aulas personalizadas descendentes, ou código de evento para diferenças Manusear de DDL para vários bases de dados, que não me incomoda).

Alguém sabe de alguma coisa fora da prateleira que faz isso ou na sua falta, alguém tem alguma opinião sobre: ??

  1. A melhor maneira de armazenar uma versão de referência de uma estrutura de banco de dados relacional genérico dentro de uma aplicação.

  2. A melhor maneira de diff a referência contra o banco de dados real.

  3. A melhor maneira de gerar DDL para atualizar o banco de dados.

Foi útil?

Solução

Eu tenho um post aqui sobre como eu faço DBISAM banco de dados de versões e SQL server .

As partes importantes são:

Porque DBISAM não suporta vistas, o número da versão é armazenado (junto com um monte de outras informações) em uma ini arquivo no diretório de banco de dados.

Eu tenho um datamodule, TdmodCheckDatabase. Isto tem um componente TdbisamTable para cada mesa no banco de dados. O componente de tabela contém todos os campos na tabela e é atualizado sempre que a tabela é alterado.

Para fazer alterações de banco de dados, os seguinte processo foi utilizado:

  1. Aumentar o número de versão no aplicativo
  2. fazer e testar DB alterações.
  3. Atualize as tabelas afetadas em TdmodCheckDatabase
  4. Se necessário (raramente) adicionar mais perguntas atualizar para TdmodCheckDatabase. Por exemplo. para definir o valores de novos campos, ou para adicionar nova linhas de dados.
  5. Gerar um script unidade CreateDatabase usando o banco de dados fornecido ferramentas.
  6. Atualização testes de unidade para atender o novo db

Quando o aplicativo é executado, ele vai através do seguinte processo

  1. Se nenhum banco de dados for encontrado, em seguida, executar unidade CreateDatabase e depois fazer Passo 3
  2. Obter o número da versão atual do arquivo ini banco de dados
  3. Se for menor do que o número versão esperada, em seguida, Run CreateDatabase (para criar novas tabelas) Verifique todos os componentes da tabela em TdmodCheckDatabase Aplicar as alterações da tabela executar quaisquer scripts de atualização manuais
  4. Atualize o número da versão no arquivo ini banco de dados

Um exemplo de código é

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;

Outras dicas

história semelhante aqui. Nós armazenar um número de versão DB em uma tabela 'sistema' e verifique se na inicialização. (Se a tabela / campo / valor não existir, em seguida, nós sabemos que é a versão 0 em que se esqueceu de acrescentar que pouco in!)

Durante o desenvolvimento, como e quando é preciso atualizar o banco de dados que escrever um script DDL para fazer o trabalho e, uma vez feliz que ele está OK ele é adicionado como um recurso de texto para o aplicativo funcionando.

Quando o aplicativo determina que ele precisa atualizá-lo carrega o recurso (s) apropriado e executa-lo / eles. Se ele precisa atualizar várias versões ele deve executar cada script em ordem. Acaba por ser apenas algumas linhas de código no final.

O ponto principal é que em vez de usar ferramentas baseadas do GUI para modificar as tabelas de forma 'aleatória' ad-hoc ou nós realmente escrever o DDL imediatamente. Isso torna muito mais fácil, quando chegar a hora, para construir o script de upgrade completo. E estrutura diff'ing não é necessária.

Eu estou usando ADO para os meus bancos de dados. Eu também uso um esquema de número de versão, mas apenas como uma verificação de sanidade. Eu tenho um programa que eu desenvolvi que utiliza os Connection.GetTableNames e Connection.GetFieldNames para identificar qualquer discrepância contra um documento XML que descreve o banco de dados "mestre". Se houver uma discrepância, então eu construir o SQL apropriado para criar os campos em falta. Eu nunca deixar cair os adicionais.

Em seguida, tenho uma tabela dbpatch, que contém uma lista de correções identificadas por um nome único. Se houver patches específicos faltando, então eles são aplicados e o registro apropriado é adicionado à tabela de dbpatch. Na maioria das vezes este é novos procedimentos armazenados, ou redimensionamento campo, ou índices

Eu também manter um min-db-versão, que também é verificado desde que eu permitir aos usuários utilizar uma versão mais antiga do cliente, eu só permitir que eles usem uma versão que é> = min-db-versão e <= cur-db-version.

O que faço é armazenar um número de versão no banco de dados, e um número de versão no aplicativo. Toda vez que eu preciso mudar a estrutura de banco de dados, eu criar alguma atualização de código a estrutura do banco de dados e aumentar o número de versão no aplicativo. Quando o aplicativo é iniciado, ele compara, números, e se necessário é executado algum código para atualizar a estrutura do banco de dados E atualizar o número da versão do banco de dados. Assim, o banco de dados já está atualizado com a aplicação. Meu código é algo como

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 apenas executa o código algo necessário como:

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

Você pode realmente usar o mesmo código para criar o banco de dados a partir do zero

CreateDatabase;
for i := 1 to AppVersion do
  UpdateStructure(i);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top