Thinking on hiring me?

Please read

Fernando Guillén

a Freelance Web Developer

cabecera decorativa

software development as an artistic expression

Cuando el modelado de una relación entre 3 tablas se vuelve un ejercicio de arquitectura.

Este va a ser un post muy técnico así que, querido/a amigo/a, si pasabas por aquí en busca de lectura ligera mejor continuas surcando tu blog-roll.

Lo que voy a intentar explicar aquí es una cuestión de diseño de un modelo relacional de base datos aparentemente muy sencillo pero que me está generando enormes dudas existenciales y repetidos re-factors de nuestra implementación.

Supongamos que tenemos en nuestro diseño de clases un bean (pojo o como queramos llamarlo), al que llamaremos Element que contiene un número indeterminado de elementos de otra clase bean que llamaremos SubElement.

Hasta aquí todo bien, para traducir esto a un modelo de base de datos nos basta con un relación 1 a n entre una tabla ELEMENT y otra SUB_ELEMENT.

El problema viene cuando existe otra clase de beans que también, entre sus atributos, se encuentra uno que contiene un número indeterminado de beans de clase SubElement, como se muestra en la figura 1.

figura 1

Es aquí cuando la relación 1 a n se complica pues ya no puedo definir una clave entre la tabla SUB_ELEMENT y la tabla ELEMENT_TYPE_1 pues puede que el registro de la tabla SUB_ELEMENT deba estar relacionado en realidad con registro de la tabla ELEMENT_TYPE_2.

La primera aproximación que se me ocurre es definir dos tablas diferentes para los objectos de clase SubElement. Una tabla (SUB_ELEMENT_TYPE_1) contendrá aquellos registros que se tengan que relacionar con la tabla ELEMENT_TYPE_1 y otra (SUB_ELEMENT_TYPE_2) contendrá los que se tengan que relacionar con la tabla ELEMENT_TYPE_2. Como se muestra en la figura 2.

figura 2

Esta implementación me empezó a disgustar desde el primer momento por el hecho de tener 2 tablas diferentes que en realidad contenían registros del mismo tipo de objeto, sin contar con que si aparece otro tipo de objeto que a su vez contenga también objetos del tipo SubElement habría que sumar otra tabla más con toda la lógica de persistencia que conlleva. Así que seguí buscando.

En un pequeño brainstorming entre mis compañeros se nos ocurrió que podíamos diseñar una tabla para acoger los objetos de tipo SubElement que tuviera una referencias externa a una de las tablas ELEMENT_TYPE_X pero sin definir a cual. Para saber a qué tabla había pertenecía había que hacer uso de un nuevo campo en la tabla SUB_ELEMENT cuyo valor, sacado de un catálogo limitado, nos indicase a qué tabla real pertenecía el ID que hacía las veces de clave externa. Como se muestra en la figura 3.

figura 3

Esta propuesta cumple nuestro deseo de agrupar todos los elementos de tipo SubElement en la misma tabla reutilizando también toda la implementación de la capa de persistencia. Nos ahorramos también el tener que crear nuevas tablas para alojar objetos tipo SubElement cada vez que surja una nueva clase tipo ElementTypeX. Pero surge un enorme problema de integredad referencial y es que tenemos que desprendernos de crear una clave externa en la tabla SUB_ELEMENT pues esa clave aveces pertenecerá a la tabla ELEMENT_TYPE_1 y otras a la tabla ELEMENT_TYPE_2, como ya hemos dicho esto depende de campo ELEMENT_TYPE. Esto puede causar que existan registros en la tabla SUB_ELEMENT que estén apuntando a registros en la tablas ELEMENT_TYPE_X que no existan.

Estaba apunto de elegir esta propuesta como definitiva, pues aunque tiene peligro de integridad me resultaba la más fácil de administrar y era un modelo mucho más escalable, cuando apareció este dibujo en mi cuaderno, ver figura 4.

figura 4

A primera vista parece bastante más complicado que los anteriores, y lo es, pero no mucho. Aparecen unas nuevas tablas que hacen de puente entre la tabla SUB_ELEMENT y las tablas ELEMENT_TYPE_X. Estas tablas hay que duplicarlas cada vez que aparezca un nuevo objeto tipo ElementTypeX pero no acogen más que una relación y los objetos reales, los SubElement, se encuentran todos agrupados en la misma tabla.

Esta implementación dificulta las sentencias SQL de inserción, búsqueda y eliminación. Es la única pega que todavía me carcome.

Ruego, paciente lector que has llegado hasta aquí, que si tienes alguna sugerencia o corrección que hacer para llegar a una solución más ágil que ésta, sin que se perjudique la seguridad en la integración, seas tan amable y orgulloso de resumírmela.

10 Comments to “Cuando el modelado de una relación entre 3 tablas se vuelve un ejercicio de arquitectura.”
  1. PEZ Says:

    me pierdo a partir de: En un pequeño brainstorming, pero guiño el ojo y me concentro… nada.. que no me entero… pero me recuerda a un problema que tuve haciendo un dvd… a lo mejor no tiene nada que ver y resulta que me he perdido antes de lo que pensaba…pero si te ayuda… quién sabe!!

    en dvd studio pro pasa algo parecido, si yo tengo una pista de vídeo gigante que llama el tipo de botón: “ver la película” esa pista de vídeo gigante no ofrece ningún problema porque se reproduce de principio a fin. Pero si lo que queremos es llamar a los capítulos de esa pista de vídeo (esto no es exactamente tu sub element pero se parece mucho) yo coloco unas cositas que se llaman marcadores de manera que cada botón cap 1 ó 2 ó 3.. llaman a ése marcador y no a otro…
    El problema viene si en lugar de que continúe de corrido yo quiero que el comportamiento de esos botones varíe y al finalizar vuelva al menú de capítulos en lugar de remitir al capítulo siguiente… (esto en una peli tal vez no, pero en otras situaciones puede tener su lógica) resulta que en el boton llamado “ver la peli” va a pasar lo mismo y eso no le gusta a nadie…estás viendo la peli y al final de cada capítulo te lleva al menú de capítulos en lugar de verla de seguido!!!

    si ya aquí ves que no tiene nada que ver con lo tuyo pues deja de leerme…
    Sería una tontería cortar la peli en chorizos-capítulos que linkar a los botones de capítulos y dejar la otra pista entera e impoluta linkada a ver toda la peli porque la info estarÍa duplicada y eso no le gusta al que lo hace, ni al dvd que no le cabe, ni es bonito ni nada…

    la solución es una cosa maravillosa que en ese programa se llama script y que supone un enunciado para cada uno de esos botones, de manera que no asocias botón a marcador si no a script…
    yo no sé cómo se traduce eso, pero imagino algo del tipo: si pulso capítulo 3 llévame al script q dice vas al marcador 3 y vuelves al finalizar al menú capítulos.
    oye, que te quiero…

  2. fguillen Says:

    Veo que el programita este del que me hablas es capaz de incluir programación en los botones. En esta programación permite hacer bifurcaciones lógicas.

    No encuentro en la base de datos algo que me permita incluir bifurcaciones lógicas a la hora de definir una clave externa. Sería una solución estupenda.

    Entonces resumo toda la duda expuesta en este post con el siguiente enunciado:

    ¿Es posible definir una clave externa a partir de dos campos?:

    Uno define a que tabla apunta y el otro contiene el id del registro de la tabla externa.

    (Coincidimos exactamente en tú última frase)

  3. PEZ Says:

    me bifurco… y todas las claves externas me arropan…
    parecen tan dulces…que dejo por un momento de bucear entre los verdes y amarillos de los elementos y sub elementos áridos de tus tablas que no entiendo y sólo me atraviesan, veloces, todos los significados y hace tanto frío…que me agarro con fuerza a uno de los campos y soy toda piernas… bifurcada y más entera que nunca.. se puede?

    esto es privado?

  4. fguillen Says:

    Esto es público, y tú pez, eres bienvenida.

  5. PEZ Says:

    nad(a) nad(o)

  6. Al Says:

    Preguntas tontas que te pueden seguir de guía. Lo de tontas es porque tengo que reconocer que me ha sido un poco denso el post a partir del braimstorming XD. Me he fijado más que nada en los dibujos XD.

    1.- ¿Un Subelement puede pertenecer a la vez a ElementType1 y ElementType2?.

    2.- Ya que hablas de beans, entiendo que partes del diseño de objetos para que te genere el relacional, ¿no puedes utilizar herencia para definir esa relación de en un AbstractElementTypeWithSubElements y que sea el ORM quién se encargue de discernir estas cosas?

  7. Al Says:

    Se me ha olvidado explicar la pregunta 1… y es que si es que sí, nada impide que haya dos FK XD, pero eso ya lo sabes.

  8. fguillen Says:

    1) No, un SubElement sólo pertenece a un ElementTypeX

    2) Uso Ibatis y el modelo lo defino yo a pelo, también toda la capa DAO.

    Te resumo la historia:

    Un SubElement sólo pertenece a un ElementTypeX, pero puede ser a un ElementType1 o a un ElementType2. ¿Cómo diseño una relación que aveces apunte a ElementType1 y otras a ElementType2? ¿O cómo demonios se hace esto? :)

  9. Al Says:

    Primero, si quieres pensar en objetos te recomendaría que te pasaras a un ORM. Hibernate con JPA, otra implementación de JPA, o de JDO, etc. Lo que más rabia te dé. Esta es una cuestión que no hubieras tenido ni que pensar XD.

    Pero…. si quieres seguir con ibatis… supongo que la solución sería del estilo de la tercera, en la que SubElement tiene un campo discrimatorio para saber a que corresponde. O tener dos FK y escoger tú según busques por un sitio o por otro. Aunque en ibatis no te costaría tampoco demasiado optar por el segundo (dos tablas separadas). Lo que te sea más cómodo y mejor se adapta el uso que le quieras dar (cantidad de registros, participaciones en joins, etc.)

    La verdad es que la que no cogería es la 4 XD.

  10. fguillen Says:

    Vengo de Hibernate, era gran defensor de los oscuros motores de persistencia que te abstraían del acceso a BD. Más escaldado que un gato soy muy feliz ahora con Ibatis. Seguramente por no haber sabido apañármelas bien, pero esta es una conversación que requiere otro post, o mejor unas cañas :).

    La cuestión ahora es que si opto por la solución de:

    1) el campo discriminatorio entonces no puedo definir la FK a nivel de base de datos pues una vez apuntará a una tabla y otras a otra.

    2) las 2 FK resulta que las dos FK pueden estar a NULL y convertir al registro en corrupto (huérfano). Además de tener que hacer un alter table cada vez que incluya en mi modelo una nueva tabla ElementTypeX para crear la nueva FK.

    3) lo de las dos tablas de SubElement es la que menos me gusta pues resultaría que objetos del mismo tipo se correspondería en BD con tablas diferentes, sin contar la multiplicación del trabajo en la capa DAO cada vez que aparece un nuevo objeto de tipo ElementTypeX que requiere SubElements.

    4) no te gusta la 4.. joer.. sí se ve muy liosa :)

Leave a comment

a Freelance Web Developer is proudly powered by WordPress
Entries (RSS) and Comments (RSS).