Está en la página 1de 25

ESQUEMA DE PERSISTENCIA

¿Qué es la Persistencia?

Permite que los datos de las aplicaciones sean persistentes en el tiempo. No se ven afectados por situaciones normales (apagado y encendido de equipos) ni anormales (cortes del suministro eléctrico).

La necesidad de persistir no es nueva. Ha habido y van a haber múltiples formas de afrontar esta necesidad. El desafío consiste en conocer los límites de las distintas partes de un sistema y ser capaz de diferenciar capas y sus funcionalidades.

Justamente una de estas capas es el tema en estudio: Esquema de Persistencia

Entidades

Representan datos del dominio de la solución. Deben encapsular sus datos y ser transparentes y reutilizables con distintos mecanismos de persistencia.

Ej: Farmacia - Remedio

 

Remedio

Farmacia

 

- codigo: int

- nombre: String

- nombre: String

- domicilio: String

- CUIL: String

- descripcion: String

- presentacion: String

1

*

 
 

+ getCodigo():int

+ getNombre():String

 
+ getNombre():String  
 

+ setCodigo(_Codigo: int):void

+ setNombre(_nombre:String):void

+getDomicilio():String

+ setDomicilio(_domicilio:String):void

+ getCuit():String

+ setCuit(_cuit):void

+ getNombre():String

+ setNombre(_Nombre: String):void

+ getDescripcion():String

+ setDescripcion(_Descripcion: String):void

+ getPresentacion():String

 

+ setPresentacion(_Presentacion:String):void

Distintos Mecanismos de Almacenamiento

1. Archivos planos: Mecanismo primitivo. Utilizado ampliamente por COBOL. Actualmente se encuentra en desuso. Presenta serias dificultades para permitir modificaciones concurrentes y poseer mecanismos de recuperación ante fallas.

y poseer mecanismos de recuperación ante fallas. 2. Documentos XML: Permiten administrar información en

2. Documentos XML: Permiten administrar información en forma jerárquica. Utilizan mecanismos estándares en forma mundial. Su principal desventaja es la incapacidad para indexar y realizar tareas de forma eficiente. Generalmente los tiempos de respuesta no son comparables con el resto de los mecanismos mencionados. Se utilizan ampliamente para la transferencia de información entre sistemas heterogéneos. Ej: Sistema de Validación de tarjetas de crédito.

Ej: Sistema de Validación de tarjetas de crédito. Persistencia Autor : Atilio Ranzuglia; Editado por: Adrián

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 1 de 25

3. BD Orientadas a Objetos: Permiten almacenar y recuperar objetos desde y hacia la BD en forma transparente (no en un 100%). No es necesario realizar modificaciones ya que la BD soporta el ingreso y egreso de objetos. Aún así, de alguna forma debe realizarse la indexación y distribución de datos para no carecer de performance al igual que los documentos XML. Poseen mecanismos internos de conversión de objetos. En la mayoría de los casos utilizan una BD Relacional de fondo para dar soporte a sus operaciones.

4. BD Relacionales: Introducen el concepto de servicios de datos. Con ello permiten la administración de múltiples usuarios, bloqueos y acceso concurrente. Permiten definir estructuras eficientes para el almacenamiento de datos primitivos. Indexan su contenido y presentan una performance muy superior a los mecanismos comentados previamente. Establecieron el lenguaje de consultas SQL, de uso mundial. Presentan la gran desventaja de poseer una estructura muy diversa a aquella utilizada en las entidades de las reglas del negocio.

utilizada en las entidades de las reglas del negocio. Relaciones – Criterios Todos los datos almacenados

Relaciones – Criterios

Todos los datos almacenados con cualquier mecanismo necesitan relacionarse. Es muy importante entender la diferencia entre el dato almacenado y la entidad que lo representa en las reglas del negocio. Por ejemplo, en la relación entre Carrera y Materia, podemos emplear las cardinalidades:

- 1 a N: Nos permite relacionar múltiples materias en una carrera, pero no una misma materia en más de una carrera

Carrera

 

Materia

- codigo: int

- codigo: int

- nombre: String

1

*

- nombre: String

+ getCodigo():int

 
+ getCodigo():int   + getCodigo():int

+ getCodigo():int

+ setCodigo(_Codigo: int):void

 

+ setCodigo(_Codigo: int):void

+ getNombre():String

+ getNombre():String

+ setNombre(_nombre:String):void

+ setNombre(_nombre:String):void

Carrera

Materia

IDCarrera

IDMateria

Codigo

Codigo

Nombre

Nombre

 

IDCarrera

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 2 de 25

- N a N: Nos permite relacionar múltiples materias con múltiples carreras. Sólo crearemos una entidad asociativa cuando necesitemos guardar información de la relación

Carrera

 

DetallePlanEstudio

 

Materia

- codigo: int

- inicio: Date

- codigo: int

- nombre: String

1

*

- fin: Date

1

*

- nombre: String

+ getCodigo():int

+ getCodigo():int + getInicio():Date + getCodigo():int

+ getInicio():Date

+ getCodigo():int + getInicio():Date + getCodigo():int

+ getCodigo():int

+ setCodigo(_Codigo: int):void

+ setInicio(_Inicio:Date):void

+ setCodigo(_Codigo: int):void

+ getNombre():String

+ getFin():Date

+ getNombre():String

+ setNombre(_nombre:String):void

+ setFin(_Fin:Date):void

+ setNombre(_nombre:String):void

Carrera

DetallePlanEstudio

Materia

IDCarrera

IDCarrera

IDMateria

Codigo

IDMateria

Codigo

Nombre

Inicio

Nombre

 

Fin

Divergencias con entidades

Todos los mecanismos de almacenamiento presentados poseen algún porcentaje de divergencia respecto de las entidades ideales pertenecientes a las reglas del negocio. Por lo tanto es necesario definir un esquema de persistencia que posea la capacidad de incluir, e incluso intercambiar, los mecanismos de almacenamiento empleados.

Debe crearse un mecanismo suficientemente útil y transparente que relacione ambos mundos:

reglas del negocio y persistencia de datos.

Este aspecto se logra creando una capa autosuficiente que pueda interactuar:

- Por un lado con las entidades de las reglas del negocio

- Por otro lado, con los mecanismos persistentes. Deben prestar y requerir servicios

Arquitectura Cliente – Servidor (2 capas)

Esta arquitectura ha sido ampliamente utilizada durante los años ’90 gracias a la aparición de servidores de BD eficientes.

Poseen la ventaja de liberar de procesamiento al servidor de datos trasladando las reglas del negocio a cada cliente (PC de usuario final).

Este mismo aspecto es su principal desventaja. Al no poseer las reglas del negocio en lugares centralizados, se dificulta la coordinación y aplicación de patrones, como Observador. Requiere el uso de estaciones de trabajo poderosas. Dificulta el mantenimiento y la capacidad de poseer tolerancia a fallas y procesamiento distribuido.

de poseer tolerancia a fallas y procesamiento distribuido. Arquitectura Middleware (3 capas) Nuevamente un
de poseer tolerancia a fallas y procesamiento distribuido. Arquitectura Middleware (3 capas) Nuevamente un

Arquitectura Middleware (3 capas)

Nuevamente un intermediario aparece para aportar mejoras. Con las dificultades presentes en la arquitectura de 2 capas se hace necesaria la presencia de una estructura más compleja pero robusta.

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 3 de 25

Comienzan a surgir algunas alternativas que permiten la eliminación de las reglas de negocio de las interfaces de usuario.

Además, permiten distinguir y posteriormente separar claramente las reglas del negocio de las tareas de persistencia.

Por ello, comienzan a aparecer conceptos como N-capas: Interfaces de usuario, controladores, expertos, mecanismos de persistencia, mecanismos de almacenamiento, etc.

¿Qué es un Framework?

Es un conjunto de componentes con un fin específico que generalmente dan soporte para llevar a cabo determinadas operaciones. Se los suele llamar esquemas, e incluso a veces se los confunde con herramientas Case.

En este caso, se verá un conjunto de componentes (clases, objetos) que proporcionarán un servicio de persistencia al resto de la aplicación.

En la mayoría de los casos existen clases concretas y clases abstractas. Estas últimas son las que el desarrollador debe implementar para terminar de definir lo que el Framework debe hacer.

Existen una gran cantidad de Frameworks disponibles en todo el mundo. Cada uno con un propósito específico. Se utilizan para diversos aspectos de un sistema informático (persistencia, presentación, reglas del negocio, etc).

Aquellos frameworks que sobresalen utilizan tecnología de punta y conceptos novedosos. La única forma de que tengan el comportamiento deseado es con el uso de buenas prácticas, lo que implica un uso extensivo de patrones de diseño.

lo que implica un uso extensivo de patrones de diseño. Persistencia Autor : Atilio Ranzuglia; Editado
lo que implica un uso extensivo de patrones de diseño. Persistencia Autor : Atilio Ranzuglia; Editado

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 4 de 25

Objetivos de un esquema de persistencia

- Guardar objetos en un mecanismo persistente

- Recuperar objetos de un mecanismo persistente

- Administrar las operaciones persistentes con el fin de asegurar que sean atómicas

- Permitir el uso de componentes y transacciones distribuidas

¿Cómo se pueden lograr los objetivos de un esquema de persistencia?

El diseño debe permitir extensiones futuras y actualizaciones para soportar diversos mecanismos de almacenamiento persistentes, como BD Orientadas a Objetos, BD Relacionales, archivos planos, documentos XML, etc.

El impacto al ser incorporado en una aplicación debe ser mínimo, por lo tanto debe requerir la menor cantidad de modificaciones posibles a lo existente. Este aspecto no es menor y requiere de un bien diseño y el uso de patrones correctamente. Muchas veces debe reasignarse algún aspecto, es decir, no lograr un gran diseño para que no tenga un impacto considerable, o que el impacto sea mayor pero nos permita tener una buena solución en el esquema.

Las interfaces del esquema deben ser claras y concretas, facilitando su uso por parte de los desarrolladores.

Debe proveer un servicio transparente al resto de la aplicación, esto en gran medida se logra respetando los aspectos analizados previamente, es decir, nace como consecuencia de haber aplicado lo anterior.

Ejemplo de asignación de responsabilidades:

« interface » Ordenable

+ getCriterio(): String

« interface » Ordenable + getCriterio(): String Algoritmo Burbuja Persona + getCriterio(): String Materia
« interface » Ordenable + getCriterio(): String Algoritmo Burbuja Persona + getCriterio(): String Materia

Algoritmo

interface » Ordenable + getCriterio(): String Algoritmo Burbuja Persona + getCriterio(): String Materia +
interface » Ordenable + getCriterio(): String Algoritmo Burbuja Persona + getCriterio(): String Materia +
interface » Ordenable + getCriterio(): String Algoritmo Burbuja Persona + getCriterio(): String Materia +

Burbuja

Persona

+ getCriterio(): String

Materia

+ getCriterio(): String

QuickSort

Mapeo

Mediante el mapeo de entidades a estructuras relacionales, se define la metodología (protocolo) para intercambiar datos entre los 2 mecanismos.

En el ejemplo, se observa la diferencia entre las mejores prácticas de un MER y la mejor aproximación a la realidad (dominio) al utilizar entidades.

Los intermediarios serán los responsables de administrar estas diferencias y permitir el intercambio de información entre estos formatos diversos

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 5 de 25

Personas IDPersona Nombre fechaNacimiento
Personas
IDPersona
Nombre
fechaNacimiento

Autos

IDAuto

Marca

Modelo

Color

Patente

IDPersona

 

Auto

- marca: String

Persona

 

- modelo: String

- color: String

- nombre: String

- patente: String

- fechaNacimiento: Date

1

*

+ getMarca():String

+ getNombre():String

+ getNombre():String + setMarca(_Marca: String):void

+ setMarca(_Marca: String):void

+ setNombre(_nombre:String):void

+ getModelo():String

+ getFechaNacimiento():Date

+ setModelo(_Modelo: String):void

+ setFechaNacimiento(_FN:Date):void

+ getColor():String

 

+ setColor(_Color: String):void

+ getPatente():String

+ setPatente(_Patente:String):void

Identidad

El estado de un objeto (especialmente de una entidad) está dado por los valores de sus atributos.

Computadora

- velocidadCPU: double

- tipoCPU: String

- memoriaRAM: int

- capacidadHDD: int

+ getVelocidadCP():

+ setVelocidadCP(_velocidadCP):void

+ getTipoCPU():

+ setTipoCPU(_tipoCPU): void

+ getMemoriaRAM():

+ setMemoriaRAM(_memoriaRAM):void

+ getCapacidadHDD():

+ setCapacidadHDD(_capacidadHDD): void

Así, en un sistema de ventas de insumos informáticos, las siguientes computadoras se consideran iguales

Computadora 1

Computadora 2

3.0 GHz

3.0 GHz

Pentium IV

AMD

1 GB

1 GB

100 GB

100 GB

En cambio, en un sistema de inventario, las computadoras anteriores no se consideran iguales.

Las entidades unívocas en los objetos están dadas por las reglas del negocio, en lugar de estar controladas por los mecanismos de persistencia relacionales.

Los ID’s y/o claves primarias desaparecen de las entidades y se trasladan únicamente a la persistencia (ver esquema Persona-Auto de más arriba)

Si bien esto es ideal, es necesario por propósitos de performance obtener algún tipo de referencia entre la entidad y el registro (o los registros) en la tabla correspondiente (o el mecanismo persistente que corresponda).

Por esto se emplea el concepto de identificador único de objetos (OID). Es muy común implementarlo utilizando el patrón UUID (Unique Universal IDentifier).

Este patrón consiste en la generación de un identificador de 32 dígitos hexadecimales. Existen diversos mecanismos para generarlos, el más común es Leach-Salz.

Algunos ejemplos de UUID en una tabla:

común es Leach-Salz. Algunos ejemplos de UUID en una tabla: Persistencia Autor : Atilio Ranzuglia; Editado

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 6 de 25

El hecho de poseer IDs que no sean compuestos, mejora los tiempos de respuesta al realizar tareas de búsqueda en la BD. Al ser un ID único también permite el manejo eficiente de memorias caché.

Los IDs son únicos para cualquier entidad del sistema, no solo en aquellas del mismo tipo.

Intermediario

Un intermediario es, como su nombre lo indica, una clase que se introduce como “puente/proxy” entre las reglas del negocio y los accesos a la base de datos, con el fin de evitar un alto acoplamiento.

Son uno de los aspectos principales del mecanismo de persistencia propuesto, por lo que presentan ciertos desafíos. Entre ellos, permitir el intercambio de mecanismos de persistencia en forma genérica, permitir la definición de tareas de acceso a datos específicos para cada entidad, permitir la combinación al utilizar entidades compuestas, permitir el uso del caché, etc.

Al avanzar se resolverán todos estos aspectos. Inicialmente, el intermediario básico podría ser:

IntermediarioPromocion

IntermediarioPromocion + buscarPromocion(): Promocion + actualizarPromocion(Promocion p: Promocion p):void

+ buscarPromocion(): Promocion + actualizarPromocion(Promocion p: Promocion p):void

Promocion

- fechaInicio: Date

- fechaFin: Date

+ getFechaInicio():Date

+ setFechaInicio(_FechaInicio:Date):void

+ getFechaFin():Date

+ setFechaFin(_FechaFin:Date):void

+ getFechaFin():Date + setFechaFin(_FechaFin:Date):void Para otras entidades, en el sistema deben indicarse

Para otras entidades, en el sistema deben indicarse intermediarios semejantes

IntermediarioPromocion

+ buscarPromocion(): Promocion + actualizarPromocion(Promocion p: Promocion p):void

+ actualizarPromocion(Promocion p: Promocion p):void Promocion - fechaInicio: Date - fechaFin: Date +

Promocion

- fechaInicio: Date

- fechaFin: Date

+ getFechaInicio():Date

+ setFechaInicio(_FechaInicio:Date):void

+ getFechaFin():Date

+ setFechaFin(_FechaFin:Date):void

IntermediarioCliente

+ buscarCliente(): Cliente

+ actualizarCliente(Cliente c: Cliente c):void

Cliente + actualizarCliente(Cliente c: Cliente c):void Cliente - nombre: String + getNombre():String +

Cliente

- nombre: String

+ getNombre():String

+ setNombre(_nombre:String):void

IntermediarioProducto

+ buscarProducto(): Producto

+ actualizarProducto(Producto p: Producto p):void

Producto + actualizarProducto(Producto p: Producto p):void Producto - codigo: int - nombre: String +

Producto

- codigo: int

- nombre: String

+ getCodigo():int

+ setCodigo(_Codigo: int):void

+ getNombre():String

+ setNombre(_nombre:String):void

Puede observarse que las tareas se repiten sistemáticamente en cada intermediario, por lo que se hace necesario el uso del concepto de generalización. Es posible reunir aquellas tareas comunes en una clase genérica que extienda todos los intermediarios mencionados:

Intermediario

+ buscar(): Entidad

+ actualizar(Entidad e: Entidad e):void

buscar(): Entidad + actualizar(Entidad e: Entidad e):void IntermediarioCliente IntermediarioPromocion
buscar(): Entidad + actualizar(Entidad e: Entidad e):void IntermediarioCliente IntermediarioPromocion
buscar(): Entidad + actualizar(Entidad e: Entidad e):void IntermediarioCliente IntermediarioPromocion
IntermediarioCliente

IntermediarioCliente

IntermediarioCliente
IntermediarioCliente

IntermediarioPromocion

IntermediarioPromocion
IntermediarioPromocion

IntermediarioProducto

IntermediarioProducto
IntermediarioProducto

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 7 de 25

Ahora es posible que cada nuevo intermediario requiera de menos trabajo para ser incorporado al sistema. Pero hemos establecido tareas genéricas en Intermediarios, que no pueden realizar solamente allí. Ello implicaría una mala cohesión haciendo que ese intermediario llegue a manejar las tareas de persistencia de todas las entidades. De hecho los intermediarios específicos podrían desaparecer.

Sin embargo, es deseable mantener una estructura similar ya que incorporar una entidad y su respectivo intermediario en persistencia, son tareas sencillas.

IntermediarioProveedor

IntermediarioProveedor
IntermediarioProveedor

Intermediario

Intermediario + buscar(): Entidad + actualizar(Entidad e: Entidad e):void

+ buscar(): Entidad + actualizar(Entidad e: Entidad e):void

+ buscar(): Entidad + actualizar(Entidad e: Entidad e):void Proveedor codigo: int - nombre: String +
+ buscar(): Entidad + actualizar(Entidad e: Entidad e):void Proveedor codigo: int - nombre: String +
+ buscar(): Entidad + actualizar(Entidad e: Entidad e):void Proveedor codigo: int - nombre: String +
+ buscar(): Entidad + actualizar(Entidad e: Entidad e):void Proveedor codigo: int - nombre: String +
+ buscar(): Entidad + actualizar(Entidad e: Entidad e):void Proveedor codigo: int - nombre: String +

Proveedor

codigo: int

- nombre: String

+ getCodigo():int

+ setCodigo(_Codigo: int):void

+ getNombre():String

+ setNombre(_nombre:String):void

-
-

IntermediarioPromocion

IntermediarioPromocion
IntermediarioPromocion

IntermediarioCliente

IntermediarioCliente
IntermediarioCliente

IntermediarioProducto

IntermediarioProducto
IntermediarioProducto

¿Qué tareas debería llevar a cabo un intermediario? Como mínimo debe permitir almacenar una entidad en el mecanismo persistente y recuperarla posteriormente. Además debe mejorarse la estructura para evitar el acoplamiento del Intermediario principal.

Como parte de la tarea de almacenar los datos respectivos a una entidad, se debe obtener todos los datos que forman parte de la entidad y utilizar el mecanismo de persistencia para almacenarlos.

La tarea de obtener los datos de una entidad es independiente de los mecanismos de persistencia a utilizar, pero dependiente de la entidad

La tarea de almacenar los datos es dependiente del mecanismo de persistencia.

Se puede extender el intermediario para que obligue a sus subclases a definir nuevos comportamientos. Por ejemplo:

Intermediario

Intermediario + buscar(): Entidad + actualizar(Entidad e: Entidad e):void

+ buscar(): Entidad + actualizar(Entidad e: Entidad e):void

+ buscar(): Entidad + actualizar(Entidad e: Entidad e):void IntermediarioPromocion + buscar(): Promocion +
IntermediarioPromocion

IntermediarioPromocion

IntermediarioPromocion + buscar(): Promocion + actualizar(Promocion p: Promocion p):void
IntermediarioPromocion + buscar(): Promocion + actualizar(Promocion p: Promocion p):void
+ buscar(): Promocion + actualizar(Promocion p: Promocion p):void

+ buscar(): Promocion + actualizar(Promocion p: Promocion p):void

Promocion + actualizar(Promocion p: Promocion p):void Promocion - fechaInicio: Date - fechaFin: Date +

Promocion

- fechaInicio: Date

- fechaFin: Date

+ getFechaInicio():Date

+ setFechaInicio(_FechaInicio:Date):void

+ getFechaFin():Date

+ setFechaFin(_FechaFin:Date):void

+ getFechaFin():Date + setFechaFin(_FechaFin:Date):void IntermediarioProveedor + buscar(): Proveedor +

IntermediarioProveedor

+ buscar(): Proveedor

+ actualizar(Proveedor p: Proveedor p):void

Proveedor + actualizar(Proveedor p: Proveedor p):void Proveedor - codigo: int - nombre: String +

Proveedor

- codigo: int

- nombre: String

+ getCodigo():int

+ setCodigo(_Codigo: int):void

+ getNombre():String

+ setNombre(_nombre:String):void

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 8 de 25

El contenido de las distintas operaciones “buscar” en cada intermediario es el mismo. Por lo tanto es posible generalizar su existencia (pero no su comportamiento) en la superclase de todos los intermediarios.

Antes de continuar mejorando el esquema, vamos a incorporar más niveles en la jerarquía con el fin de poder incorporar distintos mecanismos de persistencia. Se propone el uso de más superclases para definir un mecanismo abstracto:

Intermediario

Intermediario + buscar(): Entidad + actualizar(Entidad e: Entidad e):void

+ buscar(): Entidad + actualizar(Entidad e: Entidad e):void

+ buscar(): Entidad + actualizar(Entidad e: Entidad e):void IntermediarioRelacional IntermediarioXML
+ buscar(): Entidad + actualizar(Entidad e: Entidad e):void IntermediarioRelacional IntermediarioXML
IntermediarioRelacional
IntermediarioRelacional
IntermediarioRelacional

IntermediarioRelacional

IntermediarioRelacional
IntermediarioRelacional
IntermediarioRelacional
IntermediarioRelacional
e: Entidad e):void IntermediarioRelacional IntermediarioXML IntermediarioOrientadoObjetos

IntermediarioXML

IntermediarioXML
IntermediarioXML

IntermediarioOrientadoObjetos

IntermediarioOrientadoObjetos
IntermediarioOrientadoObjetos
IntermediarioXML IntermediarioOrientadoObjetos IntermediarioPromocion + buscar(): Promocion +

IntermediarioPromocion

+ buscar(): Promocion

+ actualizar(Promocion p: Promocion p):void

Promocion + actualizar(Promocion p: Promocion p):void IntermediarioProveedor + buscar(): Proveedor +

IntermediarioProveedor

+ buscar(): Proveedor

+ actualizar(Proveedor p: Proveedor p):void

Es importante observar que los nuevos intermediarios (aquellos presentes en el nivel medio) no poseen comportamientos, simplemente aparecen con el propósito de diferenciar las distintas posibilidades.

En esquemas de cualquier tipo, cuantos más niveles se posean mejor, sin caer en la obsesión de crear más que los necesarios porque solamente le van a agregar complejidad al esquema. Si se tienen un par de niveles existen muy buenas chances de poder “colgar” nuevas clases de las ya existentes.

En el ejemplo mostrado anteriormente, es posible realizar modificaciones muy específicas y adaptaciones gracias a la disponibilidad existente, y aún así se conserva la jerarquía de herencia.

Ahora el desafío consiste en poder generalizar pero aún así permitir aspectos específicos en las subclases. Para ello se utiliza el patrón plantilla

// doSomething(); //

AbstractClass

 

PrimitiveOperation1();

PrimitiveOperation1()

//

PrimitiveOperation2()

PrimitiveOperation2();

TemplateMethod()

//

doAbsolutelyThis()

doAbsolutellyThis();

doSomething()

//

 
 
doSomething() //     ConcreteClass PrimitiveOperation1()

ConcreteClass

PrimitiveOperation1()

PrimitiveOperation2()

doSomething()

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 9 de 25

objetoConID (OID) { obj = enCache (OID); if ( obj <> NULL) return obj; else return materializar(OID);

}

IntermediarioPersistencia

+ objetoConID (OID: OID): Object

+ enCache (OID: OID): Object

+ materializar (OID: OID): Object

(OID: OID): Object + materializar (OID: OID): Object IntermediarioRelacional + materializar (OID: OID): Object
(OID: OID): Object + materializar (OID: OID): Object IntermediarioRelacional + materializar (OID: OID): Object

IntermediarioRelacional

+ materializar (OID: OID): Object

+ buscarPrimero (consulta:consulta): Object

+ convertirRegistroAObjeto (): Object

materializar (OID) { buscarPrimero("OID =" + OID); obj = convertirRegistroAObjeto(); agregarEnCache (OID,obj); return obj;

}

IntermediarioOrientadoObjeto

IntermediarioOrientadoObjeto + materializar (OID: OID): Object

+ materializar (OID: OID): Object

+ materializar (OID: OID): Object IntermediarioRelacionalPromocion + convertirRegistroAObjeto
IntermediarioRelacionalPromocion + convertirRegistroAObjeto (): Promocion convertirRegistroAObjeto() { Promocion p =
IntermediarioRelacionalPromocion
+ convertirRegistroAObjeto (): Promocion
convertirRegistroAObjeto() {
Promocion p = new Promocion();
p.setFechaInicio (registoActual.get("fechaInicio"));
p.setFechaFin (registoActual.get("fechaFin"));
return p;
}
(registoActual.get("fechaFin")); return p; } IntermediarioRelacionalProveedor + convertirRegistroAObjeto
IntermediarioRelacionalProveedor + convertirRegistroAObjeto (): Proveedor convertirRegistroAObjeto() { Proveedor p =
IntermediarioRelacionalProveedor
+ convertirRegistroAObjeto (): Proveedor
convertirRegistroAObjeto() {
Proveedor p = new Proveedor();
p.setCodigo (registoActual.get("Codigo"));
p.setNombre (registoActual.get("Nombre"));
return p;
}

Hasta aquí se ha logrado generalizar algunos aspectos de persistencia en Intermediarios básicos. Ahora se debe permitir su portabilidad hacia distintos mecanismos de persistencia.

Los comportamientos definidos anteriormente siguen siendo válidos para cualquier mecanismo persistente, por lo que puede pensarse en continuar con la generalización. De esta manera los niveles se incrementan, permitiendo así la incorporación o modificación futura en el nivel específico necesario.

Habiendo creado la estructura, la incorporación de nuevos intermediarios se simplifica mucho:

IntermediarioPersistencia

+ objetoConID (OID: OID): Object

+ enCache (OID: OID): Object

+ materializar (OID: OID): Object

(OID: OID): Object + materializar (OID: OID): Object IntermediarioRelacional + materializar (OID: OID): Object
(OID: OID): Object + materializar (OID: OID): Object IntermediarioRelacional + materializar (OID: OID): Object

IntermediarioRelacional

+ materializar (OID: OID): Object

+ buscarPrimero (consulta:consulta): Object + convertirRegistroAObjeto (): Object

Object + convertirRegistroAObjeto (): Object IntermediarioOrientadoObjeto + materializar (OID: OID):
Object + convertirRegistroAObjeto (): Object IntermediarioOrientadoObjeto + materializar (OID: OID):

IntermediarioOrientadoObjeto

IntermediarioOrientadoObjeto + materializar (OID: OID): Object

+ materializar (OID: OID): Object

+ materializar (OID: OID): Object IntermediarioRelacionalDescuento +

IntermediarioRelacionalDescuento

IntermediarioRelacionalDescuento + convertirRegistroAObjeto (): Descuento

+ convertirRegistroAObjeto (): Descuento

IntermediarioRelacionalPromocion

IntermediarioRelacionalPromocion + convertirRegistroAObjeto (): Promocion

+ convertirRegistroAObjeto (): Promocion

IntermediarioRelacionalProveedor

IntermediarioRelacionalProveedor + convertirRegistroAObjeto (): Proveedor

+ convertirRegistroAObjeto (): Proveedor

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 10 de 25

Cuando se trabaja con estructuras jerárquicas como la mencionada, se utiliza fuertemente el concepto de herencia (incluyendo el uso de polimorfismo). Es muy común la práctica de crear instancias específicas de clases concretas y tratarlas como objetos genéricos. De esta manera es posible encapsular, proteger y ocultar aquellos comportamientos específicos no deseados desde el exterior (a través de la interfaz).

 

Auto

arrancar() {

 

colocarLlave();

 

// PLANTILLA

+ arrancar(): void

tareaExtra();

girarLlave();

+ colocarLlave(): void

+ girarLlave(): void

}

+ tareaExtra(): void

 
girarLlave(): void } + tareaExtra(): void   Naftero tareaExtra (){ } + tareaExtra(): void Diesel
girarLlave(): void } + tareaExtra(): void   Naftero tareaExtra (){ } + tareaExtra(): void Diesel
Naftero tareaExtra (){ } + tareaExtra(): void
Naftero
tareaExtra (){
}
+ tareaExtra(): void
Diesel tareaExtra (){ esperar esperar + tareaExtra(): void }
Diesel
tareaExtra (){
esperar
esperar
+ tareaExtra(): void
}

Este concepto es utilizado en el esquema de persistencia permitiéndonos trabajar en forma genérica con los intermediarios de persistencia superiores, cuando en realidad (y sin saberlo) estamos tratando con intermediarios específicos.

IntermediarioPersistencia intermediario = new IntermediarioRelacionalPromocion();

Promocion promocion = intermediario.objetoConID(nnn);

Fachada

Si bien se pretende que IntermediarioRelacional sea el punto de encuentro entre las reglas del negocio y el esquema de persistencia, se observa en el ejemplo que el experto está al tanto del intermediario específico que necesita utilizar en cada caso.

Esto no es deseable porque aumenta el acoplamiento entre distintas clases de cada parte. Una solución puede ser la utilización de una fábrica que en base a la entidad en juego decida qué intermediario utilizar, por ejemplo:

FabricaIntermediarios

FabricaIntermediarios

FabricaIntermediarios + getIntermediario (Object: Object): IntermediarioPersistencia
FabricaIntermediarios + getIntermediario (Object: Object): IntermediarioPersistencia
+ getIntermediario (Object: Object): IntermediarioPersistencia

+ getIntermediario (Object: Object): IntermediarioPersistencia

getIntermediario (Object: Object): IntermediarioPersistencia IntermediarioPersistencia IntermediarioRelacional

IntermediarioPersistencia

IntermediarioPersistencia
IntermediarioPersistencia
Object): IntermediarioPersistencia IntermediarioPersistencia IntermediarioRelacional IntermediarioCliente Si bien ha
IntermediarioRelacional
IntermediarioRelacional

IntermediarioRelacional

IntermediarioRelacional
IntermediarioRelacional
IntermediarioRelacional
IntermediarioRelacional
IntermediarioRelacional
IntermediarioPersistencia IntermediarioRelacional IntermediarioCliente Si bien ha mejorado la comunicación
IntermediarioCliente
IntermediarioCliente
IntermediarioCliente

IntermediarioCliente

IntermediarioCliente
IntermediarioCliente
IntermediarioCliente
IntermediarioCliente
IntermediarioCliente
IntermediarioCliente
IntermediarioCliente
IntermediarioCliente
IntermediarioRelacional IntermediarioCliente Si bien ha mejorado la comunicación entre el experto y el

Si bien ha mejorado la comunicación entre el experto y el esquema de persistencia obteniendo un acoplamiento mucho menor, el experto debe conocer cómo utilizar la fábrica para obtener intermediarios. Nuevamente, esto no es deseable.

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 11 de 25

Podemos aplicar el patrón Fachada (Facade) que oculta la complejidad interna de un componente (o conjunto de componentes), y provee un punto de acceso simplificado y estandarizado.

y provee un punto de acceso simplificado y estandarizado. En nuestro caso, la fachada será la
y provee un punto de acceso simplificado y estandarizado. En nuestro caso, la fachada será la

En nuestro caso, la fachada será la encargada de intermediar entre el experto y la fábrica, con el fin de simplificar la conexión con la fábrica y mejorar la cohesión del experto.

Experto

Fachada

FabricaIntermediarios

 

+ buscarCliente(): Cliente

+ getIntermediario(Object:Object): IntermediarioPersistencia

El experto se comunica con la Fachada para solicitar la búsqueda de un cliente, quien a su vez utiliza la fábrica para obtener el intermediario correspondiente

la fábrica para obtener el intermediario correspondiente IntermediarioCliente Persistencia Autor : Atilio Ranzuglia;

IntermediarioCliente

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 12 de 25

Ventajas:

Oculta la complejidad de un subsistema

Simplifica las interfaces

Simplifica el uso de las funcionalidades internas

Desventajas:

Está fuertemente acoplada a las distintas entidades y actividades de persistencia que se creen

Además, la fachada se suele implementar como Singleton. También existe un concepto de fachada genérico (similar al utilizado por Hibernate o Java Persistence) en donde no se definen operaciones específicas a las entidades

Materialización y Desmaterialización

Los procesos de materialización y desmaterialización representan las actividades de crear objetos a partir de datos y registrar datos a partir de un objeto respectivamente.

Caché

Un caché es una colección de datos duplicados respecto de los datos originales. Se encuentra almacenado en algún lugar distinto del de los datos originales, en donde los accesos son mucho más veloces.

Una vez que los datos se encuentran en el caché, los accesos futuros se realizan a una gran velocidad (al menos en comparación con el medio de almacenamiento de los datos originales).

Presentan la desventaja de poseer copias de los datos, lo que puede ser muy perjudicial para la aplicación.

datos, lo que puede ser muy perjudicial para la aplicación. En nuestro esquema se utiliza para

En nuestro esquema se utiliza para almacenar copias de las entidades que interactúan con el mecanismo de persistencia.

Los caché se utilizan para depositar objetos que generalmente van a ser accedidos para leer sus atributos solamente. En la mayoría de los casos estos objetos son de sólo lectura.

De no ser así, es necesaria la incorporación de mecanismos que le adviertan al caché sobre la existencia de datos obsoletos o desactualizados. El mecanismo más utilizado es la invalidación del caché. Representa serios desafíos especialmente cuando se trabaja con componentes distribuidos.

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 13 de 25

Objetos Complejos

Hasta el momento hemos logrado recuperar objetos sencillos utilizando el mecanismo de persistencia. Llamamos sencillos a aquellos objetos que no presentan relaciones o asociaciones con otros objetos. De hecho la mayoría de las entidades en una aplicación van a presentar algún tipo de relación con otra entidad.

¿Cómo se puede manejar una relación mediante SQL? Con el uso de Joins que permiten vincular distintos registros de diversas tablas mediante claves foráneas. Mediante el uso de objetos obtenemos relaciones a través de asociaciones.

Contribuyente

 

Automotor

- nombre: String

1

*

- patente: String

Automotor - nombre: String 1 * - patente: String     + getNombre():String   +
   

+ getNombre():String

 

+ getPatente():String

+ setNombre(_nombre:String):void

+ setPatente(_Patente:String):void

Contribuyentes

Automotores

IDContribuyente

IDAutomotor

Nombre

patente

 

IDContribuyente

Si es necesario consultar todos los nombres de los contribuyentes, ¿Cuál de las siguientes consultas sería más eficiente?

1-

SELECT * FROM Contribuyentes C, Automotores A, WHERE C.id=A.idContribuyente

2-

SELECT * FROM Contribuyentes C

La 1º opción es innecesaria si solamente se desean consultar datos específicos del contribuyente (y no de sus automotores). La 2º opción resulta apropiada para este caso.

El mismo inconveniente se presenta al realizar búsquedas de entidades de tipo Contribuyente. ¿Cuál es el resultado ideal?

- Todos los contribuyentes, cada uno con sus respectivos automotores

- Todos los contribuyentes.

Nuevamente, si el deseo de la consulta es visualizar datos específicos del Contribuyente, la 1º situación es indeseable.

Esto nos lleva a pensar como podemos recuperar los objetos mediante el mecanismo de persistencia, sin tener que recuperar todas las relaciones en forma recursiva.

Imaginen el siguiente caso:

Pedido Cliente 1 * Venta 1 * DetalleVenta 1 1 * * Vendedor Producto
Pedido
Cliente
1
*
Venta
1
*
DetalleVenta
1
1
*
*
Vendedor
Producto

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 14 de 25

Si solamente se desea el nombre del cliente, el resto de la información no tiene sentido en la presente consulta.

Es necesario permitir diferenciar las relaciones que deben recuperarse en forma inmediata de aquellas que deben hacerlo en forma tardía. De aquí nace el concepto de carga tardía o sobre demanda.

De aquí nace el concepto de carga tardía o sobre demanda. Un experto se puede encontrar

Un experto se puede encontrar con una gran cantidad de información innecesaria o con escasa información. ¿Cómo solucionarlo? Debemos poder incorporar alguna funcionalidad intermediaria, que se encargue de estos aspectos en forma transparente para el experto.

Agente Virtual

El Agente es un patrón que hace referencia a un intermediario. En nuestro caso, un Agente será de utilidad para permitirnos obtener aquellos objetos relacionados al actual en forma tardía.

Hará de intermediario haciéndonos creer que es la entidad original. Será el encargado de traer los objetos relacionados a medida que sean necesarios por el experto. Como está “engañándonos” respecto de la entidad original, se lo denomina Agente Virtual.

A g ente

A g ente E ntidad

E ntidad

 
   
 

El Agente está relacionado con la entidad e intercepta todos los llamados hacia la misma. De esta forma, tiene el control de los famosos “set/get” de la entidad.

Al tener el control, puede darse cuenta de que un objeto relacionado no ha sido recuperado previamente, y hacerlo en ese instante. Para quien invoca el “get”, la operación es transparente

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 15 de 25

Persistencia Autor : Atilio Ranzuglia; Editado por: Adrián Botta Página 16 de 25
Persistencia Autor : Atilio Ranzuglia; Editado por: Adrián Botta Página 16 de 25
Persistencia Autor : Atilio Ranzuglia; Editado por: Adrián Botta Página 16 de 25

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 16 de 25

Se ha logrado colocar al agente para que efectúe las operaciones de carga tardía, pero

Se ha logrado colocar al agente para que efectúe las operaciones de carga tardía, pero introduce un nuevo problema. ¿Cuál es?

El experto espera recibir como resultado una entidad Cliente, sin embargo está recibiendo un AgenteCliente. ¿Cómo hace para tratarlos por igual? ¿Existe alguna relación entre ellos?

Como no se encuentran relacionados, el último cambio efectuado (Incorporación del agente) afecta al experto. Debe tener la capacidad de extraer del agente los datos necesarios.

Si bien no resulta una tarea compleja, hace que el experto conozca la estructura interna de los objetos pertenecientes al esquema de persistencia, y solamente debería interactuar con el mismo mediante la Fachada.

Podemos utilizar otro patrón para encontrar un punto en común, el patrón es Business Interface (Interfaz de Negocios). Establece un punto común entre 2 aspectos similares pero diversos.

Significa que reúne dos o más objetos que se encuentran distantes en la aplicación (en subsistemas distintos) pero que representan aspectos similares en el dominio a resolver.

En nuestro caso, se crea una interfaz de negocios denominada VentaInterfaz. Tanto Venta como AgenteVenta deben implementar de la misma:

 

« interface » VentaInterfaz

 

+ setMonto():void

 

+ getMonto():double

  + setMonto():void   + getMonto():double AgenteVenta     Venta     +
  + setMonto():void   + getMonto():double AgenteVenta     Venta     +

AgenteVenta

   

Venta

 
   
 

+ setMonto():void

+ setMonto():void + setMonto():void

+ setMonto():void

+ getMonto():double

+ getMonto():double

Ahora es posible crear agentes de venta, pero enmascarados como Interfaces de venta. Y allí obtenemos un punto de encuentro entre la persistencia y los expertos.

Aún así, el experto se ve afectado. Previamente se comunicaba con la clase Venta, ahora debe hacerlo con la interfaz VentaInterfaz. Se puede resolver muy

fácilmente realizando un cambio de nombres

que además se encuentra recomendado por el patrón Interfaz de Negocios.

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 17 de 25

Transacciones

Una transacción define una operación atómica, en su mínima expresión. Lo que significa que todas aquellas tareas que formen parte de la misma transacción deben llevarse a cabo con éxito. Si alguna de ellas fallase, debería deshacerse las demás tareas como si nunca hubieran sucedido. Si todas se pueden llevar a cabo con éxito, la transacción se da por exitosa.

Nunca es posible trabajar sin el concepto de transacción de datos. Recientemente se utiliza el concepto de transacción de negocios, lo que permite agrupar tareas disímiles en una misma transacción.

Por ejemplo, una transacción de datos en lenguaje SQL:

INSERT INTO Clientes VALUES (‘001’,’Juan Carlos Mendoza’,’20-01-1980’);

INSERT INTO Usuarios VALUES (‘009’,’001’,’jcmendoza’);

Una transacción a nivel de reglas del negocio:

INSERT INTO Clientes VALUES (‘001’,’Juan Carlos Mendoza’,’20-01-1980’);

INSERT INTO Usuarios VALUES (‘009’,’001’,’jcmendoza’);

Enviar mail a cuenta de gerente para informar sobre el nuevo cliente

El último paso no refleja una tarea de la BD, sin embargo debe llevarse a cabo solamente si el cliente se ha agregado con éxito. Incluso si el mail no fuese enviado, no debería registrarse el cliente en forma persistente. Por lo tanto, las transacciones “modernas” permiten la vinculación de muchas tareas distintas para ser ejecutadas en forma atómica.

Aquí vamos a tratar solamente las transacciones a nivel de datos, es decir, sobre la base de datos. Todos los servidores de BD modernos poseen la capacidad de recibir solicitudes de conexión entre diversos puntos. Cada conexión se parece a un llamado telefónico, es un vínculo entre la BD y un usuario específico. Todo lo que suceda en dicha conexión es responsabilidad del usuario.

Cada conexión conoce la existencia de sólo una transacción en un mismo instante. Cuando la transacción actual se da por finalizada, automáticamente se comienza una nueva. Las transacciones no pueden compartirse entre distintas conexiones.

Las transacciones pueden darse por finalizadas con o sin errores. Cuando se completan sin errores se suelen confirmar (con la operación Commit). Cuando se ejecutan con errores se suelen deshacer (con la operación Rollback).

En nuestro esquema debemos asegurar la existencia de transacciones para cada actividad que se realice con el mismo. Por esto podemos comenzar administrando transacciones dentro del esquema de persistencia:

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 18 de 25

Este mecanismo nos asegura que todas las actividades de persistencia llevadas a cabo se realizarán

Este mecanismo nos asegura que todas las actividades de persistencia llevadas a cabo se realizarán con nuevas transacciones, y justamente allí nos plantea un inconveniente: el hecho de que cada tarea se lleve a cabo con una nueva transacción.

Por ejemplo, en el caso de necesitar persistir una venta y actualizar el stock de un producto:

persistir una venta y actualizar el stock de un producto: El inconveniente es que las tareas

El inconveniente es que las tareas se están llevando a cabo en transacciones aisladas, sin una de ellas, la otra no debería llevarse a cabo. Si la segunda falla, es tarde deshacer la primera porque se ha confirmado, y no estamos llevando a cabo las tareas en forma atómica.

Existen dos caminos para solucionar este inconveniente:

- Crear un nuevo comportamiento en la fachada que almacene la venta y actualice el stock

- Permitirle al experto administrar la transacción

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 19 de 25

El primer caso es muy complicado ya que se han trasladado reglas del negocio desde el experto hacia la fachada. Esto jamás debe hacerse.

el experto hacia la fachada. Esto jamás debe hacerse. El segundo caso es igualmente complicado, ya

El segundo caso es igualmente complicado, ya que se han trasladado actividades de persistencia desde la fachada hacia el experto. Esto tampoco jamás debe hacerse.

hacia el experto. Esto tampoco jamás debe hacerse . Los aspectos principales a considerar son: -

Los aspectos principales a considerar son:

- A qué clase asignarle la responsabilidad de administrar transacciones

- Cómo determinar si una transacción debe confirmarse o deshacerse

¿Quién sabe si un error es lo suficientemente grave como para deshacer todas las operaciones? ¿Quién conoce con certeza cuando empezar y terminar transacciones? ¿Quién NO debe saber nada sobre la existencia de transacciones? El Experto

En esta situación observamos que algo o alguien debe hacer una tarea, pero no es su responsabilidad, pero debe hacerlo, pero no le corresponde…

En este caso lo disfrazamos utilizando el patrón Decorador (Wrapper). Este patrón nos permite enmascarar una clase como algo ideal cuando en realidad no es tan ideal como parece.

En el ejemplo nos va a permitir que el experto se vea genial, es decir, que no realice tareas que no le correspondan. Pero como dijimos que igual las tiene que hacer, las hace en forma oculta.

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 20 de 25

Es un mecanismo sencillo muy utilizado en la actualidad por muchos esquemas de persistencia. Aún así, se analizará en forma básica:

ExpertoClientes crearCliente ( ) { // Realizar Validaciones + crearCliente(): void
ExpertoClientes
crearCliente (
)
{
// Realizar Validaciones
+ crearCliente(): void
Fachada.getInstance().guardarCliente(cliente);
+ actualizarCliente(): void
}
+ eliminarCliente(): void
crearCliente (
)
{
iniciarTransaccion();
DecoradorTransaccionesExpertoClientes
super.crearCliente(
);
+ crearCliente(): void
+ actualizarCliente(): void
// Si la operacion fue exitosa
confirmarTransaccion();
+ eliminarCliente(): void
- iniciarTransaccion():void
- confirmarTransaccion():void
// Si la operacion NO fue exitosa
deshacerTransaccion();
- deshacerTransaccion():void
}

De esta manera permitimos que un pseudo-experto administre las transacciones y al mismo tiempo oque no se ensucien las reglas del negocio con las actividades de persistencia. Gracias a la herencia podemos mentirle a los controladores y entregarles expertos “disfrazados” en lugar de los expertos “originales”. Cabe recordar que una premisa clave es mantener el impacto de las modificaciones en su mínima expresión.

el impacto de las modificaciones en su mínima expresión. ¿Qué sucede cuando el CU es extenso,

¿Qué sucede cuando el CU es extenso, es decir, requiere más de 1 evento por parte del actor?

Básicamente nuestro decorador (experto disfrazado) debe iniciar la transacción en el primer evento y finalizarla en el último evento. Posiblemente nos encontremos con múltiples eventos finales, lo que implica finalizar la transacción en más de un lugar.

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 21 de 25

¿Qué sucede cuando un CU necesita de otro CU? (Extensión, Inclusión) El esquema de persistencia

¿Qué sucede cuando un CU necesita de otro CU? (Extensión, Inclusión)

El esquema de persistencia debe asegurarnos que los casos de uso incluidos por otros lleven a cabo sus tareas de datos bajo la misma transacción. Esto debe ocurrir sin importar el nivel de anidamiento que se presente.

sin importar el nivel de anidamiento que se presente. Persistencia Autor : Atilio Ranzuglia; Editado por:

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 22 de 25

Búsquedas Diversas

En una aplicación real aproximadamente la mitad de las consultas se realizan en base al ID del objeto, el resto se realizan mediante ciertos criterios, por ejemplo: todas las ventas entre un rango de fechas, todos los productos que comiencen por “DVD”, etc.

Analizándolo desde el punto de vista del lenguaje SQL, el esquema de persistencia debe ser capaz de ejecutar consultas como:

SELECT * FROM Ventas WHERE fecha BETWEEN :fechaInicial AND :fechaFinal

SELECT * FROM Productos WHERE nombre LIKE ‘DVD%’

¿Cuál es la mejor clase para tomar la responsabilidad de llevar a cabo estas consultas? Por tratarse de consultas específicas de acuerdo a una entidad, sin duda debemos asignarle esta responsabilidad a los intermediarios correspondientes. En el primer caso se le asignará a IntermediarioRelacionalVenta, y en el segundo a IntermediarioRelacionalProducto.

IntermediarioRelacionalVentas

IntermediarioRelacionalVentas + listarVentasEntreFechas(): void

+ listarVentasEntreFechas(): void

// Armar SQL // SELECT * FROM Ventas

resultados = buscar (sql);

while (resultados) { convertirAObjeto();

}

return Objetos;

IntermediarioRelacionalProductos

IntermediarioRelacionalProductos + listarProductosPorNombre(): void

+ listarProductosPorNombre(): void

// Armar SQL // SELECT * FROM Productos

resultados = buscar (sql);

while (resultados) { convertirAObjeto();

}

return Objetos;

En el diagrama se observa que en diversos intermediarios se van a repetir las mismas tareas:

ejecutar una consulta y recorrer los resultados para convertirlos a objetos. Para mejorarlo podemos hacer uso de la generalización existente entre intermediarios y el patrón plantilla.

buscar (String busqueda) { sql = solicitarSQL(); resultados = ejecutar(sql); while (resultados) {

convertirAObjeto();

}

return objetos;

}

solicitarSQL() { return "SELECT * FROM Ventas

}

"

IntermediarioPersistencia

IntermediarioPersistencia + buscar (busqueda:busqueda): void

+ buscar (busqueda:busqueda): void

+ buscar (busqueda:busqueda): void IntermediarioRelacional + buscar (busqueda:busqueda): void

IntermediarioRelacional

+ buscar (busqueda:busqueda): void

+ solicitarSQL(): String

+ convertirAObjeto(): Object

+ solicitarSQL(): String + convertirAObjeto(): Object IntermediarioRelacionalVentas + solicitarSQL(): String
+ solicitarSQL(): String + convertirAObjeto(): Object IntermediarioRelacionalVentas + solicitarSQL(): String
+ solicitarSQL(): String + convertirAObjeto(): Object IntermediarioRelacionalVentas + solicitarSQL(): String
IntermediarioRelacionalVentas + solicitarSQL(): String convertirAObjeto() { Venta venta = new AgenteVenta(); venta.set
IntermediarioRelacionalVentas
+ solicitarSQL(): String
convertirAObjeto() {
Venta venta = new AgenteVenta();
venta.set
venta.set
return venta;
+ convertirAObjeto(): Object
}

De esta manera distribuimos de la mejor forma posible las responsabilidades y quedan las operaciones en forma genérica.

Resta poder realizar estas consultas desde los expertos. Como hemos dicho anteriormente, el único punto de contacto entre los expertos y el esquema de persistencia es la fachada de persistencia. Por lo tanto allí debemos exponer las búsquedas con criterios.

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 23 de 25

Para el caso de la búsqueda de productos:

Para el caso de la búsqueda de productos: Observar que la fachada expone las búsquedas en

Observar que la fachada expone las búsquedas en forma agradable ocultando las complejidades internas del esquema de persistencia. Así, el experto puede confiar en la fachada y simplemente llamar a la operación deseada.

Otra opción bastante más compleja (muy utilizada por los esquemas de persistencia de la actualidad) es la posibilidad de crear consultas desde los expertos en un lenguaje abstracto en lugar de utilizar un lenguaje SQL (que no debería utilizarse dentro de un experto). Por ejemplo:

SELECT * FROM Ventas v, Cliente c WHERE v.idCliente = c.id AND c.id = :idDeCLiente

SELECT v FROM Venta v WHERE v.Cliente.id = :idDeCliente

Este lenguaje abstracto (a veces denominado OQL, Object Query Languaje) nos permite realizar búsquedas a partir de objetos navegando por sus relaciones en lugar de tener que realizar JOINs y necesitar conocer la estructura de la BD. De esta forma es posible generalizar las operaciones de búsquedas con criterio en una sola, proporcionando la consulta en OQL que corresponda.

Aquí les hemos trasladado la tarea de armar el SQL al esquema de persistencia y colocamos las consultas en los expertos. Este aspecto se lo denomina “mapeo”. Define la relación entre las entidades y sus correspondientes tablas en el medio de almacenamiento persistente.

En nuestra primera aproximación, el mapeo lo realiza cada intermediario específico a través de las operaciones solicitarSQL. La fachada debe traducir buscarProductos(nombre) en buscar(“productos”,nombre). Los expertos utilizan buscarProductos(nombre).

En la segunda aproximación el mapeo lo realiza el esquema de persistencia en forma genérica, sin necesidad de los intermediarios específicos. La fachada no debe realizar ninguna traducción. Los expertos contienen las consultas en lenguaje abstracto.

Las 2 aproximaciones son absolutamente válidas. La segunda plantea un gran desafío en el diseño del esquema de persistencia, mientras que la primera requiere un esquema de persistencia más sencillo y la definición de intermediarios específicos.

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 24 de 25

Relaciones de Doble Sentido

Bajo las reglas de normalización, crear una vinculación doble entre tablas es una pésima práctica. Implica un mantenimiento más complejo de las relaciones y duplica los datos almacenados.

Aún así, en el diseño orientado a objetos es muy común observar la existencia de relaciones bidireccionales, por ejemplo:

V entas

*

1

V endedor

En UM L es posible evitar el uso de flechas si la relación es bidireccional

Desde el punto de vista del diseño puede ser muy conveniente poseer estas capacidades. Pero no deben crearse inconvenientes ni en performance, ni en desarrollo y mantenimiento. Poseer relaciones bidireccionales no implica la existencia de doble vinculación en tablas relacionales (doble foreign keys entre tablas).

El hecho de poseer navegación bidireccional entre 2 entidades nos permite recorrerlas de acuerdo a la necesidad. Por ejemplo:

- Si he obtenido una venta como resultado de una búsqueda puedo llamar a la operación getVendedor() sin necesidad de hace búsquedas extras

- Si he obtenido un vendedor como resultado de una búsqueda puedo llamar a la operación getVentas() sin necesidad de hacer búsquedas extras

Estos 2 ejemplos pueden resolverse con cualquiera de los siguientes esquemas:

Ventas

Vendedores

Id

Id

Fecha

Nombre

Numero

comision

Total

idVendedor

Ventas Id Fecha Numero Total
Ventas
Id
Fecha
Numero
Total

RelacionVV

Id

inventa

idVendedor

Vendedores Id Nombre comision
Vendedores
Id
Nombre
comision

Siempre que sea posible debe diagramarse una estructura de datos normalizada. Desde el punto de vista del diseño es muy agradable poder navegar entre entidades de esta forma; pero si deseamos tener una estructura normalizada, el único componente que puede realizar este trabajo complejo es el esquema de persistencia.

Como puede observarse, a medida que se introducen nuevas necesidades, el esquema de persistencia se vuelve cada vez más complejo.

Persistencia

Autor: Atilio Ranzuglia;

Editado por: Adrián Botta

Página 25 de 25