Ejemplo sencillo con Hibernate

De ChuWiki
Saltar a: navegación, buscar

Ejemplo sencillo con Hibernate

Este ejemplo está extraido de la documentación oficial de Hibernate. Cuando intenté seguir ese ejemplo, me tropecé con varios puntos que hacían que el ejemplo no funcionara. El ejemplo aquí mostrado es una traducción libre de la documentación oficial a la que he añadido todos los detalles necesarios para que funcione correctamente a la primera. Todos los fuentes y ficheros de configuración de este tutorial están disponibles en ejemplo-hibernate

Vamos a hacer un ejemplo sencillo de código java usando Hibernate. Para el ejemplo necesitaremos, por supuesto, java; una base de datos Mysql con su conector, los jar que vienen con Hibernate core y también slf4j. Todo esto es necesario para un ejemplo sencillo, no son requerimientos de este ejemplo concreto. Veamos los jar necesarios con detalle:

Conector de MySQL

  • mysql-connector-java-5.0.4.jar (por supuesto, la versión adecuada a la versión de nuestro servidor de MySQL)

Hibernate core

Una vez descargado Hibernate core (hibernate-distribution-3.3.1.GA-dist.tar), dentro del directorio lib/required vienen los siguientes jar

  • antlr-2.7.6.jar
  • commons-collections-3.1.jar
  • dom4j-1.6.1.jar
  • javassist-3.4.GA.jar
  • jta-1.1.jar
  • slf4j-api-1.5.2.jar (NO utilizar este jar, ignorarlo)

slf4j

Descargando y desempaquetando slf4j tenemos un montón de jar. Sólo necesitamos los dos siguientes

  • slf4j-api-1.5.6.jar (En vez de el que venía con Hibernate, usar este, que es más moderno)
  • slf4j-log4j12-1.5.6.jar
  • log4j-1.2.14.jar Este jar lo necesita slf4j-log4j12-1.5.6

Fíjate que entre los de Hibernate core viene un jar slf4j-api-1.5.2.jar de versión más antigua que el que nos hemos bajado de slfj4. Sólo debemos usar uno de ellos y debe coincidir con la versión del slf4j-log4j12-1.5.6.jar.

Proyecto Maven con hibernate

Otra opción para no descargarnos todos estos jar a mano, es usar maven y crear un Proyecto maven con hibernate. En cualquier caso, una vez que tengamos todo lo necesario, veamos los pasos que hay que dar para nuestro ejemplo simple con Hibernate.

¿Qué cosas necesitamos crear para nuestro ejemplo sencillo con Hibernate

Necesitamos las siguientes cosas:

  • Por supuesto, una base de datos en MySQL a la que hemos llamado hibernate, con un usuario de nombre hibernate y password, ¿cómo no?, hibernate con permisos para hacer cosas.
  • Un bean Java. Este no es más que una clase java sencilla, con un constructor sin parámetros y una serie de atributos, a los que se puede acceder por medio de métodos set() y get().
  • Un fichero de mapeado xml. Este fichero sólo indica el bean java en qué tabla de base de datos se debe guardar y para cada uno de los atributos del bean, en qué columna de la tabla se guarda.
  • Un fichero xml de configuración de hibernate. Este fichero contiene los parámetros de conexión a la base de datos y un listado de los ficheros de mapeo xml. En nuestro ejemplo, sólo uno.
  • El main() del código, que configura una clase específica de Hibernate pasándole el fichero de configuración de Hibernate y, usando esa clase, es capaz de insertar primero y consultar después el bean java en la base de datos.

El bean Java

Nuestro java bean puede ser tan tonto como este

package com.chuidiang.ejemplos.ejemplo1.hibernate;

import java.util.Date;

public class Event {
        private Long id;
        private String title;
        private Date date;
        public Long getId() {
            return id;
        }
        private void setId(Long id) {
            this.id = id;
        }
        public String getTitle() {
            return title;
        }
        public void setTitle(String title) {
            this.title = title;
        }
        public Date getDate() {
            return date;
        }
        public void setDate(Date date) {
            this.date = date;
        }
        public String toString() {
            return id + " - " + title + " - " + date;
        }
}

Simplemente debemos advertir que el método setId() correspondiente al atributo id que se usará como clave primaria en la base de datos, es un método privado. Hibernate se las arregla para llamar a este método privado, así que de esta forma estamos impidiendo que nuestro código dé inadvertidamente un id a una instancia de esta clase. Sólo hibernate debe gestionar los id de las clases que se almacena en base de datos.

Ficheros de mapeo de Hibernate

El fichero de mapeo es un fichero xml en el que se indica el bean java en qué tabla de base de datos debe guardarse y para cada uno de sus atributos, en qué columna va.

Normalmente a estos ficheros de mapeo se les suele poner extensión .hbm.xml. Un ejemplo de fichero de mapeo puede ser este

<?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.hibernate.ejemplo1.Event">
		<id name="id" column="EVENT_ID">
			<generator class="native"></generator>
		</id>
		<property name="date" type="timestamp" column="EVENT_DATE"/>
		<property name="title"></property>
	</class>
</hibernate-mapping>
  • En la etiqueta <class...> se indica el nombre de la clase (com.chuidiang.ejemplos.hibernate.ejemplo1.Event) y opcionalmente podría indicarse con la propiedad table="EVENTO" el nombre de la tabla en base de datos. Al no indicarlo, se usará por defecto el mismo nombre de la clase "Event".
  • La etiqueta <id...> indica la clave del bean. Con el atributo name="id" se indica qué propiedad del bean es la clave y con el atributo column="EVENT_ID" se indica el nombre de la columna para la clave. Si no pusiésemos column, se usaría por defecto el name, es decir "id". La etiqueta <generator...> con el atributo class="native" le indica a hibernate que debe usar el método que proporcione la base de datos para la gesión de ids. En el caso de MySQL, será "auto-increment".
  • Luego van el resto de atributos del bean, etiquetados en etiquetas xml <property...>. Para cada uno se pone:
    • name="..." con el nombre del atributo del java bean.
    • type="..." con el tipo de dato que es (String, Date, etc). Cuando entre el tipo java y el de base de datos hay una relación clara, no se suele poner nada. El caso de Date es especial, ya que en java y las bases de datos pueden distinguir los tipos date (fecha), time (hora) y timestamp (fecha y hora), por lo que suele ser conveniente indicar cual queremos exactamente.
    • column="..." el nombre de la columna deseado en la base de dato. No indicando nada se usará por defecto el nombre del atributo del java bean.

Este fichero debe guardarse en algún sitio del classpath. En la documentación de hibernate lo guarda junto al fichero .java (donde luego vaya el .class compilado), así que lo haremos así

/proyecto/src/com/chuidiang/ejemplos/hibernate/ejemplo1/Event.java
/proyecto/src/com/chuidiang/ejemplos/hibernate/ejemplo1/Event.hbm.xml

Fichero de configuración de hibernate

También necesitamos un fichero de configuración general de Hibernate, también XML, en el que ponemos información sobre la base de datos que estamos manejando e incluimos todos los ficheros anteriores de mapeo. Un ejemplo para una base de datos MySQL puede ser el siguiente

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    
    <session-factory>
        <!-- Database connection settings -->
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost/prueba</property>
        <property name="connection.username">el usuario</property>
        <property name="connection.password">la password</property>

        <!-- JDBC connection pool (use the built-in) -->
        <property name="connection.pool_size">1</property>

        <!-- SQL dialect -->
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>

        <!-- Enable Hibernate's automatic session context management -->
        <property name="current_session_context_class">thread</property>

        <!-- Disable the second-level cache  -->
        <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>

        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>

        <!-- Drop and re-create the database schema on startup -->
        <property name="hbm2ddl.auto">create</property>

        <mapping resource="com/chuidiang/ejemplos/hibernate/ejemplo1/Person.hbm.xml" />
    </session-factory>

</hibernate-configuration>

Vemos que a base de etiquetas xml van los datos propios de la base de datos MySQL, como el driver, cadena de conexión, usuario, password, etc.

En dialect se pone una clase propia de Hibernate que corresponda con nuestra base de datos concreta. De esta forma hibernate sabrá usar el SQL no estándar de esa base de datos.

Es importante fijarse en el tag hbm2dll.auto. Si lo descomentamos, cuando arranquemos nuestra aplicación Java, Hibernate borrará si existen y luego creará en la base de datos las tablas tal cual están definidas en los distintos ficheros .hbm.xml. Si lo dejamos comentado, Hibernate NO creará las tablas. Lo normal suele ser dejarlo descomentado mientras desarrollamos, de forma que según generamos código, cada vez que arrancamos la aplicación, se regeneren todas las tablas de la base de datos. También la primera vez que arrancamos la aplicación definitiva, para que se creen las tablas la primera vez. Luego, se comenta cuando la aplicación ya está en funcionamiento normal, de manera que no perdamos los datos que hayamos ido generando.

Finalmente, con etiquetas <mapping...> vamos añadiendo los ficheros .hbm.xml que vamos utilizando.

El resto de propiedades en principio las dejaremos con los valores indicados. Puedes ver las posibles propiedades y valores que se pueden configurar en la referencia de hibernate

Dejaremos este fichero en el raíz de nuestro proyecto, de forma que podamos encontrarlo en el directorio de ejecución como "./hibernate1.cfg.xml"

Algo de código Java con Hibernate

Ahora, un pequeño ejemplo de código Java que inserta un registro en bd y luego lo lee. Antes de nada, necesitamos hacernos una pequeña clase, HibernateUtil, que nos de las conexiones con la base de datos. Puede ser tan tonta como esta

package com.chuidiang.ejemplos.hibernate.ejemplo1;

import java.io.File;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {
    private static final SessionFactory sessionFactory;
    static {
        try {
            // Create the SessionFactory from hibernate.cfg.xml
            sessionFactory = new Configuration().configure(new File("hibernate1.cfg.xml"))
                    .buildSessionFactory();
        } catch (Throwable ex) {
            // Make sure you log the exception, as it might be swallowed
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}

De esta forma, la SessionFactory será única y compartida por todos. En el método configure() hemos puesto nuestro fichero de configuración. Podemos llamar a configure() sin parámetros. Entonces hibernate buscará el fichero "./hibernate.cfg.xml". En nuestro caso, como haremos varios ejemplos en otros tutoriales, hemos usado "hibernate1.cfg.xml" y debemos, por tanto, indicarlo en la llamada a configure().

El código java con el main() puede ser como este

package com.chuidiang.ejemplos.hibernate.ejemplo1;

import java.util.Date;
import java.util.List;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.hibernate.Session;

import com.chuidiang.ejemplos.hibernate.ejemplo1.Event;

public class Ejemplo1 {
    private final static Logger log = Logger.getLogger(Ejemplo1.class);
    /**
     * @param args
     */
    public static void main(String[] args) {
        BasicConfigurator.configure();
        Logger.getLogger("org.hibernate").setLevel(Level.WARN);
        new Ejemplo1();
    }

    public Ejemplo1() {
        createAndStoreEvent("El Event", new Date());
        listEvents();
        HibernateUtil.getSessionFactory().close();
    }

    private Long createAndStoreEvent(String title, Date theDate) {
        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        session.beginTransaction();
        Event theEvent = new Event();
        theEvent.setTitle(title);
        theEvent.setDate(theDate);
        session.save(theEvent);
        session.getTransaction().commit();
        log.info("Insertado: "+theEvent);
        return theEvent.getId();
    }

    private List<Event> listEvents() {
        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        session.beginTransaction();
        List<Event> result = (List<Event>)session.createQuery("from Event").list();
        session.getTransaction().commit();
        for (Event evento : result) {
            log.info("Leido: "+evento);
        }
        return result;
    }

}

Lo primero que hacemos, con BasicConfigurator().configure() es dar una configuración por defecto a log4j, de forma que veamos el log por pantalla. La siguiente línea, Logger.getLogger("org.hibernate").setLevel(Level.WARN) configura log4j para que sólo muestre avisos o errores de org.hibernate, eliminando toda el log de debug o info, que es excesivo en hibernate.

En el constructor de Ejemplo1, llamamos a un método createAndStoreEvent() para insertar un Event en la base de datos y luego a un método listEvents() para listar los Event que hay en base de datos. En ambos métodos, lo primero que se hace es obtener una Session de hibernate, con HibernateUtil.getSessionFactory().getCurrentSession(). Una vez que la tenemos comenzamos una transacción con beginTransaction().

En el caso de la inserción, nos bastará con instanciar un bean Event, rellenar sus datos y finalmente llamar a session.save() pasándole el Event y luego llamar a session.getTransaction().commit() para hacer el commit en base de datos.

Para la consulta, session.createQuery("from Event").list() nos dará directamente una List<Event> con todos los eventos en base de datos. Sólo tenemos que recorrer la lista e ir sacándolos por pantalla.

Esta es la gracia de Hibernate. Nos olvidamos totalmente de las sentencias SQL de base de datos. Unicamente trabajamos con clases Event y haciendo llamadas sencillas a la Session de Hibernate, como save() o createQuery().list(), se realizan de forma transparente las inserciones y consultas en base de datos.


Asociaciones con Hibernate

Ahora puedes seguir en Ejemplo sencillo con asociaciones en Hibernate

Enlaces