Herencia con Hibernate

De ChuWiki
Saltar a: navegación, buscar

Las clases java que queremos hacer persistentes con hibernate pueden tener herencia, es decir, unas clases heredar de otras. Por ello, hibernate ofrece tres mecanismos para esta herencia (en realidad cuatro, pero dos son variantes del mismo).

Supongamos tres clases Padre, Hija1 e Hija2 cuyos fuentes java puedes ver aquí.

Veamos los tres mecanismos que ofrece Hibernate para mapear esta herencia en base de datos.

Una tabla por toda la jerarquía de clases

En una sola tabla metemos todas las columnas necesarias para albergar todos los posibles atributos de las clases padres e hijas. Debe haber una columna adicional ("clase" en nuestro ejemplo) que nos diga qué tipo de clase se guarda en esa fila de la tabla. Para nuestro ejemplo de clases Padre, Hija1 e Hija2, las tablas en BD serían

id atributoPadre atributoHija1 atributoHija2 clase
1 a Padre
2 b c Hija1
3 d e Hija2


Si insertamos/consultamos clase Hijo1, Hibernate sólo hará el insert/select de las columnas que correspondan a Hijo1. La pega de este mecanismo es que para cada clase puede haber muchas columnas sin usar, las correspondientes a los atributos de las otras clases. Tampoco podemos poner muchas restriciones a esos atributos, por ejemplo, no podemos poner la restricción de que atributo1 de Hijo1 no sea null, puesto que cuando insertemos un Hijo2 igual ese atributo no existe.

El fichero de mapeo de Hibernate puede ser como el siguiente

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
	<class name="com.chuidiang.ejemplos.Padre"
		discriminator-value="Padre">
		<id name="id">
			<generator class="native"></generator>
		</id>
		<discriminator column="clase" type="string" />
		<property name="atributoPadre"></property>

		<subclass name="com.chuidiang.ejemplos.Hija1"
			discriminator-value="Hija1">
			<property name="atributoHija1"></property>
		</subclass>

		<subclass name="com.chuidiang.ejemplos.Hija2"
			discriminator-value="Hija2">
			<property name="atributoHija2"></property>
		</subclass>
	</class>
</hibernate-mapping>

El tag <class> lleva un atributo "discriminator-value" para indicar qué valor tiene la columna "clase" cuando hagamos una inserción de Padre en la base de datos. Este "discriminator-value" es opcional, si no lo ponemos, el valor por defecto será el nombre de la clase.

Definimos además el id y la "discriminator-column", que es la columna "clase" que contiene de qué tipo (Padre, Hija1 o Hija2) es esa fila concreta de la tabla.

Luego, por cada subclase ponemos un <subclass> con su "discriminator-value".


Una tabla por cada clase concreta

Aquí tendremos una tabla para clase Padre, otra tabla para clase Hijo1, otra para Hijo2, etc. En nuestro ejemplo la tabla de Padre no se crearía por ser Padre una clase abstracta. La pega aquí es que repetimos los atributos comunes en las tablas. Sólo se crean tablas para aquellas clases que no sean abstractas y se puedan instanciar en nuestro código. En el ejemplo anterior, si Padre se puede instanciar, tendríamos tres tablas

id atributoPadre
1 a


id atributoPadre atributoHija1
2 b c


id atributoPadre atributoHija2
3 d e
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
	<class name="com.chuidiang.ejemplos.tabla_por_subclase.Padre" abstract="true">
		<id name="id">
			<generator class="increment"></generator>
		</id>
		<property name="atributoPadre"></property>

		<union-subclass name="com.chuidiang.ejemplos.tabla_por_subclase.Hija1">
			<property name="atributoHija1"></property>
		</union-subclass>

		<union-subclass name="com.chuidiang.ejemplos.tabla_por_subclase.Hija2">
			<property name="atributoHija2"></property>
		</union-subclass>
	</class>

</hibernate-mapping>

A la clase Padre le hemos puesto abstract="true" porque en el ejemplo que vamos a mostrar es abstracta, puedes quitar ese atributo si tu clase Padre no es abstracta.

Como generator class del id hemos puesto "increment". Se puede poner "sequence" si la base de datos lo soporta. El motivo es que este fichero creará tres tablas (en realidad sólo dos al ser Padre abstracta) y los identificadores de las tres tablas deben ser distintos, así que debemos usar una secuencia compartida (Hibernate se encarga de crearla y compartirla) o bien decir a hibernate que cree los id incrementando de uno en uno. Esta segunda opción funciona en cualquier base de datos.

Luego hemos ido añadiendo un <union-subclass> por cada clase hija y dentro sólo hemos mapeado los atributos propios de la clase hija.

Una tabla por cada subclase (joins)

Aquí se crea una tabla por cada clase, pero sólo con los atributos específicos de esa clase, sin atributos heredados. Cada tabla hija se relaciona con la tabla padre a partir del índice. Cuando se consulta una clase hija, Hibernate consulta la tabla padre, la hija y hace el join de ambas

id atributoPadre
1 a
2 b
3 d


id_padre atributoHija1
2 c


id_padre atributoHija2
3 e

El fichero hbm.xml para esta jerarquía de clases podría ser el siguiente

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
	<class name="com.chuidiang.ejemplos.Padre">
		<id name="id">
			<generator class="native"></generator>
		</id>
		<property name="atributoPadre"></property>

		<joined-subclass name="com.chuidiang.ejemplos.Hija1">
			<key column="id_padre"></key>
			<property name="atributoHija1"></property>
		</joined-subclass>

		<joined-subclass name="com.chuidiang.ejemplos.Hija2">
			<key column="id_padre"></key>
			<property name="atributoHija2"></property>
		</joined-subclass>

	</class>
</hibernate-mapping>

Definimos el mapeo de la clase Padre y dentro añadimos tantos <joined-subclass> como clases hija tengamos. En cada una de ellas mapeamos los atributos y añadimos una key para referenciar al id del padre.


Referencias