Ficheros de mapeo de Hibernate desde las clases

1
32283

Ficheros de mapeo de Hibernate desde las clases

Introducción

En tutoriales anteriores hemos visto que, usando anotaciones, podemos crear clases que mapeen las entidades de una base de datos sin necesidad
de ficheros de configuración (del tipo «miEntidad.hbm.xml»).

Aunque esto es muy práctico, tiene el «inconveniente» (por ponerle pegas) de que las anotaciones sólo son válidas en entornos que empleen
Java 5 o superiores… Por tanto puede darse el caso de que una aplicción que hemos diseñado con anotaciones tenga que ser migrada para adaptarla
a un cliente que usa en su entorno Java 1.4 (y al que no podamos convencer para cambiar de versión de Java ;).

Esto supondría que tendríamos que eliminar del código todas las anotaciones y «picar» todo el código de los ficheros de mapeo de entidades.

En este tutorial vamos a intentar generar los ficheros de configuración de Hibernate de manera automática a partir de las clases anotadas, usando
para ello las Hibernate Tools

Aspectos como la descarga e instalación de Hibernate Tools quedan fuera de este tutorial, pues hay otro (arriba tenéis el enlace) en el que se
explica todo el proceso

Sí, sí, pero… ¿cómo se hace?

Una vez que hemos descargado las Hibernate Tools, tenemos dos opciones. Podemos usarlas como plugin de Eclipse o podemos usarlas mediante Ant

Como plugin de Eclipse

Una vez que tenemos las tools instaladas y la consola de configuración de Hibernate generada, vamos a usar dicha consola para hacer la magia…

Pinchamos en la flecha junto al botón adecuado de la barra de herramientas superior (el que está recuadrado)

En el desplegable que aparece, seleccionamos «Open Hibernate Code Generation Dialog», y se nos abre la ventana siguiente:

Pulsamos el botón de «Nueva configuración»:

En la ventana que aparece (pestaña «Main»), rellenamos el nombre de la nueva configuración, la configuración de consola que vamos a usar
y el directorio en el que se almacenarán los ficheros generados

En la pestaña «Exporters» marcamos la opción «Hibernate XML Mappings» y pulsamos el botón «Run»… y la magia está hecha

Abrimos uno de los ficheros generados:

<?xml version="1.0"?>
<!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.autentia.store.User" table="User">
        <id name="id" type="java.lang.Integer" access="field">
            <column name="id" />
            <generator class="native"></generator>
        </id>
        <property name="address" type="java.lang.String" access="field">
            <column name="address" />
        </property>
        <property name="creditCard" type="java.lang.String" access="field">
            <column name="creditCard" />
        </property>
        <property name="email" type="java.lang.String" access="field">
            <column name="email" />
        </property>
        <property name="name" type="java.lang.String" access="field">
            <column name="name" />
        </property>
        <property name="password" type="java.lang.String" access="field">
            <column name="password" />
        </property>
    </class>
</hibernate-mapping>

Usando Ant

Ya hemos visto que, usando el plugin de Eclipse, la cosa es bastante sencilla… Ahora vamos a ver cómo se podría hacer lo mismo con Ant.
Esta manera de resolver el problema nos da la ventaja de no necesitar Eclipse… un fichero BAT o un shell script podría lanzar automáticamente
el proceso de creación.

Lo primero que necesitamos es un fichero ejecutable de ant, que llamaremos «build.xml»

En él, hay que definir la tarea de Ant para Hibernate Tools (hibernatetool). Además, hay que indicarle el classpath donde poder encontrar la clase
que implementa la tarea. También vamos a necesitar las librerías tanto de Hibernate como de Hibernate Annotations, por lo que las pondremos ya en el
classpath:

<project basedir="."  >
	<property name="my.classpath" value="antClasspath"/>
	<path id="toolslib">
	 <path location="${my.classpath}/hibernate-tools.jar" />
	 <path location="antClasspath/hibernate-3.2.6.ga.jar" />
	 <path location="antClasspath/hibernate-commons-annotations-3.3.0.ga.jar"/>
	 <path location="antClasspath/hibernate-annotations-3.3.0.ga.jar"/>	
	</path>
		
	<taskdef name="hibernatetool" 
	         classname="org.hibernate.tool.ant.HibernateToolTask" 
	         classpathref="toolslib" />
	
</project>

Y ahora hay que meter la llamada a la tarea Ant propiamente dicha. La tarea precisa un sistema de trazas (commons-login)
y otras librerías que añadiremos al classpath (el resto de librerías necesarias las averiguamos por prueba/error; vemos lo que
dice que falta y «googleamos» 🙂

Otra cosa importante es que, cuando a la tarea le pasamos la ruta a las clases del proyecto, debemos pasarle la ruta a las clases compiladas

<project basedir="."  >
	<property name="my.classpath" value="antClasspath"/>
	<property name="build.dir" value="antHbmGeneratedFiles"/>
	
	<path id="toolslib">
	 <path location="${my.classpath}/hibernate-tools.jar" />
	 <path location="${my.classpath}/hibernate-3.2.6.ga.jar" />
	 <path location="${my.classpath}/hibernate-commons-annotations-3.3.0.ga.jar"/>
	 <path location="${my.classpath}/hibernate-annotations-3.3.0.ga.jar"/>	
	 <path location="${my.classpath}/commons-logging-1.1.1.jar"/>
	 <path location="${my.classpath}/commons-collections-3.2.1.jar"/>
	 <path location="${my.classpath}/dom4j-1.6.1.jar"/>
	 <path location="${my.classpath}/persistence-api-1.0.jar"/>
	 <path location="${my.classpath}/freemarker.jar" />
	 <path location="${my.classpath}/Tidy.jar"/>
	</path>
		
	<taskdef name="hibernatetool" 
	         classname="org.hibernate.tool.ant.HibernateToolTask" 
	         classpathref="toolslib" />
	
	<target name="init">
		<hibernatetool destdir="./${build.dir}">
			 <classpath>
			 
			   <path location="./target/classes" />
			  </classpath>
		 	<annotationconfiguration configurationfile="src/main/resources/hibernate.cfg.xml"/>
		 	<hbm2hbmxml/>
		</hibernatetool>
	</target>
	
</project>

Os adjunto enlaces a las librerías:

En cualquier caso, si utilizáis Maven en vuestros proyectos, la mayoría de estas librerías las podéis encontrar en los repositorios públicos.

Ejecutamos el Ant, y…

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 16-may-2008 13:31:59 by Hibernate Tools 3.2.1.GA -->
<hibernate-mapping>
    <class name="com.autentia.store.User" table="User">
        <id name="id" type="java.lang.Integer" access="field">
            <column name="id" />

            <generator class="native">
            </generator>
        </id>

        <property name="address" type="java.lang.String" access="field">
            <column name="address" />
        </property>

        <property name="creditCard" type="java.lang.String" access="field">
            <column name="creditCard" />
        </property>

        <property name="email" type="java.lang.String" access="field">
            <column name="email" />
        </property>

        <property name="name" type="java.lang.String" access="field">
            <column name="name" />
        </property>

        <property name="password" type="java.lang.String" access="field">
            <column name="password" />
        </property>
    </class>
</hibernate-mapping>

Como vemos, los ficheros generados de ambas maneras son iguales, salvo por algún salto de línea. Ahora solo falta comprobar que los ficheros
son correctos… Para ello, vamos a elminar las anotaciones de las clases (aunque no las quitemos, debería hacer caso a los ficheros antes que
a las anotaciones, pero para estar seguros, las quitamos). Además de quitar las anotaciones, en el «hibernate.cfg.xml» cambiamos todas las entradas
de tipo «<mapping class=»…» />» por entradas del tipo «<mapping resource=»mificherogenerado.hbm.xml» />».

Probando los ficheros

Lo primero que vemos es que, si tenemos clases anotadas que hereden de otras, obtenemos un error
al intentar ejecutar nuestro proyecto… ya que, si bien en las subclases sí que genera el atributo
«discriminator-value», en la clase base no genera la etiqueta «<discrminator column=»» type=»»>».
Veamos un ejemplo.

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 19-may-2008 9:32:08 by Hibernate Tools 3.2.1.GA -->
<hibernate-mapping>
    <class name="com.autentia.store.product.Product" table="Product">
        <id name="id" type="java.lang.Integer">
            <column name="id" />

            <generator class="native">
            </generator>
        </id>

        <property name="description" type="java.lang.String">
            <column name="description" />
        </property>

        <property name="name" type="java.lang.String">
            <column name="name" />
        </property>

        <property name="price" type="float">
            <column name="price" not-null="true" />
        </property>
    </class>
</hibernate-mapping>

Debería tener este otro aspecto:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 19-may-2008 9:32:08 by Hibernate Tools 3.2.1.GA -->
<hibernate-mapping>
    <class name="com.autentia.store.product.Product" table="Product">
        <id name="id" type="java.lang.Integer">
            <column name="id" />

            <generator class="native">
            </generator>
        </id>
<discriminator column="Type" type="string"/>
        <property name="description" type="java.lang.String">
            <column name="description" />
        </property>

        <property name="name" type="java.lang.String">
            <column name="name" />
        </property>

        <property name="price" type="float">
            <column name="price" not-null="true" />
        </property>
    </class>
</hibernate-mapping>

Por otro lado, hay que tener en cuenta que nuestras clases tienen que tener los getters y setters para que se pueda acceder
a los campos de la entidad

Otro fallo que he encontrado al generar los ficheros de mapeo automáticamente es que no convierte las anotaciones «@cascade». Por ejemplo,
el siguiente fragmento de código

@OneToMany(cascade = CascadeType.ALL)
private List<BuycartProduct> buycartProducts = new ArrayList<BuycartProduct>();

Lo traduce de la siguiente manera:

<!-- Todo el XML anterior -->
<bag name="buycartProducts" inverse="false">
    <key>
        <column name="Buycart_id" not-null="true" />
    </key>

    <many-to-many entity-name="com.autentia.store.BuycartProduct">
        <column name="buycartProducts_id" not-null="true" />
    </many-to-many>
</bag>
<!-- Todo el XML posterior -->

Como podéis ver, la información sobre las acciones que deben ejecutarse en cascada se ha perdido… Esto causa problemas a la hora de
persistir el objeto si no se persiste también explícitamente la entidad asociada («BuycartProduct» en este caso).
Lo correcto habría sido

<!-- Todo el XML anterior -->
<bag name="buycartProducts" inverse="false" cascade="all">
    <key>
        <column name="Buycart_id" not-null="true" />
    </key>

    <many-to-many entity-name="com.autentia.store.BuycartProduct">
        <column name="buycartProducts_id" not-null="true" />
    </many-to-many>
</bag>
<!-- Todo el XML posterior -->

Una vez que hemos tomado estas precauciones, empaquetamos nuestra aplicación y la desplegamos en nuestro
servidor de aplicaciones (en mi caso, un Tomcat 6.0.16) y…

Conclusiones

Como podéis ver, es fácil transformar nuestro entorno de Hibernate basado en clases anotadas para usar ficheros de configuración
XML, usando las Hibernate Tools… No obstante, si lo que queréis es migrar una aplicación con anotaciones a un entorno que utilice Java 1.4,
recordad que no sólo hay que quitar las anotaciones, sino que hay que eliminar también la utilización de Generics
(del tipo «List<String>»). Lo demás, como habéis visto, es sencillo…

Pero si a pesar de esta sencillez preferís que otros lo hagan por vosotros, os recuerdo que podéis contratar a
Autentia, expertos en el desarrollo de aplicaciones usando las tecnologías más avanzadas
(Spring, Hibernate, JSF, ICEFaces…)

1 COMENTARIO

  1. Tengo una consulta… A ver si me podéis ayudar…
    Estoy aprendiendo un poco y tengo un par que al ejecutarse se conecta a una bbdd creada, si está vacía crea las tablas, y sino las respeta…
    Hasta ahí bien, pero tengo una tabla donde guardo estadísticas y para identificar al usuario tengo una foreing key que relaciona un número con el usuario… Pero al ir a borrar el usuario no me permite por la foreing key dear la tabla, si la borrow manualmente funciona correctamente.
    Ahora en hibernate ¿Como debería definir el para conseguir eso? He estado probando y me gustaría que cuando se generarán las estadísticas se escribiese el valor correspondiente, y me permita borrar el usuario. Gracias Salu2!!

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad