Mapeo objeto-relacional (herencia I)

02:34PM sep 01, 2005 in category Programacion by Alberto Gimeno

En esta tercera parte de esta mini-serie voy a empezar a tratar el tema de mapear una jerarquía de herencia a una base de datos. Hasta ahora la aproximación "una clase, una tabla" nos ha funcionado, pero no contábamos con herencia.

Voy a modificar el ejemplo del artículo anterior de los profesores, alumnos y asignaturas añadiendo herencia. Voy a crear una clase abstracta Persona. Tanto Persona como Profesor extenderán de Persona. Simplemente saco las propiedades nif, nombre, apellidos, fechaNacimiento y las pongo en la clase Persona.

import java.util.Date;

public abstract class Persona {
	
	private String nif;
	private String nombre;
	private String apellidos;
	private Date fechaNacimiento;
	
	/* métodos get y set */
}

Las clases Profesor y Alumno ahora extenderán de Persona y no tendrán las propiedades nif, nombre, apellidos y fechaNacimiento. La clase Persona la he declarado como abstracta porque en mi aplicación toda persona tiene que ser algo más concreto, tiene que ser Profesor o Alumno. No voy a persistir sólo personas, sino que voy a persistir profesores o alumnos.

Voy a complicar el modelo de objetos. Voy a añadir una propiedad a la clase Alumno:

int numeroDeCreditosCursados;

Voy a añadir también una nueva clase muy sencilla: Departamento

public class Departamento {
	
	private int idDepartamento; // clave primaria
	private String nombreDepartamento;
	
	/* métodos get y set */
}

Voy a crear una relación: "un profesor pertenecerá a un departamento" y "un departamento tendrá varios profesores". Por ello añado un campo a la clase Profesor:

private Departamento departamentoAlQuePertenece;

Bien, ahora vayamos al grano. Para mapear una jerarquía de clases tenemos tres estrategias:

  • Una tabla por cada clase
  • Una tabla por cada clase concreta (no abstracta)
  • Una tabla para cada jerarquía de clases

Veamos cada estrategia con detenimiento.

Una tabla por cada clase

En este caso tendríamos las tablas: personas, profesores y alumnos. Cada tabla contendrá aquellos campos que hayan sido definidos en su clase correspondiente y la clave primaria. Así por ejemplo la tabla personas sólo tendrá: nif, nombre, apellidos, fechaNacimiento; la tabla alumnos tendrá: dni y numeroCreditosCursados; y la tabla profesores tendrá dni y una columna con la clave primaria del departamento al que pertenece.

CREATE TABLE personas (nif INTEGER NOT NULL,
        nombre VARCHAR(50),
        apellidos VARCHAR(50),
        fecha_nacimiento DATE,
        PRIMARY KEY(nif)) TYPE=INNODB;

CREATE TABLE alumnos (nif INTEGER NOT NULL,
        numero_creditos_cursados INTEGER,
        PRIMARY KEY(nif)) TYPE=INNODB;

CREATE TABLE profesores (nif INTEGER NOT NULL,
        id_departamento INTEGER,
        PRIMARY KEY(nif)) TYPE=INNODB;

CREATE TABLE asignaturas (id_asignatura INTEGER NOT NULL AUTO_INCREMENT,
        nombre VARCHAR(50),
        curso INTEGER,
        creditos INTEGER,
        profesor_titular INTEGER,
        PRIMARY KEY(id_asignatura)) TYPE=INNODB;

CREATE TABLE departamentos (id_departamento INTEGER NOT NULL AUTO_INCREMENT,
        nombre VARCHAR(50),
        PRIMARY KEY(id_departamento)) TYPE=INNODB;

CREATE TABLE matriculas (id_asignatura INTEGER NOT NULL,
        nif INTEGER NOT NULL,
        PRIMARY KEY(id_asignatura, nif)) TYPE=INNODB;

Las claves foráneas para las relaciones

ALTER TABLE asignaturas ADD FOREIGN KEY (profesor_titular) REFERENCES profesores (nif);
ALTER TABLE profesores ADD FOREIGN KEY (id_departamento) REFERENCES departamentos (id_departamento);
ALTER TABLE matriculas ADD FOREIGN KEY (id_asignatura) REFERENCES asignaturas (id_asignatura);
ALTER TABLE matriculas ADD FOREIGN KEY (nif) REFERENCES alumnos (nif);

Y también definimos claves foráneas para asegurar que un registro en la tabla "alumnos" tenga un registro asociado en la tabla "personas". Y lo mismo para los profesores.

ALTER TABLE alumnos ADD FOREIGN KEY (nif) REFERENCES personas (nif);
ALTER TABLE profesores ADD FOREIGN KEY (nif) REFERENCES personas (nif);

Es decir, para todo registro en la tabla "alumnos" tiene que haber otro que lo complemente con la misma clave primaria en la tabla "personas". Y lo mismo con "profesores".

Con esta estrategia los datos de un objeto están en varias tablas y no sólo en una como hasta ahora. Cuando queramos dar de alta a un alumno haremos inserción en la tabla "personas" y en la tabla "alumnos" (y en ese orden). Y de igual modo tendremos que hacer operacioens en dos tablas cuando queramos actualizarlo o leer los datos de un alumno o profesor. El inconveniente de esta estrategia es ese mismo: para manipular la información debemos manipular varias tablas por lo que el acceso es algo más lento y puede haber más problemas de concurrencia. La ventaja es que es más fácil añadir campos a las clases. Si queremos añadir un campo a la clase Persona, por ejemplo "private String domicilio", sólo tendremos que añadir una columna a la tabla personas.

Otro inconveniente es evitar que ocurra que se de de alta un registro en la tabla "personas" pero no se da de alta ni en "alumnos" ni en "profesores", de modo que tendríamos una persona que no es ni alumno ni profesor.

Es posible que nos interese en nuestra aplicación "listar todas las personas". Es decir obtener todos los alumnos y profesores de la base de datos. Podríamos hacerlo de la siguiente forma:

SELECT * FROM personas p LEFT JOIN alumnos ON alumnos.nif = p.nif LEFT JOIN profesores ON profesores.nif = p.nif;

De este modo obtendremos un resultado como el siguiente

+------+---------+-----------+------------------+------+--------------------------+------+-----------------+
| nif  | nombre  | apellidos | fecha_nacimiento | nif  | numero_creditos_cursados | nif  | id_departamento |
+------+---------+-----------+------------------+------+--------------------------+------+-----------------+
|  123 | alberto | gimeno    | 1984-07-16       |  123 |                      112 | NULL |            NULL |
| 1235 | pedro   | garcía    | 1972-11-19       | NULL |                     NULL | 1235 |               1 |
+------+---------+-----------+------------------+------+--------------------------+------+-----------------+

Las primeras cuatro columnas son los datos sacados de la tabla "personas". Las dos siguientes son datos sacados de la tabla "alumnos" y las últimas dos de la tabla "profesores". Si la quinta columna es diferente de NULL (el nif de "alumnos") quiere decir que la persona es concretamente un alumno. Si la séptima columna (el nif de "profesores") no es NULL quiere decir que la persona es concretamente un profesor.

Con esta consulta podremos hacer desde nuestro programa Java un método listarPersonas() que devuelva un listado de personas de la base de datos. Este listado se llama "listado polimórfico" ya que se listan diferentes tipos de objetos. El pseudocódigo para implementar ese método es el siguiente...

List lista = new ArrayList();
ResultSet result = ...;
while(result.next()) {
    if(la quinta columna != null) {
        Alumno alumno = new Alumno();
        // rellenar datos
        lista.add(alumno);
    } else if(la séptima columna != null) {
        Profesor profesor = new Profesor();
        // rellenar datos
        lista.add(profesor);
    } else {
        // error, la persona no es ni alumno, ni profesor
    }
}

Bueno, en el próximo post hablaré de la siguiente estrategia para mapear una jerarquía de herencia: "una tabla por cada clase concreta".

Comentarios[1]

Comentarios:

Interesante me saco d muchas dudas, gracias por el dato del mapeo de la herencia es eso o tablas concretas opto por la herencia

Enviado por ysaac en octubre 10, 2009 a las 05:38 AM GMT+01:00 #

Enviar un comentario:
  • Sintaxis HTML: Deshabilitado