You are on page 1of 37

Diseo de Sistemas Mapeo Objetos Relacional

Diseo lgico de datos:


Mapeo Objetos Relacional
por Fernando Dodino
Versin 3.1-SNAPSHOT
Marzo 2014
1 de 37
Diseo de Sistemas Mapeo Objetos Relacional

Indice
Introduccin
El problema
Impedance mismatch
Manejo de la identidad
Consecuencias para el modelo de objetos
Conversiones de dominio
Relacin de cardinalidad
Uno a muchos: Pedido a tems
Set, Bag, List
Muchos a uno: Item a Producto
Muchos a muchos: proveedores y productos
Generalizacin en el modelo relacional
Implementar una tabla por cada clase concreta (TABLE_PER_CLASS)
Implementar una tabla por cada clase (JOINED)
Implementar una nica tabla (SINGLE_TABLE)
Anlisis Comparativo
Relaciones many
Relaciones one
Seleccin del grafo de objetos
Lazy association
Ciclo de vida de los objetos
Implementaciones
Home + Mapeo manual contra un driver
Ejemplo de bsqueda
Ejemplo de Actualizacin
Frameworks ORM (Object/Relational Mappers)
Data Mapper vs. Active Record
O/R o R/O?
Conclusiones
2 de 37
Diseo de Sistemas Mapeo Objetos Relacional
Introduccin
Cuando uno tiene un modelo de objetos, ste vive en la memoria de la aplicacin (el ambiente). Sin
embargo, cuando querramos persistirlo, normalmente deberemos salir de este mundo de objetos,
y entrar en otro, como por ejemplo, el relacional. Lo que estudiaremos en este apunte son las
diferencias entre ambos mundos (impedance mismatch), y como podemos conciliarlas, haciendo
un mapeo objetos-relacional (ORM), lo cual implementaremos tpicamente utilizando un framework
ORM, como por ejemplo JPA/Hibernate.
El problema
Si tenemos un modelo de objetos como el siguiente

Qu tipo de estructura siguen los objetos en memoria? Un grafo dirigido
3 de 37
Diseo de Sistemas Mapeo Objetos Relacional

Estos objetos viven en el ambiente y todos somos felices. El tema es que la memoria es limitada
y necesito lograr que yo almacene los objetos en un medio que me permita recuperarlos el da
de maana (concepto que llamaremos persistencia). La opcin para preservar esa informacin
que nos interesa tratar ahora es utilizar una base de datos relacional como repositorio de
informacin.
Impedance mismatch
La pregunta es: este grafo que acabamos de dibujar encaja justo en una estructura basada en el
lgebra relacional? Mmm... claramente son distintas formas de relacionar entidades.

Anotamos en el pizarrn similitudes y diferencias
Originalmente podemos establecer una equivalencia entre el concepto de Tabla/Clase,
y Registro/instancias de esa clase. Pero ms adelante empezaremos a tener ciertas
dificultades para que este mapeo sea tan lineal.
Los objetos tienen comportamiento, mientras que las tablas slo permiten habilitar ciertos
controles de integridad (constraints) o validaciones antes o despus de una actualizacin
(triggers). Los stored procedures no estn asociados a una tabla, lo que genera el mismo
problema que en la programacin estructurada: cada actualizacin impacta en los datos por
un lado y en los programas que actualizan los datos por el otro (Find/Replace masivo).
Los objetos encapsulan informacin, no para protegerse (siempre puedo acceder a sus
atributos), sino para favorecer la abstraccin del observador. Una tabla no tiene esa
habilidad: si tengo una entidad con un atributo ACTIVO que es VARCHAR2(1), tengo que
entrar al constraint check o bien tirar un SELECT para saber si el ACTIVO es 1 0, Y
o N, S o N, e inferir en el mejor de los casos qu significan los valores de ese campo.
Puedo documentarlo en un diccionario de datos, algo ajeno a la base de datos que estoy
4 de 37
Diseo de Sistemas Mapeo Objetos Relacional
tocando, y que necesita una sincronizacin propia de gente pulcra y obsesiva.
En objetos puedo generar interfaces, que permiten establecer un contrato entre dos partes,
quien publica un determinado servicio y quien usa ese servicio. En el lgebra relacional la
interfaz no se convierte en ninguna entidad, con lo cual hay que contar con ciertos trucos
para generar objetos que slo encapsulan comportamiento (como los strategies stateless):
La herencia es una relacin esttica que se da entre clases, que favorece agrupar
comportamiento y atributos en comn. Cuando instanciamos un objeto recibimos la
definicin propia de la clase y de todas las superclases de las cuales hereda. En el
modelo lgico de un Diagrama Entidad/Relacin tenemos supertipos y subtipos, pero en la
implementacin fsica del modelo relacional las tablas no tienen el concepto de herencia.
Entonces tenemos que hacer las adaptaciones correspondientes (como veremos ms
adelante).
Al no existir el comportamiento en las tablas y no estar presente el concepto de interfaz no
hay polimorfismo en el lgebra relacional: podemos recibir un objeto sin saber exactamente
de qu clase es, y no nos interesa averiguarlo, slo nos concentramos en lo que le puedo
pedir y en su contrato. Al disear una consulta, necesitamos saber qu tablas y qu campos
estn involucrados.

Esto es lo que se conoce como Impedance mismatch las dificultades de traslacin de los dos
mundos.
5 de 37
Diseo de Sistemas Mapeo Objetos Relacional
Impedance mismatch se traduce al espaol como "desadaptacin de impedancia",
trmino que es muy aclarador... para muy pocos!
En realidad el trmino proviene del campo de la electrnica, y sirve para explicar
el fenmeno por el cual tenemos, por ejemplo, un parlante conectado a un un
amplificador, pero entre la entrada y salida de estos hay diferencias en un cierta
propiedad (la impedancia), provocando que el audio no se escuche como se debe.
Por extensin, se usa el trmino en otros campos, siempre hablando de conectar dos
elementos que no se adaptan bien.
Veamos entonces cmo adaptar el modelo relacional al de objetos,
es decir, cmo establecer un mapeo entre nuestro mundo de
objetos y nuestro mundo de filas....
Para explicar estas cuestiones generaremos el modelo fsico
relacional asociado al diagrama de clases de la pgina 2, para lo
cual nos vamos a tener que encargar de algunas cuestiones:
la identidad
los tipos de datos
las relaciones (cardinalidad)
los tipos de coleccin (manejo de duplicados y orden)
la direccin (forma de navegar)
la herencia
Manejo de la identidad
Supongamos que Pedido tiene una fecha de pedido.
Creamos la entidad Pedidos:
Fjense qu diferencia tenemos en la tabla Parido vs. la clase Pedido. Adems del comportamiento
(el tercer compartimento de la definicin de la clase), necesitamos una clave que identifique
unvocamente a cada registro de la tabla Pedido.
Al disear la clave para una entidad, siempre, las opciones son:
6 de 37
Diseo de Sistemas Mapeo Objetos Relacional
clave natural: elijo un subconjunto de los campos de la tabla en base al negocio:
CUIT del cliente, nmero de telfono del abonado, etc.
clave subrogada: ninguna de las claves naturales me parece copada, entonces
creo una clave ficticia (al azar, generada automticamente): en nuestro caso, el
que se asigna en base a una secuencia.
Cada una tiene sus ventajas y desventajas. Por ejemplo, las claves naturales muchas
veces simplifican algunas queries y nos llevan a tablas que son ms fciles de entender .
Por otro lado, hay que tener ms cuidados, porque si la clave la ingresa el usuario y se
equivoca es problemtico cambiarlo porque la misma seguramente participa de una o
ms relaciones.
Finalmente, las claves naturales a veces nos llevan a tener claves compuestas (es
decir, el subconjunto que identifica a la fila no es unitario). Esto no parece ser algo
inherentemente malo, pero los frameworks ORM que veremos ms adelante no se
llevan bien con esta idea.
El PEDIDO_ID permite identificar unvocamente una fila de otra. Qu pasa en objetos? Manejamos
el concepto de identidad: cada objeto sabe que es l y ningn otro objeto, entonces no es necesario
trabajar con un identificador. Cuando yo referencio a un objeto, s que le estoy enviando un mensaje
a ese objeto, no necesito identificarlo porque alguien me pas la referencia a l. Si no lo conozco no
le puedo pedir cosas. Hablamos del mismo objeto si objeto1 == objeto2.
Un detalle para comentar: fjense que aparece el sufijo/prefijo PEDIDO al definir columnas de la
tabla: PEDIDO_ID, FECHA_PEDIDO. Esto permite que cuando hagamos un query quede ms
explcita la informacin que estamos recuperando:

SELECT p.FECHA_PEDIDO
FROM PEDIDOS p
WHERE

En cambio, en un objeto Pedido, no repetimos en el atributo el nombre de la clase,
porque est claro dnde estamos: en la clase Cliente tenemos un atributo nombre, y no
nombreCliente.
Consecuencias para el modelo de objetos
Como queremos integrar ambos modelos (el relacional y el de objetos) debemos generar en el
modelo de objetos un atributo id en la clase Pedido. Este nuevo atributo id cumple dos funciones:
1. identificar la fila de la tabla donde se guarde
2. determinar si es un objeto nuevo o un objeto que alguna vez me traje de la base (insert
cuando id es nulo o invlido / update en caso contrario)
7 de 37
Diseo de Sistemas Mapeo Objetos Relacional
Conversiones de tipo
Un dato no menor son los problemas de conversin de tipos que se originan entre el
dominio de objetos y el de la base de datos, donde podemos tener:
un String sin limitaciones de tamao vs. el VARCHAR/CHAR que requiere definirle
longitud
dem para campos numricos respecto a la precisin de decimales, o el rango
de valores mximos y mnimos; incluso cuando el dominio es un nmero de 0 a
3000
las fechas tienen tipos no siempre compatibles entre s: Dates de Java vs. Date-
Datetime-Time
tipos booleanos
tipos enumerados (como los enums de Java)
las variables de clase no se pueden asociar a un registro, porque son globales
para todos los objetos. Una solucin es crear una tabla de Parmetros, donde
ubicamos que 20 es el % de descuento para clientes VIP, o 30 son los das de
demora default para los pedidos al interior, etc.
Relacin de cardinalidad
Uno a muchos: Pedido a tems
Un pedido tiene muchos tems, pero cada tem pertenece a un solo pedido. No quiero reutilizar los
tems en distintos pedidos, porque estn representando instancias diferentes: si ped 2 hormas de
queso camembert un da, y 2 hormas del mismo queso otro da estamos hablando de diferentes
instancias, por lo tanto de diferentes filas en la tabla de pedidos.
Cmo modelamos la relacin entre pedido e tems en el modelo relacional? Por la definicin del
modelo relacional, sabemos que no existen atributos multivaluados, no podemos ubicar en pedido
una coleccin de tems. Entonces, cuando tenemos una relacin uno-a-muchos (one-to-many), se
ubica el identificador del padre (uno) en la tabla hija (many). En este caso, el identificador del Pedido
se incorpora a la tabla de Items. Esto se puede hacer de dos maneras:
Opcin a: el identificador del pedido es un atributo que no forma parte de la clave de la tabla
hija (non-identifying). El identificador del tem es una clave subrogada autoincremental.
Opcin b: el identificador del pedido es un atributo que forma parte de la clave de la tabla
hija (identifying).
Graficamos en un DER ambas soluciones:
Opcin a) PEDIDO_ID no forma parte de la clave
Cada fila de la tabla ITEMS se identifica con una clave subrogada autoincremental.
8 de 37
Diseo de Sistemas Mapeo Objetos Relacional
ITEM_ID (PK) PEDIDO_ID (FK) CANTIDAD
1 1
2 1
3 2
4 2
Opcin b) PEDIDO_ID forma parte de la clave
Entonces las opciones son:
que la clave del tem se componga de PEDIDO_ID + PRODUCTO_ID. Esta opcin es
vlida si el pedido tiene un conjunto de productos, sin repetidos.
PEDIDO_ID (PK/FK) PRODUCTO_ID (PK/FK) CANTIDAD
1 5
1 7
2 6
2 5

o bien tener un identificador del tem que sea autoincremental teniendo en cuenta el
identificador del pedido. Qu pedido soy? El 2, ah, entonces tengo que averiguar
mi ltimo Item_Id + 1 para el Pedido 2. Esta operacin debe ser atmica y no
debe permitir accesos concurrentes. Esta estrategia es ms compleja, pero es una
alternativa cuando un pedido puede tener varios tems con los mismos productos.
PEDIDO_ID (PK/FK) ITEM_ID (PK) CANTIDAD
1 1
1 2
2 1
2 2

9 de 37
Diseo de Sistemas Mapeo Objetos Relacional
Quienes modelan con objetos prefieren trabajar con claves autoincrementales, delegando la
responsabilidad de manejar los ids a la base de datos. Esto es cmodo porque como hemos
visto en objetos
no necesitamos resolver esa responsabilidad (los objetos definen su propia estrategia
para manejar la identidad)
si tenemos varios ambientes o VM sincronizados contra la base necesitamos
asegurarnos que dos objetos de distintos ambientes no tengan el mismo ID.
Quienes piensen el diseo a partir del modelo de datos probablemente prefieran pensar en
que el tem se identifica mediante una clave compuesta, ya que no tiene sentido un tem sin
su pedido.
Set, Bag, List
En objetos, para definir una coleccin necesitamos preguntarnos
si nos importa respetar un orden para los elementos
si la coleccin admite duplicados
cmo es la estrategia para acceder a los elementos
y en base a estas preguntas, sabemos qu tipo de coleccin utilizaremos.
En el caso del pedido con sus tems, qu tipo de coleccin estuvimos modelando? un set, que no
maneja orden, por el concepto mismo de relacin. Cmo disearamos una fila de supermercado,
donde el orden es importante? Necesitamos incorporar un campo adicional:
Ej: si tenemos
fila 1: [cliente 155]
fila 2: [cliente 201, cliente 172, cliente 127]
fila 3: [cliente 144]
Filas
FILA_ID (PK) ... ...
1


2


3


Filas_Clientes
FILA_ID (PK, FK) CLIENTE_ID (PK, FK) ORDEN
1 155 0
10 de 37
Diseo de Sistemas Mapeo Objetos Relacional
2 172 1
2 201 0
2 127 2
3 144 0
.
El orden no participa de la clave, porque en este caso no se pueden repetir los mismos clientes en la
fila.
En el caso del Bag s tenemos que incorporar al orden (o un campo de caractersticas similares) para
evitar que se produzca una clave duplicada al repetir identificadores de fila y cliente.
Muchos a uno: Item a Producto
Analizamos la relacin:
cada tem tiene un producto
pero el producto puede estar en varios tems. Como sealamos en el ejemplo de las hormas
de queso por cada venta de un producto vamos a tener la referencia al mismo producto
mltiples veces en la tabla Items.
Entonces aqu la relacin es muchos-a-uno (many-to-one) y se resuelve de la misma manera que
en la relacin one-to-many: en la tabla hija se agrega el identificador del padre. Es decir, en la tabla
Items (muchos) se incorpora el PRODUCTO_ID que referencia a una fila en la tabla Productos (uno)
Items
ITEM_ID (PK) PEDIDO_ID (FK) PRODUCTO_ID (FK) CANTIDAD
1 1 5
2 1 7
3 2 6
4 2 5

Productos
PRODUCTO_ID (PK) DESCRIPCION ...
5 Queso camembert
6 Queso cuartirolo
11 de 37
Diseo de Sistemas Mapeo Objetos Relacional
7 Panceta ahumada
8 Longaniza nepalesa

one-to-many vs. many-to-one es importante distinguirlo en objetos porque son dos relaciones
diferentes:
many-to-one: un objeto A tiene un objeto B del otro lado (el many se "pierde" si no agrego la
relacin en el objeto B)
one-to-many: un objeto A tiene una coleccin de objetos B
Muchos a muchos: proveedores y productos
Ejemplo 1: Un proveedor vende muchos productos y un producto es vendido por muchos
proveedores. Cmo lo modelo en objetos? Son colecciones desde los objetos races
(proveedor y producto respectivamente).
Diagrama de clases

El modelo lgico del Diagrama Entidad-Relacin se parece bastante al modelo de objetos:

Modelo Lgico

Pero al implementar el modelo fsico s o s necesito manejarlo con una Tabla de Relacin:
Ejemplo 2: Modelar el stock de una empresa que tiene varios depsitos. Un producto tiene distintas
cantidades en stock en cada depsito. O sea: un producto est en varios depsitos y cada depsito
12 de 37
Diseo de Sistemas Mapeo Objetos Relacional
tiene n productos. La diferencia es que aqu adems de tener una relacin muchos a muchos, hay
atributos que salen de esa relacin: la cantidad en stock, por ejemplo. Entonces, en objetos ya no
me alcanza con tener dos colecciones desde cada objeto original, necesito s o s un objeto que
represente la relacin entre ambas entidades:

Esta solucin permite conocer el stock de un producto a partir de un depsito. Pero tambin
podra definir la navegacin de manera que un producto pueda conocer su stock:
Agregamos la posibilidad de que el Stock tenga una referencia al depsito: en Objetos puedo
aumentar la navegabilidad (lo que conoce un objeto), pero tengo que
1. agregar ms referencias al mismo objeto y
2. acordarme de dejar consistente el modelo (cuando genero un Stock tengo que avisarle al
Depsito y al Producto para que tengan actualizada la coleccin de stocks de cada uno).
En relacional, tengo una vez ms una tabla de Relacin:
En el modelo relacional puedo determinar el stock de los productos para un depsito o bien puedo
saber cunta cantidad hay de un producto en cada uno de los depsitos sin necesidad de modificar
ninguna relacin. Este modelo es muy flexible, permite cualquier tipo de navegacin, mientras que
en el modelo de objetos tengo que entender los casos de uso para definir si me conviene que una
asociacin sea unidireccional o bidireccional.
Subtipos en el modelo relacional
13 de 37
Diseo de Sistemas Mapeo Objetos Relacional
Ahora definiremos en detalle la entidad Producto. Armemos primero el modelo lgico equivalente a la
clase Producto con sus subclases:
Entre el modelo lgico relacional y el modelo de objetos parece no haber mucha diferencia. Ahora
bien, al implementar fsicamente este modelo tenemos 3 opciones:
Implementar una tabla por cada clase concreta: TABLE_PER_CLASS
El primer modelo que veremos consiste en tener una tabla por cada clase concreta, modelo que
se conoce como, por ejemplo, Table Per Class (JPA), Table Per Concrete Class (Hibernate) o
Polymorphic Association (ActiveRecord).
14 de 37
Diseo de Sistemas Mapeo Objetos Relacional
No existe una tabla Producto, sino que
cada subtipo de producto se implementa como una tabla separada
El tem tiene 3 claves forneas que admiten null (porque si comprado_id tiene un valor esto
significa que fabricado_id y conservado_id quedan con valores nulos)
En Comprado, Fabricado y Conservado se repiten todos los atributos definidos para
producto: nombre, valor y peso
Imaginemos que tenemos estos pedidos:
Pedido 1, con fecha 12/07/2013 y dos tems
2 unidades de Queso Camembert (producto fabricado)
1 unidad de Queso Gruyre (producto conservado)
Pedido 2, con fecha 18/08/2013 y 2 tem
7 unidades de Jamn Cocido (producto comprado)
3 unidades de Queso Camembert (producto fabricado)
Veamos cmo se implementa el modelo en base a la definicin de las tablas:
Pedidos
PEDIDO_ID (PK) FECHA_PEDIDO CLIENTE_ID (FK)
1 12/07/2013 ...
2 18/08/2013 ...
Item
ITEM_ID PEDIDO_ID CANTIDAD FABRIC_ID (FK) CONSERV_ID (FK) COMPRADO_ID (FK)
15 de 37
Diseo de Sistemas Mapeo Objetos Relacional
(PK) (FK)
1 1 2 52 null null
2 1 1 null 22 null
3 2 7 null null 31
4 2 3 52 null null
Conservados
ID (PK) DIAS PRECIO NOMBRE VALOR PESO
22 21 57,00, Queso Gruyre 16,25 1,87
Fabricados
ID (PK) CANTIDAD_HORAS_HOMBRE NOMBRE VALOR PESO
52 15 Queso Camembert 25,67 0,45
Comprados
ID (PK) PRECIO NOMBRE VALOR PESO
31 75,48 Jamn Cocido 27, 98 0,912
Implementar una tabla por cada clase: JOINED
Este modelo consiste en tener una tabla por cada clase, ya sea concreta o abstracta, modelo que se
conoce como, por ejemplo, Joined (JPA) o Table Per Subclass (Hibernate)
16 de 37
Diseo de Sistemas Mapeo Objetos Relacional
adems de la tabla Producto, creamos una tabla por cada subclase
pareciera ser la mejor adaptacin del modelo de objetos al relacional
no se duplican atributos en las tablas
se unifica la foreign key de tem hacia la tabla producto: el ID_PRODUCTO es al mismo
tiempo PK y FK.
como desventaja, para conocer la informacin de un producto comprado, fabricado o
conservado tengo un grado ms de indireccin (necesito un JOIN adicional contra esa tabla).
La excepcin a esto es que se consulte slamente por informacin slamente en la tabla hija
o en la tabla padre.
insertar un producto tambin requiere dos inserts.
el tipo de producto acta como discriminante, que debe modelarse
Pedidos
PEDIDO_ID (PK) FECHA_PEDIDO CLIENTE_ID (FK)
1 12/07/2013 ...
2 18/08/2013 ...
17 de 37
Diseo de Sistemas Mapeo Objetos Relacional
Item
ITEM_ID (PK) PEDIDO_ID (FK) CANTIDAD PRODUCTO_ID (FK)
1 1 2 52
2 1 1 22
3 2 7 31
4 2 3 52
...
Productos
ID (PK) TIPO_PROD NOMBRE VALOR PESO
22 CONS Queso Gruyre 16,25 1,87
31 COMP Queso Camembert 25,67 0,45
52 FABR Jamn Cocido 27, 98 0,912
...
Conservados
ID (PK) DIAS PRECIO
22 21 57,00
...
Fabricados
ID (PK) CANTIDAD_HORAS_HOMBRE
52 15
...
Comprados
ID (PK) PRECIO
18 de 37
Diseo de Sistemas Mapeo Objetos Relacional
31 75,48
...
Implementar una nica tabla (SINGLE_TABLE)
La idea de esta estrategia es persistir la jerarqua completa en una sola tabla. Este esquema se
conoce como Single Table (JPA) o Single Table Inheritance (Active Record)
Pedidos
PEDIDO_ID (PK) FECHA_PEDIDO CLIENTE_ID (FK)
1 12/07/2013 ...
2 18/08/2013 ...
Item
ITEM_ID (PK) PEDIDO_ID (FK) CANTIDAD PRODUCTO_ID (FK)
1 1 2 52
2 1 1 22
3 2 7 31
4 2 3 52
Productos
ID
(PK)
TIPO_
PROD
NOMBRE VALOR PESO DIAS PRECIO CANT_HORAS
_HOMBRE
22 CONS Q.Gruyre 16,25 1,87 21 57,00, null
19 de 37
Diseo de Sistemas Mapeo Objetos Relacional
31 COMP Q.Camembert 25,67 0,45 null 75,48 null
52 FABR Jamn C. 27, 98 0,912 null null 15
20 de 37
Diseo de Sistemas Mapeo Objetos Relacional
Anlisis Comparativo
Relaciones many
Queremos conocer los productos que vende un proveedor determinado que pesen ms
de 1 kg.

Una tabla
Es muy fcil lograr este requerimiento

SELECT *
FROM PRODUCTOS prd
JOIN PROVEEDORES prv
ON prd.PROVEEDOR_ID = prv.ID
WHERE prd.PESO > 1
AND prv.ID = :ID_PROVEEDOR_A_BUSCAR

Una tabla por clase concreta
Es necesario hacer la consulta de las tres entidades:

SELECT *
FROM CONSERVADOS con
JOIN PROVEEDORES prv
ON con.PROVEEDOR_ID = prv.ID
WHERE con.PESO > 1
AND prv.ID = :ID_PROVEEDOR_A_BUSCAR

SELECT *
FROM COMPRADOS com
JOIN PROVEEDORES prv
ON com.PROVEEDOR_ID = prv.ID
WHERE com.PESO > 1
AND prv.ID = :ID_PROVEEDOR_A_BUSCAR
21 de 37
Diseo de Sistemas Mapeo Objetos Relacional
SELECT *
FROM FABRICADOS fab
JOIN PROVEEDORES prv
ON fab.PROVEEDOR_ID = prv.ID
WHERE fab.PESO > 1
AND prv.ID = :ID_PROVEEDOR_A_BUSCAR
Aunque requiere de una consulta especfica por cada subclase (lo cual implica un costo adicional
de performance que hay que estar dispuesto a pagar), de esta manera podemos reconstruir una
coleccin de productos polimrficos en el proveedor.
Una tabla por clase
Y si tenemos (n + 1) tablas (1 por la superclase y las n subclases):
SELECT *
FROM PRODUCTOS prd
JOIN PROVEEDORES prv
ON prd.PROVEEDOR_ID = prv.ID
LEFT JOIN CONSERVADOS CON
ON CON.PRODUCTO_ID = P.ID
LEFT JOIN COMPRADOS COM
ON COM.PRODUCTO_ID = P.ID
LEFT JOIN FABRICADOS FAB
ON FAB.PRODUCTO_ID = P.ID
WHERE prd.PESO > 1
AND prv.ID = :ID_PROVEEDOR_A_BUSCAR
Si bien podemos resolverlo mediante un solo query, se requiere trabajar con LEFT JOIN contra todas
las tablas que representan las subclases. Esta indireccin supone tambin un costo adicional de
performance.
Relaciones one
Supongamos ahora que queremos incorporar al modelo cul es el producto estrella de
cada proveedor. Esto implica tener una relacin adicional many-to-one entre proveedor y
producto. Veamos cmo se resuelve en cada caso:
una tabla sola: es sencillo, el proveedor agrega una FK a la entidad
PRODUCTOS, utilizamos un campo PRODUCTO_ESTRELLA_ID
una tabla por cada clase: lo mismo, se incorpora un
PRODUCTO_ESTRELLA_ID que referencia a la entidad PRODUCTOS (que
mapea la clase abstracta)
22 de 37
Diseo de Sistemas Mapeo Objetos Relacional
una tabla por clase concreta: aqu tenemos un problema, porque no podemos
utilizar una clave fornea, a lo sumo las opciones son:
PROVEEDORES
ID (PK)
...
PRODUCTO_ESTRELLA_DISCRIMINATOR : Varchar(4)
PRODUCTO_ESTRELLA_ID : Numeric
(tener un discriminator que nos indique en qu tabla debemos buscar el ID)
o bien tener 3 claves forneas que acepten nulos:
PROVEEDORES
ID (PK)
...
CONSERVADO_ESTRELLA_ID (FK nullable) : Numeric
COMPRADO_ESTRELLA_ID (FK nullable): Numeric
FABRICADO_ESTRELLA_ID (FK nullable): Numeric
Resumen
Concepto A favor En contra
1 tabla - Ms simple
- Buena performance general
- Soporta todo tipo de
relaciones polimrficas
- Evito generar muchas tablas
- Campos no utilizados que
deben aceptar valores nulos
- Tentacin de reutilizar
atributos para cosas distintas
1 tabla por clase
concreta (n)
- Permite establecer campos
no nulos para cada subclase
- No requiere tener un campo
discriminador (Ej:
TIPO_PEDIDO para el
PEDIDO)
- Para trabajar con colecciones
de objetos polimrficos
tengo que hacer consultas
diferentes, no soporta relaciones
polimrficas many-to-one ni one-
to-one
- Cada subclase repite atributos
heredados de la superclase
23 de 37
Diseo de Sistemas Mapeo Objetos Relacional
1 tabla por cada
clase (n + 1)
- Es el modelo ideal segn
las reglas de normalizacin
- Permite establecer campos
no nulos para cada subclase
- Evita la redundancia de
definiciones en la estructura de
las tablas
- Soporta todo tipo de
relaciones polimrficas
- Es la opcin que ms entidades
requiere crear
- y la que ms cantidad de
accesos a la base requiere
(mayor costo de performance)

Armamos una tabla de cundo conviene cada opcin:
Concepto Cuando conviene
1 tabla Cuando las subclases comparten muchos atributos en comn
Cuando necesito simplificar los queries
tabla por clase
concreta (n)
Es la tcnica utilizada para las entidades independientes (las clases que
heredan de Object en la jerarqua: en el ejemplo de la pgina 1 seran Pedido,
Item y Producto)
Cuando las subclases comparten muy pocos atributos entre s
1 tabla por cada
clase (n + 1)
Cuando hay muchos atributos comunes entre las subclases pero tambin
muchos atributos propios en cada subclase. Si no hay una cantidad significativa
de registros no afectar la performance.
Seleccin del grafo de objetos
En el grafo de objetos navegamos a partir de un objeto raz considerando que todos los objetos estn
en memoria. Tengo un cliente, le pido el total y el cliente tiene acceso a sus pedidos. Para conocer
el monto total, cada pedido tiene acceso a sus tems, y cada tem conoce su cantidad y tiene una
referencia al producto.

Si el Pedido tiene tems y cada Item tiene Producto, por un lado es cmodo traer toda la estructura
del objeto Pedido para poder navegarlo libremente. Por otro lado, quizs arme toda una estructura
slo para averiguar la fecha de un pedido.
Grficamente:
1. Obtengo slo el pedido.

Tabla: PEDIDOS
ID_PEDIDO (PK) FECHA_PEDIDO CLIENTE_ID (FK) MONTO
1 10/10/2007 122 1562,23
2 11/10/2007 336 769,83

24 de 37
Diseo de Sistemas Mapeo Objetos Relacional
Queremos conocer todos los pedidos de un cliente:
SELECT *
FROM PEDIDOS
WHERE CLIENTE_ID = 122
Esto se transforma en un objeto Pedido:
Quin referencia al Pedido? El cliente, que tiene la coleccin de pedidos asociada (por motivos
didcticos no lo dibujamos).

2. Obtengo el pedido y todas las relaciones asociadas:

Tabla: PEDIDOS
ID_PEDIDO FECHA_PEDIDO CLIENTE_ID MONTO
1 10/10/2007 122 1562,23
2 11/10/2007 336 769,83

Tabla: ITEMS (que podramos llamar PEDIDO_PRODUCTO)
ID_PEDIDO_PRODUCTO ID_PEDIDO PRODUCTO_ID CANTIDAD
1 1 24 5
2 1 57 3

Tabla: PRODUCTOS
ID_ PRODUCTO DESCRIPCION PRECIO_UNITARIO
24 ARANDELA 39 ALUM

20,7433
57 RETEN TENSOR FI

300
Ahora tenemos que recuperar los pedidos del cliente:

25 de 37
Diseo de Sistemas Mapeo Objetos Relacional
SELECT *
FROM PEDIDOS
WHERE CLIENTE_ID = 122

Pero tambin tenemos que generar los tems y los productos. Por cada ID_PEDIDO obtenido en
la consulta anterior hacemos:

SELECT *
FROM ITEMS i
INNER JOIN PRODUCTOS p
ON p.ID_PRODUCTOS = i.ID_PRODUCTOS
WHERE i.ID_PEDIDO = ?
Con esto generamos:
teniendo en cuenta que elegimos tener una sola tabla para mapear al Producto y todas
las subclases relacionadas.

Qu me conviene? (1) (2)

Si tengo que disear una aplicacin donde tengo una pantalla de bsqueda de Pedidos:

26 de 37
Diseo de Sistemas Mapeo Objetos Relacional

vamos a querer llenar la lista de pedidos accediendo solamente a la tabla PEDIDOS (y al
cliente, en la relacin many-to-one). Si el usuario seleccion fecha desde y fecha hasta,
el query sera algo similar a:

SELECT *
FROM PEDIDOS p,
JOIN CLIENTES c
ON p.CLIENTE_ID = c.ID
WHERE FECHA_PEDIDO BETWEEN :FechaDesde AND :FechaHasta

Necesito algo ms para mostrar la lista de pedidos? No, en este caso no necesito traer los tems
ni los productos. En general en frameworks que hacen mapeo O/R yo tengo la posibilidad de
definir hasta dnde voy al repositorio y genero el grafo de objetos: ese hasta dnde vale tanto en
relaciones muchos a uno (del tem al producto), como en relaciones uno a muchos (del pedido a los
tems).
En cambio cuando el usuario haga doble click en la lista de pedidos y quiera visualizar la
informacin de ese pedido:

27 de 37
Diseo de Sistemas Mapeo Objetos Relacional
s queremos traer la informacin del pedido y todas las entidades relacionadas.
Lazy association
Definimos la coleccin de tems del pedido como una asociacin lazy, donde voy a traer la
informacin slo cuando lo necesite. Entonces:
si el atributo monto no estuviera en pedido, para calcular el total de un pedido y
mostrarlo en la grilla original necesitaramos acceder la informacin de los tems y
los productos
si por el contrario cada lnea de la grilla slo necesita acceder a los datos de las
entidades pedido y cliente, el query slo debera transformar el registro de la tabla
Pedido al objeto correspondiente
esto se implementa con un tipo especial de coleccin que slo se inicializa en el
momento en que enviamos un mensaje a la coleccin:
>>Pedido
def montoTotal
items.fold (0, [ acum, item | acum + item.total])
Ciclo de vida de los objetos
Por lo que entendemos del negocio, el pedido y los tems comparten el mismo ciclo de vida: no
tiene sentido un tem sin su correspondiente pedido, y tampoco tiene sentido un pedido sin tems.
Entonces cuando generamos un pedido deberamos generar automticamente los tems asociados.
Y al actualizar un dato del pedido, al actualizar informacin de un tem, o al agregar o eliminar tems
28 de 37
Diseo de Sistemas Mapeo Objetos Relacional
de un pedido se debera disparar la actualizacin en cascada del pedido con todos sus tems
relacionados. Por ltimo, si eliminamos un pedido deberamos eliminar sus tems asociados.
Por el contrario, los productos tienen un ciclo de vida diferente al de los pedidos:
no podemos generar un pedido sin haber cargado al menos un producto
y los productos se actualizan en forma independiente al pedido.
En caso de dudas, debemos disparar las preguntas al usuario que conoce el negocio. Tambin nos
puede ayudar partir de un anlisis donde
Carga de Pedidos
Actualizacin de Productos
son dos casos de uso diferentes y estn marcando dos ciclos de vida distintos para pedidos/tems
vs. productos.
Implementaciones
Si consideramos que almacenamos nuestra informacin en un motor de base de datos relacional
pero construimos el resto de nuestra aplicacin en objetos, las alternativas posibles son:
Home + Mapeo manual contra un driver
Diseamos un objeto Repository/Home, por ejemplo de Clientes, con la siguiente interfaz:
insert(Cliente)
update(Cliente)
delete(Cliente)
getClientes(ClienteBusqueda) o bien getClientes(String nombre)
getCliente(int id)
El Repository, Home o DAO tiene que conversar con el motor, para ello utiliza un
driver que ofrece una API o interfaz de alto nivel y que define algunas abstracciones
importantes:
Driver Manager: es el que carga el driver de JDBC de la base de datos elegida y
devuelve la conexin a dicha base.
Connection: encapsula la conexin a la base de datos. Permite crear sentencias
y manejar transacciones.
Statement: encapsula las sentencias SELECT, INSERT, UPDATE y DELETE
a la base de datos. Tambin permite trabajar con sentencias pre-compiladas
(PreparedStatement).
ResultSet: representa una coleccin de datos devuelta por una sentencia
SELECT a la base
29 de 37
Diseo de Sistemas Mapeo Objetos Relacional
Ejemplo de bsqueda
Si queremos recuperar los clientes que comiencen con Q, enviaremos el mensaje
repoClientes.getClientes(new ClienteBusqueda(nombreComienzaCon: Q))
o bien
repoClientes.getClientes(Q)
slo para simplificar este ejemplo didctico.
>>RepoClientes
override getClientes(String nombre) {
val sql = "
select cli.cliente_id,
cli.nombre,
cli.domicilio,
cli.tipo_documento_id,
tdoc.descripcion as tipo_documento_descripcion,
cli.numero_documento
from clientes cli,
tipos_documento tdoc
where cli.nombre like '" + nombre + "%'
and cli.tipo_documento_id = tdoc.tipo_documento_id;"
val ResultSet rs = this.ejecutarQuery(sql)
var clientesResult = new ArrayList<Cliente>

while (rs.next()) {
var cliente = new Cliente
cliente.id = rs.getInt("cliente_id")
30 de 37
Diseo de Sistemas Mapeo Objetos Relacional
cliente.nombre = rs.getString("nombre")
... asocio dems atributos y creo tipo documento ...
clientesResult.add(cliente)

}
rs.close()
stmt.close()
conn.close()
clientesResult
}
def ejecutarQuery(String sql) {
conn = DriverManager.getConnection(DB_URL, USER, PASS)
stmt = conn.createStatement()
rs = stmt.executeQuery(sql)
}
Desventajas:
es un trabajo tedioso y repetitivo
debemos definir el nivel de profundidad de lo que vamos a traer para cada uno de los casos
de bsqueda
al generar el SQL como un string que intercala datos y sentencia es difcil de mantener y
dificulta la reutilizacin. Ej: buscar clientes activos, vs. clientes que no estn inactivos, o
bien alumnos que aprobaron 2 materias vs. los que aprobaron Diseo de Sistemas cuesta
llevarlo a un nico lugar donde se construya el query.
Ejemplo de Actualizacin
Cada vez que se quiera persistir un objeto debemos armar un query de INSERT de cada uno
de los campos, o de UPDATE de todos los campos involucrados.
// Actualizo slo el nombre del cliente
Statement stmt = con.createStatement()
stmt.executeUpdate("UPDATE CLIENTES SET NOMBRE = " +
cliente.nombre + " WHERE ID = " + cliente.id)
Aclaracin: los ejemplos estn definidos lo ms sencillo posible. Para realizar una aplicacin
comercial es recomendable
configurar el driver y el origen de datos (usuario y contrasea) en un archivo externo a la
aplicacin
manejar un conjunto de conexiones a la base de datos o pool de conexiones al levantar la
aplicacin, para evitar tiempos de conexin y desconexin al motor. Entonces cada vez que
pidamos una conexin va a ser la primera conexin disponible de ese pool:
31 de 37
Diseo de Sistemas Mapeo Objetos Relacional
Frameworks ORM (Object/Relational Mappers)
Otra opcin es utilizar un framework que haga el mismo trabajo, ac les dejamos una
lista:
http://en.wikipedia.org/wiki/List_of_object-relational_mapping_software
Algunas caractersticas:
los frameworks permiten subir el grado de declaratividad
se elimina la parte algortmica de la conversin
se define un lenguaje de alto nivel para efectuar consultas
el repository deja de tener la responsabilidad de mapeo, esto suele delegarse
en las primeras versiones, a archivos de configuracin (en formato xml o similar)
en versiones ms modernas y menos burocrticas, a travs de annotations a objetos
de dominio
o incluso definiendo un DSL (domain specific language)
Ej: Hibernate que soporta las tres opciones, el DSL lo define GORM de Grails.
cul es la responsabilidad del repositorio entonces?
demarcar el inicio y el final de la transaccin,
manejar la sesin o un concepto equivalente de conexin a la base, a quien delegar la
actualizacin de los objetos de dominio (save/update/delete)

una ventaja que ofrecen los frameworks es que permiten manejar las
referencias lazy realizando consultas mientras la sesin est activa. Ej: si
estoy buscando los clientes que comiencen con Q, pero despus quiero
obtener el total que sale de conocer el saldo de todas las facturas pendientes,
al hacer
facturas.fold (0, [acum, factura | acum + factura.saldo ])
en ese momento se dispara la consulta a las facturas de un cliente y as
sucesivamente.
construir los queries de consulta en el lenguaje que cada framework ofrece
32 de 37
Diseo de Sistemas Mapeo Objetos Relacional
manejar los errores que ocurran en los llamados hacia el motor de base de datos (en
la mayora de los casos no habr mucho para hacer ms que generar una excepcin
de ms alto nivel para avisar al usuario de que la consulta fall)
Mostramos un ejemplo en Xtend, utilizando Annotations para mapear una relacin
muchos a muchos entre Profesor y Materia (un profesor puede dar muchas materias y
una materia tiene muchos profesores):
@Entity
class Profesor implements Serializable {
@Id
@GeneratedValue
@Property private Long id

@Column
@Property private String nombreCompleto

@ManyToMany(fetch = FetchType.LAZY)
@Property private Set<Materia> materias

// Lo necesita Hibernate
new() {
}
...
@Entity
class Materia {
@Id
@GeneratedValue
@Property private Long id

@Column
@Property private String nombre

@Column
@Property private int anio

// Lo necesita Hibernate
new() {

}
...
Esto define tres entidades:
33 de 37
Diseo de Sistemas Mapeo Objetos Relacional
Profesor
Materia
Profesor_Materia con referencias a las claves primarias de profesor y materia
Ejemplo: si queremos reflejar que el profesor Lucas Spigariol da Paradigmas y
Algoritmos y Estructura de Datos...
Materia
ID NOMBRE ANIO
1 Paradigmas de Programacin 2
2 Algoritmos y Estructura de Datos 1
Profesor
ID NOMBRE_COMPLETO
1 Lucas Spigariol
2 Leonardo Gassman
Materia_Profesor
PROFESOR_ID MATERIA_ID
1 1
1 2
Vemos la consulta que resuelve qu profesores dan una materia determinada:
def getProfesores(Materia materia) {
var List<Profesor> result = null
val session = sessionFactory.openSession
try {
result = session
.createCriteria(typeof(Profesor))
.createAlias("_materias", "materias")
.add(Restrictions.eq("materias.id", materia.id))
.list
} catch (HibernateException e) {
throw new RuntimeException(e)
} finally {
session.close
}
34 de 37
Diseo de Sistemas Mapeo Objetos Relacional
result
}
Para ms informacin recomendamos leer http://www.hibernate.org/
Data Mapper vs. Active Record
Data Mapper: en nuestros ejemplos hemos utilizado la estrategia de tener
un repositorio, un objeto que sirve como una entidad mapeadora del modelo
relacional y el de objetos.
Active Record: otro approach que se populariz a partir de frameworks como
Ruby on Rails, Grails, etc. es decorar a los objetos de dominio agregndoles las
responsabilidades de save(), update(), delete() y mtodos de clase que resuelvan
las bsquedas: findBy..., findAll, etc.
Vemos un ejemplo en Grails, que resuelve la consulta de libros por ttulo y autor, donde
le pedimos a la clase pelcula que cree un criterio de bsqueda al cual le enviamos el
mensaje list con un bloque que tiene las restricciones que aplican sobre ese query:
>>RepoPeliculas
@Transactional(readOnly=true)
def getPeliculas(titulo) {
Pelicula.createCriteria().list(max: 5) {
ilike("titulo" , "%" + titulo + "%")
}
}
35 de 37
Diseo de Sistemas Mapeo Objetos Relacional
Una pelcula tiene un gnero (una relacin many to one):
class Pelicula {
String titulo
Genero genero
String sinopsis
String actores

static constraints = {
titulo maxSize: 255
actores maxSize: 255
}

static mapping = {
sinopsis type: 'text'
}
O/R o R/O?
Una vez adoptada la decisin de trabajar con bases relacionales, la pregunta es qu hacer
primero:
1. Generamos el modelo relacional y luego adaptamos el modelo de objetos en base
a las tablas generadas
2. Generamos el modelo de objetos y en base a ste se crean las tablas.
La opcin 1) supone que es ms importante la forma en que guardo los datos que las
reglas de negocio que modifican esos datos. Pero a veces no nos queda otra chance, si
partimos de un sistema construido o enlatado. En la opcin 2) el desarrollo con objetos no
se ve ensuciado por restricciones propias de otra tecnologa.
Conclusiones
Persistir nuestro modelo de objetos en un esquema relacional requiere conocer cmo
funcionan ambos mundos y entender qu decisiones de diseo estn tras ese mapeo:
manejo de la identidad
las relaciones entre entidades
cardinalidad: uno a uno, uno a muchos, muchos a muchos
direccin: cmo navego el grafo de objetos y cunta informacin traigo en
cada pedido
relacionar los dominios de la base vs. los tipos que definen los objetos
cmo manejar las colecciones (orden y duplicados)
qu estrategia adoptar para los casos de generalizacin (herencia)
36 de 37
Diseo de Sistemas Mapeo Objetos Relacional
Pero adems, debemos definir la arquitectura que va a dar soporte a este mapeo,
adaptando la diferencia o impedance mismatch a mano o con frameworks de terceros. Y
por ltimo, debemos definir quin resuelve la conexin a la base, quin define el manejo
de la transaccin (cuando tenemos efecto colateral), quin dialoga con el framework o
con una API de bajo nivel contra la base, etc. Las opciones ms comunes son: separarlo
de los objetos de dominio y llevarlo a un objeto aparte (Repository, Home o DAO, Data
Mapper) o bien incluir dicha responsabilidad dentro del objeto de dominio decorado
(Active Record).
37 de 37

You might also like