Persistencia de Objetos Java: El Camino hacia Hibernate

Para empezar haremos una aplicaci�n Hibernate muy sencilla basada en la consola. Usaremos una base de datos en-memoria, por eso no tendremos que instalar ning�n servidor.

Asumamos que queremos tener una peque�a aplicaci�n donde poder almacenar los eventos a los que queremos asistir y qui�n los patrocina. Hemos decidido que queremos usar Hibernate para su almacenamiento, porque hemos o�do que es lo m�s en persistencia ;-)

Lo primero que tenemos que hacer es configurar nuestro directorio de trabajo y poner en �l todos los ficheros jar que necesitamos. Tenemos que descargar la distribuci�n de Hibernate de su p�gina de descarga. Extraer los jars necesarios desde el archivo de Hibernate. Los situaremos en un directorio lib bajo el directorio de trabajo, tu despliegue de directorios deber�a parecerese a esto:

.
   +lib
      cglib2.jar
      commons-logging.jar  
      hibernate2.jar      
      jta.jar    
      odmg.jar
      commons-collections.jar  
      dom4j.jar            
      jdbc2_0-stdext.jar  
      log4j.jar 

.�La Primera Clase

Lo primero que haremos ser� crear una clase que represente los eventos que queremos almacenar. Esta ser� un simple Java Bean, que contenga algunas propiedades. Veamos el c�digo:

package de.gloegl.road2hibernate;

public class Event {
    private String title;
    private Date date;
    private Long id;

    public Long getId() {
        return id;
    }

    private void setId(Long id) {
        this.id = id;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}

Aqu� tenemos algunas cosas que merecen la pena:

La propiedad id es una identificador �nico para el Event -- todos nuestros objetos persistentes necesitar�n dicha id. Una buena idea cuando se construyen aplicaciones Hibernate es mantener dichas ids �nicas ssparadas de la l�gica de la aplicaci�n. Esto significa que no manipularemos la id en ninguna parte de nuestro c�digo, y dejaremos que Hibernate se ocupe de ella. De ah� viene porqu� el m�todo set de la id es privado, permitiendo que Hibernate lo utilice (Hibernate podr�a acceder a los m�todos set y get de propiedades para todas las visibilidades), pero lo aislamos de nosotros.

Tambi�n est�mos usando un verdadero Long para la id, no un tipo long primitivo. Esto nos evitar� quebraderos de cabeza m�s tarde -- utiliza siempre Objetos para la propiedad id, nunca tipos primitivos (si es posible).

Situaremos este fichero en un directorio llamado src en nuestro directorio de trabajo. El directorio deber�a aparecer de esta forma:


.
   +lib
      <hibernate jars>
   +src
      +de
         +gloegl
             +road2hibernate
                Event.java

.�El Fichero de Mapeo

Como ya tenemos nuestra clase para almacenarla en la base de datos, debemos decirle a Hibernate c�mo persistirla. Aqu� es donde entra en juego el fichero de mapeo. El fichero de mapeo le dice a Hibernate qu� deber�a almacenar en la base de datos - y c�mo.

La estructura exterior de un fichero de mapeo se parece a esto:


<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//hibernate/hibernate Mapping DTD 2.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping>

</hibernate-mapping>

Entre las dos etiquetas <hibernate-mapping>, incluiremos un elemento class, donde podemos declarar a que clase se refiere este mapeo y a qu� tabla de nuestra base de datos SQL se deberia mapear. El paso 2 de nuestro documento de mapeo se deber�a parecer a esto:


<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//hibernate/hibernate Mapping DTD 2.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping>

        <class name="de.gloegl.road2hibernate.Event" table="EVENTS">
                
        </class>

</hibernate-mapping>

Lo que hemos hecho hasta ahora es decirle a Hibernate que persista nuestra clase Event en la tabla EVENTS. Ahora tendremos que dar a Hibernate la propiedad a utilizar como identificador �nico -- que es por lo que hemos incluido la propiedad id. Adem�s, como no queremos preocuparnos de manejar este valor de id, tenemos que decirle a Hibernate como generar estos ids. Incluyendo esto, nuestro fichero de mapeo tendr� este aspecto:


<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//hibernate/hibernate Mapping DTD 2.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping>

        <class name="de.gloegl.road2hibernate.Event" table="EVENTS">
                <id name="id" column="uid" type="long">
                        <generator class="increment"/>
                </id>
        </class>

</hibernate-mapping>

�Qu� significa todo esto? El elemento <id> es la declaraci�n de la propiedad id. name="id" es el nombre de la propiedad - Hibernate usar� los m�todos getId y setId para acceder a ella. El atributo column le dice a Hibernate que c�lumna de la tabla EVENTS contendr� el id. El atributo type le dice a Hibernate el tipo de la propiedad - en este caso un long.

El elemento <generator> especifica la t�cnica que se usar� para la generaci�n de id -- en este caso usaremos un incremento, que es un m�todo de generaci�n muy simple, pero que ser� suficiente para este ejemplo tan peque�o.

Finalmente tenemos que incluir las declaraciones para las propiedades persistentes en el fichero de mapeo:


<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//hibernate/hibernate Mapping DTD 2.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping>

        <class name="de.gloegl.road2hibernate.Event" table="EVENTS">
                <id name="id" column="uid" type="long">
                        <generator class="increment"/>
                </id>
                <property name="date" type="timestamp"/>
                <property name="title" column="eventtitle"/>
        </class>

</hibernate-mapping>

Tenemos que observar algunas cosas de aqu�. Al principio, igual que en el elemento <id>, el atributo name del elemento <property> le dice a Hibernate que m�todos get y set utilizar.

Sin embargo, observar�s que la propiedad title contiene un atributo column, y el atributo date no lo tiene. Esto es posible porque cuando no se utiliza el atributo column, Hibernate usar� por defecto el nombre de la propiedad como el nombre de columna.

La siguiente cosa interesante es que la propiedad title carece de un atributo type. Otra vez, Hibernate intentar� determinar el tipo correcto por s� mismo. Sin embargo, algunas veces Hibernate simplemente no puede hacer esto y tenemos que especificar el tipo - como es el caso de la propiedad date. Hibernate no puede saber si la propiedad se mapear� a una columna date, timestamp o time, por eso tenemos que especificarlo.

Situaremos el mapeo en un fichero llamado Event.hbm.xml en el mismo directorio donde tenemos la clase Event. Tu estructura de directorios deber�a parecerse a esto:


.
   +lib
      <hibernate jars>
   +src
      +de
         +gloegl
             +road2hibernate
                Event.java
                Event.hbm.xml

.�Configuraci�n y Base de Datos

Ahora que ya tenemos nuestra clase persistente y el fichero de mapeo es hora de configurar Hibernate. Antes de hacer esto, necesitaremos una base de datos, vamos y obtenemos HSQLDB, una base de datos SQL en-memoria basada en Java. Lo que necesitamos es copiar el fichero hsqldb.jar del directorio lib de la descarga a nuestro directorio lib dentro del directorio de trabajo, que quedar� de esta forma:


.
   +lib
      <hibernate jars>
      hsqldb.jar
   +src
      <Aqu� va el fichero fuente y el de mapeo>

Adem�s crearemos un directorio data justo debajo del directorio de trabajo, donde hsqldb almacenar� sus ficheros.

Ahora podemos configurar Hibernate utilizando un fichero XML, que llamaremos hibernate.cfg.xml y que situaremos directamente en el directorio src de nuestro directorio de trabajo. Este fichero se parecer� a esto:


<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//hibernate/hibernate Configuration DTD 2.0//EN"

 "http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">

<hibernate-configuration>

    <session-factory>
        <property name="hibernate.connection.driver_class">org.hsqldb.jdbcDriver</property>
        <property name="hibernate.connection.url">jdbc:hsqldb:data/test</property>
        <property name="hibernate.connection.username">sa</property>
        <property name="hibernate.connection.password"></property>
        <property name="dialect">net.sf.hibernate.dialect.HSQLDialect</property>
        <property name="show_sql">true</property>
        <property name="transaction.factory_class">
             net.sf.hibernate.transaction.JDBCTransactionFactory
        </property>
        <property name="hibernate.cache.provider_class">
             net.sf.hibernate.cache.HashtableCacheProvider
        </property>
        <property name="hibernate.hbm2ddl.auto">update</property>

        <mapping resource="de/gloegl/road2hibernate/data/Event.hbm.xml"/>

    </session-factory>

</hibernate-configuration>

Los primeros cuatro elementos <property> contienen la configuraci�n necesaria para la Conexi�n JDBC que utilizar� Hibernate. La propiedad dialect especifica el SQLdialect que Hibernate deber� generar. Luego especificamos que Hibernate delegar� las transaciones a la conexi�n JDBC subyacente y especificamos un proveedor de cach� (esto no tiene efecto porque todav�a no utilizamos ning�n cach�). La siguiente propiedad le dice a Hibernate que ajuste autom�ticamente las tablas en la base de datos de acuerdo a nuestros mapeos. Finalmente le damos el path a nuestro fichero de mapeo.

.�Cosntruir

Finalmente empezamos a construir nuestra primera aplicaci�n. Por conveniencia, creamos un fichero batch en nuestro directorio de trabajo que contenga todos los comandos necesarios para la complicaci�n. Bajo windows, se parecer�a a esto:


javac -classpath .\lib\hibernate2.jar -d bin src\de\gloegl\road2hibernate\*.java
copy /Y src\hibernate.cfg.xml bin
copy /Y src\de\gloegl\road2hibernate\*.xml bin\de\gloegl\road2hibernate

Situamos este fichero llamado build.bat en nuestro directorio de trabajo. Si est�s usando Linux, seguro que podr�s crear un script equivalente...

Finalmente creamos el subdirectorio bin en nuestro directorio de trabajo para situar ah� las clases compiladas.

.�Ejecutar

Ahora crearemos un sencilla clase que arrancar� Hibernate. Se parecer� a esto:


package de.gloegl.road2hibernate;

import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.cfg.Configuration;

public class EventManager {

    private SessionFactory sessionFactory;

    public EventManager() {
        try {
            System.out.println("Initializing Hibernate");
            sessionFactory = new Configuration().configure().buildSessionFactory();
            System.out.println("Finished Initializing Hibernate");
        } catch (HibernateException e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        EventManager instance = new EventManager();
        System.exit(0);    
    }

}

Esta clase simplemente crea un ejemplar de s� mismo, y crea un ejemplar de SessionFactory en su constructor. Situamos el fichero EventManager.java en el diectorio adecuado. Nuestra estructura de directorios se deber�a parecer a �sta:


.
   +lib
      cglib2.jar
      commons-logging.jar  
      hibernate2.jar      
      jta.jar    
      odmg.jar
      commons-collections.jar  
      dom4j.jar            
      jdbc2_0-stdext.jar  
      log4j.jar 
      hsqldb.jar
   +src
      +de
         +gloegl
             +road2hibernate
                Event.java
                Event.hbm.xml
                EventManager.java
      hibernate.cfg.xml
   +data
   build.bat

Para ejecutar nuestra aplicaci�n creamos otro fichero batch en el directorio de trabajo y lo llamamos run.bat, con el siguiente contenido (todo en una l�nea):


java -classpath .\lib\hibernate2.jar;.\lib\jta.jar;.\lib\commons-logging.jar;.\lib\hsqldb.jar;
.\lib\cglib2.jar;.\lib\commons-collections.jar;.\lib\dom4j.jar;.\lib\odmg.jar;
.\lib\jdbc2_0-stdext.jar;.\bin de.gloegl.road2hibernate.EventManager %1 %2 %3 %4 %5

Ahora compilamos todos los ficheros fuentes ejecutando el fichero build.bat desde el directorio de trabajo y lo ejecutamos utilizando el fichero run.bat. Deber�a producir esta salida:


Initializing Hibernate
log4j:WARN No appenders could be found for logger (net.sf.hibernate.cfg.Environment).
log4j:WARN Please initialize the log4j system properly.
Finished Initializing Hibernate

Somos felices porque no hemos obtenido ning�n error todav�a -- pero a�n queremos ver los que est� haciendo Hibernate durante la arrancada y queremos ver sus avisos, por eso tenemos que configurar log4j. Esto se hace poniendo todo esto en un fichero llamado log4j.properties en nuestro directorio src:


log4j.rootCategory=INFO, A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-5p - %m%n

Adem�s tenemos que a�adir la siguiente l�nea al fichero build.bat:

copy /Y src\log4j.properties bin

Todo lo que esto hace es decirle a log4j que escriba toda la salida de log por la consola.

Ahora recompilamos la aplicaci�n ejecutanto de nuevo build.bat, y lo ejecutamos de nuevo -- ahora si que deber�amos ver informaci�n m�s detalladas de lo que est� haciendo Hibernate.

.�Trabajar con Persistencia

Como ya hemos terminado de configurar Hibernate y nuestros mapeos, y nos hemos aprovechado de Hibernate y hemos persistido algunos objetos. Ahora ajustaremos nuestra clase EventManager para realizar alg�n trabajo con Hibernate.

Primero modificamos el m�todo main:


public static void main(String[] args) throws java.text.ParseException {
    EventManager instance = new EventManager();
    if (args[0].equals("store")) {
        String title = args[1];
        Date theDate = new Date();
        instance.store(title, theDate);
    }
    System.exit(0);    
}

Ahora leemos algunos argumentos de la l�nea de comandos, y si el primer argumento de nuestra aplicaci�n es store, tomamos el segundo argumento como un t�tulo, creamos un nuevo Date y los pasamos los dos al m�todo store, donde est� lo realmente interesante:


private void store(String title, Date theDate) {        
    try {
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();
            
        Event theEvent = new Event();
        theEvent.setTitle(title);
        theEvent.setDate(theDate);
        
        session.save(theEvent);
        
        tx.commit();
        session.close();
    } catch (HibernateException e) {
        e.printStackTrace();
    }
}

�A qu� est� bien? Simplemente creamos un nuevo objeto Event, para que lo maneje Hibernate. Hibernate ahora se ocupa de crear el SQL, y enviarlo a base de datos. Incluso podemos arrancar y parar las transaciones que Hibernate delegar� en la conexi�n JDBC.

Si ejecut�ramos la aplicaci�n con run.bat store Party se crear� un objeto Event y se persistir� en la base de datos.

Pero ahora queremos listar nuestros Eventos almacenados, modificamos el m�todo main un poco m�s:


public static void main(String[] args) throws java.text.ParseException {
    EventManager instance = new EventManager();
    if (args[0].equals("store")) {
        String title = args[1];
        Date theDate = new Date();
        instance.store(title, theDate);
    } else if (args[0].equals("list")) {
        List events = instance.listEvents();
        for (int i = 0; i<events.size(); i++) {
            Event theEvent = (Event) events.get(i);
            System.out.println("Event " + theEvent.getTitle() + " Time: " + theEvent.getDate());
        }
    }
    System.exit(0);    
}

Cuando el primer argumento sea list, llamamos a listEvents() e imprimimos todos los objetos Event contenidos en la lista devuelta. listEvents() es donde sucede todo lo interesante:


private List listEvents() {
    try {
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();
                    
        List result = session.find("from Event");
        
        tx.commit();
        session.close();
        
        return result;
    } catch (HibernateException e) {
        throw new RuntimeException(e.getMessage());
    }
}    

Lo que hacemos aqu� es utilizar una consulta HQL (Hibernate Query Language) para cargar todos los eventos que existen en la base de datos. Hibernate generar� la sentencia SQL apropiada, la enviar� a la base de datos y rellenar� objetos Event con lo datos. Podemos crear consultas m�s complejas con HQL, como veremos en las p�ginas siguiente.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP