Buenas,
Cualquiera que haya realizado una aplicación con acceso a base de datos Firebird (o a una base de datos en general), habrá «lidiado» con los mensajes de error devueltos por el motor. Estos mensajes en inglés suelen ser bastante «feos» para mostrarlos tal cual al usuario, al cual no le aportan ningún tipo de información útil porque no suele entender lo que dice o cuál es el problema. En la entrada de hoy, y con la ayuda de una anterior, vamos a ver una forma de mostrar al usuario éstos mensajes de error dando algo más de información útil y que también podremos usar para crear algún tipo de log de errores.
Los componentes de acceso a Firebird usados en la demo son FireDAC dado que nos brindan de un acceso nativo al motor. No obstante, se ha hecho de tal manera que sea sencillo extrapolarlo a otros componentes (sobretodo si dan acceso nativo).
Si bien la demo se va a centrar en los errores producidos cuando se acepta un registro, la idea es extensible a los demás errores provocados por el motor. Así pues, al lío!
Una práctica usual para capturar dichos errores es usar un bloque try...except...end
, soliendo mostrar el mensaje devuelto por el motor. Algo parecido a esto:
try DataSet.Post; except on E: Exception do ShowMessage(E.Message); end;
o bien poniendo un mensaje en español genérico del estilo «Error grabando registro», o cosas por el estilo.
Lo primero que tenemos que averiguar es la clase del error que se genera, y eso lo conseguiremos con estas sencillas líneas de código:
try DataSet.Post; except on E: Exception do ShowMessage(E.ClassName); end;
Con esto vemos que la excepción generada es de tipo EIBNativeException
. Esta clase, que deriva de EDatabaseError
, tiene una propiedad ErrorCode
que nos dará el código de excepción lanzado por el motor, y gracias al cual podremos personalizar los mensajes y actuar en consecuencia. Los valores de este código son del estilo 335544XXX aunque la mayoría de componentes de acceso nativo suelen definir una serie de constantes para cada uno de los posibles valores y, por suerte para nosotros, suelen ponerse de acuerdo en la nomenclatura de las mismas. En el caso de FireDAC es la unit uADPhysIBCli.
Estructura de clases
Estas serán las clases que usaremos para nuestro propósito y que pasaremos a explicar y desgranar a continuación.
La clase TFBErrors.
Esta clase será la clase base, la que nos hará el trabajo oscuro gracias a sus métodos protegidos (protected). Definiremos un constructor estándar capaz de aceptar cualquier tipo de excepción y conexión, para luego redefinirlo en las clases descendientes según los componentes de acceso escogidos.
Esta clase tiene 2 métodos abstractos (que tendremos que implementar en las clases descendientes), uno es GetErrorCode
, que nos devolverá el código de error devuelto por el motor y CreateMeta
, que creará el objeto TFBMetaData
para acceder al metadato de la base de datos (el cual también depende de los componentes de acceso y por tanto es necesario crearlo en las clases especializadas).
Los demás métodos protegidos se encargarán de extraer la información necesaria de los mensajes de error devueltos por el motor y devolver un mensaje más «entendible» para el usuario o para generar nuestro log de errores. Siempre que sea posible, estos métodos devolverán, entre otros, nombre de la tabla, índice, campos que provocan en error y/o motivo del mismo.
La clase TFBErrorsFireDAC
Esta será la clase especializada de TFBErrors para FireDAC.
Como podemos ver en el diagrama de clases, implementamos los métodos abstractos de la clase padre así como el método GetMessageError
para terminar de adaptarlo a nuestras necesidades.
También podemos ver que «especializamos» nuestro constructor para que sólo pueda recibir componentes FireDAC.
Para facilitar el trabajo al programador, también hemos definido una función de clase, GetFBMessageError
que básicamente creará un objeto TFBErrorsFireDAC
y devolverá el resultado de la llamada al método GetMessageError
.
Uso de la clase
Pues ya sólo nos queda ver cómo usamos nuestra clase recién creada, y qué mejor que verlo en un ejemplo:
procedure TMainFrm.bPrimaryClick(Sender: TObject); begin qMaster.Append; qMaster.FieldByName('id').AsInteger := 1; try qMaster.Post; except on E: Exception do begin ShowMessage(TFBErrorsFireDAC.GetFBMessageError(E, ADConnection1)); qMaster.Cancel; end; end; end;
Fijémonos en el control de la excepción, basta con poner en el uses la unidad donde estará definidas nuestras clases y hacer una llamada al método de clase con los parámetros necesario. Tan simple como esto 🙂
En el programa demo que adjunto podréis probar 7 de los posibles errores devueltos por Firebird, como:
- Calve primaria duplicada
- Deadlock (para éste tendréis que abrir 2 instancias de la demo)
- Clave única duplicada
- Campo requerido
- Clave foranea
- Validación de una columna (FireDAC no controla esta excepción, por lo que el tipo de error devuelto no es EIBNativeException sino EDatabaseError, por lo que no se puede gestionar correctamente)
- Constraint check
Como siempre, podéis bajaros la unit con las clases, así como un programa demo para probarlo desde aquí.
Espero que os sea de utilidad.
Nos leemos
Excelente aporte, lo implementaré pronto. Gracia
Gracias a ti por visitar el blog