27 septiembre 2007

La potencia de los ClientDataSet (II)

Después de haber creado la base de datos en Firebird 2.0 ya podemos comenzar a crear un nuevo proyecto que maneje dicha información. El objetivo del proyecto es hacer el siguiente mantenimiento de clientes:


CREANDO LA CAPA DE ACCESO A DATOS

La capa de acceso a datos encargada de conectar con Firebird va a incorporar los siguientes componentes:


- Un contenedor DataModule llamado AccesoDatos.

- Un compomente IBDatabase (pestaña Interbase) llamado BaseDatos.

- Un componente IBTransaction (pestaña Interbase) llamado Transaccion.

- Dos componentes IBQuery (pestaña Interbase) llamados LstClientes y Clientes.

Se sopone que la base de datos BASEDATOS.FDB esta al lado de nuestro ejecutable. Vamos a comenzar a configurar cada uno de estos componentes.

Hacemos doble clic sobre el componente IBDatabase y en el campo User Name le ponemos SYSDBA. En en password le ponemos masterkey:


Pulsamos OK, y después en la propiedad DefaultTransaction del componente IBDatabase seleccionamos Transaccion, desactivamos la propiedad LoginPrompt y nos aseguramos que en SQLDialect ponga un 3.

Para el componente IBTransaction llamado Transaccion seleccionaremos en su propiedad DefaultDatabase la base de datos BaseDatos.

Como nuestra intención es tener una rejilla que muestre el listado general de clientes y un formulario para dar de alta clientes, lo que vamos a hacer es crear una tabla IBQuery para la rejilla llamada LstClientes y otra tabla para el formulario del cliente llamada Clientes. Sería absurdo utilizar un mismo mantenimiento para ambos casos ya que si tenemos miles de clientes en la rejilla, el tener cargados en memoria todos los campos del cliente podría relentizar el programa.

En los componentes LstClientes y Clientes vamos a configurar también:

Database: BaseDatos
Transaction: Transaccion
UniDirectional: True

El motivo de activar el campo UniDirectional es para que el cursor SQL trabaje más rápido en el servidor ya que los componentes ClientDataSet gestionan en memoria los registros leidos anteriormente.

Como en la rejilla sólo quiero mostrar los campos ID, NOMBRE y NIF entonces en el componente LstClientes en su propiedad SQL vamos a definir:

SELECT ID,NOMBRE,NIF FROM CLIENTES
ORDER BY ID DESC

y para el componente Clientes:

SELECT * FROM CLIENTES
WHERE ID=:ID

En la condición WHERE de la SQL hemos añadido el parámetro :ID para que se pueda más adelante acceder directamente a un registro en concreto a partir de su campo ID.

Una vez definida nuestra capa de acceso a datos en el siguiente artículo nos encargaremos de definir nuestra lógica de negocio.

Pruebas realizadas en Firebird 2.0 y Delphi 7.

7 comentarios:

rossana dijo...

hola, antes que nada te agradezco el esfuerzo, muy didáctico y provechoso el material. Espero continúes.

Estoy desarrollando una aplicación con Delphi 7 + IBX + ClientDataSets, uso el evento OnReconcileError para capturar los errores, pero quisiera personalizarlos.
Tengo 2 caminos, por fuerza bruta, viendo que despliega cada mensaje y mapear al español, o obtenerlos de algun lado. He revisado los .pas que vienen con Delphi y no se donde está la correspondencia entre código de errores y msgs.
Te agradezco tu opinión, saludos cordiales, ro

Taxylon Software dijo...

El problema radica en que el código de error depende del motor de bases de datos. Por ejemplo, el típico error -404 cuando cometemos un error con la SQL.

Este es el código que utilizo yo en el evento OnUpdateError del componente TDataSetProvider:

procedure TBases.Mostrar_Error_IB(Sender: TObject;
DataSet: TCustomClientDataSet; E: EUpdateError; UpdateKind: TUpdateKind;
var Response: TResolverResponse);
begin
// Este procedimiento se encarga de traducir ciertos mensajes SQL de
// error de Interbase a mensajes en castellano y comprensibles.

if E.OriginalException is EIBInterBaseError then
begin
case EIBInterBaseError(E).SQLCode of
-803: Application.MessageBox('El código o número identificativo está repetido',
'Error de acceso a datos', MB_ICONWARNING)
else
Application.MessageBox(PChar(E.Message), 'Error de acceso a datos', MB_ICONERROR);
end;
end
else
Application.MessageBox(PChar(E.Message), 'Error de acceso a datos', MB_ICONERROR);
end;

El código -803 es el PRIMARY KEY de Interbase.

De todas formas, yo procuro que nunca un usuario vea estos tipos de error. Lo que hago es crear un formulario de error de tal manera que si al usuario le da un error de bases de datos le aparezca la típica ventana diciendo que hay un error de conexión y que pulse ENVIAR para informar al proveedor del programa.

Internamente me envía por correo a una cuenta que tengo preconfigurada el error devuelto por el motor de bases de datos. Como si lo hubiera provocado el depurador EurekaLog.

Lo ideal sería localizar en que archivo tiene guardados los mensajes de error y los código el motor de bases de datos Interbase (en Firebird ya lo he conseguido).

Espero haberte sido de ayuda.

Saludos.

rossana dijo...

Hola, te agradezco muchísimo por responder, me da una pista, el tema es que no encuentro donde están esas constantes -404, -803, vi unos en IB, pero no se corresponden con esos números.
¿Podrías decirme de donde los obtenes?

Chequeas en los eventos del Provider, no del ClientDataSets, quisiera saber las ventajas.

Muchas gracias por tu tiempo
Saludos (desde uruguay)
ro

Taxylon Software dijo...

Hola Rossana.

Los códigos los obtengo de los mismos errores SQL que escupe Interbase cuando se produce alguna excepción. Por ejemplo, cuando intentamos abrir una tabla y da un error:

try
TArticulos.Open
except
raise
end;

La misma excepción devuelve el código al lado del error. Eso sólo en los errores genéricos, hay otros que son mucho más difíciles de resolver.

Al final he conseguido prevenirlos todos realizando comprobaciones con métodos como Assert para evitar futuros errores.

Los errores que suelen ocurrir en Interbase no suelen ser muchos:

- Violación de claves primarias.

- Acceso a tablas o campos que no existen.

- Errores de concurrencia (deadlok), etc. etc.

Al final, con experiencia y paciencia aprenderás a blindar los programas como si fueran tanques.

Saludos.

Taxylon Software dijo...

Me olvidaba una cosa Rossana...

El motivo de utilizar los eventos del objeto provider es porque a veces hay tablas maestro detalle (dos clientdataset) vinculadas al mismo provider.

Entonces para evitarme escribir el código de los eventos dos veces lo hago una sola vez en el provider.

Al fin y al cabo, todo pasa por el provider. El objeto ClientDataSet es sólo el buzón de datos y el DataSetProvider es el cartero.

ok?

rossana dijo...

Te agradezco tu completa respuesta, me ha sido útil, y ya me pondré manos a la obra.

(Aclaro que también trabajo con Firebird v 2.0, aunque usando los ibx por ahora, ya que uso sentencias sql comunes a ambos motores)
En la documentación vi números de error (que están en iberror.h), este es el de violación de la PK por ejemplo 335544548L (long).
Eso es lo mas cercano que encontré.

El motivo de utilizar los eventos del objeto provider es porque a veces hay tablas maestro detalle (dos clientdataset) vinculadas al mismo provider me pareció buenísimo :)

Saludos, ro :)

Rocio Vergara Martinez dijo...

Gracias por todas tus publicaciones han sido de gran ayuda!!
Me puedes guiar con lo siguiente, he creado una tabla temporal con cliendataset pero ahora quiero traspasar los datos a la base de datos y no se coo hacerlo.
muchas gracias.

Publicidad