Descarga la aplicación para disfrutar aún más
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.
Compartir