Mostrando entradas con la etiqueta bases de datos. Mostrar todas las entradas
Mostrando entradas con la etiqueta bases de datos. Mostrar todas las entradas

14 marzo 2008

Creando aplicaciones multicapa (y VII)

Voy a finalizar este artículo hablando sobre la creación de aplicaciones multicapa utilizando servicios web a través del protocolo SOAP.

Para ello voy a utilizar el servidor Apache para alojar el ejecutable de mi servidor de aplicaciones.

CREANDO EL SERVIDOR DE APLICACIONES

El servidor de aplicaciones va a ser un ejecutable CGI que vamos a situar en el directorio cgi-bin de Apache. Los pasos para su creación son los siguientes:

1. Selecciona File -> New -> Other. Seleccionamos la pestaña WebServices, elegimos SOAP Server application y pulsamos Ok:


Al pulsar Ok nos preguntará el tipo de servidor que vamos a crear:


2. Como quiero que mi servidor web sea un ejecutable independiente voy a seleccionar la segunda opción: CGI Stand-alone executable. Al pulsar Ok nos creará un proyecto nuevo que incluye el siguiente módulo de datos web:

3. Nos preguntará si deseamos crear una interfaz para el módulo SOAP. Pulsamos No.

4. Cuando pulsemos el botón Save All para guardar todo el proyecto, vamos a poner como nombre el objeto TWebModule1 como UServidorSOAP.pas y el nombre del proyecto como ServidorSoap.dpr. El proyecto creado sólo se compone de dos partes; por un lado el archivo DPR del proyecto:


Y una unidad de la clase TWebModule que es la encargada de recoger las peticiones de los clientes:


5. Ahora vamos a añadir un módulo de datos remoto: File -> New -> Other, pestaña WebServices, seleccionamos SOAP Server Data Module y pulsamos Ok.

6. Nos preguntará el nombre del módulo: ModuloSOAP y pulsamos Ok. Lo guardamos también como UModuloSOAP.pas.

7. En este módulo de datos insertamos los componentes de acceso a nuestra base de datos Firebird, al igual que hicimos en artículos anteriores:


8. Compilamos el proyecto y nos generará el archivo ServidorSoap.exe. Si ejecutamos ese archivo nos devuelve una página web con la descripción de nuestros servicios web disponibles. Puede verse abriendo una ventana de comandos y ejecutando:

ServidorSoap.exe > servicios.html

9. Copiamos el ejecutable ServidorSoap.exe al directorio cgi-bin donde tengamos instalado el servidor web Apache. Para comprobar que nuestro servidor de aplicaciones esta operativo, abrimos un navegador web y escribimos la siguiente dirección:

http://127.0.0.1/cgi-bin/ServidorSoap.exe

Nos mostrará la misma página web que hemos guardado en servicios.html:


Con esto ya tenemos creado nuestro servidor de aplicaciones.

CREANDO LA APLICACION CLIENTE

La aplicación cliente va a ser similar a todas las que hemos creado hasta ahora:


Como puede verse en la imagen, he incluido un componente de la clase TSoapConnection de la pestaña de componentes WebServices. Al componente lo he llamado ClienteSoap y he modificado las siguientes propiedades:

URL: http://127.0.0.1/cgi-bin/ServidorSoap.exe/SOAP/
SOAPServeIID: IAppServerSOAP - {C99F4735-D6D2-495C-8CA2-E53E5A439E61}
Connected: True

Y en el componente TClientes (ClientDataSet) modificamos:

RemoteServer: ClienteSOAP
ProviderName: DSPCliente
Active: True

Haciendo esto debe verse la lista de clientes.

Este es el tipo de aplicación multicapa que más me gusta, ya que al trabajar a través del puerto 80 y mediante XML-SOAP no es necesario abrir puertos en los routers ni en los cortafuegos de los antivirus.

Como hemos visto anteriormente al crear el servidor de aplicaciones, también pueden crearse servidores que estén dentro de una librería DLL ya sea para el servidor apache o para el servidor Internet Information Server (IIS) de Microsoft.

Pruebas realizadas con Delphi 7.0, Firebird 2.0 y Apache Server 2.2.8.

07 marzo 2008

Creando aplicaciones multicapa (VI)

Hoy vamos a ver como crear las aplicaciones cliente y servidor utilizando el protocolo HTTP para establecer la comunicación. Para que funcione este método es necesario tener instalado el servidor Internet Information Server de Microsoft.

Si teneis Windows XP profesional se puede instalar de la siguiente manera:

1. Inicio -> Configuración -> Panel de control.

2. Hacemos doble clic sobre el icono Agregar o quitar programas.

3. Seleccionamos Agregar o quitar componentes de Windows.

4. Activamos Servicios de Internet Information Server (IIS).

5. Pulsamos Siguiente y nos pedirá el CD de Windows XP. Lo introducimos y pulsamos Siguiente.

6. Cuando termine la instalación pulsamos el botón Finalizar.

Con esto quedará instalado y ejecutado el servidor web de Microsoft.

CREANDO EL SERVIDOR DE APLICACIONES

La creación del servidor de aplicaciones es idéntica a la creada en el artículo anterior mediante sockets. Queda resumido en estos pasos:

1. Creamos un nuevo proyecto y lo guardamos.

2. Creamos un módulo de datos remoto: File -> New -> Other, pestaña Multitier, seleccionamos Remote Data Module y pulsamos Ok.

3. En la nueva ventana que aparece escribimos en el campo CoClass Name: ServidorHTTP.

4. Guardamos el módulo de datos remoto con el nombre UServidorWeb.pas.

5. Insertamos en el módulo de datos remoto los componentes encargados de conectar con nuestra base de datos. No voy a volver a explicarlo de nuevo ya que son los mismos componentes que he explicado en artículos anteriores:


Prácticamente no cambia nada respecto a hacerlo con sockets o con DCOM. En los tres casos lo que hace el módulo de datos es quedar registrado en Windows mediante un número de interfaz.

CREANDO LA APLICACION CLIENTE

Para crear la aplicación cliente rápidamente, podemos crear un proyecto nuevo y luego nos traemos del artículo anterior el formulario que utilizamos para crear la aplicación cliente, menos el componente de la clase TSocketConnection.

A dicho formulario le añadimos el componente WebConnection (pestaña DataSnap) y le ponemos de nombre ClienteWeb. Quedaría del siguiente modo:


Y aquí es donde empiezan las complicaciones. En primer lugar hay que copiar el archivo httpsrvr.dll que se encuentra en la carpeta bin de Delphi al directorio scripts de donde esté instalado Internet Information Server. Eso en teoria (según la documentación de Borland), porque yo no he encontrado dicho directorio (por lo menos en mi Windows XP Profesional SP2). Lo que vamos a hacer es copiar esa DLL donde esté nuestro servidor de aplicaciones HTTP.

Lo que he hecho en su lugar es ejecutar el programa que administra IIS del siguiente modo:

1. Inicio -> Configuración -> Panel de control.

2. Hacemos doble clic sobre Herramientas Administrativas.

3. Hacemos doble clic sobre Servicios de Internet Information Server.

4. Vamos pulsando el icono [+] del arbol de tenemos a la izquierda hasta acceder al icono Sitio Web Predeterminado. Se puede ver la carpeta scripts donde se pueden añadir directorios virtuales. He probado a añadir la carpeta donde está mi servidor HTTP pero ni caso.

5. Pulsamos con el botón derecho del ratón sobre el icono Sito Web Predeterminado y seleccionamos la opción Nuevo -> Directorio Virtual...


6. Pulsamos el botón Siguiente.

7. En Alias escribimos ServidorHTTP y pulsamos Siguiente.


8. Seleccionamos el directorio donde se encuentra nuestro proyecto del Servidor HTTP y el archivo httpsrvr.dll.

9. Activamos la opción Ejecutar (por ejemplo, aplicaciones ISAPI o CGI) y pulsamos Siguiente.


10. Pulsamos Finalizar.

11. Nos vamos a nuestro proyecto Cliente HTTP de Delphi 7 y seleccionamos el componente WebConnection.

12. En la propiedad ServerName seleccionamos ServidorHTTP.ServidorWeb. Se rellenará automáticamente el campo ServerGUID.

Y cuando parece que va a ir todo perfecto y activamos la opción Connected aparece este mensaje:


Y aquí es donde me he quedado. Por mucho que he dado permisos en IIS y por muchas vueltas que le he dado a los proyectos cliente y servidor no hay manera de evitar dicho mensaje. Y no he encontrado ninguna información útil por la red que solucione el problema (ni en castellano ni en inglés), bueno sí, en ruso, pero...

También he probado a copiar el archivo httpsrvr.dll dentro del directorio cgi-bin del servidor Apache y aunque llega a conectar con el mismo, me devuelve un error 500 que no lo conoce ni su padre.

Por lo tanto, aunque consiga solucionar este problema, no me gusta tener que depender de IIS para crear aplicaciones mediante HTTP. Prefiero utilizar sockets o DCOM.

Si alguien ha solucionado el problema que me lo cuente, porque yo no lo he logrado de ningún modo.

En la siguiente parte del artículo veremos como realizar lo mismo mediante servicios Web utilizando SOAP.

Pruebas realizadas en Delphi 7 y Firebird 2.0.

29 febrero 2008

Creando aplicaciones multicapa (V)

En esta ocasión vamos a crear un servidor de aplicaciones utilizando sockets en lugar de utilizar DCOM. Aunque los pasos son prácticamente los mismos, hay una pequeña diferencia: hay que utilizar un programa de Borland que hace de puente entre nuestro servidor de aplicaciones y las aplicaciones cliente.

Veamos un ejemplo paso a paso. Antes de empezar creamos una carpeta donde alojar el proyecto del servidor de aplicaciones. Por ejemplo:

D:\Desarrollo\DelphiAlLimite\Multicapa\ServidorSockets\

CREANDO EL SERVIDOR DE APLICACIONES

1. Creamos un nuevo proyecto (File -> New -> Application).

2. La ventana principal de la aplicación la llamamos FPrincipal, guardando su unidad con el nombre UPrincipal. Como título de esta ventana podemos poner: Servidor de Sockets.

3. Añadimos al proyecto un módulo de datos remoto: File -> New -> Other. Pulsamos sobre la pestaña Multitier, seleccionamos Remote Data Module y pulsamos Ok. Aparecerá la siguiente ventana:


3. En el campo CoClass Name escribimos Servidor y pulsamos Ok.

4. Guardamos la unidad del módulo de datos remoto creado con el nombre UServidor.

5. Al igual que hicimos en el servidor DCOM vamos a crear los siguientes componentes para acceder a la base de datos Firebird:


Se compone de:

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

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

- Un componente IBQuery llamado TClientes (pestaña Interbase).

- Un componente DataSetProvider llamado DSPClientes (pestaña Data Access).

6. Para el componente IBDatabase modificamos las siguientes propiedades:

DatabaseName: 127.0.0.1:D:\Desarrollo\DelphiAlLimite\Multicapa\ServidorSockets\BASEDATOS.FDB
DefaultTransaction: Transaccion.
LoginPrompt: False
SQLDialect: 3

7. Para el componente IBTransaction ponemos en su propiedad DefaultDataBase: BaseDatos.

8. Para el componente IBQuery llamado TClientes cambiamos las propiedades:

Database: BaseDatos
Transaction: Transaccion
SQL: SELECT * FROM CLIENTES

9. Y en el componente DataSetProvider seleccionamos TClientes en su propiedad DataSet.

Con esto ya tenemos un servidor básico de aplicaciones utilizando Sockets. Como se puede apreciar no hay mucha diferencia respecto al servidor DCOM que creamos con anterioridad. Ahora vamos a ver como crear la aplicación cliente.

CREANDO LA APLICACION CLIENTE

En esta ocasión vamos a crear la aplicación cliente utilizando un módulo de datos remoto normal (no transaccional). Este módulo de datos no tiene la misma seguridad avanzada que un módulo de datos transaccional pero la comunicación es muy rápida, gastando muy poco ancho de banda.

Cuando establecemos una conexión con el servidor de aplicaciones usando sockets la comunicación se realiza a través del protocolo normal TCP/IP, gozando de la ventaja de poder traspasar cualquier red.

Aquí podemos encontrar una diferencia respecto a un cliente DCOM. Cuando conectamos con el servidor debemos tener arrancado un programa llamado Borland Socket Server que es el intermediario entre nuestro servidor y el cliente. Este programa esta en el directorio bin de la carpeta donde esté instalado Delphi. Lo recomendable sería copiar este programa (scktsrvr.exe) al directorio donde tengamos nuestro servidor de aplicaciones.

Para crear la aplicación cliente vamos a seguir los siguientes pasos:

1. Creamos una carpeta para alojar la aplicación cliente. Por ejemplo:

D:\Desarrollo\DelphiAlLimite\Multicapa\ClienteSockets\

2. Creamos un nuevo proyecto (File -> New -> Application) y le damos a la ventana principal el nombre FCliente. La guardamos con el nombre UCliente.pas.

3. En esta ventana vamos a añadir los siguientes componentes:


- Un componente ClientDataSet llamado TClientes (pestaña Data Access).

- Un componente DataSource llamado DSClientes (pestaña Data Access).

- Un componente SocketConnection que vamos a llamar ClienteSocket (pestaña DataSnap).

- Un componente DBGrid llamado ListadoClientes (pestaña Data Controls).

4. Antes de intentar conectar el cliente debemos arrancar el programa de Borland scktsrvr.exe. Al ejecutar este programa se quedará residente en la bandeja del sistema (el icono azul con el rayo amarillo):


Si pulsamos el botón derecho del ratón sobre el icono y seleccionamos Properties podemos ver la su configuración y puertos por defecto (por si nos interesa cambiarlos).

5. Para el componente SockectConnection modificamos sus propiedades:

Address: 127.0.0.1
ServerName: ServidorSockets.Servidor
Connected: True

Al seleccionar ServidorSockets.Servidor rellenará automáticamente el campo ServerGUID. Al activar la propiedad Connected arrancará automáticamente nuestro programa que hace de servidor, al igual que ocurría con nuestro servidor DCOM. Pero recordad que esto no funciona si no esta en ejecución el programa Borland Socket Server (scktsrvr.exe).

6. Para el componente ClientDataSet fijamos las siguientes propiedades:

ProviderName: DSPClientes
RemoteServer: ClienteSocket

7. Seleccionamos TClientes en la propiedad DataSet del componente DSClientes.

8. Y por último vinculamos el componente DBGrid al componente DSClientes a través de su propiedad DataSource.

9. Si ponemos a True la propiedad Active del componente ClientDataSet aparecerán los datos en pantalla en tiempo de diseño:


Si abrimos una ventana de comandos (MS-DOS) y ejecutamos:

netstat -bn (para Windows XP con SP2)

podemos ver las conexiones abiertas tanto en Delphi como en el servidor de aplicaciones:


Como puede apreciarse la comunicación se ha realizado a través del puerto 211, lo que significa que habrá que abrir dicho puerto tanto en nuestros routers como en los cortafuegos si queremos establecer la comunicación correctamente.

En la siguiente parte de este artículo veremos como crear un servidor de aplicaciones y un cliente a través del protocolo HTTP (puerto 80).

Pruebas realizadas con Dephi 7.0 y Firebird 2.0.

23 noviembre 2007

Creando aplicaciones multicapa (IV)

Una vez que hemos creado nuestro servidor DCOM vamos a implementar nuestra aplicación cliente que se va a encargar de llamarlo.

CREANDO LA APLICACION CLIENTE

Para conectar un cliente multicapa al servidor DCOM vamos a utilizar un componente de la clase TDOMConnection que buscará en tiempo de diseño el servidor.

Como se verá a continuación no es necesario añadir un componente de la clase TDataSetProvider ni ningún otro relacionado con motores de bases de datos específicos. Eso ya se lo hemos dejado al servidor de aplicaciones. Tampoco es necesario que el servidor de aplicaciones que creamos en el artículo anterior se esté ejecutando mientras diseñamos el cliente.

Para realizar las pruebas vamos a crear un nuevo proyecto que contenga un sólo formulario y los siguientes componentes:


- Un componente TDBGrid llamado ListadoClientes destinado a listar todos los campos de la tabla CLIENTES.

- Un componente TClientDataSet llamado TClientes.

- Un componente TDataSource que lo vamos a llamar DSClientes.

- Un componente TDCOMConnection que lo llamaremos Conexion.

Ahora vamos a vincular unos componentes a otros:

- Vinculamos el componente TDataSource llamado DSClientes con la rejilla ListadoClientes a través de su propiedad DataSource.

- Vinculamos el componente TClientDataSet llamado TClientes al componente TDataSource llamado DSCliente en su propiedad DataSet.

- Asociamos el componente TDOMConnection llamado Conexion al componente TClientDataSet llamado TClientes utilizando su propiedad RemoteServer.

Aquí nos detenemos para analizar un problema. El componente TClientDataSet no mostrará nada que no venga de un componente TDataSetProvider. Como dicho componente se encuentra en el servidor de aplicaciones, lo primero que hay que hacer es conectar con el servidor de aplicaciones para poder vincular su DataSetProvider:

- Seleccionamos el componente TDOMConnection y en su propiedad ServerName elegimos ServidorDatos.ServidorDCOM. Al hacer esto debe rellenarnos automáticamente el campo ServerGUID, el cual es un identificador de la interfaz del módulo de datos remoto que creamos en el servidor de aplicaciones.

- Activamos la propiedad Connected en el componente TDOMConnection y veremos que se ejecuta automáticamente el servidor de aplicaciones mostrándonos su formulario principal.

- Dejando el servidor de aplicaciones en ejecución seleccionamos el componente TClientDataSet y en su propiedad ProviderName seleccionamos DSPClientes.

Con sólo realizar estos pasos, si activamos la propiedad Active del componente TClientDataSet nos mostrará en tiempo de diseño los datos de la tabla clientes:


Al compilar y ejecutar nuestro programa cliente ya tenemos un programa que maneja los datos del servidor sin preocuparse del motor de bases de datos o de otros usuarios conectados.

El componente TDOMConnection tiene la propiedad llamada ComputerName la cual contiene el nombre del equipo donde se va a alojar el servidor de aplicaciones. Si no seleccionamos ninguno se asume que el servidor de aplicaciones y la aplicación cliente residen en la misma máquina.

Si intentamos cerrar el servidor de aplicaciones mientras está conectado el cliente saldrá este mensaje:

There are still active COM objects in this application. One o more clients may have references to these objects, so manually closing this application may cause those client application(s) to fail.

Are you sure want to close this application?

Lo que traducido al castellano sería:

Todavía hay objetos COM activos en esta aplicación. Uno o más clientes podrían estar utilizando estos objetos, así que si cierra esta aplicación podría causar un error de conexión en los clientes.

¿Esta seguro de cerrar esta aplicación?

Esto significa que nunca debemos cerrar el servidor mientras quede algun cliente conectado a nosotros. Además, si el último cliente que estaba conectado al servidor de aplicaciones desconecta entonces el servidor de aplicaciones se cerrará automáticamente al ver que no hay ninguna conexión abierta.

En la siguiente parte de este artículo veremos como crear un servidor de aplicaciones y una aplicación cliente utilizando otros protocolos distintos a DCOM.

Pruebas realizadas en Delphi 7.

20 noviembre 2007

Creando aplicaciones multicapa (III)

Vamos seguir con la base teórica de como funcionan las aplicaciones multicapa antes de comenzar a crear un ejemplo práctico.

ELIGIENDO EL PROTOCOLO DE CONEXION

Cada protocolo de comunicación que podemos usar para conectar de las aplicaciones cliente a las aplicaciones servidor tienen sus propias ventajas e inconvenientes. Antes de elegir un protocolo tenemos que considerar que servicio va a prestar la aplicación, cuantos clientes va a ser atendidos, que reglas de negocio se van a establecer, etc.

USANDO CONEXIONES DCOM

DCOM es el protocolo que proporciona el acceso más directo y rápido de comunicación, no requiriendo aplicaciones externas aparte del servidor de aplicaciones.

Este protocolo proporciona servicios de seguridad cuando se utiliza un módulo de datos transaccional. Cuando usamos DCOM podemos identificar quien llama al servidor de aplicaciones (ya sea con COM+ o MTS). Por tanto, es posible determinar que reglas de acceso vamos a asignar a las aplicaciones cliente.

Son los clientes los que instancian directamente el módulo de datos remoto para realizar la comunicación, no requiriendo ninguna aplicación externa que haga de intermediario.

USANDO CONEXIONES SOCKET

La comunicación mediante sockets nos permiten crear clientes muy ligeros. Se suele utilizar este protocolo si no tenemos la seguridad que los sistemas clientes soporten DCOM. Los sockets proporcionan un sistema comunicación simple para establecer conexiones entre los clientes y el servidor.

En lugar de instanciar el módulo de datos remoto desde el cliente (como sucede con DCOM), los sockets usan una aplicación separada en el servidor (ScktSrvr.exe), la cual acepta las peticiones de los clientes e instancia el módulo de datos remoto usando COM. El componente de conexión en el cliente y ScktSrvr.exe en el servidor son los responsables de organizar las llamadas mediante la interfaz IAppServer.

El programa ScktSrvr.exe también puede ejecutarse como un servicio NT. Para ello habría que registrarlo como servicio usando línea de comandos. También se puede eliminar de la lista de servicios del mismo modo.

Uno de los inconvenientes que tienen los sockets es que no hay protección en el servidor contra fallos en la conexión con los clientes. Mientras este protocolo de comunicación consume menos ancho de banda que el protocolo DCOM (el cual envía periódicamente mensajes de comprobación y mantenimiento), no puede detectar si un cliente se está ausente o no.

USANDO CONEXIONES WEB

Una las ventajas de utilizar el protocolo de comunicación HTTP es que los clientes pueden comunicarse con el servidor de aplicaciones aunque este protegido por un cortafuegos. Al igual que las conexiones socket, los mensajes HTTP proporcionan un sistema sencillo y de poco consumo de ancho de banda para comunicar los clientes y el servidor.

En lugar de instanciar el módulo de datos remoto desde el cliente (como sucede con DCOM), las conexiones basadas en HTTP pueden usar un servidor de aplicaciones web (como Apache) o el servidor puede ser la librería httpsrvr.dll, el cual acepta las peticiones de los clientes e instancia el módulo de datos remoto usando COM. El componente de conexión en la máquina cliente y httpsrvr.dll en el servidor son los responsable de organizar las llamadas a la interfaz IAppServer.

Las conexiones web aportan también la ventaja de la seguridad SSL suministrada por la librería wininet.dll (una librería de utilidades de internet que corre en los sistemas cliente). Una vez que hemos configurado el servidor web en la máquina que hace de servidor de aplicaciones, podemos establecer los nombre y claves de acceso para los usuario aprovechando las propiedades de conexión que aporta el componente web.

Las conexiones web tienen la ventaja de tener en una memoria caché los objetos de módulos de datos que se van instanciando, conocido como pooling. Esto permite que nuestro servidor cree un número de instancias de módulos remotos limitado para dar servicio a los clientes, sin tener que estar instanciando y destruyendo objetos sin parar cada vez que se conecta o desconecta un cliente.

A diferencia de otras conexiones con componentes, no podemos crear funciones que permitan comunicar directamente el servidor de aplicaciones con las aplicaciones clientes, lo que se llaman funciones callback.

USANDO CONEXIONES SOAP

El protocolo SOAP es el estándar utilizado para crear aplicaciones de servicios web. Este protocolo envia y recibe mensajes codificados en documentos XML, y los envía utilizando el protocolo HTTP.

Las conexiones SOAP tienen la ventaja de que son multiplataforma, ya que son soportadas por prácticamente todos los sistemas operativos actuales. Al utilizar como transporte el protocolo HTTP tienen las mismas ventajas: permite servir a los clientes a través de un cortafuegos y pueden utilizarse diversos servidores HTTP.

CONSTRUYENDO UNA APLICACION MULTICAPA

Resumiendo a grandes rasgos, los pasos generales para crear una aplicación de bases de datos multicapa son los siguientes:

1. Crear el servidor de aplicaciones.

2. Registrar el servidor de aplicaciones como un servicio o instalarlo y ejecutarlo como una aplicación (recomendado).

3. Crear la aplicación cliente.

El orden de creación es importante. Debemos crear y ejecutar el servidor de aplicaciones antes de crear el cliente. Esto se debe a que cuando vayamos a construir la aplicación cliente, en tiempo de diseño tenemos que conectar con el servidor de aplicaciones para realizar pruebas de conexión. Aunque se también se podría un crear un cliente sin especificar el servidor de aplicaciones en tiempo de diseño, pero no lo recomiendo porque es más incómodo.

Si no estamos creando la aplicación cliente en el mismo equipo que el servidor, y estamos usando una conexión DCOM, podemos registrar el servidor de aplicaciones en la máquina cliente. Esto hace que los componentes de conexión se enteren de donde está el servidor de aplicaciones en tiempo de diseño, así podemos elegir el componente Provider desde el inspector de objetos.

CREANDO UN SERVIDOR DE APLICACIONES DCOM

La mayor diferencia entre crear un servidor de aplicaciones y la típica aplicación de bases de datos cliente/servidor reside en el módulo de datos remoto. Vamos a crear una aplicación EXE que va a hacer de servidor de aplicaciones para una base de datos Firebird 2.0 que tiene una tabla llamada CLIENTES.

Para crear un servidor de aplicaciones, hay que seguir los pasos:

1. Crear un nuevo proyecto: File -> New -> Application.

2. Guardar el proyecto como ServidorDatos.exe

3. Añadimos un módulo de datos remoto al proyecto: File -> New -> Other.

4. Nos vamos a la pestaña Multitier, seleccionamos Remote Data Module y pulsamos Ok.

5. Rellenamos los campos:

CoClass Name: ServidorDCOM

Instancing: Multiple Instance

Threading Model: Apartment

6. Pulsamos Ok. Nos aparecerá una nueva ventana que representa el nuevo módulo de datos remoto creado.

7. Guardamos el módulo de datos remoto en disco con el nombre UServidorDCOM.pas

8. Insertamos en el módulo de datos remoto el componente TIBDatabase con el nombre BaseDatos.

9. Hacemos doble clic sobre el componente BaseDatos y configuramos lo siguiente:

Connection: Remote
Protocol: TCP
Database: 127.0.0.1:D:\Desarrollo\DelphiAlLimite\Multicapa\DCOM\BaseDatos.fdb
LoginPrompt: Desactivado

10. Pulsamos Ok.

11. Añadimos al módulo de datos remoto un componente TIBTransaction llamado Transaccion.

12. Vinculamos el objeto Transaccion al componente TIBDatabase a través de su propiedad DefaultTransaction.

13. Asignamos BaseDatos en la propiedad DefaultDatabase del componente Transaccion.

14. Insertamos un componente TIBQuery llamado TClientes. En su propiedad Database ponemos BaseDatos. Y su propiedad SQL escribimos:

SELECT * FROM CLIENTES

15. Hacemos doble clic en el componente TClientes y pulsamos la combinación de teclas CTRL + A para añadir todos los campos.

16. Insertamos un componente TDataSetProvider llamado DSPClientes. En su propiedad DataSet seleccionamos TClientes.

Con esto ya tenemos nuestro propio servidor de aplicaciones conectado a la base de datos Firebird. Como puede apreciarse es casi lo mismo que crear una aplicación cliente/servidor.

La diferencia está en que no es necesario instanciar en memoria el módulo de datos remoto ya que la aplicación cliente se encargará de hacerlo.

En la siguiente parte de este artículo crearemos la aplicación cliente encargada de conectarse con este servidor de aplicaciones que hemos creado.

Pruebas realizadas en Delphi 7.

16 noviembre 2007

Creando aplicaciones multicapa (II)

Después de tener una visión global de cómo funcionan las aplicaciones multicapa vamos a ver cada una de sus partes.

LA ESTRUCTURA DE LA APLICACION CLIENTE


El usuario que va a utilizar la aplicación cliente no notará la diferencia entre una aplicación cliete/servidor y una aplicación multicapa, ya que el acceso a la información se realiza a través de los componentes estándar TClientDataSet.

El componente ClientDataSet se comunica con el proveedor de datos a través de la interfaz IAppServer. Se pueden seleccionar diferentes protocolos de comunicación según el componente de conexión que se utilice, donde tenemos los siguientes componentes:

Componente Protocolo
--------------------------------------------------
TDCOMConnection DCOM
TSocketConnection Windows sockets (TCP/IP)
TWebConnection HTTP
TSOAPConnection SOAP (HTTP y XML)

LA ESTRUCTURA DE LA APLICACION SERVIDOR

Una vez instalado el servidor de aplicaciones, cuando se ejecuta por primera vez no establece una conexión con los clientes. Más bien son los clientes los que inician y mantienen la conexión con el servidor de aplicaciones. Todo esto sucede automáticamente sin que tengamos que manejar solicitudes o administrar interfaces.

La base de un servidor de aplicaciones es el módulo de datos remoto, el cual esta especializado en soportar la interfaz IAppServer (para los servidores de aplicaciones que tienen servicios web, el módulo de datos remoto soporta la interfaz IAppServerSOAP, y usa como preferencia IAppServer).

Las aplicaciones cliente usan las interfaces de módulos de bases de datos remotos para comunicarse con los componentes Provider del servidor de aplicaciones. Cuando el módulo de datos remoto usa IAppServerSOAP, el componente de conexión se adapta a este para la interfaz IAppServer que el que usa el componente ClientDataSet.

Hay tres tipos de módulos de datos remotos:

TRemoteDataModule: Se usa este tipo si los clientes van a utilizan protocolos DCOM, HTTP, sockets o una conexión OLE hacia el servidor de aplicaciones, a menos que queramos instalar el servidor de aplicaciones con COM+.

TMTSDataModule: Se utiliza este tipo si vamos a crear un servidor de aplicaciones como librería DLL que esté instalada con COM+ (or MTS). Se puede utilizar este módulo de datos remoto MTS con protocolos DCOM, HTTP, sockets u OLE.

TSoapDataModule: Este es un módulo de datos que implementa una interfaz IAppServerSOAP en una aplicación de servicios web. Utilizaremos este tipo de módulo de datos para proveer datos a los clientes con acceso a servicios web.

Si el servidor de aplicaciones es distribuido mediante COM+ (o MTS), el módulo de datos incluye eventos para cuando el servidor de aplicaciones sea activado o desactivado. Esto permite conectar automáticamente con los motores de bases de datos cuando se active y desconectarlas cuando se desactive.

EL CONTENIDO DEL MODULO DE BASES DE DATOS REMOTO

Como cualquier otro módulo de datos, se puede incluir cualquier componente no visual en el módulo de datos remoto. Pero hay que tener en cuenta ciertos aspectos:

- Para cada dataset que tiene el módulo de datos remoto en los clientes, debemos incluir un DataSetProvider. Un DataSetProvider parte la información en paquetes que son enviados a los ClientDataSet y aplican las actualizaciones de las bases de datos contra el servidor de aplicaciones.

- Para cada documento XML que el módulo de datos remoto envía al cliente, debemos incluir un proveedor XML. Un proveedor XML actua como un DataSetProvider, exceptuando que la actualización de los datos se efectua a través de documentos XML en vez de ir al motor de bases de datos.

No hay que confundir los componentes de conexión a bases de datos con los componentes de conexión a servidores de aplicaciones, ya que estos últimos utilizan los componentes de las pestañas DataSnap y WebServices.

MODULOS DE DATOS REMOTOS TRANSACCIONALES

Si vamos a crear un servidor de aplicaciones que va a utilizar los protocolos COM+ o MTS entonces podemos sacar ventaja de esto creando módulos de datos transaccionales (Transactional Data Module) en lugar de un módulo de datos remoto ordinario (Remote Data Module). Esto sólo se puede hacer en sistemas opetarivos Windows 2000 en adelante.

Al utilizar un módulo de datos transaccional tenemos las siguientes ventajas:

- Seguridad: COM+ (o MTS) proporciona unas reglas de seguridad en nuestro servidor de aplicaciones. A los clientes se les asignan reglas, las cuales determinan como pueden acceder a la interfaz MTS del módulo de datos.

- Los módulos de datos transaccionales permiten mantener la conexión de los clientes abierta cuando se conectan y desconectan muchas veces hacia el servidor de aplicaciones. De este modo, mediante unas pocas conexiones podemos controlar a muchos clientes que se conectan y se desconectan continuamente (como si fuera un servidor web).

- Los módulos de datos transaccionales pueden participar en transacciones que abarquen múltiples bases de datos o incluir funciones que no están implementadas en las bases de datos.

- Pudemos crear nuestro servidor de aplicaciones como un módulo de datos remoto cuya instancia es activada y desactivada según se necesite. Cuando se usa la activación en tiempo real, nuestros módulos de datos remotos son instanciados solamente si los necesitan los clientes. Esto evita de gastar recursos que no se vayan a utilizar.

Pero no todo son ventajas. Con una simple instancia de un módulo de datos el servidor de aplicaciones se puede manejar todas las llamadas a bases de datos a través de una simple conexión de bases de datos. Pero si se abusa de esto se puede crear un cuello de botella y puede impactar en la ejecución cuando hay muchos clientes, con lo tenemos que mantener un equilibrio entre el número de clientes que van a acceder al servidor de aplicaciones y el número de módulos de datos remotos que se van instanciar.

También puede ocurrir el efecto contrario: si se utilizan múltiples instancias de un módulo de bases de datos remotas, cada instancia puede mantener una conexión a bases de datos independiente. De modo que si hay muchas instancias del módulos de datos remotos se abrirían demasiadas conexiones con la base de datos, disminuyendo el rendimiento del motor de bases de datos como si fuera la típica aplicación cliente/servidor con muchos usuarios.

AGRUPAMIENDO DE MODULOS DE DATOS REMOTOS

El agrupamiento de objetos (pooling) nos permite crear una caché de módulos de datos remotos que estarán disponibles en memoria para las llamadas de las aplicaciones cliente. De esta manera se conservarán recursos en el servidor y evitará el tener que instanciar y destruir de memoria los módulos de datos remotos cuando se utilicen o se dejen de utilizar.

Cada vez que no estamos usando el módulo de datos transaccional obtenemos todas las ventajas que proporciona el tener una cache de objetos agrupados tanto si la conexión se efectua a través de DCOM como si es mediante el componente TWebConnection. Además podemos limitar el número conexiones a bases de datos.

Cuando el servidor de aplicaciones web recibe una petición del cliente, este las pasa a su primer módulo de datos remoto disponible en la caché de módulos de datos remotos. Si no hay disponible ninguna instancia de módulo de datos remoto, el servidor crea una nueva (no sobrepasando el máximo de módulos especificado por nosotros). Esto proporciona una gran flexibilidad permitiendo manejar varios clientes a través de un simple instancia de módulo de datos remoto (el cual puede actuar como un cuello de botella) e irá creando nuevas instancias según se vaya necesitando.

Cuando una instancia de un módulo de datos remoto que está en la caché no recibe ninguna petición de clientes durante un tiempo prolongado, es automáticamente liberado. Esto evita que el servidor de aplicaciones se sature con muchas instancias abiertas.

En la siguiente parte de este artículo abarcaremos los tipos de conexión que se pueden realizar entre las aplicaciones clientes y el servidor de aplicaciones.

Pruebas realizadas en Delphi 7.

14 noviembre 2007

Creando aplicaciones multicapa (I)

En este artículo que estará separado en varias partes vamos a ver como crear aplicaciones de bases de datos multicapa cliente/servidor. Este tipo de aplicaciones esta dividido en unidades lógicas, llamadas capas, las cuales se ejecutan en distintas máquinas. Las aplicaciones multicapa distribuyen los datos y se comunican en una red de área local o bien sobre Internet. Esto proporciona muchas ventajas tales como centralizar la lógica de negocio en un sólo servidor y donde varios clientes van tirando de él. Además podemos crear aplicaciones que comuniquen varios centros de trabajo se estén separados geográficamente a través de Internet.

Una aplicación multicapa queda particionada de la siguiente manera:

- Aplicación Cliente: se encarga de mostrar la interfaz de usuario.

- Servidor de aplicaciones: reside en la red local central y es accesible por todos los clientes donde reciben datos directamente de este servidor.

- Servidor de bases de datos: en este servidor es donde está instalado el motor de bases de datos (Interbase, Firebird, Oracle, etc.), aunque el servidor de aplicaciones y el servidor de bases de datos pueden ser la misma máquina.

En este modelo a tres capas los clientes sólo pueden comunicarse con el servidor de aplicaciones y en ningún caso directamente con el motor de bases de datos, como ocurre en las aplicaciones cliente/servidor habituales.

Este tipo de aplicaciones multicapa no tiene porque estar compuesto sólo de tres capas, podría constar de varios servidores de bases de datos y servidores de aplicaciones.

VENTAJAS DE CREAR UN MODELO MULTICAPA

En este modelo de bases de datos la aplicación cliente sólo se dedica a mostrar los datos al usuario, no sabe nada sobre como los datos son actualizados y mantenidos.

El servidor de aplicaciones (capa media) coordina y procesa las peticiones y actualizaciones de múltiples clientes. El servidor maneja todos los detalles, define el conjunto de datos e interactua con el servidor de bases de datos.

Las ventajas de este modelo multicapa son las siguientes:

- Encapsulación de lógica de negocio. Diferentes clientes de la aplicacion pueden acceder al mismo servidor intermedio. Esto permite evitar la redundancia (y coste de mantenimiento) de duplicar las reglas de negocio para cada aplicación cliente separada.

- Aplicaciones clientes pequeñas. Al delegar las tareas más pesadas en la capa media las aplicaciones clientes ocupan menos y consumen menos procesador y memoria, permitiendo instalarse en máquinas de bajo rendimiento. Esto trae la ventaja de que por muchos clientes que accedan a la aplicación, el motor de bases de datos sólo tiene una conexión, que va directamente al servidor de aplicaciones, evitando así problemas de concurrencia o latencia de datos entre distintas aplicaciones cliente. Estas aplicaciones clientes también pueden funcionar a través de Internet ya que su consumo de ancho de banda es mínimo, al contrario de conectar directamente con el motor de bases de datos.

- Procesar datos distribuidos. Distribuir el trabajo de una aplicación entre varias máquinas puede mejorar la ejecución, ya que el balanceo de carga permite reducir la carga de las máquinas que funcionan como servidor de aplicaciones. Por ejemplo, si vemos que una aplicación de gestión se relentiza podemos distribuir en una máquina las compras, en otra las ventas y la gestión de recibos en otra.

- Incrementar la seguridad. Podemos aislar la funcionalidad en las capas dando restricciones de seguridad. Esto proporciona unos niveles de seguridad configurables y flexibles. Las capas intermedias pueden limitar los puntos de entrada a material protegido, permitiendo controlar el control de acceso más fácilmente. Si usamos HTTP o COM+, podemos utilizar los modelos de seguridad que soportan.

COMPOSICION DE LAS APLICACIONES DE BASES DE DATOS MULTICAPA

Las aplicaciones multicapa usan los componentes de la pestaña DataSnap, los de la pestaña Data Access y a veces los de la pestaña WebServices, más un módulo de datos remoto que es creado por el asistente de la pestaña Multitier o WebServices del cuadro de diálogo New Items. Estos componentes proporcionan las funcionalidades para empaquetar información transportable sólo con los datos que se hayan modificado.

Los componentes que necesitamos para aplicaciones multicapa son los siguientes:

Módulo de bases de datos remoto: Los módulos de datos pueden actual como un servidor COM o implementar un servicio web para dar a las aplicaciones clientes acceso a los datos. Este componente es utilizado en el servidor de aplicaciones en la capa intermedia (el servidor de aplicaciones).

Provider: Es el que proporciona los datos creando paquetes de datos y resolviendo las actualizaciones de los clientes. Este componente es utilizado en el servidor de aplicaciones en la capa intermedia (servidor de aplicaciones).

ClientDataSet: es un dataset especializado que usa la librería midas.dll o midaslib.dcu para manejar los datos almacenados en los paquetes que se envían y reciben. Este componente es utilizado por la aplicación cliente. Tiene una caché local y sólo envía al servidor los datos que han cambiado.

Connection: Es una familia de componentes que estan localizados en el servidor y que utilizan la interfaz IAppServer para leer las peticiones de las aplicaciones clientes. Cada componente de conexión está especializado en protocolo particular de comunicaciones.

Los componentes proveedores y clientes requiren las librerías midas.dll o midaslib.dcu cuando se va a distribuir la aplicación en otros equipos.

FUNCIONAMIENTO DE UNA APLICACION A TRES CAPAS

Los siguientes pasos ilustran la secuencia normal de eventos para una aplicación a tres capas:

1. El usuario cliente arranca la aplicación. El cliente conecta con el servidor de aplicaciones (el cual puede ser elegido en tiempo de ejecución). Si el servidor de aplicaciones no estuviera arrancado, lo arranca automáticamente. Los clientes reciben una interfaz IAppServer para comunicarse con el servidor de aplicaciones.

2. El cliente solicita los datos al servidor de aplicaciones, donde puede requerir todos los datos de una vez o pedir una parte de ellos poco a poco.

3. El servidor de aplicaciones lee la información solicitada por el cliente del motor de bases de dtaos (estableciendo una conexión con si fuera necesario), empaqueta la información y devuelve la información al cliente. La información adicional (por ejemplo, las características de los campos) pueden ser incluida en los metadatos del paquete de datos. Este proceso de empaquetamiento de datos es llamado providing.

4. El cliente decodifica el paquete recibido y muestra los datos al usuario.

5. Cuando el usuario de la aplicación cliente realiza modificaciones sobre los datos (añadir, borrar o modificar registros) estas modificaciones son almacenadas en un log temporal.

6. Finalmente los clientes envian las actualizaciones al servidor de aplicaciones, el cual responde normalmente a cada acción del usuario. Para aplicar los cambios, los paquetes de los clientes leerán y cambiarán el log antes de enviar sus paquetes de datos al servidor.

7. El servidor de aplicaciones decodifica el paquete y efectua los cambios (en el contexto de una transacción cuando sea apropiada). Si un registro no puede ser actualizado (por ejemplo, porque otra aplicación cambie el registro antes de que el cliente lo solicite y después de que el cliente haya aplicado los cambios), el servidor de aplicaciones intenta reconciliar los cambios de los clientes con los datos actuales, y guarda los registros que podrían no ser actualizados. Este proceso de guardar y resolver problemas con los registros es llamado resolving.

8. Cuando el servidor de aplicaciones finaliza el proceso de actualización de registros, devuelve los registros no actualizados al cliente para que pueda resolverlos por el mismo.

9. El cliente resuelve los registros que el servidor no ha podido actualizar. Hay muchas maneras de que un cliente pueda resolver estos registros. Generalmente el cliente intenta corregir la situación asegurándose de que los registros estén validados antes de enviarlos. Si la situación puede ser rectificada, entonces vuelve a enviar los datos al servidor.

10. El cliente desempaqueta los datos que vienen del servidor y refresca su caché local.

En la siguiente parte de este artículo veremos la estructura de una aplicación cliente.

Pruebas realizadas en Delphi 7.

12 noviembre 2007

Creando tablas de memoria con ClientDataSet

Una de las cosas que más se necesitan en un programa de gestión es la posibilidad crear tablas de memoria para procesar datos temporalmente, sobre todo cuando los datos origen vienen de tablas distintas.

Es muy común utilizar componentes de tablas de memoria tales como los que llevan los componentes RX (TRxMemoryData) o el componente kbmMemTable. Pues veamos como hacer tablas de memoria utilizando el componente de la clase TClientDataSet sin tener que utilizar ningún componente externo a Delphi.

DEFINIENDO LA TABLA

Lo primero es añadir a nuestro proyecto un componente ClientDataSet ya sea en un formulario o en un módulo de datos. Como vamos a crear una tabla de recibos lo vamos a llamar TRecibos.

Ahora vamos a definir los campos de los que se compone la tabla. Para ello pulsamos el botón [...] en la propiedad FieldDefs. En la ventana que se abre pulsamos el botón Add New y vamos creando los campos:

Name DataTpe Size
-----------------------------------
NUMERO ftInteger 0
CLIENTE ftString 80
IMPORTE ftFloat 0
PAGADO ftFloat 0
PENDIENTE ftFloat 0

Para crear la tabla de memoria pulsamos el componente TClientDataSet con el botón derecho del ratón y seleccionamos Create DataSet. Una vez creado sólo nos falta hacer que los campos sean persistentes. Eso se consigue haciendo doble clic sobre el componente y pulsando la combinación de teclas CTRL + A.

Con estos sencillos pasos ya hemos creado una tabla de memoria y ya podemos abrirla para introducir datos. No es necesario abrir la tabla ya que estas tablas de memoria hay que dejarlas activas por defecto.

DANDO FORMATO A LOS CAMPOS

Como tenemos tres campos de tipo real vamos a dar formato a los mismos del siguiente modo:

1. Hacemos doble clic sobre el componente ClientDataSet.

2. Seleccionamos los campos IMPORTE, PAGADO y PENDIENTE.

3. Activamos en el inspector de objetos su propiedad Currency.

Con esto ya tenemos los campos en formato moneda y con decimales.

REALIZANDO CALCULOS AUTOMATICAMENTE

A nuestra tabla de recibos le vamos a hacer que calcule automáticamente el importe pendiente. Esto lo vamos a hacer antes de que se ejecute el Post, en el evento BeforePost:

procedure TFormulario.TRecibosBeforePost( DataSet: TDataSet );
begin
TRecibosPENDIENTE.AsFloat := TRecibosIMPORTE.AsFloat - TRecibosPAGADO.AsFloat;
end;

De este modo, tanto si insertamos un nuevo registro como si lo modificamos realizará el cálculo del importe pendiente antes de guardar el registro.

AÑADIENDO, EDITANDO Y ELIMINANDO REGISTROS DE LA TABLA

Insertamos tres registros:

begin
TRecibos.Append;
TRecibosNUMERO.AsInteger := 1;
TRecibosCLIENTE.AsString := 'TRANSPORTES PALAZON, S.L.';
TRecibosIMPORTE.AsFloat := 1500;
TRecibosPAGADO.AsFloat := 500;
TRecibos.Post;

TRecibos.Append;
TRecibosNUMERO.AsInteger := 2;
TRecibosCLIENTE.AsString := 'TALLERES CHAPINET, S.L.';
TRecibosIMPORTE.AsFloat := 200;
TRecibosPAGADO.AsFloat := 200;
TRecibos.Post;

TRecibos.Append;
TRecibosNUMERO.AsInteger := 3;
TRecibosCLIENTE.AsString := 'GRUAS MARTINEZ, S.L.';
TRecibosIMPORTE.AsFloat := 625;
TRecibosPAGADO.AsFloat := 350;
TRecibos.Post;
end;

Si queremos modificar el primer registro:

begin
TRecibos.First;
TRecibos.Edit;
TRecibosCLIENTE.AsString := 'ANTONIO PEREZ BERNAL';
TRecibosIMPORTE.AsFloat := 100;
TRecibosPAGADO.AsFloat := 55;
TRecibos.Post;
end;

Y para eliminarlo:

begin
TRecibos.First;
TRecibos.Delete;
end;

MODIFICANDO LOS CAMPOS DE LA TABLA

Si intentamos añadir un nuevo campo a la tabla en FieldDefs y luego pulsamos CTRL + A para hacer el campo persistente veremos que desaparece de la definición de campos. Para hacerlo correctamente hay que hacer lo siguiente:

1. Pulsamos el componente ClientDataSet con el botón derecho del ratón.

2. Seleccionamos Clear Data.

3. Añadimos el nuevo campo en FieldDefs.

4. Volvemos a pulsar el el botón derecho del ratón el componente y seleccionamos Create DataSet.

5. Pulsamos CTRL + A para hacer persistente el nuevo campo.

Estos son los pasos que hay que seguir si se crean, modifican o eliminan campos en la tabla.

CREANDO CAMPOS VIRTUALES PARA SUMAR COLUMNAS

Vamos a crear tres campos virtuales que sumen automáticamente el valor de las columnas IMPORTE, PAGADO y PENDIENTE para totalizarlos. Comencemos con el cálculo del importe total:

1. Pulsamos el componente ClientDataSet con el botón derecho del ratón.

2. Seleccionamos Clear Data.

3. Hacemos doble clic en el componente ClientDataSet.

4. Pulsamos la combinación de teclas CTRL + N para añadir un nuevo campo:

Name: TOTALIMPORTE
FieldType: Agregate

5. Pulsamos Ok. Seleccionamos el campo creado escribimos en su propiedad Expression:

SUM(IMPORTE)

y activamos su propiedad Active. También activamos su propiedad Currency.

6. Creamos en el formulario un campo de tipo DBText y asociamos en nuevo campo creado:

DataSource: TRecibos
DataField: TOTALIMPORTE

7. Volvemos a pulsar el el botón derecho del ratón el componente y seleccionamos Create DataSet.

8. Activamos en el componente TClientDataSet la propiedad AggregatesActive.

Igualmente habría que crear dos campos más para sumar las columnas del importe pagado y el importe pendiente.

Utilizar ClientDataSet para crear tablas de memoria es ideal para procesar listados en tablas temporales sin tener que volcar el resultado en ninguna base de datos. Además podemos importar y exportar datos a XML usando el menú contextual de este componente.

Pruebas realizadas en Delphi 7.

09 octubre 2007

Leyendo los metadatos de una tabla de Interbase/Firebird

No hay nada que cause más pereza a un programador que la actualización de campos en las bases de datos de nuestros clientes. Hay muchas maneras de hacerlo: desde archivos de texto con metadatos, archivos SQL o incluso actualizaciones por Internet con FTP o correo electrónico.

Yo lo que suelo hacer es tener un archivo GDB o FDB (según sea Interbase o Firebird respectivamente) que contiene todas las bases de datos vacías. Si tengo que ampliar algún campo lo hago sobre esta base de datos.

Después cuando tengo que actualizar al cliente lo que hago es mandarle mi GDB vacío y mediante un pequeño programa que tengo hecho compara las estructuras de la base de datos de mi GDB vacío y las del cliente, creando tablas y campos nuevos según las diferencias sobre ambas bases de datos.

Ahora bien, ¿Cómo podemos leer el tipo de campos de una tabla? Pues en principio tenemos que el componente IBDatabase tiene la función GetTableNames que devuelve el nombre de las tablas de una base de datos y la función GetFieldNames que nos dice los campos pertenecientes a una tabla en concreto. El problema radica en que no me dice que tipo de campo es (float, string, blob, etc).

LEYENDO LOS CAMPOS DE UNA TABLA

Para leer los campos de una tabla utilizo el componente de la clase TIBQuery situado en la pestaña Interbase. Cuando este componente abre una tabla carga el nombre de los campos y su tipo en su propiedad FieldDefs. Voy a realizar una aplicación sencilla en la cual seleccionamos una base de datos Interbase o Firebird y cuando se elija una tabla mostrará sus metadatos de esta manera:


Va a contener los siguientes componentes:


- Un componente Edit con el nombre RUTADB que va a guardar la ruta de la base de datos.

- Un componente de la clase TOpenDialog llamado AbrirBaseDatos para buscar la base de datos Firebird/Interbase.

- Un botón llamado BExaminar.

- Un componente ComboBox llamado TABLAS que guardará el nombre de las tablas de la base de datos.

- Un componente IBDatabase llamado BaseDatos que utilizaremos para conectar.

- Un componente IBTransaction llamado Transaccion para asociarlo a la base de datos.

- Un componente IBQuery llamado IBQuery que utilizaremos para abrir una tabla.

- Y por último un campo Memo llamado Memo para mostrar la información de los metadatos.

Comenzemos a asignar lo que tiene que hacer cada componente:

- Hacemos doble clic sobre el componente BaseDatos y le damos de usuario SYSDBA y password masterkey. Pulsamos Ok y desactivamos su propiedad LoginPrompt.

- Asignamos el componente Transaccion a los componentes BaseDatos y IBQuery.

- Asignamos el componente BaseDatos a los componentes Transaccion y IBQuery.

- En la propiedad Filter del componente AbrirBaseDatos ponemos:

Interbase|*.gdb|Firebird|*.fdb

- Al pulsar el botón Examinar hacemos lo siguiente:

procedure TFormulario.BExaminarClick( Sender: TObject );
begin
if AbrirBaseDatos.Execute then
begin
RUTADB.Text := AbrirBaseDatos.FileName;
BaseDatos.DatabaseName := '127.0.0.1:' + RUTADB.Text;

try
BaseDatos.Open;
except
on E: Exception do
Application.MessageBox( PChar( E.Message ), 'Error al abrir base de datos',
MB_ICONSTOP );
end;

BaseDatos.GetTableNames( TABLAS.Items, False );
end;
end;

Lo que hemos hecho es abrir la base de datos y leer el nombre de las tablas que guardaremos dentro del ComboBox llamado TABLAS.

Cuando el usuario seleccione una tabla y pulse el botón Mostrar hacemos lo siguiente:

procedure TFormulario.BMostrarClick( Sender: TObject );
var
i: Integer;
sTipo: String;
begin
with IBQuery do
begin
Memo.Lines.Add( 'CREATE TABLE ' + TABLAS.Text + ' (' );

SQL.Clear;
SQL.Add( 'SELECT * FROM ' + TABLAS.Text );
Open;

for i := 0 to FieldDefs.Count - 1 do
begin
sTipo := '';

if FieldDefs.Items[i].FieldClass.ClassName = 'TIBStringField' then
sTipo := 'VARCHAR(' + IntToStr( FieldByName( FieldDefs.Items[i].Name ).Size ) + ')';

if FieldDefs.Items[i].FieldClass.ClassName = 'TFloatField' then
sTipo := 'DOUBLE PRECISION'; // También podría ser FLOAT (32 bits) aunque prefiero DOUBLE (64 bits)

if FieldDefs.Items[i].FieldClass.ClassName = 'TIntegerField' then
sTipo := 'INTEGER';

if FieldDefs.Items[i].FieldClass.ClassName = 'TDateField' then
sTipo := 'DATE';

if FieldDefs.Items[i].FieldClass.ClassName = 'TTimeField' then
sTipo := 'TIME';

if FieldDefs.Items[i].FieldClass.ClassName = 'TDateTimeField' then
sTipo := 'TIMESTAMP';

if FieldDefs.Items[i].FieldClass.ClassName = 'TBlobField' then
sTipo := 'BLOB';

// ¿Es un campo obligatorio?
if FieldByName( FieldDefs.Items[i].Name ).Required then
sTipo := sTipo + ' NOT NULL';

Memo.Lines.Add( ' ' + FieldDefs.Items[i].Name + ' ' + sTipo );

// Si no es el último campo añadimos una coma al final
if i < FieldDefs.Count - 1 then
Memo.Lines[Memo.Lines.Count-1] := Memo.Lines[Memo.Lines.Count-1] + ',';
end;

Memo.Lines.Add( ')' );
Close;
Transaction.Active := False;
end;
end;

Lo que hace este procedimiento es abrir con el componente IBQuery la tabla seleccionada y según los tipos de campos creamos la SQL de creación de la tabla.

Este método también nos podría ser útil para hacer un programa que copie datos entre tablas Interbase/Firebird.

Pruebas realizadas con Firebird 2.0 y Delphi 7.


PUBLICIDAD

lentillas

bajar de peso

bajar peso

05 octubre 2007

Creando consultas SQL con parámetros

En el artículo anterior vimos como realizar consultas SQL para INSERT, DELETE, UPDATE y SELECT utilizando el componente IBSQL que forma parte de la paleta de componentes IBExpress.

También quedó muy claro que la velocidad de ejecución de consultas con este componente respecto a otros como IBQuery es muy superior. Todo lo que hemos visto esta bien para hacer consultas esporádicas sobre alguna tabla que otra, pero ¿que ocurre si tenemos que realizar miles de consultas SQL de una sola vez?

UTILIZANDO UNA TRANSACCION POR CONSULTA

Supongamos que tenemos que modificar el nombre de 1000 registros de la tabla CLIENTES:

var
i: Integer;
dwTiempo: DWord;
begin
with Consulta do
begin
////////////// METODO LENTO ////////////////

dwTiempo := TimeGetTime;

for i := 1 to 1000 do
begin
SQL.Clear;
SQL.Add( 'UPDATE CLIENTES' );
SQL.Add( 'SET NOMBRE = ' + QuotedStr( 'NOMBRE CLIENTE Nº ' + IntToStr( i ) ) );
SQL.Add( 'WHERE ID = ' + IntToStr( i ) );

Transaction.StartTransaction;

try
ExecQuery;
Transaction.Commit;
except
on E: Exception do
begin
Application.MessageBox( PChar( E.Message ), 'Error de SQL', MB_ICONSTOP );
Transaccion.Rollback;
end;
end;
end;

ShowMessage( 'Tiempo: ' + IntToStr( TimeGetTime - dwTiempo ) + ' milisegundos' );
end;
end;

Como puede verse arriba, por cada cliente actualizado he generado una SQL distinta abriendo y cerrando una transacción para cada registro. He utilizado la función TimeGetTime que se encuentra en la unidad MMSystem para calcular el tiempo que tarda en actualizarme el nombre de los 1000 clientes. En un PC con Pentium 4 a 3 Ghz, 1 GB de RAM y utilizando el motor de bases de datos Firebird 2.0 me ha tardado 4167 milisegundos.

Aunque las consultas SQL van muy rápidas con los componentes IBSQL aquí el fallo que cometemos es que por cada registro actualizado se abre y se cierra una transacción. En una base de datos local no se nota mucho pero en una red local con muchos usuarios trabajando a la vez le puede pegar fuego al concentrador.

Lo ideal sería poder modificar la SQL pero sin tener que cerrar la transacción. Como eso no se puede hacer en una consulta que esta abierta entonces hay que utilizar los parámetros. Los parámetros (Params) nos permiten enviar y recoger información de una consulta SQL que se esta ejecutando sin tener que cerrarla y abrila.

UTILIZANDO PARAMETROS EN LA CONSULTA

Para introducir parámetros en una consulta SQL hay que añadir dos puntos delante del parámetro. Por ejemplo:

UPDATE CLIENTES
SET NOMBRE = :NOMBRE
WHERE ID = :ID

Esta consulta tiene dos parámetros: ID y NOMBRE. Los nombres de los parámetros no tienen porque coincidir con el nombre del campo. Bien podrían ser así:

UPDATE CLIENTES
SET NOMBRE = :NUEVONOMBRE
WHERE ID = :IDACTUAL

De este modo se pueden modificar las condiciones de la consulta SQL sin tener que cerrar la transacción. Después de crear la consulta SQL hay que llamar al método Prepare para que prepare la consulta con los futuros parámetros que se le van a suministrar (no es obligatorio pero si recomendable). Veamos el ejemplo anterior utilizando parámetros y una sóla transacción para los 1000 registros:

var
i: Integer;
dwTiempo: DWord;
begin
with Consulta do
begin
////////////// METODO RÁPIDO ////////////////

dwTiempo := TimeGetTime;

Transaction.StartTransaction;

SQL.Clear;
SQL.Add( 'UPDATE CLIENTES' );
SQL.Add( 'SET NOMBRE = :NOMBRE' );
SQL.Add( 'WHERE ID = :ID' );
Prepare;

for i := 1 to 1000 do
begin
Params.ByName( 'NOMBRE' ).AsString := 'NOMBRE CLIENTE Nº '+ IntToStr( i );
Params.ByName( 'ID' ).AsInteger := i;
ExecQuery;
end;

try
Transaction.Commit;
except
on E: Exception do
begin
Application.MessageBox( PChar( E.Message ), 'Error de SQL', MB_ICONSTOP );
Transaccion.Rollback;
end;
end;

ShowMessage( 'Tiempo: ' + IntToStr( TimeGetTime - dwTiempo ) + ' milisegundos' );
end;
end;

En esta ocasión me ha tardado sólo 214 milisegundos, es decir, se ha reducido al 5% del tiempo anterior sin saturar al motor de bases de datos abriendo y cerrando transacciones sin parar.

Este método puede aplicarse también para consultas con INSERT, SELECT y DELETE. En lo único en lo que hay que tener precaución es en no acumular muchos datos en la transacción, ya que podría ser peor el remedio que la enfermedad.

Si teneis que actualizar cientos de miles de registros de una sola vez, recomiendo realizar un Commit cada 1000 registros para no saturar la memoria caché de la transacción. Todo depende del número de campos que tengan las tablas así como el número de registros a modificar. Utilizad la función TimeGetTime para medir tiempos y sacar conclusiones.

Y si el proceso a realizar va a tardar más de 2 o 3 segundos utilizar barras de progreso e hilos de ejecución, ya que algunos usuarios neuróticos podrían creer que nuestro programa se ha colgado y empezarían ha hacer clic como posesos (os aseguro que existe gente así, antes de que termine la consulta SQL ya te están llamando por teléfono echándote los perros).

Pruebas realizadas con Firebird 2.0 y Dephi 7.

04 octubre 2007

Creando consultas SQL rápidas con IBSQL

Cuando se crea un programa para el mantenimiento de tablas de bases de datos (clientes, artículos, etc.) el método ideal es utilizar componentes ClientDataSet como vimos anteriormente.

Pero hay ocasiones en las que es necesario realizar consultas rápidas en el servidor tales como incrementar existencias en almacén, generar recibos automáticamente o incluso incrementar nuestros contadores de facturas sin utilizar generadores.

En ese caso el componente más rápido para bases de datos Interbase/Firebird es IBSQL el cual permite realizar consultas SQL sin que estén vinculadas a ningún componente visual. Vamos a ver unos ejemplos de inserción, modificación y eliminación de registros utilizando este componente.

INSERTANDO REGISTROS EN UNA TABLA

Aquí tenemos un ejemplo de insertar un registro en una tabla llamada CLIENTES utilizando un objeto IBSQL llamado Consulta:

with Consulta do
begin
SQL.Clear;
SQL.Add( 'INSERT INTO CLIENTES' );
SQL.Add( '( NOMBRE, NIF, IMPORTEPTE )' );
SQL.Add( 'VALUES' );
SQL.Add( '( ''ANTONIO GARCIA LOPEZ'', ''46876283D'', 140.23 )' );

Transaction.StartTransaction;

try
ExecQuery;
Transaction.Commit;
except
on E: Exception do
begin
Application.MessageBox( PChar( E.Message ), 'Error de SQL', MB_ICONSTOP );
Transaccion.Rollback;
end;
end;
end;

Como puede apreciarse hemos tenido que abrir nosotros a mano la transacción antes de ejecutar la consulta ya que el objeto IBSQL no la abre automáticamente tal como ocurre en los componentes IBQuery.

Una vez ejecutada la consulta, si todo ha funcionado correctamente enviamos la transacción al servidor mediante el método Commit. En el caso de que falle mostramos el error y cancelamos la transacción utilizando el método RollBack.

MODIFICANDO LOS REGISTROS DE UNA TABLA

El método para modificar los registros es el mismo que para insertarlos:

with Consulta do
begin
SQL.Clear;
SQL.Add( 'UPDATE CLIENTES' );
SQL.Add( 'SET NOMBRE = ''MARIA GUILLEN ROJO'',' );
SQL.Add( 'NIF = ''69236724W'', ' );
SQL.Add( 'IMPORTEPTE = 80.65' );
SQL.Add( 'WHERE ID=21963' );

Transaction.StartTransaction;

try
ExecQuery;
Transaction.Commit;
except
on E: Exception do
begin
Application.MessageBox( PChar( E.Message ), 'Error de SQL', MB_ICONSTOP );
Transaccion.Rollback;
end;
end;
end;

ELIMINANDO REGISTROS DE LA TABLA

Al igual que para las SQL para INSERT y UPDATE el procedimiento es el mismo:

with Consulta do
begin
SQL.Clear;
SQL.Add( 'DELETE FROM CLIENTES' );
SQL.Add( 'WHERE ID=21964' );

Transaction.StartTransaction;

try
ExecQuery;
Transaction.Commit;
except
on E: Exception do
begin
Application.MessageBox( PChar( E.Message ), 'Error de SQL', MB_ICONSTOP );
Transaccion.Rollback;
end;
end;
end;

CONSULTANDO LOS REGISTROS DE UNA TABLA

El método de consultar los registros de una tabla mediante SELECT difiere de los que hemos utilizado anteriormente ya que tenemos que dejar la transacción abierta hasta que terminemos de recorrer todos los registros:

with Consulta do
begin
SQL.Clear;
SQL.Add( 'SELECT * FROM CLIENTES' );
SQL.Add( 'ORDER BY ID' );

Transaction.StartTransaction;

// Ejecutarmos consulta
try
ExecQuery;
except
on E: Exception do
begin
Application.MessageBox( PChar( E.Message ), 'Error de SQL', MB_ICONSTOP );
Transaccion.Rollback;
end;
end;

// Recorremos los registros
while not Eof do
begin
Memo.Lines.Add( FieldByName( 'NOMBRE' ).AsString );
Next;
end;

// Cerramos la consulta y la transacción
Close;
Transaction.Active := False;
end;

Aunque pueda parecer un coñazo el componente de la clase TIBSQL respesto a los componentes TIBTable o TIBQuery, su velocidad es muy superior a ambos componentes, sobre todo cuando se ejecutan las consultas sucesivamente.

En el próximo artículo veremos cómo utilizar parámetros en las consultas.

Pruebas realizadas en Firebird 2.0 y Delphi 7.

Publicidad