Curso de C++ Builder


Acceso a bases de datos

TDataSet



TDataSet

Conjuntos de datos

TDataSet es la clase superior de la jerarquía de clases definida en C++Builder para definir conjuntos de datos. TTable, TQuery y TStoredProc son casos particulares de conjuntos de datos. De hecho, la mayoría de las propiedades, métodos y eventos que se utilizan en estas clases están definidas realmente en TDataSet, en TBDEDataSet o en TDBDataSet:

Propiedades

Propiedad Descripción
Active Abre el conjunto de datos cuando se establece a true y lo cierra cuando se fija a false.
Bof Devuelve true si el cursor está en el primer registro del conjunto de datos.
CachedUpdates Cuando está a true, las actualizaciones se mantienen en la caché de la máquina del cliente hasta que el programador los envíe al servidor. Cuando está a false, todos los cambios realizador en los conjuntos de datos se pasan automáticamente a la base de datos.
CanModify Determina si el usuario puede editar el conjunto de datos o no.
DataSource El componente TDataSource asociado.
DatabaseName El nombre de la base de datos con la que se está trabajando.
Eof Devuelve true cuando el cursor está llega al final del conjunto de datos.
FieldCount Es el número de columnas o campos del conjunto de datos.
Fields Columnas del conjunto de datos: array de objetos de tipo TField.
FieldValues Devuelve el valor de un campo especificado en el registro actual.
Found Indica si una operación de búsqueda ha tenido éxito o no.
Modified Indica si el registro actual ha sido modificado.
RecNo El número de registro actual en el dataset.
RecordCount Devuelve el número de registros en el dataset.
State Devuelve el estado actual del conjunto de datos.
UpdatesPending Cuando está a true, el buffer del conjunto de datos contiene modificaciones de los datos que aún no han sido enviadas a la base de datos.

Estados

Estados de TDataSet

vg: Post graba una nueva fila si el estado es dsInsert, actualiza la actual si es dsEdit

Métodos

Método Descripción
Append() Crea un registro vacío y lo añade al final del conjunto de datos.
AppendRecord() Añade un registro especificado al final del conjunto de datos.
ApplyUpdates() Envía a la base de datos cualquier actualización en caché que estuviese pendiente. La escritura no se realiza realmente hasta que se invoca al método CommitUpdates().
Cancel() Cancela cualquier modificación en el registro actual si no ha sido enviada con Post().
CancelUpdates() Cancela cualquier actualización en caché que estuviese pendiente.
ClearFields() Vacía el contenido de todos los campos del registro actual.
CommitUpdates() Se indica a la base de datos que realice las actualizaciones que estaban en caché y se vacía el buffer de actualizaciones en caché.
Close() Cierra el conjunto de datos.
Delete() Elimina el registro actual.
DisableControls() Desactiva los controles de datos asociados al conjunto de datos (muy útil para evitar el parpadeo de los controles visuales).
Edit() Permite la edición del registro actual.
EnableControls() Activa los controles de datos asociados al conjunto de datos.
FetchAll() Obtiene todos los registros desde la posición actual del cursor hasta el final del conjunto de datos y los almacena localmente.
FieldByName() Devuelve un campo TField dado su nombre.
First() Mueve el cursor al primer registro.
GetFieldNames() Recupera la lista con los nombres de los campos del conjunto de datos.
Insert() Inserta un registro en blanco y pone el conjunto de datos en modo de edición.
InsertRecord() Inserta un registro en el conjunto de datos con los datos que se le indiquen y los envía con Post().
Last() Sitúa el cursor en el último registro.
Locate() Búsqueda de un registro en particular.
Lookup() Localiza un registro por el medio más rápido posible y devuelve el dato contenido en el mismo (se usan índices si es posible).
MoveBy() Mueve el cursor un número determinado de filas.
Next() Mueve el cursor al siguiente registro.
Open() Abre el conjunto de datos.
Post() Escribe los datos modificados de un registro en el conjunto de datos (los envía a la base de datos o al buffer de actualizaciones en caché).
Prior() Mueve el cursor al registro anterior.
Refresh() Actualiza el contenido del registro actual leyéndolo de la base de datos.
RevertRecord() Cuando se utiliza la caché, este método ignora los cambios hechos previamente que todavía no han sido enviados a la base de datos.
SetFields() Establece los valores para todos los campos de un registro.
UpdateStatus() Devuelve el estado actual cuando las actualizaciones en caché se activan.

Eventos

Evento Descripción (¿cuándo se genera?)
AfterCancel Después de cancelar las modificaciones.
AfterClose Cuando se cierra un conjunto de datos
AfterDelete Después de que un registro se haya eliminado.
AfterEdit Después de que se ha modificado un registro.
AfterInsert Después de insertar un registro.
AfterOpen Después de abrir un conjunto de datos.
AfterPost Después de enviar los cambios de un registro.
BeforeCancel Antes de que se cancelen los cambios.
BeforeClose Antes de que se cierre un conjunto de datos.
BeforeDelete Antes de eliminar un registro.
BeforeEdit Antes de entrar en modo edición.
BeforeInsert Antes de que se inserte un registro.
BeforeOpen Antes de abrir el conjunto de datos.
BeforePost Antes de enviar las modificaciones.
OnDeleteError Cuando ocurre algún error al eliminar una tupla.
OnEditError Cuando ocurre algún error al editar una tupla.
OnNewRecord Siempre que se añade un nuevo registro.
OnPostError Cuando ocurre un error al enviar los cambios.
OnUpdateError Cuando ocurre un error al actualizar los datos.
OnUpdateRecord Cuando se actualiza el registro en la BD.

EJEMPLOS DE USO DE EVENTOS

Introducción de datos

Asignación de valores por omisión en el evento OnNewRecord utilizando FieldValues

void __fastcall TDataModuleXXX::TableNewRecord(TDataSet *DataSet)
{
  DataSet->FieldValues["Date"] = Date();
}

Validaciones a nivel de registros

Comprobaciones como respuesta al evento BeforePost, con la posibilidad de generar una excepción (DatabaseError)

void __fastcall TDataModulePersonal::tbEmpleadosBeforePost(TDataSet *DataSet)
{
  if (Date() - tbEmpleadosHireDate->Value < 365
     && tbEmpleadosSalary->Value > TopeSalarial)
     DatabaseError("¡Este es un enchufado!", 0);
}

Eliminación en cascada

Eliminar las líneas detalle de una relación master/detail interceptando el evento BeforeDelete

void __fastcall TDataModulePedidos::tbPedidosBeforeDelete(TDataSet *DataSet)
{
  tbLineas->First();
 
  if (! tbLineas->Eof)
     if (MessageDlg("¿Eliminar detalles?",mtConfirmation, TMsgDlgButtons()<<mbYes<<mbNo, 0)==mrYes) {
        do {
           tbLineas->Delete();
        } while (! tbLineas->Eof);
     } else {
        Abort();
	 }
}

Gestión de errores

Eventos de detección de errores: OnEditError, OnPostError, OnDeleteError

Errores

Posible solución ante un error de bloqueo no concedido
void __fastcall TDataModuleXXX::TableEditError
    (TDataSet *DataSet, EDatabaseError *E, TDataAction &Action)
{
  // Consultar al usuario

  if ( MessageDlg ( E->Message, mtWarning, 
                    TMsgDlgButton() << mbRetry << mbAbort, 0) == mrRetry ) {

     // Esperar entre 1 y 2 segundos antes de reintentar

     Sleep(1000 + random(1000));
     Action = daRetry;

  } else {

     // Abortar 

     Action = daAbort;
  }

}
 
Códigos de error BDE
void __fastcall TDataModuleXXX::TablePostError
     (TDataSet *TDataSet, EDatabaseError *E, TDataAction &Action)
{
  AnsiString S;
  EDBEngineError *Err = dynamic_cast<EDBEngineError*>(E);

  if (Err) {
     for (int i = 0; i < Err->ErrorCount; i++) {
         if (i > 0) AppendStr(S, '\n');
         TDBError *E = Err->Errors[i];
         AppendStr(S, Format( "%.4x (%d): %s",
                              ARRAYOFCONST((E->ErrorCode, E->NativeError,E->Message))));
     }
     DatabaseError(S, 0);
  }
}
 
Mensajes de error personalizados
int GetBDEError(EDatabaseError *E)
{
  EDBEngineError *Err = dynamic_cast<EDBEngineError*>(E);

  if (Err)
     for (int I = 0; I < Err->ErrorCount; I++) {
         TDBError *dbe = Err->Errors[I];
         if (dbe->NativeError == 0)
            return dbe->ErrorCode;
     }

  return -1;
}

// Relaciones maestro/detalle

void __fastcall TDataModulePedidos::tbPedidosDeleteError
    (TDataSet *TDataSet, EDatabaseError *E, TDataAction &Action)
{
  if (GetBDEError(E) == DBIERR_DETAILRECORDSEXIST)
     if ( MessageDlg( "¿Eliminar también las líneas de detalles?",
                      mtConfirmation, TMsgDlgButtons() << mbYes << mbNo, 0) == mrYes) {

        tbLineas->First();

        while (! tbLineas->Eof)
              tbLineas->Delete();

        // Reintentar el borrado en la tabla de pedidos

        Action = daRetry;

     } else // Fallar sin mostrar otro mensaje
        Action = daAbort;
}


// Evento OnPostError para cualquier tabla

void __fastcall TDataModuleXXX::PostError 
    (TDataSet *DataSet, EDatabaseError *E, TDataAction &Action)
{
  TTable *T = static_cast<TTable*>(DataSet);

  switch (GetBDEError(E)) {

    case DBIERR_KEYVIOL:
         DatabaseErrorFmt("Clave repetida en la tabla %s", ARRAYOFCONST((T->TableName)), 0);
         break;

    case DBIERR_FOREIGNKEYERR:
         DatabaseErrorFmt("Error en clave externa. Tabla: %s", ARRAYOFCONST((T->TableName)), 0);
         break;
  }
}


Índice de la sección