icono_twiter
Daniel Hernandez del Peso

Consultor tecnológico de desarrollo de proyectos informáticos.

Ingeniero en Informática

Puedes encontrarme en Autentia: Ofrecemos servicios de soporte a desarrollo, factoría y formación

Somos expertos en Java/J2EE

Ver todos los tutoriales del autor

Fecha de publicación del tutorial: 2008-06-02

Tutorial visitado 21.663 veces Descargar en PDF
Ficheros de mapeo de Hibernate desde las clases

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...)

A continuación puedes evaluarlo:

Regístrate para evaluarlo

Por favor, vota +1 o compártelo si te pareció interesante

Share |
Anímate y coméntanos lo que pienses sobre este TUTORIAL: