You are on page 1of 28

Interbase

José Alonso
pepealonso@supercable.es

IBX

Cedida su publicación a el Rinconcito de Delphi

No está autorizada su reproducción sin permiso expreso de su autor

Autor: José Alonso - pepealonso@supercable.es

IBX. Conexión directa a Interbase
Con los componentes IBX, podemos tener una conexión directa con una base de datos Interbase.

Hasta ahora, cuando nuestras aplicaciones necesitaban acceder a una fuente de datos, era necesario usar un “intermediario” que nos hiciese el trabajo. Para esto disponíamos del BDE, ODBC o ADO. Estas capas intermedias entre nuestro programa y los datos, han de ser distribuidas por nosotros ya que deben estar presentes en las máquinas donde queramos hacer correr nuestra aplicación. A cambio, nos ofrecen la posibilidad de soluciones genéricas. Con la versión 5 de Delphi, una nueva pestaña nos ofrece la posibilidad de una relación mas estrecha entre nuestra aplicación y los datos... siempre que nuestro soporte de datos sea Interbase. En la pestaña Interbase Express, podemos encontrar los nuevos componentes. Estos componentes usan directamente la API de Interbase lo que hace que su rendimiento sea muy superior a los componentes basados en el BDE. La relación que mantendrá nuestra aplicación con Interbase, será la de estar continuamente haciendo peticiones de datos al servidor y “escuchando” las respuestas a estas peticiones. ¿Cómo solicitamos datos a Interbase?. Mediante el uso de los componentes (en este caso los IBX). En este artículo no trataré los componentes TIBTable,TIBQuery,TIBStoredProc ni TIBUpdateSQL, por considerar que están solamente para ofrecer compatibilidad y permitir una migración menos traumática a las aplicaciones basadas en el BDE. Aunque si me permitís una opinión, nunca he considerado buena idea lo de los cambios a medias. Casi siempre lo que se empieza a medias, termina a medias.

El componente TIBDatabase.
Este componente es el que nos facilita la conexión con la base de datos. Para ello, solo hemos de indicarle la ruta completa y el nombre de nuestra base de datos en la propiedad DatabaseName del componente Es posible conectarse a bases de datos locales, es decir, situadas en nuestra máquina o a bases de datos situadas en máquinas remotas. En el caso de una base de datos local, habremos de especificar la unidad, directorio y nombre de la base de datos. IBDatabase1.DataBaseName := ‘C:\Contabilidad\Datos\Gestion.gdb’ En el caso de que la base de datos se encuentre en una máquina remota, la forma de conectarse a ella, variara dependiendo del protocolo de comunicación que utilicemos. Por ejemplo, si usamos el protocolo TCP/IP la forma típica de conectarnos sería: IBDatabase1.DatabaseName := ‘Morgan:C:\Contabilidad\Datos\Gestion.gdb’

El Rinconcito de Delphi

1

Pero TIBDatabase, no solo se ocupa de la conexión con la base de datos. También encontraremos propiedades y métodos que nos ofrecen información de la conexión y de la propia base de datos: Si queremos saber si la conexión con el servidor se ha establecido, podemos usar la función TestConnected que nos devolverá True si la conexión se ha efectuado y False en caso contrario. IBDatabase1.Connected := True; If IBDatabase1.TestConnected then ShowMessage(‘La conexión se ha establecido con éxito’) Else ShowMessage(‘Fallo al intentar conectar con ‘ + IBDatabase1.DatabaseName); Podemos especificar el tiempo que queremos que dure la conexión con la base de datos mediante la propiedad IdleTimer, la cual admite un valor entero que expresa la cantidad de milisegundos que durará la conexión o cero para que la conexión sea permanente. Antes de establecer la conexión con la base de datos, tenemos que definir los parámetros que queremos pasar al servidor. Mediante la propiedad Params podemos hacerlo. IBDatabase1.Params.Values[‘user_name’] := ‘SYSDBA’; IBDatabase1.Params.Values[‘password’] := ‘masterkey’; Todas estas propiedades se pueden asignar en tiempo de diseño de una forma cómoda, con el editor de propiedades al cual se puede acceder mediante el botón derecho del ratón y escogiendo la opción Database Editor. La figura 1, muestra el aspecto de este editor.

Figura 1. La ventana de configuración de TIBDataBase.

GetTableNames(List: Tstrings; SystemTables: Boolean); Nos devuelve en el argumento List una lista con todas las tablas que contiene la base de datos. El argumento SystemTables

El Rinconcito de Delphi

2

indica si queremos que nos devuelva además las tablas de sistema. Por defecto el valor es False lo que hace que no se incluyan. GetFieldNames(const TableName: string; List: TStrings); Nos devuelve en el argumento List una lista con el nombre de todos los campos que integran una tabla determinada que indicaremos en el argumento TableName.

El componente TIBTransaction.
Una transacción se puede definir como un conjunto de operaciones que deben ser tomadas como una unidad. Todas las operaciones que realicemos en la base de datos, la definición y manipulación de datos, trabajan en el contexto de una o mas transacciones. Una transacción nos ofrece una “foto” del estado de la base de datos y en un Gestor de bases de datos multigeneracional como es Interbase, el control de las transacciones cobra una importancia vital. A diferencia del BDE en la que no es obligatorio el uso de explícito de transacciones, con IBX es necesario manejar las transacciones, ya que de otra forma, no podremos acceder a las tablas de la base de datos. Para ello contamos con el componente TIBTransaction. Con este componente, podemos controlar todo lo relativo a las transacciones. Como ya he dicho, en una aplicación es obligatorio que haya al menos una transacción, aunque lo normal es que usemos varias, dependiendo de las necesidades y requerimientos del caso. Es posible asignar una transacción a un componente TIBDatabase de manera que este sea esta la transacción por defecto que se usará, de no indicar otra cosa, al asociar cualquiera de los componentes de manipulación de datos al TIBDatabase. Para ello, el TIBDatabase cuenta con una propiedad: DefaultTransaction donde podremos indicar cual será la transacción por defecto que usaremos. Así mismo, TIBTransaction cuenta con una propiedad DefaultDataBase, donde asignaremos el TIBDatabase que usara por defecto el componente. Sin duda, la operación a la que habremos de prestar más atención a la hora de configurar el componente TIBTransaction será la propiedad Params. Para facilitarnos la tarea de configurar correctamente la transacción, podemos contar con el editor que vemos en la figura 2 y que como siempre, se obtiene a través de pulsar el botón derecho del ratón sobre el componente.

Figura 2. La ventana de configuración de TIBTransaction.

Escapa al objetivo de este artículo explicar con detenimiento los distintos niveles de aislamiento que representa cada una de las opciones que se nos presentan. Si os sirve de algo mi experiencia, la mayoría de las transacciones que manejo en mis aplicaciones las configuro como “Read Commited”. Este nivel de aislamiento permite que se seleccionen, inserten, actualicen y borren datos de transacciones ya confirmadas y es suficiente para la mayoría de las ocasiones.

El Rinconcito de Delphi

3

Típicamente, el trabajo con un componente TIBTransaction, se limitará a iniciarla y a confirmar o descartar los cambios que se hayan efectuado en su contexto. Para esto se vale de los siguientes métodos: StartTransaction da comienzo a la transacción. Una vez llamado este método, todas las operaciones que se realicen en la base de datos, habrán de confirmarse o cancelarse invocando a los siguientes métodos. Commit termina la transacción confirmando los cambios que se hayan producido. La transacción se cierra y se liberan los recursos. Rollback termina la transacción descartando los cambios que se hayan producido. La transacción se cierra y se liberan los recursos. Como habrán observado, tanto Commit como Rollback hacen que las conexiones con las tablas se cierren, con lo que una vez invocados estos métodos habría que volver a abrir la transacción y restablecer las conexiones con las tablas para que pudiésemos ver los datos. Si deseamos confirmar o cancelar los cambios, sin que la conexión con la transacción se cierre, podemos usar los métodos CommitRetaining o RollbackRetaining que hacen lo mismo que los métodos anteriores, pero sin perderse la conexión con los datos. El uso típico de estos métodos se asemejaría a lo siguiente: Procedure TData.DataCreate( Sender: Tobject); Begin // Al crearse el Datamodule, iniciamos la transacción IbTransaction1.StartTransaction; End; Procedure Tdata.DataDestroy(Sender: Tobject); Begin // Al cerrar el Datamodule, cerramos la transacción... Si está abierta if IBTransaction1.InTransaction then IBTransaction1.Commit; End; Procedure TData.UnaTablaAfterPost(Sender: DataSet); Begin // Confirmamos los cambios y continuamos try IBTransaction1.CommitRetaining Except // Si hay errores, descartamos y seguimos IBTransaction1.RollbackRetaining End; End;

TIBDataSet
Este componente será el que centre gran parte de nuestra atención, ya que será el que usemos preferentemente en nuestra aplicación. Con él, podremos acceder a una tabla o vista de nuestra base de datos y realizar las operaciones típicas de mantenimiento de la misma. Como descendiente de la clase TDataSet, mantiene con esta propiedades y métodos que son muy comunes, por lo que no se entrará a explicarlos. En la figura 3 puede ver el aspecto del Object Inspector apuntando a un TIBDataSet.

El Rinconcito de Delphi

4

Figura 3. El inpector de objetos mostrando las propiedades de TIBDataSet.

Hay dos propiedades que son de asignación obligatoria si queremos trabajar con un TIBDataSet: Database y Transaction. Si al componente TIBDatabase le asignamos en su momento un valor a la propiedad DefaultTransaction, será este valor el que aparezca automáticamente en la propie dad Transaction del TIBDataSet. Una de las quejas más comunes que se hace al BDE, consiste en la cantidad de instrucciones que lanza cuando nosotros le hacemos un requerimiento de datos. Cualquiera puede verlo con el SQLMonitor. Una simple operación de apertura de una tabla, genera un buen puñado de instrucciones. En cambio, con TIBDataSet tenemos la oportunidad de decirle exactamente que instrucciones debe lanzar para las distintas operaciones. Para ello disponemos de cinco propiedades: InsertSQL. Almacena la instrucción SQL válida para insertar una o varias filas en la tabla. DeleteSQL. Almacena la instrucción SQL válida para borrar una o varias filas de la tabla. ModifySQL. Almacena la instrucción SQL válida para modificar una o varias filas de la tabla. RefreshSQL. Almacena la instrucción SQL válida para releer la información de una o varias filas. SelectSQL. Almacena la instrucción SQL válida para obtener un conjunto de datos de la tabla. El componente dispone de unos asistentes que nos facilitan la introducción de estas

El Rinconcito de Delphi

5

sentencias de una forma visual. Para acceder a ellos solo hay que hacer clic con el botón derecho del ratón y escoger la opción adecuada. Ver figuras 4 y 5.

Figura 4. Construyendo visualmente la sentencia SELEC de un TIBDataSet.

Figura 5. Construyendo visualmente las sentencias Insert, Update y Delete de un TIBDataSet.

El Rinconcito de Delphi

6

Este componente, sus propiedades y métodos y sus eventos, requieren que nos demos un respiro, tomemos aire y lo abordemos con la suficiente profundidad en un próximo artículo. Pero para abrir boca, sugiero al amable lector que mire el ejemplo que acompaña a este artículo y compruebe la potencia que nos proporciona este componente. Se trata de una pequeña aplicación cuyo objetivo es rescatar filas de una tabla. Las condiciones que deban cumplir las filas son totalmente configurables. Se trata de un ejemplo de cómo manejar la propiedad SelectSQL de un TIBDataSet en tiempo de ejecución, amén del uso de ciertos métodos de TIBDatabase.

El Rinconcito de Delphi

7

Trabajando con TIBDaSet.Se verá como se puede configurar el componente, paso de parámetros, etc para las operaciones de mantenimiento de datos. En la anterior entrega de esta serie, dimos un repaso a todos los componentes de la paleta de Interbase. Vimos como configurar un TIBDatabase, como controlar las transacciones con TIBTransaction y alguna de las propiedades mas usuales de TIBDataSet. Deciamos en esa ocasión que en una futura entrega, se veria mas extensamente este ultimo componente, que sin duda centrará el 99% del trabajo con datos en nuestra aplicación. Como lo prometido es deuda, aquí va esta segunda entrega.

TIBDataSet. El centro de operaciones
Definición de los datos
Para una mejor comprensión y en el convencimiento de que un ejemplo vale más que mil explicaciones, dedicaré el artículo a desarrollar una pequeña aplicación de entrada de artículos y facturas (¡que original!) Una advertencia. Si quereis probar el ejemplo que acompaña al artículo y teneis la versión 6 de Interbase, habreis de recrear la base de datos ejecutando el script que se acompaña, ya que la base de datos está creada con Interbase 5.6. Tambien será bueno señalar que la versión de los componentes con la que se ha desarrollado el ejemplo, es la versión 4,52. Podeis averiguar la versión que teneis instalada, accediendo al menú contextual de cualquiera de los componentes de la paleta Interbase. Si los vuestros no están actualizados, podeis descargar la actualización de la página de Borland. Bien. Metámonos en faena. Lo primero que haremos será definir la base de datos contra la que trabajaremos. En el Listado 1, podemos ver la parte del script donde se definen las tablas y sus tipos de datos.
Listado 1. /*Definición de dominios*/ CREATE DOMAIN CAR_10 AS VARCHAR(10) CHARACTER SET NONE COLLATE NONE; CREATE DOMAIN CAR_25 AS VARCHAR(25) CHARACTER SET NONE COLLATE NONE; CREATE DOMAIN CAR_5 AS VARCHAR(5) CHARACTER SET NONE COLLATE NONE; CREATE DOMAIN CODIGOS_CHAR AS VARCHAR(15) CHARACTER SET NONE default "" NOT NULL COLLATE NONE; CREATE DOMAIN EJERCICIOS AS VARCHAR(4) CHARACTER SET NONE default "" NOT NULL COLLATE NONE; CREATE DOMAIN FECHA_AHORA AS DATE default "NOW" NOT NULL; CREATE DOMAIN LOGICO AS SMALLINT default 0 NOT NULL check(value in(0,1)); CREATE DOMAIN MONEDA AS DOUBLE PRECISION default 0 NOT NULL; CREATE DOMAIN NOMBRES AS VARCHAR(60) CHARACTER SET NONE COLLATE NONE; CREATE DOMAIN NUMERO_CORTO AS SMALLINT default 0 NOT NULL; CREATE DOMAIN NUMERO_LARGO AS INTEGER default 0 NOT NULL; /*Definición de generadores*/ CREATE GENERATOR CLIENTES; CREATE GENERATOR FACTURAS; /*Definición de excepciones*/ CREATE EXCEPTION ART_NO_CODIGO "NO HA INDICADO EL CODIGO DEL ARTICULO"; CREATE EXCEPTION CLI_NO_NOMBRE "NO HA INDICADO EL NOMBRE DEL CLIENTE"; CREATE EXCEPTION ERR_NO_HAY_EJERCICIO_ACTIVO "No se puede dar un número de factura. No hay ejercicio activo"; CREATE EXCEPTION FAC_NO_NUMERO "No se ha indicado el numero de factura";

El Rinconcito de Delphi

8

/*Definición de tablas*/ /*Tabla: ARTICULOS, Owner: SYSDBA*/ CREATE TABLE ARTICULOS (CODIGO CODIGOS_CHAR, DESCRIPCION NOMBRES, EXISTENCIAS NUMERO_LARGO, PRECIO_VENTA MONEDA, ACTIVO LOGICO, CONSTRAINT ARTICULOS_PK PRIMARY KEY (CODIGO)); /*Tabla: CLIENTES, Owner: SYSDBA*/ CREATE TABLE CLIENTES (CODIGO NUMERO_LARGO, NOMBRE NOMBRES, NIF CAR_10, DIRECCION NOMBRES, C_POSTAL CAR_5, LOCALIDAD CAR_25, PROVINCIA CAR_25, IVA MONEDA, ACTIVO LOGICO, CONSTRAINT CLIENTES_PK PRIMARY KEY (CODIGO)); /*Tabla: CONTADORES, Owner: SYSDBA*/ CREATE TABLE CONTADORES (EJERCICIO EJERCICIOS, FACTURAS NUMERO_LARGO, ACTIVO LOGICO, CONSTRAINT CONTADORES_PK PRIMARY KEY (EJERCICIO)); /*Tabla: FACTURAS, Owner: SYSDBA*/ CREATE TABLE FACTURAS (CODIGO NUMERO_LARGO, NUMERO NUMERO_LARGO, EJERCICIO EJERCICIOS, CLIENTE NUMERO_LARGO, FECHA FECHA_AHORA, IVA MONEDA, BASE_IMP MONEDA, TOTAL_IVA MONEDA, IMPORTE_TOTAL MONEDA, CONSTRAINT FACTURAS_PK PRIMARY KEY (CODIGO)); /*Tabla: FAC_DETALLES, Owner: SYSDBA*/ CREATE TABLE FAC_DETALLES (LINEA NUMERO_CORTO, FACTURA NUMERO_LARGO, ARTICULO CODIGOS_CHAR, DESCRIPCION NOMBRES, P_UNITARIO MONEDA, CANTIDAD NUMERO_LARGO, TOTAL_LINEA MONEDA, CONSTRAINT FAC_DETALLES PRIMARY KEY (FACTURA,LINEA));

Una vez desarrolada la base de datos, pasemos a lo que es el objeto de este artículo: El trabajo con los IBX.

El módulo de datos principal
Creamos un módulo de datos donde situaremos un TIBDatabase y un TIBTransaction. Este módulo de datos se crea con la aplicación y se destruye cuando esta finaliza. Configuramos debidamente ambos componentes para que podamos establecer la conexión con la base de datos. Para ello, basta con que señalemos en la propiedad DatabaseName del IBDatabase la ruta completa donde se encuentra nuestra base de datos e indicar en DefaultTransaction la transacción por defecto. El Rinconcito de Delphi 9

Si se observa el código, aprovechamos el evento OnCreate del DataModule, para establecer la conexión con la base de datos e iniciar la transacción.
procedure TDMMain.DataModuleCreate(Sender: TObject); begin try // ¡Conectamos! DB.Connected := True; // TestConnected, devuelve True si se ha podido conectar. // Aprovechamos para iniciar la transacción if DB.TestConnected then TPermanente.StartTransaction; except ShowMessage('No se ha podido establecer la conexión con la base de datos'); // veamos el error que nos dá raise // Y terminamos Application.Terminate; end; end; // Al destruise el módulo de datos, cerramos la transacción procedure TDMMain.DataModuleDestroy(Sender: TObject); begin TPermanente.Commit; end;

Poco mas necesitaremos en este módulo. Solo indicar que la transacción la he configurado con un nivel de aislamiento READ COMMITED, ya que este nivel posibilita que la transacción vea todos los datos confirmados en la base de datos y actualice filas confirmadas por otras transacciones simultáneas. Los demás módulos de datos del ejemplo, se crean y destruyen conforme nos van siendo necesarios. Sobre ellos no haré mas comentarios, pues creo que con una mirada al código es mas que suficiente para entender lo que hace.

Facturas, facturas y más facturas.
No, no preocuparos, que no os voy a aburrir con mi estado financiero. ¡Este artículo podría convertirse en un valle de lágrimas!. Mi intención es hablaros del módulo de datos que gestiona todo lo realcionado con los datos de las facturas y sus líneas de detalle. Tenemos dos TIBDataSet (IBFacturas e IBDetalles) unidos con una relación maestro-detalle. La forma de conseguir esto es llenando la propiedad DataSource (por cierto, que no es el mejor nombre que podian haberle puesto) de IBDetalles con el DataSource relacionado con IBFacturas . Además, hay que añadir a la sentencia SelectSQL de IBDetalles, la clausula WHERE FACTURA=:CODIGO. El parámetro CODIGO, tiene el mismo nombre que el campo de la tabla de facturas por el que se relacionan. En estas circunstancias, el componente se encarga de sustituir el parámetro, por el valor del campo del mismo nombre de la tabla maestra. Hay dos IBDataSet mas, xClientes y xArticulos, que solo se usan a efectos de visualización de datos. El primero de ellos, para mostrar los datos relacionados con el cliente al que se le hace la factura y el segundo, para los de los artículos. En este módulo de datos, tambien se han incluido cuatro TIBSql que nos servirán para acceder a los Stored Procedures de la base de datos.

El Rinconcito de Delphi

10

En el siguiente fragmento de código, vemos el uso de un TIBSQL que se encarga de obtener el número de factura.
function TDMFacturas.DameNumero(Ejercicio : string) : Integer; begin // Ejecuta SP_Dame_Numero_Factura de la base de datos with SPNumero do begin // Cerramos Close; // Le pasamos el ejercicio de la factura Params.ByName('ejercicio').AsString := Ejercicio; // Ejecutamos la consulta ExecQuery; // Recogemos el resultado Result := FieldByName('numero').AsInteger; // Liberamos los recursos asociados al IBSQL FreeHandle; end; end;

Buscando en el baúl de los recuerdos
Una de las cosas que primero se intenta hacer, es tratar de implementar búsquedas blandas. Sobre todo por aquellos que vienen de trabajar con tablas planas tipo Paradox, Dbase, etc. Es decir, usar mecanismos del tipo FidNearest o Locate. El primero, no está soportado en TIBDataSet y el segundo, sí. Pero las pruebas que he realizado con él, lo desaconsejan por su lentitud. El mecanismo de búsqueda de Locate, es secuencial. Así que si trabajamos con tablas con muchas filas, nos puede mandar a tomar un café. Esta tendencia, a mi juicio, viene dada por tratar de usar Interbase para navegar por todas las filas de la tabla. A mi juicio, no es esta la mejor filosofia que podemos adoptar con una base de datos de este tipo, ya que estaremos sobrecargando el servidor de una manera innecesaria. Puede parecer, y es una vieja creencia, muy extendida desgraciadamente, que darle al usuario la posibilidad de moverse por sus miles de registros es facilitarle las cosas, pero si tenemos en cuenta el coste en recursos que esto conlleva, resolveremos que es mas eficiente aplicar el dicho: “Cuantos menos mejor”. Es decir, sustituir los viejos hábitos y procurar darle al usuario acceso fácil a aquello que realmente necesita. Con esa filosofía, se ha implementado la rutina de búsqueda que se puede ver a continuación:
UDMFacturas private { Private declarations } FOriginalSQL : string; procedure TDMFacturas.DataModuleCreate(Sender: TObject); begin Tlocal.StartTransaction; // Anotamos la sentencia SelectSQL de IBFacturas. FOriginalSQL := IBFacturas.SelectSQL.Text; end; procedure TDMFacturas.Buscar(Campo, Texto : string); begin With IBFacturas do try

El Rinconcito de Delphi

11

// Desconectamos los controles DisableControls; // Cerramos Close ; // Restablecemos la sentencia original SelectSQL.Clear; // FOriginal, contiene la sentencia SelectSQL que se le ponga en diseño // a IBFacturas SelectSQL.Add(FOriginalSQL); // Si no se ha de buscar nada, se abre la tabla y se sale if Texto = '' then begin Open; Exit; end; // Si se ha de buscar, usamos el operador LIKE para hacer busquedas aproximadas SelectSQL.Add('WHERE ' + Campo + ' LIKE "' + Texto + '%"' ); // Ordenamos por el campo que se hace la busqueda SelectSQL.Add('ORDER BY '+Campo); Open ; finally // Conectamos los controles EnableControls; end; end;

Evidentemente, se trata de una rutina muy básica y manifiestamente mejorable, pero que puede ilustrar bien cómo se pueden implementar busquedas flexibles y potentes con un mínimo esfuerzo. Los que estén acostumbrados a trabajar con Paradox o Dbase, sabrán que para mostrar un conjunto de datos ordenado, no quedaba más remedio que tener indexada la tabla. Con Interbase, podemos hacer que la tabla se ordene por el campo que nos apetezca, tenga asociado un indice o no. Evidentemente, si ese campo tiene asociado un indice, la ordenación se hará mas rápido que si no lo tiene, ya que Interbase lo usará para ofrecernos los datos ordenados según le pidamos, pero esto, para el desarrollador, es trasparente. Si queremos ordenar los datos por un campo en concreto, solo tenemos que manipular la propiedad SelectSQL para añadirle la sentencia adecuada. La rutina que muestra el siguiente fragmento de código se encarga de ello.
procedure TDMFacturas.Ordena(Campo, Orientacion : String); var I : Integer; begin with IBFacturas do try // Desconectamos los controles DisableControls; // Cerramos la tabla Close; // ¿ Tenia ya un orden ? Si lo tenia, se lo quitamos for I := SelectSQL.Count - 1 downto 0 do if pos('ORDER BY', uppercase(SelectSQL[I])) <> 0 then SelectSQL.Delete(I); // Y le añadimos el nuevo SelectSQL.Add('ORDER BY '+Campo+Orientacion); // Volvemos a abrir Open; finally // Volvemos a conectar los controles EnableControls; end;

El Rinconcito de Delphi

12

end;

Este procedimiento se emplea, por ejemplo, para hacer que la tabla se ordene en respuesta al evento OnTitleClick de un DBGrid.

UFMFacturas CBCampo: TComboBox; private { Private declarations } ColumnaOrdenada : TColumn; AscDesc : string;

procedure TWFacturas.FormCreate(Sender: TObject); begin // En principio, la tabla está ordenada por el código ColumnaOrdenada := GLista.Columns.Items[0]; // Y ordenada en forma ascendente AscDesc := ''; // Llenamos CBCampo, con los nombres de los campos DMFacturas.IBFacturas.Fields.GetFieldNames(CBCampo.Items); CBCampo.ItemIndex := 0; end; procedure TWFacturas.GListaTitleClick(Column: TColumn); var I : Integer; begin // Se ha vuelto a hacer click en la misma columna if ColumnaOrdenada = Column then // Conmutamos el orden if AscDesc = '' then AscDesc := ' DESC' else AscDesc := '' else ColumnaOrdenada := Column; // Ordenamos la tabla de clientes, por el campo de la columna DMFacturas.Ordena(Column.FieldName,AscDesc); // Indicamos que la columna está ordenada Column.Color := $00E2E2E2; // El resto de columnas van en clWhite. for I := 0 to GLista.Columns.Count - 1 do if not (GLista.Columns.Items[I] = Column) then GLista.Columns.Items[I].Color := clWhite; end;

Hasta aquí esta breve panorámica sobre los IBX. Espero que os hayan podido servir de ayuda a los que os planteais empezar a trabajar con esta colección de componentes. He intentado tanto en este artículo como en el anterior, que sirvió de introducción, hacer hincapié en los aspectos prácticos más que en los teóricos. Ojalá haya sabido ser lo suficientemente claro en mis exposiciones. Que lo disfruteis.

El Rinconcito de Delphi

13

Consultas dinámicas o como protegernos de clientes pelmazos. Una de las cosas que hace que una aplicación sea mas potente e interesante para el usuario, es la extracción de información de una base de datos. Si importante es el ciclo de mantenimiento de las distintas tablas que forman un sistema, no menos importante es la explotación de los datos que estas contienen. En mi opinión debiera ser el primer objetivo a tener en cuenta en el momento de diseñar una base de datos: Que necesidades de información, presentes, y en la medida de lo posible, futuras, tiene que cubrir nuestro sistema. La solución clásica es incluir en nuestras aplicaciones toda clase de listados e informes. Para ello se suele usar la colección de componentes Qreport, proporcionada por el propio Delphi. Estos componentes nos permiten crear casi cualquier tipo de informe y parece el camino lógico a seguir cuando nos planteamos diseñar sistemas de explotación de datos. La desventaja que presenta este método es que cada vez que se necesite modificar o incluir nuevos informes, hay que recompilar nuestro ejecutable. Mi experiencia me dice que por más informes y listados que incluyamos en nuestros programas, nunca serán suficientes para los clientes. A esto hay que añadir la primera ley de “los trabajos frustantes”. Cuanto más elaborado se un informe y mas horas le dediquemos a su planificación y diseño,... menos importancia le dará nuestro cliente. Cualquier programador que se precie, ha intentado en algún momento crearse su propio sistema de listados. Un sistema que le permita incluir listados de todo tipo, a ser posible sin tener que recompilar a cada momento, y con el menor esfuerzo posible. Existen en el mercado herramientas que pueden cubrir mas que sobradamente estas necesidades. Delphi incluyó en sus primeras versiones una herramienta llamada ReportSmith, que mas tarde fué abandonada sin que nadie llorara lo mas mínimo por ella, todo hay que decirlo. Hay quien habla maravillas de ReportBuilder y seguro que en algún momento, habreis probado herramientas como FastReport o QRDesing. Desde luego, la alternativa que voy a explicar, no es la única que se puede seguir. Pero la ventaja que presenta esta solución, es que es transparente al usuario y... mas barata, que visto como se está poniendo el patio, no es una razón desdeñable.

Preparando el escenario
Cualquier camino que tomemos, pasa por hacer que nuestra aplicación se comunique con el mundo exterior. Para ello nos vamos a servir de unos simples ficheros de texto en los que incluiremos las definiciones de las consultas que queramos y algunas cosas mas. Para ilustrar lo que aquí vamos a explicar, este artículo se acompaña de un ejemplo en el que el amable lector, podrá encontrar la implementación completa de cuanto aquí se expone. Partiremos de la pequeña aplicación que desarrollamos en el número 5 de esta revista para incorporarle el módulo de consultas Como ya dije, nos vamos a servir de unos sencillos ficheros de texto para almacenar la definición de la consulta que deseemos ejecutar. Usaremos código SQL en dichas definiciones; código que almacenaremos en la propiedad SelectSQL de un TIBDataSet y para mostrar los datos, un sencillo DBGrid. Como veis, nada del otro jueves. En la figura 1, podeis ver la ventana que con todos los elementos que nos harán falta.

El Rinconcito de Delphi

14

Figura 1. La ventana de gestión de consultas

Empezando por el tejado
El módulo usa dos TstringList donde se cargará el contenido del fichero de texto. Para ello nos servimos del método LoadFromFile, que nos permite especificar el fichero que queremos cargar. Por lo demás, como se puede ver en el Listado 1, el código es sumamente sencillo y no requiere de más comentarios.

Listado 1. Código para cargar una nueva consulta private { Private declarations } LstOriginal, LstModificado : TStringList;

procedure TWConsultasSQL.accAbrirExecute(Sender: TObject); begin if OD.Execute then begin // Cargamos en LstOriginal, el contenido del archivo LstOriginal.Clear; LstOriginal.LoadFromFile(OD.FileName); // Cerramos el dataset if IBVista.Active then IBVista.Close; // Indicamos el fichero que se ha abierto PNTitulo.Caption := ExtractFileName(OD.FileName); // Y activamos el botón de ejecutar accEjecutar.Enabled := LstOriginal.Text <> ''; end;

El Rinconcito de Delphi

15

end;

El código de respuesta del botón Ejecutar, merece que nos detengamos algo más, para explicar la lógica que se ha seguido. Normalmente, necesitaremos recuperar un rango de registros de nuestras tablas, lo que implica el uso de parámetros y la necesidad de facilitar la introducción de los valores para cada uno de esos parámetros por parte del usuario. Para esto, podriamos haber usado la propiedad Params del TIBDataSet, pero para esta ocasión me decidí a usar otra estartegia que, en mi opinión, aporta más posibilidades. Ya sabreis que Interbase ignora las líneas de código que se encuentran delimitadas por los simbolos “/* */”, es decir, que las toma como comentarios. Aprovecharemos este espacio para introducir información acerca de los parámetros que necesita la consulta para ejecutarse. La información que nos interesa guardar es: el nombre del parámetro, el tipo de dato y el mensaje para el usuario. Para poder identificar esta información dentro de los comentarios, la delimitaremos con “{ }” y dentro de estas, cada campo irá separado por “;”. Para identificar los prámetros, estos los he encerrado entre dos interrogaciones. En el listado 2, se puede ver un ejemplo de todo lo dicho.
Listado 2. Ejemplo de definición de una consulta y de sus parámetros. /* Estas líneas son tratadas como comentarios e ignoradas por Interbase, por lo que son el lugar ideal para colocar la definición de los parámetros de esta consulta. {DESDE;D;Desde Fecha;} {HASTA;D;Hasta Fecha;} {CLIENTE;N;Codigo del Cliente;} Cerramos los comentarios */ SELECT F.FECHA, C.NOMBRE, SUM(F.BASE_IMP) BASE, SUM(F.TOTAL_IVA) IVA, SUM(F.IMPORTE_TOTAL) TOTAL FROM FACTURAS F INNER JOIN CLIENTES C ON(F.CLIENTE=C.CODIGO) WHERE C.CODIGO=¿CLIENTE? AND (F.FECHA >= ¿DESDE? AND F.FECHA <= ¿HASTA?) GROUP BY F.FECHA,C.NOMBRE

Una vez definido este protocolo, solo tendremos que procesar la información leida del fichero de texto, buscando y sustituyendo los parámetros contenidos en la consulta, por los valores que haya introducido el usuario. Una vez que tengamos este trabajo hecho, obtendremos una nueva sentencia, que asignaremos al TIBDataSet y lo abriremos. Parte de este código, lo podemos ver en el listado 3

Listado 3. Respuesta al botón Ejecutar. procedure TWConsultasSQL.accEjecutarExecute(Sender: TObject); begin // PideParametros devuelve True si la consulta tiene parámetros y el usuario ha // introducido la información que se le pide. False, si ha cancelado la operación if not PideParametros then Exit; try Screen.Cursor := crHourGlass; IBVista.Close; IBVista.SelectSQL.Clear; IBVista.SelectSQL.Assign(LstModificado);

El Rinconcito de Delphi

16

try IBVista.Open; except ShowMessage('Error de sintaxis: '+#13+IBVista.SelectSQL.Text); raise; end; finally Screen.Cursor := crDefault; end; end;

Las interioridades del invento
Ya tenemos todo lo necesario para hacer funcionar nuestro sistema de consultas dinámicas. Solo nos queda dar el toque final y hacer atractivo al usuario la introducción de los valores que definirán el rango de registros que nos devolverá la consulta. Para esta tarea, vamos a construir una ventana donde se pedirá al usuario que introduzca los valores. El aspecto de esta ventana, lo podemos ver en la figura 2

Figura 2. Ventana de petición de valores Para ayudarnos en la tarea de edición y gestión de parámetros, he creado una clase que se encarga de asociar a cada parámetro un control de edición. He preferido usar el control TmaskEdit, porque se pueden definir máscaras de edición, lo que aprovecharemos para, por ejemplo, solicitar fechas. En el listado 4, podemos ver la definición de la clase.

Listado 4. Definición de la clase Tparametros TParametros = protected FCadenaGen FNombre FTipo FRotulo FEditor FEtiqueta class(TObject) : : : : : : string; string; string; string; string; TLabel; // // // // // // Cadena que contiene la definición del parámetro Nombre del parámetro Tipo del parámetro Rótulo que se mostrará al editar el parámetro Valor que ha introducido el usuario Label que mostrará el mensaje

El Rinconcito de Delphi

17

FControl : TMaskEdit; // Control donde se editará el parámetro FPropietario : TComponent; // Ventana donde se mostrará el control procedure SetEditor(Valor : string); public property Nombre : string read FNombre write FNombre; property Tipo : string read FTipo write FTipo; property Rotulo : string read FRotulo write FRotulo; property Editor : string read FEditor write SetEditor; property Etiqueta : TLabel read FEtiqueta write FEtiqueta; property Control : TMaskEdit read FControl write FControl; property Propietario : TComponent read FPropietario write FPropietario; constructor Create; destructor Destroy; override; procedure CargaDatos(Propietario : TComponent; ACadena : string); end;

El método CargaDatos recibe dos argumentos: El contenedor donde se mostrarán los MaskEdits y la cadena que contiene el parámetro que queremos gestionar. Es decir, la cadena delimitado por los signos “{}”. La ventana de petición de datos, cuenta con un método que recibe como argumento un TstringList con todos los parámetros. Por cada uno de ellos, crearemos un objeto del tipo Tparametros y lo almacenaremos en un TstringList, valiendonos del método AddObjects. Parte de este código, se puede ver en el listado 5

Listado 5. var P : TParametros; I,L,T : Integer; begin // Cargamos los parámetros FParametros := TStringList.Create; for I := 0 to AParametros.Count - 1 do begin try P := TParametros.Create; P.CargaDatos(Contenedor,AParametros.Strings[I]); // Usamos el método AddObject para cargar en cada elemento de la lista // un objeto del tipo Tparametros. Fparametros.AddObject(P.Nombre,TParametros.Create); Tparametros(FParametros.Objects[I]).CargaDatos(Contenedor, Aparametros.Strings[I]); finally P.Free; end; end; end;

Una vez que el usuario ha introducido la información que se le solicita, solo nos queda volver a procesar la cadena que leimos del fichero, y sustituir los parámetros por los valores que haya introducido el usuario, asignar a la propiedad SelectSQL del TIBDataSet esa cadena ya procesada y abrirlo.

El Rinconcito de Delphi

18

Para terminar
Como siempre, el código del ejemplo que acompaña a este artículo, podrá iluminar mas que mis torpes explicaciones todo lo dicho. Como dije al principio del artículo, este trabajo es fruto de la necesidad y no pretende ser, de hecho no lo es, una maravilla tecnológica. Seguro que se podria escribir de 200 formas mas eficientes, pero he querido compartirlo con vosotros por si a alguien le pudiese servir como punto de partida. O por si alguno hace un estudio sobre rarezas, ya sabe que ejemplo incluir :-).

El Rinconcito de Delphi

19

Viajando a las entrañas de nuestra base de datos
Vamos a ver como podemos extraer información de la estructura de nuestra base de datos, usando el componente IBExtract de la paleta Interbase. Siguiendo con la serie sobre los componentes IBX de la paleta Interbase, vamos a ver en esta ocasión el componente TIBExtract. Este componente sirve para extraer información de la estructura de una base de datos. No es un componente de uso frecuente en una aplicación, pero nos puede ser muy útil para obtener la estructura de la base de datos. Y si el lector es del tipo emprendedor, quizás se anime a crearse su propio programa para manipular bases de datos de Interbase. ¡Quien sabe!. A lo mejor conseguimos entre todos mejorar ese engendro infumable llamado IBConsole. Para mostrar la funcionalidad de este componente, he desarrollado un pequeño ejemplo que se encarga de mostrar y extraer toda la información de una base de datos y guardarla en un fichero de texto. Este fichero nos puede servir para crear la base de datos desde cero. Pero ¿donde se guarda la información que nos interesa?. Seguro que la mayoría sabe que Interbase cuenta con una serie de tablas de sistema, donde se guarda toda la información referente a las tablas, dominios, triggers , relaciones y demás elementos que hemos ido definiendo cuando creamos la base de datos. El nombre de estas tablas comienza por el prefijo RDB$ seguido por el nombre mas o menos descriptivo acerca de lo que contienen. Así, por ejemplo, el nombre de las tablas y vistas, se obtiene de listar la tabla “RDB$RELATIONS”.En “RDB$PROCEDURES”, podremos encontrar el listado de procedimientos, etc. Es de estas tablas donde el IBExtract bucea para ofrecernos la información que le solicitemos. Y la solicitud no puede ser mas simple. Una sola llamada a un método del objeto, con los parámetros apropiados, basta para obtener la estructura de cualquiera de los elementos de nuestra base de datos. Para obtener el listado de los distintos elementos, he usado unas constantes con la definición de la consulta que será necesaria para obtener de las tablas de sistema, el listado de tablas, dominios, generadores, procedimientos, etc. El código de dichas consultas se puede ver en el listado 1

Listado 1. Definición de constantes. const // Listado de Dominios DomainSQL = 'SELECT RDB$FIELD_NAME FROM RDB$FIELDS WHERE RDB$FIELD_NAME NOT LIKE "RDB$%"'; // Listado de Tablas TablasSQL = 'SELECT RDB$RELATION_NAME FROM RDB$RELATIONS WHERE (RDB$SYSTEM_FLAG <> 1 or RDB$SYSTEM_FLAG IS (RDB$VIEW_SOURCE IS NULL)'; // Listado de vistas VistasSQL = 'SELECT RDB$RELATION_NAME FROM RDB$RELATIONS WHERE (RDB$SYSTEM_FLAG <> 1 or RDB$SYSTEM_FLAG IS (RDB$VIEW_SOURCE IS NOT NULL)'; // Listado de Procedimientos almacenados ProcedSQL = 'SELECT RDB$PROCEDURE_NAME FROM RDB$PROCEDURES WHERE (RDB$SYSTEM_FLAG <> 1 or RDB$SYSTEM_FLAG IS // Listado de Funciones FuncioSQL = 'SELECT RDB$FUNCTION_NAME FROM RDB$FUNCTIONS WHERE (RDB$SYSTEM_FLAG <> 1 or RDB$SYSTEM_FLAG IS // Listado de Generadores GeneraSQL = 'SELECT RDB$GENERATOR_NAME FROM RDB$GENERATORS WHERE (RDB$SYSTEM_FLAG <> 1 or RDB$SYSTEM_FLAG IS // Listado de Excepciones ExcepcSQL = 'SELECT RDB$EXCEPTION_NAME FROM RDB$EXCEPTIONS';

NULL) AND

NULL) AND

NULL)';

NULL)';

NULL)';

El Rinconcito de Delphi

20

// Listado de Triggers TriggeSQL = 'SELECT RDB$TRIGGER_NAME FROM RDB$TRIGGERS WHERE (RDB$SYSTEM_FLAG <> 1 or RDB$SYSTEM_FLAG IS NULL)';

He dispuesto también una serie de TcheckListBox, donde se cargará la información obtenida de ejecutar dichas sentencias. Para esto me he servido de un TIBDataSet al que asigno a la propiedad SelectSQL la definición correspondiente. De todo esto se encarga el procedimiento CargaListas. La definición de de este procedimiento se puede ver en el listado 2. Este procedimiento es llamado en el evento OnCreate de la ventana, con lo que cuando entremos en ella, tendremos disponible la información. También he colocado un Tmemo, donde se podrá visualizar la información que vayamos extrayendo de la base de datos. Esta ventana cuenta con varios botones que nos permitirán extraer la información de toda la base de datos, de los objetos que seleccionemos en los TcheckListBox y guardarla en un fichero de texto.

Listado 2. Definición del procedimiento CargaListas procedure TWExtraer.CargaListas(Lista : TCheckListBox; const Sentencia : String); begin // IBMeta es un TIBDataSet with IBMeta do begin // Lo cerramos para poder manipular el SelectSQL Close; // Limpiamos el contenido de SelectSQL SelectSQL.Clear; // Añadimos la sentencia que nos pasan SelectSQL.Add(Sentencia); // Abrimos y nos vamos a la primera fila Open; First; // Limpiamos el contenido de la lista y recorremos todas las filas, añadiendo // la información encontrada Lista.Items.Clear; while not EOF do begin Lista.Items.Add(Fields[0].AsString); Next; end; end; end;

Ahora que tenemos el escenario dispuesto, es hora de pasar a mayores y ver como extraer la información deseada. Como ya dije al principio, el método es muy sencillo. Basta con llamar al procedimiento ExtractObject cuyo prototipo es el siguiente:
ExtractObject(ObjectType:TExtractObjectTypes; ObjectName:String=''; ExtractTypes:TExtractTypes=[]);

Como se puede ver, el procedimiento acepta tres argumentos, de los que los dos últimos son opcionales. Los parámetros son: ObjectsType. Indica el tipo de objeto vamos a extraer y es del tipo TextractObjectTypes. La definición de este tipo es:
TExtractObjectTypes =

El Rinconcito de Delphi

21

(eoDatabase, eoDomain, eoTable, eoView, eoProcedure, eoFunction, eoGenerator, eoException, eoBLOBFilter, eoRole, eoTrigger, eoForeign, eoIndexes, eoChecks, eoData);

Cabe señalar que si indicamos el objeto eoDatabase en este argumento, se extraerá el metadata de toda la base de datos. El segundo argumento es ObjectName de tipo string, donde podremos indicar el nombre del objeto que queremos extraer. Si no indicamos nada, en este argumento, se extraerán todos los objetos del tipo señalado en el primer argumento. El tercer argumento es ExtractTypes y es del tipo TextractTypes. Indica el tipo de extracción que se va a hacer del objeto. Su definición es:
TExtractType = (etDomain, etTable, etRole, etTrigger, etForeign, etIndex, etData, etGrant, etCheck, etAlterProc); TExtractTypes = Set of TExtractType;

En el ejemplo que acompaña a este artículo, podrá ver los resultados que ofrece la llamada a este método con la tabla de una base de datos. El resultado de la extracción se almacena en la propiedad Items del TIBExtract.

Poco mas necesitamos para poder usar este componente. Solo nos resta indicar el TDataBase y el TTransaction al que estará conectado el componente y hacer las llamadas a ExtractObject que nos interesen. Sencillo, rápido y eficaz. ¡El sueño de cualquier programador!. ¿No creéis?.

El Rinconcito de Delphi

22

IBX. El toque final
A lo largo de esta serie de artículos, hemos visto los distintos aspectos acerca del trabajo con estos componentes para el acceso a bases de datos Interbase. Completaremos en este artículo la visión con el repaso a los componentes que se agrupan en la paleta “Interbase Admin.

El repaso a IBX no estaría completo si no nos detuviésemos en esos componentes que se agrupan en la paleta “Interbase Admin.”. Así mismo, también veremos las novedades que han aparecido con las distintas versiones que el autor ha ido liberando desde que se inicio esta serie de artículos. Para la realización del ejemplo que acompaña a este artículo, se ha usado la versión 5.3 de los IBX. Para los que cuenten con la versión 6 de Delphi, está disponible la versión 6.3. Así mismo, se ha de contar con la versión 6.x de Interbase, para que los componentes que vamos a ver, se instalen en Delphi.

IBBackupService. O como hacer copias de seguridad.
Sin duda, una de las operaciones de mantenimiento mas importantes en una base de datos, es la copia de seguridad. No hace falta convencer a nadie, a estas alturas, de la importancia de esta operación. Bueno, “a nadie” no es el término adecuado. Conozco a gente que piensa que su disco duro va a ser eterno o que los accidentes nunca le van a pasar a él. Estos, son de los que se enteran demasiado tarde de que los usuarios informáticos se dividen en dos: Los que han tenido problemas con el disco duro y los que los van a tener.

Estos usuarios siempre tienen las mismas excusas: “Los programas de copia de seguridad son muy complejos”. “Se pierde demasiado tiempo haciendo la copia”. O una de mis preferidas: “¡Pues vaya un programa que se rompe si se fastidia el disco!”. Quien no haya oído nunca uno de estos argumentos, tiene mi permiso para saltarse esta parte del artículo. Para los que les suene esta música, ¡presten atención!. Realmente, hacer una copia de seguridad con este componente, es una tarea de lo mas sencilla. Basta con indicarle la base de datos origen y el archivo GBK destino, llamar al método ServiceStart y configurar las opciones que deseemos y ya está. Vean si no el listado 1, donde se puede observar la respuesta a la pulsación de un botón que inicia el proceso de Copia de una base de datos.

Listado 1. Copia de seguridad // Realizamos la Copia de seguridad procedure TWUtilidades.TBCopiaClick(Sender: TObject); begin with DMServicios.IBBackup do begin Active := True; try Screen.Cursor := crHourGlass; BackupFile.Clear; MCopia.Lines.Clear; // Cargamos la opciones que nos indique el usuario Options := []; if chkCheckSum.Checked then Options := Options if chkIgnoreLimbo.Checked then Options := Options if chkStructure.Checked then Options := Options if chkGarbage.Checked then Options := Options if chkTransportable.Checked then Options := Options

+ + + + +

[IgnoreCheckSums]; [IgnoreLimbo]; [MetadataOnly]; [NoGarbageCollection]; [NonTransportable];

El Rinconcito de Dephi

23

// Cargamos la información que nos han indicado // Base de datos origen DatabaseName := EOrigen.Text; // Fichero GBK destino BackupFile.Add(EDestino.Text); // ¿Ver el progreso de la operación? Verbose := chkVerbose.Checked; // Comenzar ServiceStart; // Si quieren ver el progreso if Verbose then begin // Mientras no se llegue al final... While not Eof do // Cargamos las lineas de salida en el memo MCopia.Lines.Add(GetNextLine); // Avisamos que hemos terminado MCopia.Lines.Add('¡ PROCESO TERMINADO !'); end; finally Active := False; Screen.Cursor := crDefault; end; end; end;

Interbase permite definir una serie de opciones para hacer la copia de seguridad. Estas opciones podemos definirlas en el componente mediante la propiedad Options. Las opciones posibles son: IgnoreCheckSums : Si se fija a True, desactiva la comprobación de la integridad de los datos en el análisis página a página que se hace al efectuar el Backup. IgnoreLimbo : En determinadas circunstancias, puede que queden transacciones perdidas, es decir, que no se han cerrado correctamente. Si fijamos a True esta propiedad, estas transacciones serán ignoradas. MetadataOnly: Incluye en la copia de seguridad, solo la estructura de la base de datos. Esta opción es perfecta para cuando nos interesa “vaciar” una base de datos. Por ejemplo, una vez terminado el desarrollo de nuestra aplicación, para borrar los datos de pruebas que hayamos introducido. NoGarbageCollection: Durante la copia de seguridad, se marcan las páginas que contienen versiones antiguas de los datos, como candidatas a ser eliminadas. Si marcamos esta opción a True, no se marcaran y no serán eliminadas. OldMetadataDesc: Nos permite mantener el metadata de nuestra base de datos en el formato antiguo NonTransportable: El valor por defecto, nos permite obtener una copia que puede ser restaurada en otra máquina con otro sistema operativo. Si se fija a True, desactivamos esta posibilidad. Si nos fijamos en la propiedad BackupFile es un Tstrings, cuando lo lógico seria pensar en una string donde almacenar el nombre (con la ruta) del archivo destino. Esto es así, porque tenemos la posibilidad de repartir nuestra copia de seguridad en varios archivos.

El Rinconcito de Dephi

24

IBRestoreServices. Restaurar copias.
Obviamente, no tendría sentido tener la posibilidad de sacar copias de seguridad, si luego no contásemos con un sistema capaz de poder restaurarlas. Para ello contamos con este servicio. La forma de uso es tan sencilla como la del IBBackupService. Unas pocas líneas de código bastan para dotar a nuestras aplicaciones de la posibilidad de restaurar copias de nuestra base de datos. Ved si no el listado 2, donde se ofrece un ejemplo del uso de este componente.

Listado 2. Restaurar una copia de seguridad // Restaurar desde una copia de seguridad procedure TWUtilidades.TBRestoreClick(Sender: TObject); begin with DMServicios.IBRestore do begin Active := True; try Screen.Cursor := crHourGlass; DatabaseName.Clear; BackupFile.Clear; MRestore.Lines.Clear; // Cargamos las opciones que nos indiquen Options := []; if chkIndices.Checked then Options := Options if chkShadow.Checked then Options := Options if chkValidate.Checked then Options := Options if chkReplace.Checked then Options := Options if chkCreate.Checked then Options := Options // ¿Quieren ver el progreso? Verbose := chkVerboseRestore.Checked;

+ + + + +

[DeactivateIndexes]; [NoShadow]; [NoValidityCheck]; [Replace]; [CreateNewDB];

PageBuffers := 3000; PageSize := 4096; // Indicamos la base de datos destino DatabaseName.Add(EDb.Text); // El archivo GBK origen BackupFile.Add(EGbk.Text); // Cerramos la conexión con la base de datos. // En una aplicación real, tendriamos que cerrar todas las ventanas activas // que tengan una conexión con tablas de la base de datos DMMain.Desconectar; // Comenzar ServiceStart; // Si se quiere ver el progreso... if Verbose then begin While not Eof do MRestore.Lines.Add(GetNextLine); MRestore.Lines.Add('¡ PROCESO TERMINADO !'); end; finally Active := False; // Vovemos a conectar con la base de datos DMMain.Conectar; Screen.Cursor := crDefault; end; end;

El Rinconcito de Dephi

25

end;

Las propiedades son similares a las ya vistas para el Backup. Lógicamente, las opciones que tenemos disponibles son distintas en esta ocasión. DeactivateIndexes:: Si fijamos esta opción a True, los indices no serán reconstruidos durante el proceso de restauración. NoValidityCheck: Si establecemos a True esta opción, desactivaremos la verificación de la integridad de los datos. Replace: La base de datos existente, será sobreescrita. Esta opción es para los amigos de las emociones fuertes.

IBScript. O como rejuvenecer nuestra base de datos.
Es normal que una vez terminado el desarrollo de nuestra aplicación, se necesiten hacer ajustes en la estructura de la base de datos, bien para añadirle tablas, modificar Triggers, crear indices... Este componente nos permite llevar a cabo esta tarea, sin demasiado esfuerzo. El componente cuenta con la propiedad Script. Un Tstring al que le pasaremos (desde un archivo de texto, por ejemplo) el código que queremos ejecutar. Disponemos de un método llamado Validate que nos permite revisar la sintaxis del código que queremos ejecutar, de modo que podremos asegurarnos de que al menos por esa razón el Script no fallará. Pero no solo por esa razón puede fallar. Imaginemos que intentamos crear un procedimiento almacenado que ya existe previamente en la base de datos. La llamada a Validate, no detectará ningún error, sin embargo, la actualización fallará pues no pueden haber dos procedimientos con el mismo nombre. ¿Que haremos en ese caso?. El componente nos provee de dos eventos para poder controlar esas situaciones. OnExecuteError y OnParseError. En el primer evento, además, disponemos del argumento Ignore . Si cuando se produce un error le damos como valor True, el error será ignorado y la ejecución del Script continuará.

IBFilterDialog. ¡Que bonito es no tener que trabajar!
Algunas veces se agradece que a uno le den el trabajo hecho. Se siente uno tan bien, que hasta se predispone a no poner demasiadas pegas a los regalos. Hay un refrán muy extendido (al menos en España) que se podría aplicar perfectamente en esta ocasión. “A caballo regalado, no le mires el dentado”. Pues eso haremos en esta ocasión. No mirarle demasiado el dentado a este componente para búsquedas en una tabla de nuestra base de datos. La interfaz, no es que sea una maravilla de imaginación y buen gusto. La operativa podría ser algo mas amigable, pero a ver quien es el guapo que con menos, consigue más. Y es que realmente, hay poco que hablar acerca del funcionamiento de este componente. Basta indicarle el IBDataSet al que se enlazará y escribir una sola línea de código, invocando al método Execute, para obtener un cuadro de diálogo que nos permite buscar por cualquier campo de nuestra tabla. Con búsquedas sensibles o no a las mayúsculas y minúsculas, búsquedas parciales, etc. Tambien nos permite definir “alias” para nuestros campos, de forma que al usuario se le muestren nombres mas intuitivos que los que solemos poner los programadores cuando diseñamos una base de datos.

El Rinconcito de Dephi

26

Con los ejemplos que acompañan a este artículo, he incluido una versión del componente con la interfaz traducida al “cristiano”, dado que de origen vienen en un perfecto inglés (faltaría mas).

Despedida y cierre.
A lo largo de esta serie de artículos he pretendido dar una visión amplia de estos componentes. He hecho especial hincapié en el IBDataset, ya que me parece que es, con mucho, el que más se usa en el desarrollo de una aplicación. Espero que mi proverbial torpeza cuando de explicar las cosas se trata, no haya sido un obstáculo para los que se acercan a estos componentes. Y sobre todo, espero que estos artículos hayan sido útiles. Solo con esa intención se escribieron.

El Rinconcito de Dephi

27