Aplicaciones de Bases de Datos con Delphi III

Anuncio
Guías técnicas Grupo Danysoft:
Aplicaciones de Bases de
Datos con Delphi III
Equipo Grupo Danysoft
julio de 2003 - (902) 123146
www.danysoft.com
Guías Técnicas Grupo Danysoft: Aplicaciones de Bases de datos con Delphi
Este documento se ha realizado utilizando Doc-To-Help ®, distribuido por :
Danysoft Internacional
Avda de España 17
28100 Alcobendas – Madrid
Tfno. 902.123146
Fax. 902.123145
http://www.danysoft.com
http://www.danyshop.com
[email protected]
www.danysoft.com - Página 2/7
Guías Técnicas Grupo Danysoft: Aplicaciones de Bases de Datos con Delphi
Aplicaciones de bases de datos con Delphi
Una estrategia para su desarrollo (3ra. entrega)
Otro punto fuerte de los componentes ClientDataSet y DataSetProvider es la
reconciliación de errores de actualización. Borland provee un mecanismo completo
que nos permite indicarle al usuario con precisión él o los errores ocurridos al intentar
aplicar sus modificaciones. Incluso podemos permitirle que realice las acciones que
crea oportunas para intentar resolver el problema que generó el error y volver a
aplicar las actualizaciones.
El registro de cambios
El ClientDataSet mantiene los cambios realizados por el usuario en memoria, más
concretamente en su propiedad Delta. Para cada registro modificado, el ClientDataSet
almacena la siguiente información en el registro de cambios:
• Si el registro fue insertado, el registro insertado completo.
• Si el registro fue eliminado, el registro eliminado completo.
• Si el registro fue modificado, el registro original completo y el registro
modificado pero solamente los campos que han sido modificados.
La propiedad ChangeCount indica la cantidad de registros en el registro de cambios.
Si el valor de ChangeCount es cero entonces lo s datos del ClientDataSet no han sido
modificados.
La propiedad UpdateStatus indica el estado del registro actual. Los posibles estados
son: usInserted, usDeleted, usModified y usUnmodified. La propiedad StatusFilter
nos permite filtrar los registros visibles dependiendo del valor de la propiedad
UpdateStatus. Por defecto están visibles los registros insertados, modificados y, por
supuesto, los no modificados.
Los datos originales son mantenidos en la propiedad Data. La estructura de los datos
contenidos en la propiedad Data es igual a la estructura de los datos mantenidos en la
propiedad Delta. Esto significa que podemos asignarle el valor de la propiedad Delta
de un primer ClientDataSet a la propiedad Data de un segundo ClientDataSet y tener
acceso, desde el segundo ClientDataSet, al registro de cambios del primer
ClientDataSet.
Errores de actualización
Lo descrito en los párrafos anteriores es utilizado por el DataSetProvider para resolver
las actualizaciones. El proceso de actualización comienza con una llamada al método
ApplyUpdates del ClientDataSet. Este método recibe un parámetro que indica la
cantidad máxima de errores de actualización tolerados antes de finalizar el proceso de
actualización. El valor -1 indica que el proceso de actualización debe ser completado
sin importar la cantidad de errores ocurridos. El valor 0 indica que el proceso de
actualización debe ser finalizado ante el primer error. El valor 1 ante el segundo y así
sucesivamente. El comportamiento del proceso de actualización depende entonces del
parámetro que le pasamos al método ApplyUpdates y de la cantidad de registros en el
registro de cambios. La siguiente tabla muestra como se comporta el DataSetProvider
en distintos escenarios. En dicha tabla puede verse claramente como la cantidad de
registros actualizados depende no sólo de la cantidad de errores de actualización
ocurridos sino también del valor que le pasamos a ApplyUpdates.
ApplyUpdates
Cantidad de registros
Error en registro Nº Registros actualizados
www.danysoft.com - Página 3/7
Guías Técnicas Grupo Danysoft: Aplicaciones de Bases de datos con Delphi
-1
0
1
3
3
3
1y3
2
2
1 (el 2º)
1 (el 1º)
2 (el 1º y el 3º)
En la segunda entrega vimos la secuencia de eventos que genera el DataSetProvider
durante el proceso de actualización. Cuando ocurre un error al intentar actualizar un
registro el DataSetProvider genera el evento OnUpdateError, en el que recibimos
información detallada acerca del error ocurrido, el registro que generó el error y el
tipo de actualización que se intentó realizar. En este evento podemos indicarle al
DataSetProvider la acción que queremos que lleve a cabo con el registro en cuestión.
Dependiendo del valor pasado a ApplyUpdates, el DataSetProvider continuará con el
proceso de actualización o lo cancelará.
Cuando ocurre un error de actualización el DataSetProvider finaliza la transacción
actual deshaciendo los cambios realizados, es decir, la finaliza haciendo Rollback.
Debido a que las relaciones maestro/detalle son actualizadas en el contexto de la
misma transacción, si, por ejemplo, ocurre un error al actualizar un registro del
detalle, los cambios al maestro serán cancelados. Sin embargo, si en el registro de
cambios hay varios registros del maestro con sus correspondientes detalles, los
registros actualizados correctamente no serán cancelados ya que fueron actualizados
en el contexto de otra transacción.
Reconciliación de errores de actualización
Al finalizar el proceso de actualización, ya sea porque se comp letó o porque finalizó
debido a que se superó la cantidad máxima de errores de actualización tolerados, el
DataSetProvider le informa al ClientDataSet lo ocurrido. Para cada registro hay tres
situaciones posibles: que el registro haya sido actualizado, que el registro no haya sido
actualizado debido a un error de actualización o que el registro no haya sido
actualizado porque el proceso de actualización finalizó antes de intentar actualizarlo.
A continuación se describe lo que ocurre en cada caso:
• Los registros que fueron actualizados son quitados del registro de cambios y
mezclados con los registros originales.
• Para cada uno de los registros que no fueron actualizados debido a un error de
actualización, el ClientDataSet genera el evento OnReconcileError. En este
evento recibimos información detallada acerca del error ocurrido, el registro
que generó el error y el tipo de actualización que se intentó realizar. Aquí
también podemos indicarle al ClientDataSet la acción que queremos que lleve
a cabo con el registro en cuestión. Es importante escribir un manejador para
este evento ya que de lo contrario corremos el riesgo de no enterarnos si han
ocurrido errores de actualización. Esto se debe a que el ClientDataSet no
genera excepciones por errores de actualizació n.
• Los registros que no fueron actualizados porque el proceso de actualización
finalizó antes de intentar actualizarlos, son mantenidos en el registro de
cambios. Para estos registros no se genera ningún evento.
El método ApplyUpdates devuelve un valor entero que indica la cantidad de errores
de actualización ocurridos. Por ejemplo, el valor 0 indica que no hubo errores de
actualización. En lugar de escribir un manejador para el evento OnReconcileError
podemos evaluar el valor devuelto por ApplyUpdates para saber si ocurrieron errores
de actualización.
Para reconciliar los errores de actualización Borland provee la unidad RecError,
conteniendo la función HandleReconcileError, que le presenta al usuario un
www.danysoft.com - Página 4/7
Guías Técnicas Grupo Danysoft: Aplicaciones de Bases de Datos con Delphi
formulario con información del error y la posibilidad de realizar acciones correctivas
para intentar actualizar nuevamente el registro que generó el error.
La imagen de arriba muestra el formulario presentado por la función
HandleReconcileError para el caso de un registro que no pudo ser actualizado debido
a un problema de concurrencia. En este caso, el usuario intentó modificar el campo
OnOrder de la tabla Parts del alias DBDemos pero otro usuario modificó el mismo
registro asignándole otro valor a dicho campo. El ClientDataSet nos permite acceder
al valor ingresado por el usuario (propiedad Value), el valor original obtenido por el
usuario (propiedad OldValue) y el valor actual en la base de datos (propiedad
CurrValue).
El idioma original de este formulario es inglés pero el código fuente está disponible
en el directorio de la VCL por lo que podemos modificarlo para traducirlo, por
ejemplo, al castellano.
En todos los casos, los registros que no fueron actualizados permanecen en el registro
de cambios tal y como los modificó el usuario. Esto es muy importante ya que
significa que el usuario no pierde las modificaciones realizadas y que puede realizar
acciones correctivas para intentar solucionar el problema que generó el error de
actualización. Por ejemplo, supongamos que el usuario ha ingresado una orden con 40
ítems y la cantidad de stock disponible para el producto del último ítem no es
suficiente para satisfacer el pedido. El usuario no sólo no pierde todos los datos de la
orden sino que podemos informarle la cantidad disponible para que modifique el ítem
número 40 para que la orden pueda ser procesada.
Transacciones y actualizaciones encadenadas
Los eventos del DataSetProvider nos permiten encadenar actualizaciones como si
estuviéramos utilizando disparadores en una base de datos SQL. Por ejemplo, en el
evento BeforeUpdateRecord generado antes de actualizar cada ítem de una orden,
podemos escribir código para actualizar el stock del producto. Este código podría
estar contenido en el Data Module de lógica de datos de productos y utilizar un
www.danysoft.com - Página 5/7
Guías Técnicas Grupo Danysoft: Aplicaciones de Bases de datos con Delphi
ClientDataSet y un DataSetProvider para acceder a un producto en particular. No
sería necesario repetir este código en el mencionado evento. Todo lo que tenemos que
hacer es llamar al método apropiado en el Data Module de lógica de datos de
productos. En este Data Module, el DataSetProvider de productos, al momento de
actualizar el stock de un producto, no iniciará una nueva transacción porque detectará
que ya existe una transacción iniciada (el DataSetProvider de órdenes la inició) por lo
que sólo se limitará a actualizar la tabla de productos. Es más, como el
DataSetProvider de productos no inició la transacción tampoco la finalizará. En este
esquema es fundamental generar una excepción si ocurre un error al actualizar la tabla
de productos. De esta forma el DataSetProvider de órdenes, al capturar la excepción,
podrá finalizar la transacción actual cancelando los cambios realizados.
Estas características del DataSetProvider nos permiten encapsular la lógica de
productos en el Data Module de productos; la lógica de órdenes en el Data Module de
órdenes y así sucesivamente. De esta forma la aplicación será más fácil de modificar y
tendremos más posibilidades de reutilizar código. Por ejemplo, si por algún motivo
debemos modificar el proceso de actualización del stock de productos, sólo tendremos
que hacer cambios en el Data Module de lógica de datos de productos.
Actualizaciones en cascada
El DataSetProvider soporta el uso de actualizaciones en cascada para la eliminación y
modificación de registros en relaciones maestro/detalle. Este soporte está pensado
para aquellos casos en los cuales, por ejemplo, la eliminación de una orden está
completamente resuelta en la base de datos. Es decir, aquellos casos en los que sólo
tenemos que eliminar el registro maestro ya que en la base de datos, por medio de
disparadores, los registros del detalle serán eliminados automáticamente. En estos
casos, el DataSetProvider sólo deberá generar sentencias SQL de DELETE para el
registro maestro.
Por medio de la propiedad Options del DataSetProvider podemos indicar si dicho
DataSetProvider soporta eliminaciones y actualizaciones en cascada. En el
ClientDataSet, cuando el DataSetProvider soporta eliminaciones en cascada, podemos
eliminar un registro del maestro aun cuando existan registros en el detalle. Esto es así
porque el DataSetProvider asume que la base de datos se encargará de eliminar los
registros del detalle.
Hay que tener mucho cuidado con las eliminaciones y actualizaciones en cascada ya
que el DataSetProvider no generará sentencias SQL para eliminar o actualizar los
registros del detalle y, por lo tanto, no generará los eventos BeforeUpdateRecord y
AfterUpdateRecord para los registros del detalle. Si la lógica de datos de nuestra
aplicación utiliza estos eventos para los registros del detalle entonces no deberíamos
habilitar eliminaciones y actualizaciones en cascada. La única limitación será que no
podremos eliminar un registro del maestro si existen registros en el detalle. Esta
limitación puede ser fácilmente resuelta si antes de eliminar un registro del maestro
eliminamos todos los registros del detalle.
Conclusiones
La reconciliación de errores de actualización es un tema muy importante que pocas
veces consideramos como es debido.
Los errores de actualización pueden significar una experiencia frustrante para los
usuarios de nuestra aplicación. El esfuerzo dedicado a que este proceso sea lo más
amigable posible será muy bienvenido por ellos y nos ahorrarán muchas llamadas de
soporte.
www.danysoft.com - Página 6/7
Guías Técnicas Grupo Danysoft: Aplicaciones de Bases de Datos con Delphi
El mecanismo ofrecido por Borland puede ser suficiente en la mayoría de los casos,
pero si necesitamos modificarlos tenemos todas las herramientas para hacerlo. El
formulario contenido en la unidad RecError es una fuente de aprendizaje invalorable,
como la mayor parte del código fuente de la VCL. Analizando el código fuente de
esta unidad podemos aprender mucho y ajustar a nuestras necesidades el mecanismo
ofrecido por Borland.
Otro concepto fundamente es la encapsulación. La gestión de transacciones del
DataSetProvider nos permite anidar actualizaciones como si se tratara de
disparadores. Esto nos da la posibilidad de encapsular la lógica de datos en distintos
Data Modules y hacer que nuestras aplicaciones sean más fáciles de actualizar y
mejorar las posibilidad de reutilizar código.
www.danysoft.com - Página 7/7
Descargar