Hibernate 4: una introducción y un ejemplo básico

Introducción

Hibernate es un Framework de persistencia de Java. En esencia, la persistencia es la pervivencia de información en memoria para trabajar con ella o para traspasarla a otro lugar.

En el caso de la programación orientada a objetos, los frameworks de persistencia como Hibernate abren una sesión para trabajar con ellos y o bien utilizarlos durante la ejecución del programa o bien pasarlos a base de datos. Hablamos, entonces, de que un objeto está persistido cuando éste está en la sesión de Hibernate.

En los frameworks de persistencia existe otro concepto clave: el de transacción. En la sesión de Hibernate se abre también normalmente una transacción, que es la que controla todos los pasos que hay que dar con los objetos y, caso de que exista un error o fallo en medio del proceso, hace rollback, es decir, deshace lo hecho para evitar que nada quede a medias. El entorno de sesión y la transacción hacen de los frameworks de persistencia algo muy útil para trabajar con objetos y bases de datos.

Ejemplo básico – introducción con Eclipse e Hibernate Tools

En este ejemplo vamos a utilizar Eclipse Kepler for Java EE Developer (se recomienda Kepler por su integración con Maven aquí tenéis una excelente introducción a Maven) y una de las varias herramientas de Hibernate para ayudarnos a trabajar con Eclipse: Hibernate Tools for Indigo. Esta herramienta nos ayudará a crear el archivo de configuración, el fichero reveng.xml, la consola, los hbm.xml a partir de la base de datos, etc.

Para trabajar con Hibernate necesitamos:

  • un archivo de configuración (hibernate.cfg.xml) con los datos de conexión a la base de datos y el mapeo de los archivos *.hbm.xml (los ficheros de mapeo entre las clases y la base de datos). También se pueden usar annotations, aunque nosotores usaremos mapeo XML
  • los pojos y su correspondientes archivos hmb.xml, donde mapeamos los objetos con la base de datos y establecemos las relaciones.
  • una (opcional) clase con la que generar sesiones y controlar transacciones. Si no la creamos, podemos generar sesiones y transacciones en el mismo DAO. Estableciendo una clase creamos un constructor estático que nos permite crear la conexión y hacer un singleton con una instancia única de la conexión.

El flujo de trabajo es el siguiente:

  • Hibernate espera recibir un objeto y sus objetos relacionados para efectuar una acción sobre él.
  • Una vez lo recibe, abre una sesión (generada por la sessionFactory y como singleton, se llena de los datos del fichero de configuración de Hibernate y genera las conexiones dinámicamente para cada transacción) con la información que contiene el fichero de configuración hibernate.cfg.xml (lee el fichero, carga la información y crea el método getSessionFactory, que crea una sesión en memoria RAM y la retorna; en la sesión se traen los objetos mapeados a la memoria RAM; cuando hago un select o un get los objetos van a parar a la sesión de hibernate (son persistidos)).
  • Inicia la transacción, hace lo que tenga que hacer con ese objeto, cierra la transacción y cierra la sesión.
  • Si la operación es compleja e implica a muchos objetos relacionados, si hay un error o fallo en el proceso se establece un rollback para eliminar cualquier acción efectuada durante la sesión.

Ejemplo básico – pasos

1)    Creamos un poryecto Maven con el siguiente pom.xml

2)    Creamos la base de datos con el siguiente script.sql

3)    Si no las tenemos, instalamos las Hibernate Tools (Help -> Eclipse Marketplace -> Hibernate Tools for Indigo)

4)    Creamos el fichero de configuración hibernate.cfg.xml. Para ello nos situamos en nuestro proyecto y con el botón derecho New -> Other -> Hibernate -> Hibernate Configuration File (cfg.xml). Escogemos su situación (normalmente en resources, siguiendo la lógica de Maven) y ponemos los datos de la conexión a la base de datos.

5)    Creamos la consola de configuración de Hibernate (que le servirá al plugin entre otras cosas para generar código) (No veremos que Eclipse haya añadido nada, ya que se trata de una herramienta interna del plugin de Hibernate Tools)

6)    Creamos el fichero reveng.xml, que nos permitirá la ingeniería inversa) New -> Other -> Hibernate -> Hibernate Reverse Engineering File (reveng.xml). Nos situamos sobre el directorio resorces, ratificamos y en la página de configuración escogemos la consola que yahemos creado, le damos a Refresh para ver las bbdd e incluimos las tablas a mapear.

7)    Generamos el código poniéndonos sobre el iconos de las Hibernate Tools y seleccionamos Hibernate Code Generations Configurations…

8)    Llenamos los datos e indicamos qué queremos generar. Es buena idea volver a generar el fichero de configuración, pues en él se incluirá el mapeo de los ficheros hmb.xml.

9)    Si creamos las clases nosotros, sí podemos crear los hmb.xml mediante las Hibernate Tools y luego cambiar algunas cosillas. Para ello New -> Other -> Hibernate -> Hibernate Mapping file (hbm.xml), añadimos el package donde hemos creado las clases, nos aseguramos de que so las clases crradas, y las generamos. Es buena idea tenerlas en el directorio resources, junto con los otros ficheros de configuración y mapeo.

10) Tendremos que hacer, quizás, algunos cambios. Por ejemplo en mi caso:

  • No pilla la relación n-n, de manera que en el fichero cliente.hbm.xml:
<set name="productos" table="PRODUCTO" inverse="false" lazy="true">
    <key>
        <column name="IDCLIENTE" />
    </key>
    <one-to-many class="com.hibernate.model.Producto" />
</set>

Pasa a ser:

<set name="productos" table="CLIENTEPRODUCTO" inverse="false" lazy="true">
    <key>
        <column name="IDCLIENTE" />
    </key>
    <many-to-many class="com.hibernate.model.Producto" />
</set>

También ponemos:

<list-index column="idCliente"></list-index>
  • En el fichero Correoelectronico.hbm.xml:
<many-to-one name="cliente" class="com.hibernate.model.Cliente" fetch="join">
    <column name="CLIENTE" />
</many-to-one>

Pasa a ser:

<many-to-one name="cliente" class="com.hibernate.model.Cliente" fetch="join">
    <column name="IDCLIENTE" />
</many-to-one>
  • En el fichero Producto.hbm.xml:
<set name="clientes" table="CLIENTE" inverse="false" lazy="true">
    <key>
        <column name="IDPRODUCTO" />
    </key>
    <one-to-many class="com.hibernate.model.Cliente" />
</set>

Pasa a ser:

<set name="clientes" table="CLIENTEPRODUCTO" inverse="false" lazy="true">
    <key>
        <column name="IDPRODUCTO" />
    </key>
    <many-to-many class="com.hibernate.model.Cliente" />
</set>

11) Añadamos también cascade=”all” en los set de cliente.hbm.cfg para evitar el error object references an unsaved transient instance – save the transient instance before flushing

12) IMPORTANTE: en correoelectronico, cliente, identity:

<generator class="assigned" />

Pasa a ser

<generator class="identity" />

para evitar problemas con las foreign keys.

13) En dirección pasa a ser

<generator class="foreign">
    <param name="property">cliente</param>
</generator>

14) Como hemos dicho, la sesión y las transacciones las controlaremos desde una clase específica del DAO. Creamos un package q contenga los dao y añadimos el siguiente archivo HibernateHelper.java, que ya analizaremos convenientenmenet. Basicamente lo que ahce es inicializar una favctor´ñia de sesiones si no está ya incializada, y alrecibir la orden de guardar coge una sesión nueva, abre una transacción, ejecuta el sabe del objeto que se la ha enviado y cierra la transacción y la sesión.

15) Para probarlo podríamos hacerlo mediante JUnit, como haremos con las sesiones, pero vamos a crear un main, que tenéis en main.java.