miércoles, 14 de octubre de 2009

Manejo de versiones o 'versioning' con Hibernate

El manejo automático de versiones es una técnica simple para asegurar la integridad de datos.
Rápidamente podemos pensar en un ejemplo en donde tenemos 2 clientes A y B que cargan el mismo registro R. Luego de un tiempo A hace 'commit' de R con los datos cambiados, y después B hace lo mismo, cambiando los datos de R, pero no desde el estado de R luego del 'commit' de A.

Lo que hace el manejo automático de versiones es mantener un campo para versionar el registro. En el ejemplo anterior, A y B obtienen el registro R con una versión V. Luego del 'commit' de A la versión se cambia, y de esta forma cuando B intenta hacer lo mismo, las versiones son distintas y el chequeo de esta versión falla.

En Hibernate podemos declarar el campo de versión y se recomienda que sea del tipo int o long.
Aquí vemos un ejemplo en donde asumimos que el atributo se llama 'version' y lo queremos mapear a la columna 'version' en la base de datos:

<version name="version" column="version"/>

En general con Hibernate surgen dudas sobre cómo la aplicación se da cuenta de que un objeto ha cambiado en la base de datos.

El funcionamiento es el siguiente: Al usar Hibernate se deberia llamar a saveOrUpdate() o update() cuando se quiere actualizar un dato cambiado. Estos métodos lo que hacen es realizar una consulta del estilo:
UPDATE Persona SET nombre='juan', VERSION=2
WHERE ID=123 AND VERSION=1

Luego, Hibernate chequea la cantidad de filas afectadas en el resultado de JDBC, y lanza una excepción del tipo StaleObjectStateException si ninguna fila fue actualizada. De esta forma, podemos atrapar dicha excepción y darnos cuenta de que hubo un conflcto.
Algo importante a notar es que cada actualización incrementa el número de versión en uno y chequea que no haya sido cambiado desde que fue leído.

martes, 6 de octubre de 2009

Cadena de Responsabilidad (Chain of Responsibility)

Este patrón tiene como propósito establecer una cadena dentro del sistema, de tal forma que un mensaje puede ser manejado en el nivel donde es recibido por primera vez, o puede ser reenviado hacia un objeto que pueda manejarlo.

Cuando alguna acción se produce dentro de un sistema orientado a objetos, es común que se represente a través de un evento o mensaje.

En los casos más simples, el mismo objeto que produce el mensaje responde a él. Por ejemplo, un campo de texto puede producir eventos en respuesta a acciones del usuario (como escribir con el teclado) y también puede responder a esos eventos(mostrando texto en el campo).

En casos más complejos, la respuesta a los mensajes puede tener un papel más importante.

La Cadena de Responsabilidades es una cadena de reenvío de mensajes. Si un objeto no puede manejar un mensaje dado, lo pasa hacia otro objeto más arriba. Frecuentemente la cadena se implementa con un modelo padre-hijo o contenedor. Con esta idea, los mensajes no manejados por el objeto hijo son enviados al padre, y potencialmente el padre del padre, hasta que se alcance el objeto indicado.

Este patrón es ùtil para el desarrollo de una interfaz de usuario.

Para implementar la Cadena de Responsabilidades, necesitas:

Handler - La interfaz que define el método usado para pasar un mensaje al siguiente handler. El mensaje es normalmente una llamada a un método, aunque si se necesitan más datos encapsulados, un objeto puede ser pasado también.

HandlerImpl - Una clase que implementa la interfaz Handler. Mantiene una referencia al siguiente Handler. Esta referencia es asignada en el constructor de la clase o a través de un método setter. La implementación del método que manejo de mensajes puede llamar a un método para manejarlo, reenviar el mensaje al siguiente handler, o ambos.

Ventajas y desventajas

La Cadena de Responsabilidades ofrece una gran flexibilidad en el procesamiento de eventos para una aplicación, ya que domina el manejo de eventos complejo dividiendo las responsabilidades a través de un número de elementos simples. Permite a un grupo de clases comportarse como un todo, ya que los eventos producidos por una clase pueden ser enviados a otras clases para que los atrapen dentro del grupo.
Por otro lado, la flexibilidad que este patrón provee tiene un precio: es difícil de desarrollar, testear y debuguear. A medida que se avanza la cadena se hace mas compleja, hay que ser cuidadosos viendo si los eventos están siendo correctamente reenviados.

Un error en el reenvío puede resultar en mensajes perdidos (mensajes que no pueden ser manejados por un handler y por lo tanto nunca tienen respuesta). También puede producirse un problema de alto volumen de mensajes y múltiples estados dentro de la cadena(chatter). Si muchos mensajes son producidos durante un periodo corto de tiempo y son pasados varias veces hasta que son atrapados, entonces el sistema puede volverse lento.

Variantes

Hay varias formas de adaptar la Cadena de Responsabilidades para alcanzar distintos requerimientos de las aplicaciones. Los dos criterios principales se basan en la estrategia de manejo y la estrategia de reenvío.

La estrategia de manejo se centra en cómo esta implementado el Handler. Algunas variantes pueden ser:

Dafault Handler - Algunas implementaciones tiene un handler de base, que es el handler por defecto para toda la cadena. Es usado normalmente solo cuando no hay una clase manejadora especificada. Un handler por defecto es muy útil para evitar el problema de mensajes perdidos mencionado anteriormente.

Manejar y Extender - En esta variante, el manejo de eventos tiene un comportamiento base a medida que el evento se propagan. Útil para logging.

Manejadores dinámicos - Algunas implementaciones permiten que la estructura de reenvío de mensajes cambien en tiempo de ejecución, usando un método setter para cada clase de la cadena. Esta flexibilidad deriva en una mayor complejidad.

Para la estrategia de reenvío se definen estas variantes:

Handle por defecto – Manejar cualquier mensaje que no es específicamente reenviado.

Propagar por defecto - reenviar cualquier mensaje que no es explícitamente manejado.

Reenviar al manejador por defecto - Más complejo que el patrón base, aquí se usa un manejador de eventos por defecto. Cualquier mensaje no manejado explícitamente en el componente, o no reenviado a otro manejador, va a ser enviado al manejador por defecto.

Ignorar por defecto - Cualquier mensaje que no es explícitamente manejado o reenviado es descartado. Si las clases en la cadena producen eventos que no son usados en la aplicación, esta puede ser una forma aceptable de reducir la sobrecarga. Sin embargo, se debe ser cuidadoso con este método para evitar descartar eventos que el sistema debería estar manejando.
 
 
Copyright © Slim code
Blogger Theme by BloggerThemes Design by Diovo.com