2009-11-26 19 views
8

Estoy trabajando en una serie de aplicaciones Delphi que necesitarán actualizar sus propias estructuras de base de datos en el campo cuando se lanzan nuevas versiones y cuando los usuarios eligen instalar módulos adicionales. Las aplicaciones están usando una variedad de bases de datos integradas (DBISAM y Jet actualmente, pero esto puede cambiar).Versiones de bases de datos en aplicaciones instaladas usando Delphi

En el pasado he hecho esto con DBISAM utilizando los números de versión de usuario que se pueden almacenar con cada tabla. Envié un conjunto adicional y vacío de archivos de base de datos y, al inicio, comparé los números de versión de cada tabla usando FieldDefs para actualizar la tabla instalada si es necesario. Mientras esto funcionó, me pareció torpe tener que enviar una copia adicional de la base de datos y las versiones más recientes de DBISAM han cambiado la metodología de reestructuración de la tabla, así que necesitaré volver a escribir esto de todos modos.

Veo dos formas de implementar esto: almacenar un número de versión con la base de datos y usar scripts DDL para obtener versiones anteriores o almacenar una versión de referencia de la estructura de la base de datos dentro de la aplicación, comparando la referencia al base de datos en la puesta en marcha, y hacer que la aplicación genere comandos DDL para actualizar la base de datos.

Creo que probablemente tendré que implementar partes de ambos. No querré que la aplicación diferencie la base de datos de la estructura de referencia cada vez que se inicie la aplicación (demasiado lento), por lo que necesitaré un número de versión de la estructura de la base de datos para detectar si el usuario está usando una estructura desactualizada. Sin embargo, no estoy seguro de poder confiar en las secuencias de comandos escritas previamente para realizar la actualización estructural cuando la base de datos podría haberse actualizado parcialmente en el pasado o cuando el usuario puede haber cambiado la estructura de la base de datos, por lo que me inclino a utilizar una diff de referencia para la actualización real.

Investigar la pregunta He encontrado un par de herramientas de control de versiones de bases de datos, pero todas parecen dirigidas hacia SQL Server y se implementan fuera de la aplicación real. Estoy buscando un proceso que esté estrechamente integrado en mi aplicación y que pueda adaptarse a diferentes requisitos de la base de datos (sé que tendré que escribir adaptadores, clases de descendientes personalizadas o código de evento para manejar las diferencias en DDL para varios bases de datos, eso no me molesta).

¿Alguien sabe de nada fuera de la plataforma que hace esto o en su defecto, ¿alguien tiene alguna idea sobre: ​​

  1. La mejor manera de almacenar una versión de referencia de una estructura de base de datos relacional genérica dentro de una aplicación .

  2. La mejor manera de diferenciar la referencia contra la base de datos real.

  3. La mejor forma de generar DDL para actualizar la base de datos.

Respuesta

2

tengo una publicación de blog aquí sobre cómo hago dbisam database versioning y sql server.

Las partes importantes son:

Debido DBISAM no admite vistas, el número de versión se almacena (junto con un montón de otra información) en un archivo ini en el directorio de base de datos.

Tengo un módulo de datos, TdmodCheckDatabase. Tiene un componente TdbisamTable para cada tabla en la base de datos. El componente de tabla contiene todos los campos de la tabla y se actualiza cada vez que se cambia la tabla .

Para realizar cambios de base de datos, se utilizó el siguiente proceso :

  1. Aumenta el número de versión de la aplicación
  2. hacer y probar cambios de base de datos.
  3. Actualizar las tablas afectadas en TdmodCheckDatabase
  4. Si es necesario (raramente) Añadir mejorar aún más las consultas a TdmodCheckDatabase. P.ej. para establecer los valores de nuevos campos o para agregar nuevas filas de datos .
  5. Genera un script de unidad CreateDatabase utilizando las herramientas de base de datos suministradas.
  6. pruebas de actualización de unidad para adecuarla a la nueva db

Cuando se ejecuta la aplicación, se va a través del siguiente proceso

  1. Si no se encuentra ninguna base de datos, a continuación, ejecutar la unidad CreateDatabase y entonces paso 3
  2. Obtenga el número de versión actual de la base de datos en el archivo
  3. Si es menor que el número de versión esperado, entonces Ejecutar CreateDatabase (para crear nuevas tablas) Compruebe cada componente de tabla en TdmodCheckDatabase Aplicar cualquier tabla cambia ejecuta ninguna secuencia de actualización manual
  4. Actualizar el número de versión en el archivo ini base de datos

Un ejemplo de código es

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

Gracias por una respuesta extremadamente detallada y útil. Pasarán uno o dos días hasta que pueda digerirlo todo. –

1

Lo que hago es almacenar un número de versión en la base de datos y un número de versión en la aplicación. Cada vez que necesito cambiar la estructura de la base de datos, creo un código para actualizar la estructura de la base de datos y aumentar el número de versión en la aplicación. Cuando se inicia la aplicación, compara números y, si es necesario, ejecuta algún código para actualizar la estructura de la base de datos Y actualizan el número de versión de la base de datos. Por lo tanto, la base de datos está actualizada con la aplicación.Mi código es algo así 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 sólo se ejecuta el código de algo necesario como:

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

En realidad se puede utilizar el mismo código para crear la base de datos desde cero

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

Historia similar aquí. Almacenamos un número de versión de DB en una tabla de 'sistema' y lo comprobamos al inicio. (Si la tabla/campo/valor no existe, entonces sabemos que es la versión 0 donde olvidamos agregar ese bit)

Durante el desarrollo cuando necesitamos actualizar la base de datos, escribimos un script DDL para hacer el trabajo y una vez feliz de que esté funcionando bien, se agrega como recurso de texto a la aplicación.

Cuando la aplicación determina que debe actualizar, carga los recursos apropiados y los ejecuta. Si necesita actualizar varias versiones, debe ejecutar cada script en orden. Resulta ser solo unas pocas líneas de código al final.

El punto principal es que en lugar de utilizar las herramientas basadas en GUI para modificar las tablas de manera ad-hoc o 'aleatoria', escribimos el DDL directamente. Esto hace que sea mucho más fácil, cuando llegue el momento, construir el script de actualización completo. Y la estructura no es necesaria.

2

Estoy usando ADO para mis bases de datos. También uso un esquema de número de versión, pero solo como un control de cordura. Tengo un programa que desarrollé que usa Connection.GetTableNames y Connection.GetFieldNames para identificar cualquier discrepancia contra un documento XML que describe la base de datos "maestra". Si hay una discrepancia, entonces creo el SQL apropiado para crear los campos faltantes. Nunca dejo caer otros adicionales.

Luego tengo una tabla dbpatch, que contiene una lista de parches identificados por un nombre único. Si faltan parches específicos, se aplican y el registro apropiado se agrega a la tabla dbpatch. Lo más frecuente es que se trate de nuevos procesos almacenados o de cambio de tamaño de campo o de índices

También mantengo una versión min-db, que también se comprueba porque les permito a los usuarios usar una versión anterior del cliente, solo les permito utilice una versión que sea> = min-db-version y < = cur-db-version.

Cuestiones relacionadas