Logo Studenta

Recuperatorio 2 PP2

¡Estudia con miles de materiales!

Vista previa del material en texto

Concrete Table Inheritance
Representa una jerarquía de herencia de clases con una tabla por clase concreta en
la jerarquía. Como cualquier purista de objetos te dirá, las bases de datos relacionales no
admiten la herencia, un hecho que complica el mapeo objeto-relacional. Si pensamos en las
tablas desde el punto de vista de una instancia de objeto, una ruta sensata es tomar cada
objeto en memoria y asignarlo a una sola fila de la base de datos. Esto implica la Herencia
de Tabla Concreta, donde hay una tabla para cada clase concreta en la jerarquía de
herencia.
Debo confesar que tuve dificultades para nombrar este patrón. La mayoría de las
personas lo consideran orientado a las hojas ya que generalmente hay una tabla por clase
hoja en una jerarquía. Siguiendo esa lógica, podría llamar a este patrón herencia de tabla
hoja, y el término "hoja" se usa a menudo para este patrón. Sin embargo, estrictamente
hablando, una clase concreta que no es una hoja también recibe una tabla, por lo que decidí
optar por el término más correcto, aunque menos intuitivo.
Cómo funciona
La Herencia de Tabla Concreta utiliza una tabla de base de datos para cada clase
concreta en la jerarquía. Cada tabla contiene columnas para la clase concreta y todos sus
ancestros, por lo que los campos de una superclase se duplican en las tablas de las
subclases. Como en todos estos esquemas de herencia, el comportamiento básico utiliza
Mapeadores de Herencia.
Debes prestar atención a las claves con este patrón. En forma ingeniosa, lo más
importante es asegurarse de que las claves sean únicas no solo para una tabla, sino para
todas las tablas de una jerarquía. Un ejemplo clásico de dónde se necesita esto es si tienes
una colección de jugadores y estás usando un campo de identidad con claves únicas para
toda la tabla (Identity Field). Si las claves pueden duplicarse entre las tablas que mapean
las clases concretas, obtendrás múltiples filas para un valor de clave particular. Por lo tanto,
necesitas un sistema de asignación de claves que lleve un registro del uso de claves en
todas las tablas; además, no puedes confiar en el mecanismo de unicidad de claves
primarias de la base de datos.
Esto se vuelve particularmente incómodo si te conectas a bases de datos utilizadas
por otros sistemas. En muchos de estos casos, no puedes garantizar la unicidad de las
claves en todas las tablas. En esta situación, puedes evitar el uso de campos de superclase
o usar una clave compuesta que involucre un identificador de tabla.
Puedes evitar algunos de estos problemas al no tener campos que tengan el tipo de
la superclase, pero obviamente esto compromete el modelo de objetos. Como alternativa,
puedes tener accesores para el supertipo en la interfaz, pero usar varios campos privados
para cada tipo concreto en la implementación. La interfaz luego combina valores de los
campos privados. Si la interfaz pública es un solo valor, selecciona cualquiera de los valores
privados que no sean nulos. Si la interfaz pública es un valor de colección, responde con la
unión de valores de los campos de implementación.
Para claves compuestas, puedes usar un objeto de clave especial como tu campo
de identificación (Identity Field). Esta clave utiliza tanto la clave primaria de la tabla como el
nombre de la tabla para determinar la unicidad.
Relacionado con esto, hay problemas con la integridad referencial en la base de
datos. Considera un modelo de objetos como se muestra en la Figura 12.10. Para
implementar la integridad referencial, necesitas una tabla de enlace que contenga columnas
de clave externa para la función de caridad y para el jugador. El problema es que no hay
una tabla para el jugador, por lo que no puedes crear una restricción de integridad
referencial para el campo de clave externa que acepte tanto futbolistas como jugadores de
cricket. Tu opción es ignorar la integridad referencial o usar múltiples tablas de enlace, una
para cada una de las tablas reales en la base de datos. Además, tienes problemas si no
puedes garantizar la unicidad de las claves.
Si estás buscando jugadores con una declaración SELECT, debes buscar en todas
las tablas para ver cuáles contienen el valor adecuado. Esto implica usar múltiples consultas
o usar una unión externa, ambas son malas para el rendimiento. No sufres el impacto en el
rendimiento cuando conoces la clase que necesitas, pero debes usar la clase concreta para
mejorar el rendimiento. Este patrón a menudo se denomina herencia de tabla hoja.
Algunas personas prefieren una variante en la que hay una tabla por clase hoja en
lugar de una tabla por clase concreta. Si no tienes ninguna superclase concreta en la
jerarquía, esto resulta en lo mismo. Incluso si tienes superclases concretas, la diferencia es
bastante pequeña.
Cuándo usarlo
Al determinar cómo mapear la herencia, las alternativas son Herencia de Tabla
Concreta, Herencia de Tabla de Clase (Class Table Inheritance) y Herencia de Tabla Única
(Single Table Inheritance). Las fortalezas de la Herencia de Tabla Concreta son:
- Cada tabla es autónoma y no tiene campos irrelevantes. Como resultado, tiene mucho
sentido cuando se utiliza en otras aplicaciones que no utilizan los objetos.
- No hay uniones que hacer al leer los datos de los mapeadores concretos.
- Cada tabla se accede solo cuando se accede a esa clase, lo que puede distribuir la carga
de acceso.
Las debilidades de la Herencia de Tabla Concreta son:
- Las claves primarias pueden ser difíciles de manejar.
- No puedes aplicar relaciones de base de datos a clases abstractas.
- Si los campos de las clases del dominio se mueven hacia arriba o hacia abajo en la
jerarquía, debes modificar las definiciones de las tablas. No tienes que hacer tantas
modificaciones como con la Herencia de Tabla de Clase, pero no puedes ignorar esto como
lo haces con la Herencia de Tabla Única.
- Si cambia un campo de la superclase, debes cambiar cada tabla que tenga este campo
porque los campos de la superclase se duplican en las tablas.
- Una búsqueda en la superclase te obliga a revisar todas las tablas, lo que conduce a
múltiples accesos a la base de datos (o una unión extraña).
Recuerda que el trío de patrones de herencia puede coexistir en una sola jerarquía.
Por lo tanto, puedes usar Herencia de Tabla Concreta para una o dos subclases y Herencia
de Tabla Única para el resto.
Inheritance Mappers
Una estructura para organizar mapeadores de bases de datos
que manejan jerarquías de herencia.
Cuando mapeas una jerarquía de herencia orientada a objetos en memoria a una
base de datos relacional, debes minimizar la cantidad de código necesaria para guardar y
cargar los datos en la base de datos. También deseas proporcionar un comportamiento de
mapeo abstracto y concreto que te permita guardar o cargar una superclase o una subclase.
Aunque los detalles de este comportamiento varían según el esquema de mapeo de
herencia que utilices (Herencia de Tabla Única (278), Herencia de Tabla de Clase (285) y
Herencia de Tabla Concreta (293)), la estructura general funciona de la misma manera para
todos ellos.
Cómo funciona
Puedes organizar los mapeadores con una jerarquía de manera que cada clase de
dominio tenga un mapeador que guarde y cargue los datos para esa clase de dominio. De
esta manera, tienes un punto donde puedes cambiar el mapeo. Este enfoque funciona bien
para los mapeadores concretos que saben cómo mapear los objetos concretos en la
jerarquía. Sin embargo, hay momentos en los que también necesitas mapeadores para las
clases abstractas. Estos se pueden implementar con mapeadores que en realidad están
fuera de la jerarquía básica pero delegan en los mapeadores concretos correspondientes.
Para explicar mejor cómo funciona esto, comenzaré con los mapeadores concretos.
En el boceto, los mapeadores concretos son los mapeadores para futbolista, jugador de
críquet y jugador de bolos. Su comportamiento básico incluye las operaciones de búsqueda
(find), inserción (insert), actualización (update) y eliminación (delete). Los métodosde
búsqueda se declaran en las subclases concretas porque devolverán una clase concreta.
Por lo tanto, el método de búsqueda en BowlerMapper debería devolver un jugador de
bolos, no una clase abstracta. Los lenguajes de programación orientados a objetos
convencionales no permiten cambiar el tipo de retorno declarado de un método, por lo que
no es posible heredar la operación de búsqueda y declarar un tipo de retorno específico.
Por supuesto, puedes devolver un tipo abstracto, pero eso obliga al usuario de la clase a
realizar una conversión de tipo hacia abajo, lo cual es mejor evitar. (Un lenguaje con tipado
dinámico no tiene este problema).
El comportamiento básico del método de búsqueda consiste en encontrar la fila
adecuada en la base de datos, instanciar un objeto del tipo correcto (una decisión tomada
por la subclase) y luego cargar el objeto con datos de la base de datos. El método de carga
(load) se implementa en cada mapeador de la jerarquía, que carga el comportamiento para
su objeto de dominio correspondiente. Esto significa que el método de carga del mapeador
de jugador de bolos carga los datos específicos de la clase de jugador de bolos y llama al
método de carga de la superclase para cargar los datos específicos del jugador de críquet,
que a su vez llama a su método de la superclase, y así sucesivamente.
Los métodos de inserción (insert) y actualización (update) operan de manera similar
utilizando un método de guardado (save). Aquí puedes definir la interfaz en la superclase,
incluso en una superclase de capa (Layer Supertype) (475). El método de inserción crea
una nueva fila y luego guarda los datos del objeto de dominio utilizando los métodos de
gancho de guardado (save). El método de actualización simplemente guarda los datos,
también utilizando los métodos de gancho de guardado. Estos métodos operan de manera
similar a los métodos de gancho de carga, con cada clase almacenando sus datos
específicos y llamando al método de guardado de la superclase.
Este esquema facilita escribir los mapeadores adecuados para guardar la
información necesaria para una parte específica de la jerarquía. El siguiente paso es admitir
la carga y el guardado de una clase abstracta, en este caso, un jugador. Aunque se puede
pensar en agregar métodos apropiados en el mapeador de la superclase, en realidad eso se
vuelve incómodo. Mientras que las clases mapeadoras concretas pueden simplemente usar
los métodos de inserción y actualización del mapeador abstracto, los métodos de inserción
y actualización del mapeador de jugadores deben anular estos métodos para llamar a un
mapeador concreto en su lugar. El resultado es una combinación de generalización y
composición que enreda tus células cerebrales.
Prefiero separar los mapeadores en dos clases. El mapeador abstracto de jugador
es responsable de cargar y guardar los datos específicos del jugador en la base de datos.
Esta es una clase abstracta cuyo comportamiento solo es utilizado por los objetos de
mapeador concretos. Se utiliza una clase separada de mapeador de jugador para la interfaz
de las operaciones a nivel de jugador. El mapeador de jugador proporciona un método de
búsqueda y anula los métodos de inserción y actualización. Para todos estos, su
responsabilidad es determinar qué mapeador concreto debe manejar la tarea y delegar en
él.
Aunque un esquema amplio como este tiene sentido para cada tipo de mapeo de
herencia, los detalles varían. Por lo tanto, no es posible mostrar un ejemplo de código para
este caso. Puedes encontrar buenos ejemplos en cada una de las secciones de patrones de
mapeo de herencia: Herencia de Tabla Única (278), Herencia de Tabla de Clase (285) y
Herencia de Tabla Concreta (293).
Cuándo usarlo
Este esquema general tiene sentido para cualquier mapeo de bases de datos
basado en herencia. Las alternativas implican cosas como duplicar el código de mapeo de
la superclase entre los mapeadores concretos y combinar la interfaz del jugador en la clase
abstracta de mapeador de jugador. Lo primero es un crimen atroz y lo segundo es posible
pero conduce a una clase de mapeador de jugador desordenada y confusa. En general, es
difícil pensar en una buena alternativa a este patrón.
Class Table Inheritance
Herencia de Tabla de Clase representa una jerarquía de herencia de clases con una
tabla para cada clase.
Un aspecto muy evidente de la falta de correspondencia entre objetos y bases de
datos relacionales es el hecho de que las bases de datos relacionales no admiten la
herencia. Deseas estructuras de base de datos que se mapeen claramente a los objetos y
permitan enlaces en cualquier lugar de la estructura de herencia. La Herencia de Tabla de
Clase resuelve esto utilizando una tabla de base de datos por cada clase en la estructura de
herencia.
Cómo funciona:
Lo directo acerca de la Herencia de Tabla de Clase es que tiene una tabla por cada
clase en el modelo de dominio. Los campos de la clase de dominio se mapean directamente
a los campos en las tablas correspondientes. Al igual que con otros mapeos de herencia, se
aplica el enfoque fundamental de los Mapeadores de Herencia.
Un problema importante en la implementación de la Herencia de Tabla de Clase es
cómo vincular las filas correspondientes de las tablas de la base de datos. Una posible
solución es utilizar un valor de clave primaria común para que, por ejemplo, la fila con clave
101 en la tabla de futbolistas y la fila con clave 101 en la tabla de jugadores correspondan al
mismo objeto de dominio. Dado que la tabla de la superclase tiene una fila para cada fila en
las demás tablas, las claves primarias serán únicas en todas las tablas si se utiliza este
esquema. Otra alternativa es permitir que cada tabla tenga sus propias claves primarias y
utilizar claves foráneas hacia la tabla de la superclase para vincular las filas entre sí.
El problema principal de implementación con la Herencia de Tabla de Clase es cómo
recuperar los datos de múltiples tablas de manera eficiente. Obviamente, hacer una
consulta para cada tabla no es bueno porque implica múltiples llamadas a la base de datos.
Esto se puede evitar realizando una unión (join) entre las diversas tablas componentes; sin
embargo, las uniones de más de tres o cuatro tablas tienden a ser lentas debido a la forma
en que las bases de datos optimizan las consultas.
Además, el problema es que en una consulta determinada a menudo no sabes
exactamente qué tablas debes unir. Si estás buscando un futbolista, sabes que debes usar
la tabla de futbolistas, pero si estás buscando un grupo de jugadores, ¿qué tablas utilizas?
Para unir de manera efectiva cuando algunas tablas no tienen datos, deberás realizar una
unión externa (outer join), que no es estándar y a menudo es lenta. La alternativa es leer
primero la tabla raíz y luego utilizar un código para determinar qué tablas leer a
continuación, pero esto implica realizar múltiples consultas.
Cuándo usarlo:
La Herencia de Tabla de Clase, la Herencia de Tabla Única (Single Table
Inheritance) y la Herencia de Tabla Concreta (Concrete Table Inheritance) son las tres
alternativas a considerar para el mapeo de herencia. Las fortalezas de la Herencia de Tabla
de Clase son:
- Todas las columnas son relevantes para cada fila, por lo que las tablas son más fáciles de
entender y no desperdician espacio.
- La relación entre el modelo de dominio y la base de datos es muy directa.
Las debilidades de la Herencia de Tabla de Clase son:
- Necesitas acceder a múltiples tablas para cargar un objeto, lo que implica una unión o
múltiples consultas y ensamblaje en memoria.
- Cualquier cambio en los campos hacia arriba o hacia abajo en la jerarquía requiere
cambios en la base de datos.
- Las tablas de los tipos superiores pueden convertirse en un cuello de botella debido a que
se accede a ellas con frecuencia.
- La alta normalización puede dificultar la comprensión de las consultas ad hoc.
No es necesario elegir un solo patrón de mapeo de herencia para una jerarquía de
clases. Puedes utilizar la Herenciade Tabla de Clase para las clases en la parte superior de
la jerarquía y un conjunto de Herencia de Tabla Concreta para las clases inferiores.
Single Table Inheritance
Herencia de Tabla Única representa una jerarquía de herencia de clases como una
única tabla que tiene columnas para todos los campos de las diferentes clases.
Las bases de datos relacionales no admiten la herencia, por lo que al mapear
objetos a bases de datos, debemos considerar cómo representar nuestras estructuras de
herencia en tablas relacionales. Al mapear a una base de datos relacional, tratamos de
minimizar las uniones (joins) que pueden acumularse rápidamente al procesar una
estructura de herencia en múltiples tablas. La Herencia de Tabla Única mapea todos los
campos de todas las clases de una estructura de herencia en una sola tabla.
Cómo funciona:
En este esquema de mapeo de herencia, tenemos una tabla que contiene todos los
datos de todas las clases en la jerarquía de herencia. Cada clase almacena los datos
relevantes en una fila de la tabla. Cualquier columna en la base de datos que no sea
relevante se deja vacía. El comportamiento básico de mapeo sigue el esquema general de
los Mapeadores de Herencia.
Al cargar un objeto en memoria, debes saber qué clase instanciar. Para esto, tienes
un campo en la tabla que indica qué clase se debe utilizar. Esto puede ser el nombre de la
clase o un campo de código. Un campo de código debe ser interpretado por algún código
para asignarlo a la clase correspondiente. Este código debe ser extendido cuando se
agrega una clase a la jerarquía. Si incrustas el nombre de la clase en la tabla, puedes usarlo
directamente para instanciar una instancia. Sin embargo, el nombre de la clase ocupará
más espacio y puede ser menos fácil de procesar para aquellos que utilizan directamente la
estructura de la tabla de la base de datos. Además, puede acoplar más estrechamente la
estructura de la clase al esquema de la base de datos.
Al cargar datos, lees el código primero para determinar qué subclase instanciar. Al
guardar los datos, el código debe ser escrito por la superclase en la jerarquía.
Cuándo usarlo:
La Herencia de Tabla Única es una de las opciones para mapear los campos en una
jerarquía de herencia a una base de datos relacional. Las alternativas son la Herencia de
Tabla de Clase y la Herencia de Tabla Concreta.
Estas son las fortalezas de la Herencia de Tabla Única:
- Solo hay una única tabla de la que preocuparse en la base de datos.
- No hay uniones al recuperar datos.
- Cualquier refactorización que mueva campos hacia arriba o hacia abajo en la jerarquía no
requiere cambios en la base de datos.
Las debilidades de la Herencia de Tabla Única son:
- Los campos a veces son relevantes y a veces no, lo que puede ser confuso para las
personas que utilizan las tablas directamente.
- Las columnas utilizadas solo por algunas subclases generan un desperdicio de espacio en
la base de datos. Cuánto esto sea realmente un problema depende de las características
específicas de los datos y de cómo la base de datos comprime las columnas vacías. Por
ejemplo, Oracle es muy eficiente para reducir el espacio desperdiciado, especialmente si
mantienes las columnas opcionales en el lado derecho de la tabla de la base de datos.
Cada base de datos tiene sus propias técnicas para esto.
- La tabla única puede terminar siendo demasiado grande, con muchos índices y bloqueos
frecuentes, lo que puede afectar el rendimiento. Puedes evitar esto teniendo tablas de
índices separadas que ya sea listan claves de filas que tienen una cierta propiedad o que
copian un subconjunto de campos relevantes para un índice.
- Solo tienes un único espacio de nombres para los campos, por lo que debes asegurarte de
no usar el mismo nombre para diferentes campos. Los nombres compuestos con el nombre
de la clase como prefijo o sufijo ayudan en este aspecto.
Recuerda que no es necesario utilizar una única forma de mapeo de herencia para
toda tu jerarquía. Es perfectamente válido mapear media docena de clases similares en una
sola tabla, siempre y cuando utilices Herencia de Tabla Concreta para aquellas clases que
tengan muchos datos específicos.
Serialized LOB
Guarda un grafo de objetos serializándolos en un único objeto grande (LOB), que se
almacena en un campo de base de datos.
Los modelos de objetos a menudo contienen gráficos complicados de pequeños
objetos. Gran parte de la información en estas estructuras no está en los objetos, sino en
los vínculos entre ellos. Considera almacenar la jerarquía de organización de todos tus
clientes. Un modelo de objetos muestra naturalmente el patrón de composición para
representar jerarquías organizativas, y puedes agregar fácilmente métodos que te permitan
obtener ancestros, hermanos, descendientes y otras relaciones comunes.
No es tan fácil colocar todo esto en un esquema relacional. El esquema básico es
simple: una tabla de organización con una clave foránea del padre, sin embargo, la
manipulación del esquema requiere muchas uniones (joins), que son lentas y complicadas.
Los objetos no tienen que persistirse como filas de tabla relacionadas entre sí.
Otra forma de persistencia es la serialización, donde se escribe un grafo completo
de objetos como un único objeto grande (LOB) en una tabla. Este LOB serializado se
convierte en una forma de memento.
Cómo funciona:
Hay dos formas de hacer la serialización: como un objeto binario (BLOB) o como
caracteres de texto (CLOB). El BLOB suele ser el más sencillo de crear, ya que muchas
plataformas incluyen la capacidad de serializar automáticamente un grafo de objetos.
Guardar el grafo es simplemente aplicar la serialización en un búfer y guardar ese búfer en
el campo relevante.
Las ventajas del BLOB son que es simple de programar (si tu plataforma lo admite) y
que utiliza un mínimo de espacio. Las desventajas son que tu base de datos debe admitir un
tipo de dato binario y que no puedes reconstruir el grafo sin el objeto, por lo que el campo
es completamente impenetrable para una visualización casual. El problema más serio, sin
embargo, es el versionamiento. Si cambias la clase de departamento, es posible que no
puedas leer todas sus serializaciones anteriores; dado que los datos pueden vivir en la base
de datos durante mucho tiempo, esto no es algo insignificante.
La alternativa es un CLOB. En este caso, serializas el grafo del departamento en
una cadena de texto que lleva toda la información que necesitas. La cadena de texto puede
ser leída fácilmente por una persona que visualiza la fila, lo cual ayuda al navegar
casualmente por la base de datos. Sin embargo, el enfoque de texto generalmente necesita
más espacio y es posible que necesites crear tu propio analizador para el formato textual
que utilices. También es probable que sea más lento que una serialización binaria.
Muchas de las desventajas de los CLOB se pueden superar con XML. Los analizadores de
XML están ampliamente disponibles, por lo que no tienes que escribir el tuyo propio.
Además, XML es un estándar ampliamente compatible, por lo que puedes aprovechar las
herramientas a medida que estén disponibles para realizar manipulaciones adicionales. La
desventaja que XML no resuelve es el problema del espacio. De hecho, empeora el
problema del espacio debido a que es un formato muy verboso. Una forma de abordar esto
es usar un formato XML comprimido como tu BLOB: pierdes la legibilidad humana directa,
pero es una opción si el espacio es un problema real.
Cuando uses LOB Serializado, ten cuidado con los problemas de identidad.
Supongamos que quieres usar LOB Serializado para los detalles del cliente en un pedido.
Para esto, no coloques el LOB del cliente en la tabla de pedidos; de lo contrario, los datos
del cliente se copiarán en cada pedido, lo que dificulta las actualizaciones. (Esto, de hecho,
es bueno si deseas almacenar una instantánea de los datos del cliente tal como estaban al
realizar el pedido, ya que evita las relaciones temporales). Si deseas que los datos del
clientese actualicen para cada pedido en el sentido relacional clásico, debes colocar el LOB
en una tabla de clientes para que muchos pedidos puedan vincularse a él. No hay nada de
malo en una tabla que solo tenga una ID y un único campo de LOB para sus datos.
En general, ten cuidado de duplicar datos al usar este patrón. A menudo, no se
duplica un LOB serializado completo, sino parte de uno que se superpone con otro. Lo que
debes hacer es prestar atención cuidadosa a los datos que se almacenan en el LOB
serializado y asegurarte de que no se pueda acceder a él desde ningún lugar excepto un
objeto único que actúa como el propietario del LOB serializado.
Cuándo usarlo:
LOB Serializado no se considera tan a menudo como podría serlo. XML lo hace
mucho más atractivo, ya que ofrece un enfoque textual fácil de implementar. Su principal
desventaja es que no puedes consultar la estructura mediante SQL. Las extensiones de
SQL permiten acceder a los datos XML dentro de un campo, pero aún no es lo mismo (ni
portátil).
Este patrón funciona mejor cuando puedes extraer una parte del modelo de objetos
y usarlo para representar el LOB. Piensa en un LOB como una forma de tomar un montón
de objetos que no es probable que se consulten desde ninguna ruta SQL fuera de la
aplicación. Este grafo luego se puede conectar al esquema SQL.
LOB Serializado funciona mal cuando tienes objetos fuera de los objetos de
referencia LOB enterrados en él. Para manejar esto, debes idear alguna forma de esquema
de referencia que admita referencias a objetos dentro de un LOB; no es imposible, pero es
incómodo, lo suficientemente incómodo como para no valer la pena hacerlo. Nuevamente,
XML, o más bien XPath, reduce en cierta medida esta dificultad.
Si estás utilizando una base de datos separada para informes y todo el SQL va en
esa base de datos, puedes transformar el LOB en una estructura de tabla adecuada. El
hecho de que una base de datos de informes generalmente esté desnormalizada significa
que las estructuras adecuadas para el LOB Serializado a menudo también son adecuadas
para una base de datos de informes separada.
Embedded Value
Mapea un objeto en varios campos de la tabla de otro objeto.
Muchos objetos pequeños tienen sentido en un sistema orientado a objetos pero no
tienen sentido como tablas en una base de datos. Ejemplos incluyen objetos de dinero que
son conscientes de la moneda y rangos de fechas. Aunque el pensamiento por defecto es
guardar un objeto como una tabla, ninguna persona cuerda querría una tabla de valores
monetarios.
Un Valor Empotrado mapea los valores de un objeto en campos en el registro del
objeto propietario. En el esquema, tenemos un objeto de empleo con enlaces a un objeto de
rango de fechas y un objeto de dinero. En la tabla resultante, los campos de esos objetos se
mapean a campos en la tabla de empleo en lugar de crear nuevos registros ellos mismos.
Cómo funciona
Este ejercicio es en realidad bastante simple. Cuando el objeto propietario (empleo)
se carga o se guarda, los objetos dependientes (rango de fechas y dinero) se cargan y
guardan al mismo tiempo. Las clases dependientes no tendrán sus propios métodos de
persistencia, ya que toda la persistencia la realiza el objeto propietario. Puedes pensar en el
Valor Empotrado como un caso especial de Mapeo Dependiente (262), donde el valor es un
único objeto dependiente.
Cuándo usarlo
Este es uno de esos patrones donde hacerlo es muy sencillo, pero saber cuándo
usarlo es un poco más complicado. Los casos más simples para el Valor Empotrado son los
Objetos de Valor claros y simples (486), como el dinero y el rango de fechas. Dado que los
Objetos de Valor no tienen identidad, puedes crearlos y destruirlos fácilmente sin
preocuparte por cosas como los Mapas de Identidad (195) para mantenerlos sincronizados.
De hecho, todos los Objetos de Valor deben persistirse como Valor Empotrado, ya que
nunca querrías una tabla para ellos.
El área gris está en si vale la pena almacenar objetos de referencia, como un pedido
y un objeto de envío, utilizando Valor Empotrado. La pregunta principal aquí es si los datos
de envío tienen alguna relevancia fuera del contexto del pedido. Un problema es la carga y
el guardado. Si solo cargas los datos de envío en memoria cuando cargas el pedido, eso es
un argumento para guardar ambos en la misma tabla. Otra pregunta es si querrás acceder a
los datos de envío por separado a través de SQL. Esto puede ser importante si estás
generando informes a través de SQL y no tienes una base de datos separada para
informes.
Si estás mapeando a un esquema existente, puedes usar Valor Empotrado cuando una
tabla contiene datos que divides en más de un objeto en memoria. Esto puede ocurrir
porque quieres un objeto separado para factorizar algún comportamiento en el modelo de
objetos, pero todo sigue siendo una entidad en la base de datos. En este caso, debes tener
cuidado de que cualquier cambio en el objeto dependiente marque al propietario como
modificado, lo cual no es un problema con los Objetos de Valor que se reemplazan en el
propietario.
En la mayoría de los casos, solo utilizarás Valor Empotrado en un objeto de referencia
cuando la asociación entre ellos sea unívoca en ambos extremos (una asociación uno a
uno). Ocasionalmente, puedes usarlo si hay múltiples dependientes candidatos y su número
es pequeño y fijo. En ese caso, tendrás campos numerados para cada valor. Esto es un
diseño de tabla desordenado y horrible de consultar en SQL, pero puede tener beneficios de
rendimiento. Sin embargo, si este es el caso, el LOB Serializado (272) suele ser la mejor
opción.
Dado que gran parte de la lógica para decidir cuándo usar Valor Empotrado es la
misma que para el LOB Serializado (272), está el obvio asunto de elegir entre los dos. La
gran ventaja del Valor Empotrado es que permite hacer consultas SQL contra los valores en
el objeto dependiente. Aunque el uso de XML como serialización, junto con complementos
de consulta basados en XML para SQL, puede alterar eso en el futuro, en este momento
realmente necesitas Valor Empotrado si quieres usar valores dependientes en una consulta.
Esto puede ser importante para mecanismos de informes separados en la base de datos.
El Valor Empotrado solo se puede utilizar para dependientes bastante simples. Un
dependiente solitario o algunos dependientes separados funcionan bien. El LOB Serializado
(272) funciona con estructuras más complejas, incluidos subgrafos de objetos
potencialmente grandes.

Continuar navegando