Persistencia de Objetos Java: El Camino hacia Hibernate

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

Asumamos que queremos tener una pequea aplicacin donde poder almacenar los eventos a los que queremos asistir y quin los patrocina. Hemos decidido que queremos usar Hibernate para su almacenamiento, porque hemos odo que es lo ms 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 distribucin de Hibernate de su pgina 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 debera 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 cdigo:

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 necesitarn dicha id. Una buena idea cuando se construyen aplicaciones Hibernate es mantener dichas ids nicas ssparadas de la lgica de la aplicacin. Esto significa que no manipularemos la id en ninguna parte de nuestro cdigo, y dejaremos que Hibernate se ocupe de ella. De ah viene porqu el mtodo set de la id es privado, permitiendo que Hibernate lo utilice (Hibernate podra acceder a los mtodos set y get de propiedades para todas las visibilidades), pero lo aislamos de nosotros.

Tambin estmos usando un verdadero Long para la id, no un tipo long primitivo. Esto nos evitar quebraderos de cabeza ms 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 debera 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 cmo persistirla. Aqu es donde entra en juego el fichero de mapeo. El fichero de mapeo le dice a Hibernate qu debera almacenar en la base de datos - y cmo.

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 debera 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. Adems, 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 declaracin de la propiedad id. name="id" es el nombre de la propiedad - Hibernate usar los mtodos getId y setId para acceder a ella. El atributo column le dice a Hibernate que clumna 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 tcnica que se usar para la generacin de id -- en este caso usaremos un incremento, que es un mtodo de generacin muy simple, pero que ser suficiente para este ejemplo tan pequeo.

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 mtodos get y set utilizar.

Sin embargo, observars 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 debera parecerse a esto:

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

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

Adems 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 configuracin necesaria para la Conexin JDBC que utilizar Hibernate. La propiedad dialect especifica el SQLdialect que Hibernate deber generar. Luego especificamos que Hibernate delegar las transaciones a la conexin JDBC subyacente y especificamos un proveedor de cach (esto no tiene efecto porque todava no utilizamos ningn cach). La siguiente propiedad le dice a Hibernate que ajuste automticamente 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 aplicacin. Por conveniencia, creamos un fichero batch en nuestro directorio de trabajo que contenga todos los comandos necesarios para la complicacin. Bajo windows, se parecera 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 ests usando Linux, seguro que podrs 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 debera 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 aplicacin creamos otro fichero batch en el directorio de trabajo y lo llamamos run.bat, con el siguiente contenido (todo en una lnea):

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. Debera 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 ningn error todava -- pero an 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

Adems tenemos que aadir la siguiente lnea 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 aplicacin ejecutanto de nuevo build.bat, y lo ejecutamos de nuevo -- ahora si que deberamos ver informacin ms 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 algn trabajo con Hibernate.

Primero modificamos el mtodo 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 lnea de comandos, y si el primer argumento de nuestra aplicacin es store, tomamos el segundo argumento como un ttulo, creamos un nuevo Date y los pasamos los dos al mtodo 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 conexin JDBC.

Si ejecutramos la aplicacin 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 mtodo main un poco ms:

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 ms complejas con HQL, como veremos en las pginas siguiente.

COMPARTE ESTE ARTÍCULO

ENVIAR A UN AMIGO
COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN GOOGLE +
¡SÉ EL PRIMERO EN COMENTAR!
Conéctate o Regístrate para dejar tu comentario.