martes, mayo 23, 2006

1

ORM, Object Relational Mapping - II Parte

ORM, Object-Relational Mapping - II Parte


Mapeo del modelo de objetos al modelo relacional


La persistencia de la información es la parte más crítica en una aplicación de software. Si la aplicación está diseñada con orientación a objetos, la persistencia se logra por: serialización del objeto o, almacenando en una base de datos. Las bases de datos más populares hoy en día son relacionales. El modelo de objetos difiere en muchos aspectos del modelo relacional. La interfase que une esos dos modelos se llama marco de mapeo relacional-objeto (ORM en inglés).


En este documento describo el mapeo de un modelo de objetos hacia un modelo relacional. También, los aspectos a considerar cuando se elige un marco ORM: caché, transacciones, carga retardada, concurrencia. Y por último, una descripción de los 2 marcos ORM más usados en la tecnología Microsoft.Net: Ojb.Net y NHibernate.


Este documento es la continuación de ORM, Object-Relational Mapping - I Parte (Capítulos 1 a 4).


Creative Commons License
Esta obra está licenciada bajo una Licencia Creative Commons Atribución 2.5.

5   Persistencia


«Sección basada en [GLOGLS1], [BERGLAS1], [BELLWARE1], [KELLER1] y [AMBLER2]»  


“Persistencia es la habilidad que tiene un objeto de sobrevivir al ciclo de vida del proceso en el que reside. Los objetos que mueren al final de un proceso se llaman transitorios.” [KELLER1]


Ya dije que había un incongruencia entre el mundo orientado a objetos y las base de datos relacionales. Para reducir ese incongruencia recurrimos a una capa auxiliar que mapeará entre los 2 mundos, adaptándolo según las especificaciones hechas. Esa capa auxiliar se denomina ORM, object relational mapping.


Los ORM nacieron a mediados de la década del 90, se hicieron masivos a partir de la masificación de Java. Por esa razón, los frameworks más populares hoy en día en .Net son adaptaciones del modelo pensado para Java.


Imaginemos que queremos cargar un objeto persistido en memoria, los pasos a seguir serían: abrir la conexión a la base de datos, crear una sentencia sql parametrizada, llenar los parámetros (por ejemplo la clave primaria), recién allí ejecutarlo como una transacción y, cerrar la conexión a la base de datos.


Con un framework, la tarea se reduce a: abrir una sesión con la base de datos, especificar el tipo de objeto que queremos (y su clave primaria correspondiente), cerrar la sesión.


No obstante, el framework debería resolver los siguientes puntos:



  • Transacciones. ¿Se hacen todos los cambios sin excepción, o, se hace nada?

  • Caché. ¿se almacenan en memoria los objetos usados? ¿se almacenan las consultas SQL hechas?

  • Carga retardada. cuándo se carga el objeto en memoria, ¿se cargan en memoria todas sus relaciones? ¿se cargan en memoria sus campos menos usados (por ejemplo los BLOBS de gran tamaño)?

  • Referencia circular. Si el objeto está relacionado 2 veces con el mismo objeto, ¿se carga 2 veces el objeto relacionado?

  • OID. ¿las claves primarias se asignan manualmente o automáticamente?


Estudios muestran que aproximadamente, el 35% de desarrollo de software está dedicado al mapeo entre el modelo de objetos y su correspondiente modelo relacional. Ahora si necesitamos actualizar nuestro software: al ser objetos, lo que proveemos al cliente son las interfases de acceso.


5.1  Patrón CRUD


«Sección basada en [KELLER1]»


Acrónimo de Create-Read-Update-Delete. Conocido como el padre de todos los patrones de capa de acceso. Describe que cada objeto debe ser creado en la base de datos para que sea persistente. Una vez creado, la capa de acceso debe tener una forma de leerlo para poder actualizarlo o simplemente borrarlo.


Teóricamente el borrado de objetos debería quedar a acargo de la misma base de datos. Pero un recolector de objetos “basura” (garbage collector) en una base de datos gigante afecta en gran medida la perfomance. Por ello es que la tarea de borrado queda delegada al programador.


5.2  Caché


«Sección basada en [PERSISTENCE1]»


En la mayoria de las aplicaciones, se aplica la regla del 80-20 en cuanto al acceso a datos, el 80% de accesos de lectura accede al 20% de los datos de la aplicación. Esto significa que hay un un conjunto de datos dinámicos que son relevantes a todos los usuarios del sistema, y por lo tanto accedido con mas frecuencia. Las aplicaciones empresariales de sincronización de caché normalmente necesitan escalarse para manejar grandes cargas transaccionales, así múltiples instancias pueden procesar simultáneamente. Es un problema serio para el acceso a datos desde la aplicación, especialmente cuando los datos involucrados necesitan actualizarse dinámicamente a través de esas instancias. Para asegurar la integridad de datos, la base de datos comúnmente juega el ol de árbitro para todos los datos de la aplicación. Es un rol muy importante dado que los datos representan la proporción de valor más significante de una organización. Desafortunadamente, este rol también no está fácilmente distribuido sin introducir problemas significantes, especialmente en un entorno transaccional.


Es común para la base de datos usar replicación para lograr datos sincronizados, pero comúnmente ofrece una copia offline del estado de los datos más que una instancia secundaria activa. Es posible usar base de datos que puedan soportar multiples instancias activas, pero se pueden volver caras en cuanto a perfomance y escalabilidad, debido a que introducen el bloqueo de objetos y la latencia de distribución. La mayoría de los sistemas usan una única base de datos activa, con múltiples servidores conectada directamente a ella, soportando un número variables de clientes.


En esta arquitectura, la carga en la base de datos incrementará linealmente con el número de instancias de la aplicación en uso, a menos que se emplee alguna caché. Pero implementando un mecanismo de caché en esta arquitectura puede traer muchos problemas, incluso corrupción en los datos, por que la caché en el servidor 1 no sabrá sobre los cambios en el servidor 2.


5.2.1  Identidad en la caché


«Sección basada en [KELLER1], [PERSISTENCE1]»


En la siguiente porción de código veremos algunos aspectos de la cache.


Persona prsnPerezJuan = new Persona(”Perez”, “Juan”)

Persona prsnPerezAlberto = new Persona(”Perez”, “Alberto”)

Persona prsnPerez = ORM.LoadByApellido(“Perez”) //¿?

Hemos creado 2 instancias en memoria de la clase Perosna. O sea que para el objeto tenemos 2 objetos diferentes usando el mismo apellido. Si el apellido es la clave primaria en la base de  datos, tendríamos problemas cuando la apliación trate de escribir la segunda instancia en la base de datos. Por eso debemos tener cuidado con la identidad del objeto y sus diferentes notaciones en la base de datos y en el programa. La solución para esto es aplicar la identidad en la misma caché. Normalmente, se usa una tabla de hashing en base a las claves primarias.


Imaginemos un código como el siguiente:


Persona prsnJuan = (Persona) ORM.Load(“PersonaOID”,100)

Persona prsnPerez = (Persona) ORM.Load(“PersonaOID”,100)

If (prsnJuan != prsnPerez) // problemas !!!

En la porción de código anterior se tienen 2 varibles que deberían hacer referencia al mismo objeto: Juan Perez. Si la capa de acceso no es buena, habrá cargado dos instancias del objeto en memoria de los mismos datos en la base de datos. Para evitar que suceda, se necesita un mecanismo que verifique si el objeto ya está cargado en memoria desde la base de datos. La primera vez que se carga el objeto, la capa de acceso lo lee desde la base de datos; la a segunda vez, el método Load necesita verificar si la Persona con OID igual a 100 ya está cargada en memoria. Si es así, solo retorna la referencia al objeto en memoria creado por la primera llamada.


5.3  Carga de las relaciones


«Sección basada en [KELLER1], [AMBLER2]»


Uno de los problemas que se deciden luego del mapeo es si siempre se cargan todos los objetos relacionados a uno principal. La respuesta mas probable es no, porque las redes de relaciones tienden a ser más compleja y la cadena de relación tienden a ser más larga en la vida real.Si, por defecto se carga cada relación de un objeto, En el caso de una gran base de datos se volverá grandísimo y habrá pérdida de perfomance. La solución para este problema es conocida como “Carga retardada” de las relaciones, y se implementa por algún tipo de objetos con “Patrón Proxy” que lanzan la carga cuando se acceden (punteros inteligentes en C++).


Para lograrlo, el objeto tiene un método accesor (“Get”) cuyo único propósito es proveer el valor de un atributo simple, que verifica a ver si el atributo ha sido inicializado y si no es así lo lee desde la base de datos.


Otro uso común de cargas retardada es la generación de reporte y objetos que se dan como resultados de una búsqueda, casos en los cuales se necesita solo un subconjunto de datos del objeto.


Lo mismo sucede para aquellos campos grandes y menos usados. Por ejemplo si se almacena la foto de una Persona ocupará alrededor de 100k mientras que el resto de los atributos no llegan, en total, a 1k; y, raramente son accedidas.


Pero también, habrá veces en la que la “Carga Directa” de las relaciones se prefiera. Con ella, cada vez que se cargue un objeto, se querrán tener algunas de sus relaciones cargadas sin necesidad de volver a consultar la base de datos.


5.3.1  Proxy (listado y selección, reporte)


«Sección basada en [SCE1]»


Es normal que la aplicación presente un listado de un objeto particular, mostrando campos como OID, nombre y un resumen breve de otros campos; el objetivo de ese listado es darle al usuario la posibilidad de seleccionarlo para ver información más detallada. Algo similar se da en la generación de reportes. Si se elige aplicar un ORM, quizás se sobrecarga innecesariamente la capa de persistencia con cargas no triviales en la base de datos.


La aproximación más básica, es no manejarla con los mecanismos de persistencia.


Algunos consejos serían:



  1. Evitar mostrar al usuario todos los objetos al principio. Dejarlo como posibilidad, pero mientras tanto, darle una opción de búsqueda. Mostrar ventanas de selección en vez de cajas de selección (combo box). Da una mayor usabilidad y puede incluir un buen resumen.

  2. La capa de persistencia debe proveer el manejo de solo lectura en masa. Retornando los datos en forma de filas o tuplas.

  3. La consulta a la base de datos debería usar un cursor. Ir retornando los cursores gradualmente al cliente. También debería tener en cuenta, que la mayoria de las bases proveen cursores hacia delante; con esto la capa de persistencia debería bufferear internamente así el cliente puede volver atrás.

  4. A veces, el reporte y la selección requieren que los datos sean unidos a través de múltiples tablas. Por defecto son las uniones hacia la izquierda las que se usan en las asociaciones incluyendo los roles de las tablas unidas; nombre de columnas no ambiguas, alias únicos cuando una talba se une a si misma; tipicamente para estructuras de jerarquía o árbol.


5.3.1.1   Proxy


«Sección basada en [AMBLER1]»


Un proxy es un objeto que representa a otro objeto pero no incurre en la misma sobrecarga que al objeto representado. Un proxy contiene bastante información para que, tanto la aplicación como el usuario puedan identificar el objeto. Por ejemplo el proxy para el objeto Persona debería contener su oid así la aplicación pueda identificarlo y el apellido así el usuario puede reconocer a quien representa el objeto proxy. Los proxies se usan, generalmente, cuando se muestran los resultados de una consulta, de la cual el usuario elegirá solo una o dos. Cuando el usuario elige el objeto proxy de la lista, recién ahí se trae automáticamente el objeto real del framework ORM, el cual es mucho mas grande que el proxy. Por ejemplo, el objeto real de Persona incluye dirección, foto de la persona. Con el patrón proxy solo se da la información que el usuario necesita.



Figura 5.1 Diagrama UML del Patrón Proxy. [DOFACTORY1]


 


5.4  Transacción


«Sección basada en [SCE1]»


Los datos almacenados en una base de datos necesitan ser protegidos por una transacción. Esto permite múltiples inserciones, modificaciones y borrados con la seguridad de que todo o se ejecuta o falla, como si fuera una sola entidad coherente. Las transacciones también pueden ofrecer protección de concurrencia; el bloqueo pesimista de tuplas mientras los usuarios están trabajando en ellos, evita que otros usuarios comiencen con sus cambios.


Sin embargo, el mecanismo de transacción de la base de datos tiene alguna limitaciones:



  • Cada transacción requiere una sesión separada, para permitir que los usuarios abran ventanas relacionadas a su trabajo requeriría de licencias extras o que las ventas estén limitada a acceso de solo lectura.

  • Una alta aislación en la transacción y bloqueo basado en páginas puede evitar que otros usuarios accedan a los datos que deberían estar legitimamente permitidos.


Una alternativa es que los datos sean puestos en un buffer en la capa de persistencia, con todas las escrituras que se harán hasta que el usuario lo confirme. Esta transacción de base de datos se necesita solo durante la operación de escritura en masa (bulk operation), permitiendo ser compartida entre múltiples ventanas. Esto requiere un esquema de bloqueo optimista donde las tuplas son chequeadas mientras se escriben.


Obviamente, esta escritura en masa corre protegida por la integridad referencial. Esas restricciones especifican los requerimientos lógicos según el caso: la tupla debe existir antes de que se la relacione y, las tuplas relacionadas a otra deben ser borradas antes de que se borre la tupla objetivo. Esto se logra mediante un mecanismo en la base de datos que mantiene correctamente las dependencias entre las tuplas.


5.5  Concurrencia


«Sección basada en [KELLER1] y [SCE1]»


La capa de persistencia debe permitir que múltiples usuarios trabajen en la misma base de datos y proteger los datos de ser escritos equivocadamente. También es importante minimizar las restricciones en su capacidad concurrente para verl y acceder.


La integridad de datos es un riesgo cuando 2 sesiones trabajan sobre la misma tupla: la pérdida de alguna actualización está asegurada. También se puede dar el caso, cuando una sesión está leyendo los datos y la otra los está editando: una lectura incosistente es muy probable.


Hay dos técnicas principales para el problema: bloqueo pesimista y bloqueo optimista. Con el primero, se bloquea todos acceso desde que el usuario empieza a cambiar los datos hasta que se haga un COMMIT de la transacción. Mientras que en el optimista, el bloqueo se aplica cuando los datos son aplicados y se van verificando mientras los datos son escritos.


5.5.1  Bloqueo optimista


«Sección basada en [FOWLER1] y [SCE1]»


Cuando detecta un conflicto entre transacciones concurrentes, cancela alguna de las transacciones.


Para resolver el problema, valida que los cambios COMMIT por una sesión no entran en conflicto con los cambios hechos en otra sesión. Una validación exitosa pre-COMMIT es obtener un bloqueo de los registros con una transacción simple.


Asume que la chance de que aparezcan conflictos es baja: se espera que no sea probable que los usuarios trabajen sobre los mismos datos al mismo tiempo.


El bloqueo optimista desacopla la capa de persistencia de la necesidad de mantener una transacción pendiente, chequeando los datos mientras se escribe.


Se usan varios esquemas de bloqueo optimista. Difieren en que campos son verificados; a veces se usa un campo de estampa de tiempo (timestamp) o un simple contador de versiones(counter version). El bloqueo por estampa de tiempo no es confiable, hoy en día el hardware es cada vez más rápido y la cuantización del tiempo llega al orden de los microsegundos. Los contadores van indicando la última versión guardada en la base de datos.


Generalmente, la aplicación iniciará una transacción, leerá los datos desde la base, cerrará su transacción, seguirá con las reglas de negocio involucradas para volver a iniciar una transacción, esta vez con los datos a ser escritos. En el caso de la estampa de tiempo, la capa de persistencia verificará que sea la misma que existe en la base de datos, escribirá los datos y actualizará la estampa de tiempo a la hora actual. Muy similar para el contador, difiere en que la actaulización consiste en incrementar en 1 el campo.


5.5.2  Bloqueo pesimista


«Sección basada en [FOWLER1] y [SCE1]»


Evita que aparezcan conflictos entres transacciones concurrentes permitiendo acceder a los datos a solo una transacción a la vez.


La aproximación más simple, consiste en tener una transacción abierta para todas la reglas de negocios involucradas. Hay que tener precaución con transacciones largas. Por eso se recomienda, usar múltiples transacciones en las reglas de negocios.


Por ejemplo, si varios usuarios acceden a los mismos datos dentro de una regla de negocios, uno de ellos COMMIT todo mientras que otros usuarios no lo lograrán y fallarán. Dado que el conflicto se detecta cuando termina la transacción, las víctimas harán todas las reglas de negocios hasta que en el último instante, cuando todo fallará, con lo cual fue una pérdida de tiempo.


El bloqueo pesimista evita el conflicto anterior en total. Fuerza a las reglas de negocios a adquirir el bloqueo de los datos antes de empezar a usarlo, así, la transacción se usa completamente sin preocuparse por los controles de concurrencia.


El bloqueo pesimista notifica a los usuarios tempranamente de la contención de los datos, pero para los propósitos de negocios la concurrencia es considerada altamente importante.


5.6  Otros aspectos


«Sección basada en [KELLER1], [FOWLER1] y, mayoritariamente, en [AMBLER2]»


En esta sección muestro otros aspectos a tener en cuenta en la elección de un framework ORM.


Referencia circular . Se refiere a si el framework es capaz de detectar cual es el objeto que se está solicitando, sin hacer el roundtrip a la base de datos.


Información oculta (Shadow information) . Así como el OID hay muchas columnas de la tabla que no necesitan ser mapeadas a una propiedad del objeto. Esta columnas contienen información oculta para el modelo de objetos pero necesaria para el modelo relacional. En esta categoría entran los mecanismos de concurrencia: estampa de tiempo y versión de objeto. Al leer el objeto, se lee esta información que es ocultada al objeto pero mantenida por el framework.


Lenguaje de consulta (OQL - Object Query Language). La obtención de varios objetos a través de un lenguaje especial es una de las características más apreciadas. Por ejemplo, obtener todos los Juan Perez de una base de Personas. “SELECT Persona FROM Personas WHERE Nombre = ‘Juan'”. NHibernate con HQL es uno de los mejores.


Actualización en cascada . La posibilidad de que modificaciones hechas a un objetos repliquen en los objetos relacionados.


Operaciones en masa . Habrá veces que por razones de perfomance, se querrá hacer una operación en masa. Por ejemplo, actualizar todos los objetos con nombre Juan a J. De la manera tradicional, deberíamos leer cada objeto, modificarlos en memoria, y recién allí guardarlos de nuevo.


6   Frameworks de trabajo


6.1  Ojb.Net


«Sección basada en [KELLER1], [FOWLER1] y, mayoritariamente, en [AMBLER2]»


OJB.NET es un framework ORM para .Net que está basado en OJB de la plataforma JAVA. Es un proyecto open source.


Algunas características que posee son:



  • Herramienta que genera las clases correspondientes y asociaciones a partir del modelo relacional (ingeniería inversa).

  • Genera el CRUD básico en tiempo de ejecución, lo que permite una configuración out-of-band más administrable.

  • Caché de objetos y de consultas.

  • Soporta límites de transacciones (patrón units-of-work), administrandolos transparentemente.


class FacturacionBiz { … [Transaction(TransactionOption.Required)] void Facturar(){…} … }  

Una de las diferencias de OJB.NET respecto a otros frameworks, es que las clases persistentes deben heredar de algunas de las siguientes clases abstractas:



  • PO::EditableObject, aquellas clases cuyos atributos o relaciones pueden ser actualizadas a nuevos valores o referencias.

  • PO::ImmutableObject, aquellas clases persistentes que solo permiten la inserción o el borrado en la base de datos relacional.

  • PO::ReadOnlyObject, aquellas clases cuya información ya reside en una base de datos, pero no es posible actualizarlas.


En la siguiente figura se ilustra los tipos persistibles en OJB.NET (en color azul).



Figura 6-1 Diagrama de clases de OJB.Net [OJB1]


6.2  Nhibernate


«Sección basada en [KELLER1], [FOWLER1] y, mayoritariamente, en [AMBLER2]»


NHibernate es uno de los frameworks más usados. Se debe principalmente al poderoso lenguaje de consulta que trae consigo: HQL. Hibernate Query Language.


Está basado en el proyecto Hibernate de Java, al igual que OJB.NET es un proyecto open source. 



Figura 6-2 Arquitectura de NHibernate


Algunas de las características más importantes:



  • Permite el mapeo de relaciones a tipos .NET específicos.

  • Permite una buena interfase para estructurar la consulta a partir de criterios.

  • Las transacciones son administradas por objetos Session.

  • La configuración del mapeo y demás se administra tanto por código como por fuera de él.

  • Accede tanto a propiedades privadas, públicas o protegidas.

  • No soporta aún la carga retardada en asociaciones 1 a 1.

  • Las asociaciones son mapeadas a objetos IList, Collection o Dictionary.

  • Asocias las clases con sus correspondientes proxys.

  • Soporta bases en SQLServer, Oracle y parcialmente OleDB.

  • Soporta actualizaciones en cascada, con posibilidad de especificar que tipo de actualización está permitido: insert, update y delete.


La configuración de NHibernate es fuera de banda, a través de archivos en xml.



Figura 6-3 Configuración de NHibernate


7   Conclusión


En el presente trabajo, mostré como unir los 2 mundos de software actuales más difundido, modelo de objetos y base de datos relacionales, a través de frameworks conocidos como Object Relacional Mapping.


Hay 3 pasos para mapear objetos a relaciones:



  1. Diseñar el modelo de objetos.

  2. Modelar el relacional.

  3. Aplicar los mapeos correpondientes.


Mostré los 2 frameworks en más usados en ,Net: NHibernate y OJB.NET; con sus ventajas y desventajas.


En conclusión, el uso de herramientas ORM presenta las siguientes ventajas:



  • Reducción de hasta un 25% del tiempo de desarrollo dedicado al mapeo.

  • Evita el SQL HARD-CODED, especificando en archivos de configuración las tablas, propiedades, etc. Sin preocuparse por el renombre de tablas, columnas etc.

  • Correspondencia lógica y natural del modelo de objetos.



8   Bibliografía


 [AMBLER1] Ambler, Scott. The Design of a Robust Persistence Layer for Relational Databases, http://www.ambysoft.com/persistenceLayer.pdf . Leído el 11 de Marzo del 2005.


[AMBLER2] Ambler, Scott. Mapping Objects to Relational Databases: O/R Mapping In Detail. http://www.agiledata.org/essays/mappingObjects.html . Leído el 20 de Marzo del 2005.


[BEAUCHAMP1] Beauchamp, Richard. OJB.NET an object-to-relational persistence tool for the .NET platform: User QuickStart Tutorial. http://ojb-net.sourceforge.net . Leído el 20 de Marzo del 2005.


[BELLWARE1] Bellware, Scott. Object-Relational Persistence for .NET, http://www.15seconds.com/issue/040112.htm . Leído el 20 de Marzo del 2005.


[BERGLAS1] Berglas, Anthony. Object Relational Mapping Tools. http://www.uq.net.au/~zzabergl/simpleorm/ORMTools.html . Leído el 11 de Marzo del 2005.


[CRESPO1] Crespo Martín, Cesar. Tutorial de Hibernate, http://www.adictosaltrabajo.com/tutoriales/view.php?tutorial=hibernate . Leído el 8 de Marzo del 2005.


[FOWLER1] Fowler, Martin.Catalog of Patterns of Enterprise Application Architecture. http://martinfowler.com/eaaCatalog/index.html . Leído el 11 de Marzo del 2005.


[FUSSELL1] Fussell, Mark. Foundations of Object Relational Mapping. http://www.chimu.com/objectRelational.pdf . Leído el 20 de Abril del 2005.


[GLOGLS1] Glögls, Michael. Why we need Hibernate: What an object-relational mapper does. http://www.gloegl.de/17.html . Leído el 20 de Marzo del 2005.


[HANSON1] Hanson, Jeff. Persistencia de Objetos Java utilizando EJBs, Traducción de Juan Antonio Palos. ¡Error! Referencia de hipervínculo no válida. . Leído el 20 de Marzo del 2005.


[HANSON2] Hanson, Jeff. Persistencia de Objetos Java utilizando Hibernate, Traducción de Juan Antonio Palos. http://programacion.com/articulo/jap_persis_hib . Leído el 20 de Marzo del 2005.


[KELLER1] Keller, Wolfgang. Persistence Options for Object-Oriented Programs. http://www.objectarchitects.de/PersistenceOptionsOOP2004e.pdf . Leído el 20 de Marzo del 2005.


[KELLER2] Keller, Wolfgang. Object/Relational Access Layers: A Roadmap, Missing Links and More Patterns. http://www.objectarchitects.de/or06_proceedings.pdf . Leído el 20 de Abril del 2005.


[MARGUERIE1] Marguerie, Fabrice. Choosing an object-relational mapping tool. http://madgeek.com/Articles/ORMapping/EN/mapping.htm , http://weblogs.asp.net/fmarguerie . Leído el 20 de Marzo del 2005.


[NHIBERNATE1] NHibernate Project. What is NHibernate. http://nhibernate.sourceforge.net/ . Leído el 20 de Abril del 2005.


[OJB1] OJB project. OJB Project. http://ojb-net.sourceforge.net/ . Leído el 20 de Abril del 2005.


[PERSISTENCE1] Persistence Software. Edge Extend for .Net. http://www.persistence.com/docs/edgextendfor.net_us.pdf .  Leído el 20 de Marzo del 2005.


  [SCE1] SCE. Persistence Layer Architecture. http://www.sce-tech.com/architecture/persistence_layers.html . Leído el 20 de Marzo del 2005.



 


Sobre el autor


Pablo Pizarro es un Ingeniero en Informática que se especializa en
aplicaciones web empresariales. Está radicado en Mendoza, Argentina. Su
objetivo profesional es "mejorar la competitividad de las organizaciones a
través de la integración de la información". Actualmente, trabaja en el
área de I+D de la consultora minera Rojas y Asociados. Antes, trabajó en el área
de tecnología de Tarjeta Nevada (tarjeta de crédito). Hoy en día, se encuentra
desarrollando un proyecto sobre aplicaciones empresariales junto a otros
profesionales. Puede ser contactado a través de su email:
pizarropablo@gmail.com.


About the author


Pablo Pizarro is an Engineer in Information Technology, which specializes in
enterprise web applications. He lives in Mendoza, Argentina. His professional
objective is "Enhance competitiveness of organizations through the integration
of the information". Actually, he works in I+D department in Rojas & Associates
mining consultancy. Prior, he worked in the area of Technology in Tarjeta
Nevada (credit card). Today, he is developing a project about enterprise
applications with other professionals. He can be contacted at
pizarropablo@gmail.com.

3

ORM, Object-Relational Mapping - I Parte

ORM, Object-Relational Mapping - I Parte


Mapeo del modelo de objetos al modelo relacional


La persistencia de la información es la parte más crítica en una aplicación de software. Si la aplicación está diseñada con orientación a objetos, la persistencia se logra por: serialización del objeto o, almacenando en una base de datos. Las bases de datos más populares hoy en día son relacionales. El modelo de objetos difiere en muchos aspectos del modelo relacional. La interfase que une esos dos modelos se llama marco de mapeo relacional-objeto (ORM en inglés).


En este documento describo el mapeo de un modelo de objetos hacia un modelo relacional. También, los aspectos a considerar cuando se elige un marco ORM: caché, transacciones, carga retardada, concurrencia. Y por último, una descripción de los 2 marcos ORM más usados en la tecnología Microsoft.Net: Ojb.Net y NHibernate.


Creative Commons License
Esta obra está licenciada bajo una Licencia Creative Commons Atribución 2.5.

2   Índice


1 Resumen


2 Índice


3 Introducción


4 Mapeo de objetos al modelo relacional


4.1 Generalidades


4.2 Incongruencia entre el modelo relacional y el de objetos


4.3 Terminología


4.4 Ejemplo a desarrollar


4.5 Mapeo de objetos


4.5.1 Identidad del Objeto


4.6 Mapeo de relaciones


4.6.1 Asociaciones


4.6.2 Agregación / Composición


4.6.3 Herencia


4.7 Ejemplo desarrollado


4.8 Resumen


5 Persistencia


5.1 Patrón CRUD


5.2 Caché


5.2.1 Identidad en la caché


5.3 Carga de las relaciones


5.3.1 Proxy (listado y selección, reporte)


5.4 Transacción


5.5 Concurrencia


5.5.1 Bloqueo optimista


5.5.2 Bloqueo pesimista


5.6 Otros aspectos


6 Frameworks de trabajo


6.1 Ojb.Net


6.2 Nhibernate


7 Bibliografía


ORM, Object-Relational Mapping - II Parte (Capítulo 5 en adelante).


3   Introducción


Organicé el trabajo en 3 partes. En la primera parte, hablaré sobre el paso del modelo de objetos al modelo relacional. Introduciré el tema. Luego, describiré las técnicas de mapeo entre las tablas y los atributos de un objeto. Entonces, trataré sobre la complejidad de los ORM. Finalmente, describiré brevemente las tecnologías más usadas en la plataforma Micorosoft.Net.


4   Mapeo de objetos al modelo relacional


4.1  Generalidades


«Sección basada en [GLOGLS1], [Hanson1], [Hanson2], [AMBLER2], [KELLER1]»


La persistencia de la información es la parte más crítica en una aplicación de software. Si la aplicación está diseñada con orientación a objetos, la persistencia se logra por: serialización del objeto o, almacenando en una base de datos.


El modelo de objetos difiere en muchos aspectos del modelo relacional. La interfase que une esos dos modelos se llama marco de mapeo relacional-objeto (ORM en inglés).  Marcos de trabajo como Java o .Net han popularizado el uso de modelos de objetos (UML) en el diseño de aplicaciones dejando de lado el enfoque monolítico de una aplicación.


Las bases de datos más populares hoy en día son relacionales. Oracle, SQLServer, Mysql, Postgress son los DBMS más usados.


En el momento de persistir un objeto, normalmente, se abre una conexión a la base de datos, se crea una sentencia SQL parametrizada, se asignan los parámetros y recién allí se ejecuta la transacción. Si se tiene un objeto con varias propiedades, además de varias relaciones, ¿como se asocian relacionalmente? ¿cómo almacenarlo? ¿automáticamente, manualmente? ¿qué pasa con las claves secundarias?


Ahora, si se necesita recuperar los datos persistidos. Se carga únicamente el objeto? o también las asociaciones? el árbol completo? Y si los mismos objetos están relacionados con otros, se cargan n veces hasta sastifacerlos?


Está demostrado que un 35% de tiempo de desarrollo de software está dedicado al mapeo entre objeto y su correspondiente relación. Como se ve, la incongruencia entre los 2 modelos aumenta a medida que crece el modelo de objetos. Hay varios puntos por considerar:



  • Carga perezosa.

  • Referencia Circular.

  • Caché.

  • Transacciones.


El ORM debería resolver la mayoría de las cargas. Un buen ORM permite:



  • Mapear clases a tablas: propiedad a columna, clase a tabla.

  • Persistir objetos. A través de un método Orm.Save(objeto). Encargándose de generar el SQL correspondiente.

  • Recuperar objetos persistidos. A través de un método

    objeto =  Orm.Load(objeto.class, clave_primaria)

  • Recuperar una lista de objetos a partir de un lenguaje de consulta especial. A través de un método.

    ListObjetos = Orm.Find(“Objeto FROM MyObject WHERE Objeto.Propiedad=5”), o algo más complejo ListObjetos = Orm.Find(“Objeto FROM MyObject WHERE Objeto.Relacion1.Relacion2.Propiedad2=5”), y el ORM transformará a través de varios joins de tablas.


4.2  Incongruencia entre el modelo relacional y el de objetos


Se sabe que las tablas tienen atributos simples, o sea, tipo definidos previamente por los arquitectos del software. Por otro lado, un objeto tiene tanto atributos simples como aquellos definidos por el usuario, que en sí es otro objeto más.


La incongruencia entre el modelo relacional y el de objetos es la diferencia en la forma de representar atributos de los 2 modelos. Así en uno se tiene una representación tabular, mientras que en otro se tiene una representación jerárquica.


La incongruencia entre la tecnología de objetos y la relacional, fuerza al programador a mapear el esquema de objetos a un esquema de datos.


Digo que los objetos deberían almacenarse en una base de datos relacional. Ahora, una tabla mantiene relacionados los atributos que contiene. Un modelo de objetos tiene una jerarquía en árbol.


Para ello se usa una capa extra muy fina pero suficiente para servir como un puente entre los 2 modelos. Para implementar esos mapeos, se necesita agregar código a los objetos de negocios, código que impacta en la aplicación.


Para la mayoría de las aplicaciones, almacenar y recuperar información implica alguna forma de interacción con una base de datos relacional. Esto ha representado un problema fundamental para los desarrolladores porque algunas veces el diseño de datos relacionales y los ejemplares orientados a objetos comparten estructuras de relaciones muy diferentes dentro de sus respectivos entornos.


Las bases de datos relacionales están estructuradas en un configuración tabular y los ejemplares orientados a objetos normalmente están relacionados en forma de árbol. Esta 'diferencia de impedancia' ha llevado a los desarrolladores de varias tecnologías de persistencia de objetos a intentar construir un puente entre el muno relacional y el mundo orientado a objetos. El marco de trabajo Enterprise JavaBeans (EJB) proporciona uno de los muchos métodos para reducir esta distancia.


4.3  Terminología


«Sección basada en [FUSSELL1]»


Estos son los términos usados en los 2 modelos.


Modelo de objetos:



  • Identidad de objeto. Propiedad por la que cada objeto es distinguible de otros aún si ambos tienen el mismo estado (o valores de atributos).

  • Atributo. Propiedad del objeto al cual se le puede asignar un valor.

  • Estado.

  • Comportamiento. Es el conjunto de interfaces del objeto.

  • Interfase. Operación mediante la cual el cliente accede al objeto.

  • Encapsulación. Es el ocultamiento de los detalles de implementación de las interfases del objeto respecto al cliente.

  • Asociación. Es la relación que existe entre dos objetos.

  • Clase. Define como será el comportamiento del objeto y como almacenará su información. Es responsabilidad de cada objeto ir recordando el valor de sus atributos.

  • Herencia. Especifica que una clase usa la implementación de otra clase, con la posible sobreescritura de la implementación de las interfases.


Modelo relacional:



  • Base de datos. Conjunto de relaciones variables que describen el estado de un modelo de información. Puede cambiar de estado (valores) y puede responder preguntas sobre su estado.

  • Relación variable (Tabla). Mantiene tuplas relacionadas a lo largo del tiempo, y puede actualizar sus valores. Esquematiza como están organizados los atributos para todas las tuplas que contiene.

  • Tupla (fila). Es un predicado de verdad que indica la relción entre todos sus atributos.

  • Atributo (columna). Identifica un nombre que participa en una relación y especifica el dominio sobre el cual se aplican los valores.

  • Valor de atributo (valor de columna). Valor particular del atributo con el dominio especificado.

  • Dominio. Tipos de datos simples.


4.4  Ejemplo a desarrollar


La siguiente figura es un modelo de obejos simplificados. Al final del capítulo mostraré uno de los posibles modelos relacionales a la que mapea.


El modelo de ejemplo nos dice que los empleados de una organización publican cada cierto tiempo documentos (o contratos de especificaciones). Esos documentos deben ser aceptados por los clientes a quienes involucran. Los clientes de la organización son los que piden las especificaciones a los empleados. Esas especificaciones quedan plasmadas en un documento. Los empleados a su vez, pueden ser supervisores de otros.



  • Hay 4 clases básicas: Persona, Empleado, Cliente, Documento.

  • Hay 3 relaciones: Firma (Cliente que Firma Documentos de especificaciones), Publica (cuando un empleado formaliza el documento) y Supervisa (relación de dependencia entre los empleados).



Figura 4.1 Modelo de objetos del ejemplo a desarrollar.


4.5  Mapeo de objetos


«Sección basada en [AMBLER2], [FOWLER1]»


Un objeto está compuesto de propiedades y métodos. Como las propiedades, representan a la parte estática de ese objeto, son las partes que son persistidas. Cada propiedad puede ser simple o compleja. Por simple, se entiende que tiene algún tipo de datos nativos como por ejemplo entero, de coma flotante, cadena de caracteres. Por complejo se entiende algún tipo definido por el usuario ya sea objetos o estructuras. En esta sección se verá como mapear propiedades simples de los objetos. El mapeo de tipos de datos complejos se ve en la sección siguiente.


En el modelo relacional, cada fila en la tabla se mapea a un objeto, y cada columna a una propiedad.


Normalmente, cada objeto de nuestro modelo, representa una tabla en el modelo relacional. Así que cada propiedad del objeto se mapea a cero, 1 o, más de 1 columna en una tabla. Cero columnas, pues se puede dar el caso de propiedades que no necesitan ser persistidas, el ejemplo más sencillo, es el de las propiedades que representan cálculos temporarios. Lo más común que sucede es que cada propiedad del objeto se mapea a una única columna, se debe tener en cuenta el tipo de datos. Una propiedad puede ser mapeada en mas de una columna, por ejemplo, las propiedades de cálculos temporarios.


También se da el caso contrario de que en la tabla se tienen otros atributos que no se representan en el objeto real. Estos atributos suelen estar disponibles para el manejo de la concurrencia, auditorías, etcetera.


Cada columna de la tabla debería respetar el tipo de datos con su correspondiente en la propiedad del objeto. Aunque a veces, por optimización, es necesario algunos ajustes en la base de datos relacional. Una de las razones principales, es que la perfomance aumenta considerablemente si se trabaja con valores numéricos que con caracteres. En caso de no poder representarse el tipo de datos se debe tener en cuenta la pérdida de información. Por ejemplo si se almacena un valor de punto flotante como cadena de caracteres, al reconstruirlo en propiedad, es posible la pérdida de información.


Para acceder a las propiedades normalmente se usan métodos especiales llamados getters y setters, o mutadores y accesores.


4.5.1  Identidad del Objeto


Para distinguir cada fila de las otras, se necesita un identificador de objetos (OID) que es una columna más. Este identificador no es necesario en memoria, por que la unicidad del objeto queda representada por la unicidad de la posición de memoria que ocupa. El OID siempre representa la clave primaria en la tabla. Normalmente es númerico por razones de perfomance.


En esta sección mostré que solo se mapea la parte estática de un objeto. Así cada clase se mapea a una tabla. Cada objeto se mapea a una fila en la tabla. Y, cada propiedad del objeto se mapea por lo general a una columna. También mostré la existencia una columna especial que identifica al objeto en la tabla llamada Oid.


4.6  Mapeo de relaciones


«Sección basada en [AMBLER2]»


En esta sección muestro como se mapean las relaciones entre los objetos. Por relación se entiende asociación, herencia o agregación. Cualquiera sea el caso, para persistir las relaciones, se usan transacciones, ya que los cambios pueden incluir varias tablas. En el caso de que la transacción falle, se aumenta la probabilidad de éxito integridad referencial .


4.6.1  Asociaciones


«Sección basada en [AMBLER2]»


Una regla general para el mapeo es respetar el tipo de multiplicidad en el modelo de objetos, y en el modelo relacional. Así una relación 1-1 en el modelo de objetos, deberá corresponder a una relación 1-1 en el modelo relacional.


La asociaciones, a su vez, están divididas según su multiplicidad y su navagabilididad. Según su multiplicidad, pueden existir asociaciones 1-1, 1-n, m-n. Según su navegabilidad, se tiene unidireccional o bidireccional. Se puede dar la seis combinaciones posibles. Una aclaración importante, es que en las base de datos relacionales, todas las asociaciones son bidireccionales, también es un factor de la incongruencia del modelos.


4.6.1.1   Asociación clave foránea


«Sección basada en [FOWLER1]»


Para mapear las relaciones, se usan los identificadores de objetos (OID). Estos OID se agregan como una columna más en la tabla donde se quiere establecer la relación. Dicha columna es una clave foránea a la tabla con la que se está relacionada. Así, queda asignada la relación. Recordar que las relaciones en el modelo relacional son siempre bidireccionales. El patrón se llama Foreign Key Mapping.


4.6.1.2   Multiplicidad 1-1


«Sección basada en [AMBLER2]»


Cada objeto A está asociado con cero o un objeto B y, cada objeto B está asociado con cero o un objeto A.


En el modelo de objetos, este tipo de relación, se representa como una propiedad de tipo de datos de Usuario. Así es común accederla via setObjetoB(), getObjetoB().


En el modelo relacional, cualquiera de las 2 tablas relacionadas implementará una columna con el Oid de la otra tabla, y esta columna será la clave foránea para relacionarlas.


Por ejemplo, entre Empleado y Posicion existe una relación 1 a 1 y se mapea a 2 tablas.


No obstante, este tipo de relación al ser un subconjunto de 1-n, y ésta a su vez de n-m, puede mapearse como esta última a través de una tabla asociativa o implementando la clave en la otra tabla relacionada (ver Nevegabilidad unidireccional).


4.6.1.3   Multiplicidad 1-n


«Sección basada en [AMBLER2]»


Cada objeto A puede estar asociado con cero o más objetos B, pero cada objeto B está asociado con cero o un objeto A.


En el modelo de objetos, este tipo de relación, se representa como una colección o array de tipo de datos de usuario. Así es común accederla via addObjectosB(ObjetoB), removeObjetosB(ObjetoB).


En el modelo relacional se pueden seguir 2 estrategias para establecer la relación:



  • Implementando la clave foránea OID en la tabla “muchos” a la tabla “uno”.

  • Implementando una tabla asociativa, convirtiendo la relación en muchos a muchos. Ver sección siguiente.


4.6.1.4   Multiplicidad n-m


«Sección basada en [AMBLER2]»


Cada objeto A está asociado con cero o más objetos B, y a su vez, cada objeto B está asociado a cero o más objetos A.


En el modelo de objetos, esta relación será similar a la anterior. Será implementada mediante una colección o array en ambos objetos.


En el modelo relacional, se usa una tabla auxiliar asociativa para representar la relación y cuyo +unico objetivo es relacionar 2 o más tablas. Dicha tabla tendrá al menos 2 columnas, cada una representando la clave foránea a las 2 tablas que relaciona. Con esto se transforma la relacion n-m a dos relaciones (1-n y 1-m).


4.6.1.5   Navegabilidad unidireccional


«Sección basada en [AMBLER2]»


Una relación unidireccional es aquella que sabe con que objetos está relacionado, pero dichos objetos no conocen al objeto original.


En el modelo de objetos, la navegabilidad unidireccional está representada por una flecha en el extremo de la asociación. Por lo tanto el objeto original es quien implementa los métodos de acceso, mientras que el secundario no.


En el modelo relacional, la navegabilidad está representada por un clave foránea y depende de la multiplicidad la tabla en la que se implementa.


Como ya se dijo, todas las relaciones en la base de datos son bidireccionales. Por ejemplo, para implementar una relación 1-1 se podría haber puesto la clave foránea en cualquiera de las 2 tablas, pues al hacer la unión (INNER JOIN, LEFT JOIN, RIGHT JOIN) solo se indica la clave foránea y su unión.


Si la tabla Posicion implementara el empleado que la ocupa el select sería:


SELECT Empleado.Nombre, Posicion.Nombre FROM Posicion INNER JOIN Empleado ON Posicion.EmpleadoOid = Empleado.EmpleadoOid

Pero, si la tabla Empleado implementara la posición, no habría ningún tipo de problemas:


SELECT Empleado.Nombre, Posicion.Nombre FROM Posicion INNER JOIN Empleado ON Posicion.PosicionOid = Empleado.PosicionOid

4.6.1.6   Navegabilidad bidireccional


«Sección basada en [AMBLER2]»


Una relación bidireccional existe cuando los objetos en ambos extremos de la relación saben del objeto en el extremo contrario.


En el modelo de objetos se representa por una linea que los une, sin flechas que indiquen direccionalidad. Ambos objetos deben implementar los métodos de acceso hacia el objeto con el cual está relacionado.


4.6.1.7   Asociación recursiva


«Sección basada en [AMBLER2]»


Una asociación recursiva (o reflexiva) es aquella donde ambos extremos de la asociación es una misma entidad (clase, tabla).


En el modelo de objetos se representa por una linea asociativa que empieza y termina en la misma clase. Puede tener cualquier multiplicidad y navegabilidad.


En el modelo relacional, se representa por una clave foránea a sí misma, y depende de la multiplicidad si esa clave se implementa en la misma tabla o en una tabla asociativa.


4.6.2  Agregación / Composición


«Sección basada en, [AMBLER2]»


Como ya mostré, una asociación es una relación débil e independiente entre 2 objetos. Una agregación es una relación más fuerte que una asocación pero aún independiente. Una composición es a la vez una relación fuerte y dependiente entre 2 objetos.


Con lo anterior se asume que tanto una asociación, agregación o composición se mapea en el modelo relacional como una relación.


La agregación normalmente se mapea como una relación n-m, o sea que hay una tabla auxiliar para mapear la relación.


La composición puede mapearse como una relación 1-n. Donde los objetos compuestos mantienen una relación con el objeto compositor. A nivel relacional, indica que la tabla de los objetos compuestos tienen una columna con la clave foránea al objeto que los compuso.


En el modelo objetos, tanto las agregaciones como las composiciones se corresponden con un array o una colección de objetos.


4.6.3  Herencia


«Sección basada en [KELLER1], [FOWLER1] y [AMBLER2]»


Como mostré anteriormente, las asociaciones funcionan en ambos modelos, objeto y relacional. Para el caso de la herencia se presenta el problema que las base de datos relacionales no la soportan. Así es que somos nosotros quienes debemos modelar como se verá la herencia en el modelo relacional. Una regla a seguir es que se debe minimizar la cantidad de joins posibles. Existen 3 tipos de mapeos principales: modelar la jerarquía a una sola tabla, modelar la jerarquía concreta en tablas, modelar la jerarquía completa en tablas. La decisión estará basado en la perfomance y, en la escalabilidad del modelo.


4.6.3.1   Mapeo de la jerarquía a una tabla.


«Sección basada en [FOWLER1] y [AMBLER2]»


Se mapean todos los atributos, de todas las clases del árbol de herencia en una única tabla. En el mapeo, se agregan 2 columnas de información oculta (ver capítulo siguiente): la clave primaria de la tabla OID y, el tipo de clase que es cada registro. El tipo de clase se resuelve con 1 columna carácter o numérica entera. Para los casos más complejos se necesita de varias columnas booleanas (si/no).


Por ejemplo, la jerarquía Persona (que es abstracta), Cliente y Empleado quedarían en una única tabla llamada persona (es recomendable colocarle el nombre de la raíz de la estructura). A la tabla se le agrega el tipo de clase que es, así, se tiene la columna TipoPersona donde C será cliente, E empleado, D para aquellos que son clientes y empleados al mismo tiempo.


Las ventajas son:



  • Aproximación simple.

  • Cada nueva clase, simplemente se agregan columnas para datos adicionales.

  • Soporta el polimorfismo cambiando el tipo de fila.

  • El acceso a los datos es rápido por que los datos están en una sola tabla.

  • El reporte es facil por que los datos están en una tabla.


Las desventajas son:



  • Mayor acoplamiento entre las clases. Un cambio en una de ellas puede afectar a las otras clases ya que comparten la misma tabla.

  • Se desperdicia espacio en la base de datos (por la tanto disminuye perfomance), para aquellas columnas en las que son de las clases derivadas.

  • El tipo se complica cuando hay sobrelapamiento de clases derivadas.

  • La tabla crece rápidamente a mayor jerarquía.


Cuando usar: es una estrategia para clases de jerarquias simples And/Or, donde hay poco sobrelapamiento entre los tipos de la jerarquía.


4.6.3.2   Mapeo de cada clase concreta a una tabla.


«Sección basada en [AMBLER2]»


Cada clase concreta es mapeada a una tabla. Cada tabla incluye los atributos heredados más los implementados en la clase. La clase base abstracta entoncs, es mapeada en cada tabla de las derivadas.


Ventajas:



  • Reportes facíles de obtener ya que los datos necesarios están en una sola tabla.

  • Buena perfomance para acceder a datos de un objeto.


Desventajas:



  • Poco escalable, si se modifica la clase base, se debe modificar en todas las tablas de las clases derivadas para reflejar ese cambio. Por ejemplo, si a la clase Persona se le agrega el atributo de EstadoCivil, tambien debe agregarse en las tablas Cliente y en Empleado.

  • Actualización compleja, si un objeto cambia su rol, se le asigna un nuevo OID y se mueven los datos a la tabla correrpondiente.

  • Se pierde integridad en los datos, para el caso en que el objeto tenga los 2 roles. Por ejemplo Cliente y Empleado a la vez.


Cuando usar: Cuando el cambio en el tipo y/o sobrelapamiento sea raro.


4.6.3.3   Mapeo de cada clase a su propia tabla.


«Sección basada en [AMBLER2]»


Se crea una tabla por cada clase de la herencia, aún la clase base abstracta. Se agregan también las columnas para el control de la concurrencia o version a cualquiera de las tablas.


Cuando se necesita leer el objeto heredado se unen (join) las 2 tablas de la relación o se leen las 2 tablas en forma separada.


Las claves primarias de todas las tablas heredadas, será la misma que la tabla base. A su vez, también serán claves foráneas hacia la tabla base.


Para simplificar las consultas, a veces será necesario agregar una columna en la tabla base indicando los subtipos de ese elemento, o, agregando varias columnas booleanas. El mismo efecto se logra, y mucho más eficiente, a través de vistas.


Ventajas:



  • Fácil de entender, por que es un mapeo uno a uno.

  • Soporta muy bien el polimorfismo, ya que tiene almacenado los registros en la tabla correspondiente.

  • Facil escalabilidad, se pueden modificar atributos en la superclase que afectan a una sola tabla. Agregar subclases es simplemente agregar nuevas tablas.


Desventajas:



  • Hay muchas tablas en la base de datos.

  • Menor perfomance, pues se necesita leer y escribir sobre varias tablas.

  • Reportes rápidos dificiles de armar, a menos que se tengan vistas.


Usar cuando el sobrelapamiento de tipos o cuando el cambio de tipos sea muy común.


4.7  Ejemplo desarrollado


Por lo visto, este es el resultado final del ejemplo desarrollado.


Para el caso de la herencia preferí usar la técnica del mapeo clase a tabla, por que el sobrelapamiento es muy común.



Figura 4.2 Modelo relacional del ejemplo desarrollado


4.8  Resumen


En esta sección mostré como mapear las relaciones que existen entre los objetos: asociaciones y herencia. Las asociaciones se distinguen según su multiplicidad y según su navegabilidad. La herencia se puede mapear en 3 estrategias básicas, y la agregación se puede simplificar. Y, mostré el mapeo de asociaciones recursivas.


 


En la 2da parte, ORM, Object-Relational Mapping - II Parte, muestro otros aspectos a tener en cuenta en la elección o diseño de un marco ORM.



 


Sobre el autor


Pablo Pizarro es un Ingeniero en Informática que se especializa en
aplicaciones web empresariales. Está radicado en Mendoza, Argentina. Su
objetivo profesional es "mejorar la competitividad de las organizaciones a
través de la integración de la información". Actualmente, trabaja en el
área de I+D de la consultora minera Rojas y Asociados. Antes, trabajó en el área
de tecnología de Tarjeta Nevada (tarjeta de crédito). Hoy en día, se encuentra
desarrollando un proyecto sobre aplicaciones empresariales junto a otros
profesionales. Puede ser contactado a través de su email:
pizarropablo@gmail.com.


About the author


Pablo Pizarro is an Engineer in Information Technology, which specializes in
enterprise web applications. He lives in Mendoza, Argentina. His professional
objective is "Enhance competitiveness of organizations through the integration
of the information". Actually, he works in I+D department in Rojas & Associates
mining consultancy. Prior, he worked in the area of Technology in Tarjeta
Nevada (credit card). Today, he is developing a project about enterprise
applications with other professionals. He can be contacted at
pizarropablo@gmail.com.