Tutorial Hibernate

248
10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 1: Persistiendo Objetos Simples usando Mapeos en XML www.javatutoriales.com/2009/05/hibernate-parte-1-persistiendo-objetos.html 1/29 Blog dedicado a temas de programación actuales usando el lenguaje de programación Java y las últimas versiones de sus APIs y Herram Tutoriales de Programacion Java Página principal Página de Concursos Tutoriales UPAO 2010 Presentaciones Capacitación VIERNES, 1 DE M AYO DE 2009 HIbernate - Parte 1: Persistiendo Objetos Simples usando Mapeos en XML En la mayoría de las aplicaciones empresariales, si no es que en todas, una parte muy importante es el almacenamiento de datos de forma que estos datos puedan sobrevivir más allá del tiempo que nuestra aplicación está encendida (ya sea en una aplicación standalone o en una aplicación web). El almacén de datos más común son las bases de datos relacionales. La naturaleza de estas bases hace que no sea tan fácil usarlas en el almacenamiento de datos en aplicaciones orientadas a objetos (por las diferencias entre el modelo de datos de objetos y el modelo de datos relacionales), ya que para guardar un objeto debemos extraer cada una de sus propiedades que queremos persistir y armar con ellos una sentencia INSERT de SQL . De la misma forma, cuando queremos recuperar los datos de un objeto, debemos usar una sentencia SELECT de SQL y después extraer el valor de cada una de las columnas recuperadas y llenar así nuestro objeto. Esto puede no parecer un problema en aplicaciones pequeñas, pero cuando comenzamos a tener muchos objetos que debemos guardar se vuelve algo muy pesado y engorroso, además de que consume mucho de nuestro tiempo que podríamos dedicar a mejorar la lógica de la aplicación, o a realizar pruebas de la misma. Esto se hizo de esta manera durante muchos años, hasta que comenzaron a surgir las soluciones de Mapeo Objeto/Relacional (ORM por sus siglas en inglés). El mapeo objeto/relacional se refiere a una técnica de mapear representaciones de datos de un modelo de objetos a un modelo de datos relacionales con un esquema basado en SQL . Hibernate, como la definen sus autores, es una herramienta de mapeo objeto/relacional para ambientes Java. Además no solo se encarga del mapeo de clases Java a tablas de la base de datos (y de regreso), sino que también maneja los queries y recuperación de datos, lo que puede reducir de forma significativa el tiempo de desarrollo que de otra forma gastaríamos manejando los datos de forma manual con SQL y JDBC , encargándose de esta forma de alrededor del 95% de las tareas comunes relacionadas con la persistencia de datos, manejando todos los problemas relativos con la base de datos particular con la que estemos trabajando, de forma transparente para nosotros como desarrolladores. Entonces, si cambiamos el manejador de base de datos no será necesario que modifiquemos todo el SQL que ya teníamos para adaptarse al SQL que maneja la nueva base de datos. Solo será necesario modificar una línea en un archivo de configuración de Hibernate, y este se encargará del resto. En esta serie de tutoriales veremos cómo usar Hibernate para realizar las tareas más comunes cuando trabajamos con bases de datos. Existen dos formas de hacer los mapeos en Hibernate, la primera es a través de archivos de mapeo en XML, que es la forma que veremos en este primer tutorial. La otra forma es usando anotaciones , que dejaremos para la segunda. Así que comencemos. Para los tutoriales usaremos MySQL 5.1 como base de datos. También deben bajar el conector para Java versión 5.1.7. Creamos una base de datos llamada "pruebahibernate ". No se preocupen por crear alguna tabla, ya que haremos que sea el propio Hibernate el que las genere. Ahora comencemos con nuestra aplicación. Lo primero que haremos es crear una biblioteca de NetBeans con los jars básicos de Hibernate, de esta forma cada vez que queramos usar Hibernate en un proyecto solo tendremos que agregar esta biblioteca. En realidad no crearemos esta biblioteca desde cero, ya que NetBeans ya tiene una biblioteca de Hibernate 3.2.5, solo actualizaremos la biblioteca con la última versión de Hibernate, la 3.3.1 GA. Así que descargamos la última versión del core de Hibernate desde la página de descarga de Hibernate. Una vez que descarguemos y descomprimamos el archivo correspondiente veremos que en la raíz hay dos jars de Hibernate: "hibernate3.jar " y "hibernate-testing.jar ". "hibernate3.jar " contiene las clases principales para el uso de Hibernate. Existen varias clases de soporte que son usadas por Hibernate para su funcionamiento normal. Algunas son opcionales y otras son requeridas. Afortunadamente estas se encuentran separadas en el mismo archivo que hemos descargado. Las clases obligatorias, que son las que nos interesan en este caso, se encuentran en el directorio "lib\required ". Bien, entonces para actualizar la biblioteca abrimos el NetBeans y nos dirigimos al menú "Tools -> Libraries ": En la ventana que se abre buscamos la biblioteca "Hibernate " y la seleccionamos. Con esto se mostrarán los archivos que la conforman. Seleccionamos todos los archivos y presionamos el botón "Remove " para eliminarlos de la biblioteca. DONACIONES JAVA TUTORIALES EN FAC Java Tutori Me gusta A 7895 personas les gusta Plug-in social de Faceboo Java Tutor Amigo prog estás todo e código, esc recomendac hechas para esto?... Gra Rubira.... No olviden p sentencia w un borraro e datos.. http://www.y watch?v=i_c Compartir 14 Más Siguiente blog»

description

Tutorial de Hibernate muy completo

Transcript of Tutorial Hibernate

Page 1: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

www.javatutoriales.com/2009/05/hibernate-parte-1-persistiendo-objetos.html 1/29

Blog dedicado a temas de programación actuales usando el lenguaje de programación Java y las últimas versiones de sus APIs y Herramientas.

Tutoriales de Programacion Java

Página principal Página de Concursos Tutoriales UPAO 2010 Presentaciones Capacitación

VIERNES, 1 DE MAYO DE 2009

HIbernate - Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

En la mayoría de las aplicaciones empresariales, si no es que en todas, una parte muy importante es el almacenamiento de datos de

forma que estos datos puedan sobrevivir más allá del tiempo que nuestra aplicación está encendida (ya sea en una aplicación standalone

o en una aplicación web).

El almacén de datos más común son las bases de datos relacionales. La naturaleza de estas bases hace que no sea tan fácil usarlas en

el almacenamiento de datos en aplicaciones orientadas a objetos (por las diferencias entre el modelo de datos de objetos y el modelo de

datos relacionales), ya que para guardar un objeto debemos extraer cada una de sus propiedades que queremos persistir y armar con

ellos una sentencia INSERT de SQL. De la misma forma, cuando queremos recuperar los datos de un objeto, debemos usar una

sentencia SELECT de SQL y después extraer el valor de cada una de las columnas recuperadas y llenar así nuestro objeto.

Esto puede no parecer un problema en aplicaciones pequeñas, pero cuando comenzamos a tener muchos objetos que debemos guardar

se vuelve algo muy pesado y engorroso, además de que consume mucho de nuestro tiempo que podríamos dedicar a mejorar la lógica de

la aplicación, o a realizar pruebas de la misma.

Esto se hizo de esta manera durante muchos años, hasta que comenzaron a surgir las soluciones de Mapeo Objeto/Relacional (ORM

por sus siglas en inglés). El mapeo objeto/relacional se refiere a una técnica de mapear representaciones de datos de un modelo de

objetos a un modelo de datos relacionales con un esquema basado en SQL.

Hibernate, como la definen sus autores, es una herramienta de mapeo objeto/relacional para ambientes Java. Además no solo se

encarga del mapeo de clases Java a tablas de la base de datos (y de regreso), sino que también maneja los queries y recuperación de

datos, lo que puede reducir de forma significativa el tiempo de desarrollo que de otra forma gastaríamos manejando los datos de forma

manual con SQL y JDBC, encargándose de esta forma de alrededor del 95% de las tareas comunes relacionadas con la persistencia de

datos, manejando todos los problemas relativos con la base de datos particular con la que estemos trabajando, de forma transparente

para nosotros como desarrolladores. Entonces, si cambiamos el manejador de base de datos no será necesario que modifiquemos todo el

SQL que ya teníamos para adaptarse al SQL que maneja la nueva base de datos. Solo será necesario modificar una línea en un archivo de

configuración de Hibernate, y este se encargará del resto.

En esta serie de tutoriales veremos cómo usar Hibernate para realizar las tareas más comunes cuando trabajamos con bases de datos.

Existen dos formas de hacer los mapeos en Hibernate, la primera es a través de archivos de mapeo en XML, que es la forma que

veremos en este primer tutorial. La otra forma es usando anotaciones, que dejaremos para la segunda. Así que comencemos.

Para los tutoriales usaremos MySQL 5.1 como base de datos. También deben bajar el conector para Java versión 5.1.7.

Creamos una base de datos llamada "pruebahibernate". No se preocupen por crear alguna tabla, ya que haremos que sea el propio

Hibernate el que las genere. Ahora comencemos con nuestra aplicación.

Lo primero que haremos es crear una biblioteca de NetBeans con los jars básicos de Hibernate, de esta forma cada vez que queramos

usar Hibernate en un proyecto solo tendremos que agregar esta biblioteca. En realidad no crearemos esta biblioteca desde cero, ya que

NetBeans ya tiene una biblioteca de Hibernate 3.2.5, solo actualizaremos la biblioteca con la última versión de Hibernate, la 3.3.1 GA.

Así que descargamos la última versión del core de Hibernate desde la página de descarga de Hibernate.

Una vez que descarguemos y descomprimamos el archivo correspondiente veremos que en la raíz hay dos jars de Hibernate:

"hibernate3.jar" y "hibernate-testing.jar". "hibernate3.jar" contiene las clases principales para el uso de Hibernate.

Existen varias clases de soporte que son usadas por Hibernate para su funcionamiento normal. Algunas son opcionales y otras son

requeridas. Afortunadamente estas se encuentran separadas en el mismo archivo que hemos descargado. Las clases obligatorias, que

son las que nos interesan en este caso, se encuentran en el directorio "lib\required".

Bien, entonces para actualizar la biblioteca abrimos el NetBeans y nos dirigimos al menú "Tools -> Libraries":

En la ventana que se abre buscamos la biblioteca "Hibernate" y la seleccionamos. Con esto se mostrarán los archivos que la

conforman. Seleccionamos todos los archivos y presionamos el botón "Remove" para eliminarlos de la biblioteca.

DONACIONES

JAVA TUTORIALES EN FACEBOOK

Java Tutoriales

Me gusta

A 7895 personas les gusta

Plug-in social de Facebook

Java Tutoriales

Amigo programador, tú que

estás todo el día picando

código, escucha estas

recomendaciones sólo

hechas para ti..... ¿qué es

esto?... Gracias Jorge

Rubira....

No olviden poner la

sentencia where al hacer

un borraro en la base de

datos..

http://www.youtube.com/

watch?v=i_cVJgIz_Cs

Compartir 14 Más Siguiente blog»

Page 2: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

www.javatutoriales.com/2009/05/hibernate-parte-1-persistiendo-objetos.html 2/29

Una vez eliminados los archivos presionamos el botón "Add JAR/Folder..." para agregar los nuevos archivos:

Agregamos a la biblioteca los siguientes archivos:

hibernate3.jar

y del directorio de jars requeridos:

antlr-2.7.6.jar

commons-collections-3.1.jar

dom4j-1.6.1.jar

javassist-3.4.GA.jar

jta-1.1.jar

O sea, casi todos los archivos requeridos. Estamos excluyendo el archivo "slf4j-api-1.5.2.jar" porque si lo incluimos solo, se

lanza una excepción. Lo que ocurre es que slf4j es una fachada para el logging, o bitácora de la aplicación (similar a "commons-

logging" que estuvimos usando el los tutoriales de JasperReports). Esto significa que podemos usar el framework de logging que

queramos (en teoría). Sin embargo en la distribución de Hibernate no se incluye ningún jar con una clase que implemente dicha fachada,

por lo que debemos bajar uno nosotros mismos.

Podemos hacer dos cosas, la primer es agregar el archivo "slf4j-api-1.5.2.jar" que viene con Hibernate a nuestra biblioteca y el

zip con la versión correspontiente de slf4j. De él extraemos "slf4j-simple-1.5.2.jar" (o el archivo que se ajuste al framework de

logging que estemos usando) y lo incluimos en la biblioteca.

Lo otro que podemos hacer es, aprovechando que ya estamos bajando jars, descargar la última versión de slf4j de aquí (actualmente es la

1.5.6) y aprovechar para agregar los archivos "slf4j-api-1.5.6.jar" y "slf4j-simple-1.5.6.jar" a nuestra biblioteca. Yo haré

esto último.

Una vez seleccionados estos archivos la biblioteca de Hibernate debe verse así:

Presionamos el botón "OK" y nuestra biblioteca ya estará actualizada.

Ahora creamos un nuevo proyecto de NetBeans (menú "File -> New Project... -> Java -> Java Application"). Le damos

un nombre y una ubicación al proyecto y nos aseguramos de que las opciones "Create Main Class" y "Set as Main Project"

Participar en este sitioGoogle Friend Connect

Miembros (173) Más »

¿Ya eres miembro? Iniciar sesión

SEGUIDORES

► 2012 (2)

► 2011 (11)

► 2010 (10)

▼ 2009 (22)

► septiembre (2)

► agosto (2)

► julio (1)

► junio (3)

▼ mayo (2)

HIbernate - Parte 2: Persistiendo ObjetosSimples ...

HIbernate - Parte 1: Persistiendo ObjetosSimples ...

► abril (5)

► marzo (2)

► febrero (2)

► enero (3)

ARCHIVO DEL BLOG

Page 3: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

www.javatutoriales.com/2009/05/hibernate-parte-1-persistiendo-objetos.html 3/29

estén habilitadas. Presionamos el botón "Finish" y veremos aparecer en el editor nuestra clase "Main".

Agregamos la biblioteca de "Hibernate", que creamos hace unos momentos, a nuestro proyecto. Hacemos clic derecho en el nodo

"Libraries" del proyecto. En el menú contextual que se abre seleccionamos la opción "Add Library...":

En la ventana que se abre seleccionamos la biblioteca "Hibernate":

Presionamos el botón "Add Library" para que la biblioteca se agregue a nuestro proyecto. Aprovechamos también para agregar el

conector de MySQL. Debemos tener los siguientes archivos en nuestro proyecto:

El siguiente paso es crear una clase cuyas instancias serán almacenadas como fila de una tabla en base de datos. Este tipo de objetos

son llamados "Entidades". Para este ejemplo crearemos una pequeña y básica agenda, así que las entidades serán objetos de la clase

"Contacto".

Los atributos que tendrá esta serán un nombre, un correo, y un número telefónico. Además, por regla, necesitamos un atributo que

funcione como identificador para cada una de las entidades.

En este caso he hecho que la clase implementa la interface "java.io.Serializable", esto no es obligatorio pero es una buena

práctica.

La clase "Contacto" queda de la siguiente forma:

Alex

Programador Java con algunos años de

experiencia en múltiples poyectos y con

múltiples APIs y herramientas deseoso de

compartir experiencias con el resto de

programadores.

Ver todo mi perfil

DATOS PERSONALES

Page 4: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

www.javatutoriales.com/2009/05/hibernate-parte-1-persistiendo-objetos.html 4/29

public class Contacto implements Serializable

{

private long id;

private String nombre;

private String email;

private String telefono;

public Contacto()

{

}

public Contacto(String nombre, String email, String telefono)

{

this.nombre = nombre;

this.email = email;

this.telefono = telefono;

}

public String getEmail()

{

return email;

}

public void setEmail(String email)

{

this.email = email;

}

public long getId()

{

return id;

}

private void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

public String getTelefono()

{

return telefono;

}

public void setTelefono(String telefono)

{

this.telefono = telefono;

}

}

La clase "Contacto" usa la convención de nombres JavaBeans para los getters y setters y visibilidad privada para los atributos.

Este también es un diseño recomendado pero no obligatorio. Hibernate puede acceder a los campos o atributos de las entidades

directamente.

El constructor sin argumentos si es obligatorio ya que Hibernate creará instancias de esta clase usando reflexión cuando recupere las

entidades de la base de datos. Este constructor puede ser privado (si es que no quieren permitir que alguien más lo utilice), pero

usualmente el nivel de acceso más restrictivo que usaremos es el de paquete (el default), ya que esto hace más eficiente la creación de

los objetos.

La propiedad "id" mantendrá, como dije antes, un valor único que identificará a cada una de las instancias de "Contacto". Todas las

clases de entidades persistentes deben tener una propiedad que sirva como identificador si queremos usar el conjunto completo de

funcionalidades que nos ofrece Hibernate (y que veremos a lo largo de esta serie de tutoriales). De todas formas, la mayoría de las

aplicaciones necesitan poder distinguir objetos de la misma clase mediante algún tipo de identificador. Usualmente no manipulamos

directamente este identificador (dejamos que sea la base de datos quien lo genere, cuando la entidad sea guardada, y Hibernate quien lo

asigne al objeto), por lo tanto el setter del "id" es privado (fíjense cómo lo he puesto en la clase "Contacto").

Hibernate puede acceder a los campos private directamente, así que no debemos preocuparnos por el hecho de que el identificador no

pudiera ser establecido.

El siguiente paso es crear el archivo de mapeo. Si recuerdan, anteriormente dije que Hibernate es una herramienta de mapeos

Objeto/Relacional, o sea que mapea los atributos de los objetos con las columnas de una tabla de una base de datos relacional.

También dije que hay dos formas de hacerlo: mediante un archivo de mapeo en XML y mediante anotaciones y que en esta ocasión

veríamos solo como usar los archivos de mapeo.

Page 5: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

www.javatutoriales.com/2009/05/hibernate-parte-1-persistiendo-objetos.html 5/29

Crearemos un nuevo paquete que contendrá los archivos de mapeo, no es obligatorio tener los archivos de mapeo en un paquete separado

ya que más adelante indicaremos donde se encuentra cada uno de los archivos, pero nos ayudará a mantener un poco de orden en

nuestro proyecto. Hacemos clic derecho sobre el nodo "Source Packages" del proyecto para mostrar un menú contextual. De ese

menú seleccionamos la opción "New -> Java Package..":

Nombramos a este paquete como "mapeos" y presionamos el botón "Finish" para agregar el nuevo paquete.

Ahora creamos un nuevo archivo XML que contendrá el mapeo de la clase "Contacto" con la tabla "CONTACTOS" de la base de datos.

Por convención la extensión de estos archivos es ".hbm.xml" y tiene el mismo nombre de la entidad que está mapeando (aunque eso

tampoco es necesario ya que podemos mapear más de una clase o entidad dentro del mismo archivo).

Hacemos clic derecho sobre el paquete que acabamos de crear. En el menú contextual que se abre seleccionamos la opción "New ->

XML Document". Si no se muestra está opción seleccionamos "Other..." y en la ventana que se abre seleccionamos "XML -> XML

Document":

Le damos al archivo el nombre "Contacto.hbm" (el asistente se encargará de agregar de forma automática el ".xml" al final):

Presionamos el botón "Next >" y se nos preguntará qué tipo de documento queremos crear: solo un documento XML bien formado, un

documento regido por un DTD, o un documento regido por un Schema XML. Aunque el documento de mapeo está regido por un DTD

nosotros elegiremos crear un documento XML bien formado ("Well-formed Document") ya que es más fácil simplemente pegarlo en el

archivo que se creará que indicar mediante el asistente dónde se encuentra este archivo. Presionamos el botón "Finish" para que se

nos muestre en el editor el nuevo archivo XML, el cual debe verse más o menos así:

Modificaremos este archivo. Eliminamos todo el contenido del archivo y lo reemplazamos por este:

Page 6: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

www.javatutoriales.com/2009/05/hibernate-parte-1-persistiendo-objetos.html 6/29

<?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>

</hibernate-mapping>

Teniendo esta declaración, la cual indica el DTD que se usa para validar el archivo, el IDE nos ayudará a autocompletar las etiquetas y a

asegurarnos que el archivo sea válido.

El elemento "<hibernate-mapping>" es el nodo raíz del documento y, por lo tanto, el resto de los elementos irán entre estas dos

etiquetas.

El elemento con el que indicamos qué clase es la que estamos mapeando es el elemento "<class>" este elemento tiene los atributos

"name" y "table" que nos permiten indicar el nombre completo de la clase y la tabla con la que será mapeada, respectivamente:

<hibernate-mapping>

<class name="hibernatenormal.Contacto" table="CONTACTOS">

</class>

</hibernate-mapping>

Con esto indicamos que las entidades de la clase "Contacto" serán almacenadas en la tabla "CONTACTOS". Ahora debemos indicar

cuál de los elementos de la clase entidad es el identificador. Este identificador será mapeado con la llave primaria de la tabla, además

como nosotros no manejaremos el valor del identificador, le indicamos a Hibernate cómo queremos que este valor sea generado (la

estrategia de generación). Para esto usamos el elemento "<id>", indicando el nombre del atributo de la clase entidad que representa

el identificador (que en este caso también se llama "id" - ). Opcionalmente en este elemento (como en el resto de los elementos de

mapeo de propiedades) podemos indicar con qué columna queremos que se mapee usando el atributo "column":

<hibernate-mapping>

<class name="hibernatenormal.Contacto" table="CONTACTOS">

<id name="id" column="ID">

<generator class="identity" />

</id>

</class>

</hibernate-mapping>

El elemento "<generator>" es el que nos permite indicar la estrategia de generación del identificador usando su atributo "class".

Existen varias estrategias que están explicadas en esta página, pero las que usaremos más frecuentemente son "identity" (con la que

Hibernate se encarga de generar el query necesario para que el nuevo identificador sea igual a el último identificador + 1) y "native" (con

el que se usa la estrategia por default del manejador que estemos utilizando (dialecto)).

Para terminar con el mapeo incluimos las declaraciones para el resto de las propiedades persistentes (las que queremos que sean

almacenadas) de la clase entidad ("nombre", "email", y "telefono") usando el elemento "<property>" en el cual indicamos el

nombre de la propiedad como aparece en la clase entidad y, opcionalmente, el tipo de la propiedad y la columna con la que será mapeada

usando los atributos "type" y "column" respectivamente:

<hibernate-mapping>

<class name="hibernatenormal.Contacto" table="CONTACTOS">

<id name="id" column="ID">

<generator class="identity" />

</id>

<property name="nombre" type="string" column="NOMBRE" />

<property name="email" />

<property name="telefono" />

</class>

</hibernate-mapping>

El archivo "Contacto.hbm.xml" final debe verse así:

Page 7: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

www.javatutoriales.com/2009/05/hibernate-parte-1-persistiendo-objetos.html 7/29

¿Qué ocurre si no indicamos el tipo de la propiedad? En ese caso Hibernate utiliza nuevamente reflexión en nuestra clase para determinar

el tipo del atributo y así elegir el tipo más adecuado para la columna de la tabla (en caso de que dejemos que Hibernate genere las

tablas).

¿Qué ocurre si no indicamos el nombre de la columna con la que mapea una propiedad? Esto depende de si Hibernate está generando

las tablas de la base de datos o las hemos generado nosotros mismos. Si es Hibernate quien está generando las tablas no hay problema,

simplemente dará a la columna correspondiente el mismos nombre de la propiedad. Por ejemplo, para la propiedad "email" creará una

columna "email". Sin embargo, si nosotros hemos creado las tablas hay un riesgo potencial de un error. Hibernate buscará en las

consultas una columna con el mismo nombre de la propiedad, pero si hemos usado un nombre distinto, por ejemplo en vez de "email"

hemos llamado a la columna "E_MAIL" ocurrirá una excepción indicando que no se ha encontrado la columna "email", así que en ese

caso debemos tener cuidado de indicar el nombre de las columnas como están en la base de datos.

Bien, después de esta breve explicación podemos proseguir.

Ya que hemos configurado el mapeo de nuestra clase entidad debemos configurar Hibernate. Hibernate está en la capa de nuestra

aplicación que se conecta a la base de datos (la capa de persistencia), así que necesita información de la conexión. La conexión se

hace a través de un pool de conexiones JDBC que también debemos configurar. La distribución de Hibernate contiene muchos pools de

conexiones JDBC Open Source, como por ejemplo C3P0, pero en esta ocasión usaremos el pool de conexiones que Hibernate trae

integrado.

La configuración de Hibernate puede hacerse en tres lugares:

Un archivo de propiedades llamado "hibernate.properties".

Un archivo XML llamado "hibernate.cfg.xml".

En código dentro de la misma aplicación.

En realidad los archivos pueden tener cualquier nombre, pero Hibernate buscará por default los archivos con los nombres que he

mencionado, en una ubicación predeterminada (la raíz del classpath de la aplicación). Si queremos usar archivos con otros nombres

deberemos especificarlo en el código.

La mayoría, por lo que he podido ver en Internet, preferimos usar el archivo XML ya que, entre otras cosas, los IDEs nos ayudan a su

creación y configuración gracias a que está regido por un DTD. Así que crearemos este archivo en nuestro proyecto. Hacemos clic

derecho en el nodo "Source Packages" del proyecto. En el menú contextual que se abre seleccionamos "New -> XML

Document...":

Nombramos al archivo "hibernate.cfg", el IDE se encargará de colocarle la extensión ".xml". Ubicamos el archivo en el directorio

"src":

Con esto lograremos que el archivo quede en la raíz del classpath de la aplicación, también conocido como el paquete default.

Presionamos el botón "Next >" y en la pantalla siguiente indicamos que queremos crear un documento XML bien formado (como en el

caso anterior). Presionamos el botón "Finish " para generar el archivo. Si nos fijamos en el archivo generado, este debe encontrarse en

un paquete llamado "<default package>":

Page 8: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

www.javatutoriales.com/2009/05/hibernate-parte-1-persistiendo-objetos.html 8/29

Borramos todo el contenido del archivo generado y colocamos las siguientes líneas:

<?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>

</hibernate-configuration>

El DTD nos ayudará para que el IDE autocomplete las etiquetas.

Como podemos ver, el elemento raíz del archivo de configuración es "<hibernate-configuration>" y, por lo tanto, todos los

elementos los colocaremos entre estas dos etiquetas.

Lo primero que debemos hacer es configurar un "session-factory", que básicamente es lo que le dice a Hibernate cómo conectarse y

manejar la conexión a la base de datos. Podemos tener más de un "session-factory" en el archivo de configuración (por si

quisiéramos conectarnos a más de una base de datos), pero por lo regular solo ponemos uno:

<hibernate-configuration>

<session-factory>

</session-factory>

</hibernate-configuration>

Lo siguiente es configurar la conexión a nuestra base de datos. En este archivo se configuran los parámetros básicos y típicos para una

conexión (la URL, nombre de usuario, contraseña, driver, etc.). Cada uno de estos parámetros se configura dentro de una etiqueta "

<property>" (al igual que casi todos los elementos del archivo de configuración). Como dije antes, usaré una base de datos MySQL

para este ejemplo, así que mi configuración queda de la siguiente forma:

<property name="connection.driver_class">com.mysql.jdbc.Driver</property>

<property name="connection.url">jdbc:mysql://localhost/pruebahibernate</property>

<property name="connection.username">usuario</property>

<property name="connection.password">password</property>

Después, configuramos el pool de conexiones de Hibernate. En este caso como es un ejemplo muy simple, solo nos interesa tener una

conexión en el pool, por lo que colocamos la propiedad "connection.pool_size" con un valor de "1":

<property name="connection.pool_size">1</property>

El siguiente paso es muy importante. Debemos indicar el "dialecto" que usará Hibernate para comunicarse con la base de datos. Este

dialecto es la variante de SQL que usa la base de datos para ejecutar queries. Indicamos el dialecto con el fully qualified class name, o

el nombre completo de la clase incluyendo el paquete. En el caso de MySQL 5 usamos

"org.hibernate.dialect.MySQL5Dialect". En esta página pueden encontrar una lista más o menos completa de los dialectos

soportados por Hibernate, pero siempre es mejor revisar la documentación de la versión que estén usando para estar seguros:

<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>

Page 9: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

www.javatutoriales.com/2009/05/hibernate-parte-1-persistiendo-objetos.html 9/29

Otras dos propiedades importantes que podemos configurar son: "show_sql" que indica si queremos que las consultas SQL generadas

sean mostradas en el stdout (normalmente la consola), y "hbm2ddl.auto", que indica si queremos que se genere automáticamente el

esquema de la base de datos (las tablas). "show_sql" puede tomar valores de "true" o "false", yo lo colocaré en "true" (lo que

puede ser bueno mientras estamos en etapas de desarrollo o pruebas, pero querrán cambiar su valor cuando su aplicación pase a

producción). Por otro lado "hbm2ddl.auto" puede tomar los valores, segun la documentación oficial(falta "none"), de "validate",

"update", "create", y "create-drop" (aunque no todos los valores funcionan para todas las bases de datos). Yo los colocaré de la

siguiente forma:

<property name="show_sql">true</property>

<property name="hbm2ddl.auto">create-drop</property>

Con el valor "create-drop" hacemos que cada vez que se ejecute la aplicación Hibernate elimine las tablas de la base de datos y las

vuelva a crear. Para terminar con este archivo de configuración, debemos indicar dónde se encuentra cada uno de los archivos de mapeo

que hemos creado, usando el elemento "<mapping>". En nuestro caso solo hemos creado un archivo de mapeo, pero debemos colocar

un elemento "<mapping>" por cada uno de los archivos que hayamos creado:

<mapping resource="mapeos/Contacto.hbm.xml"/>

En el elemento "resource" debemos colocar la ubicación de los archivos de mapeo dentro de la estructura de paquetes de la aplicación.

El archivo de configuración "hibernate.cfg.xml" final debe verse así:

<?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>

<!-- parametros para la conexion a la base de datos -->

<property name="connection.driver_class">com.mysql.jdbc.Driver</property>

<property name="connection.url">jdbc:mysql://localhost/pruebahibernate</property>

<property name="connection.username">usuario</property>

<property name="connection.password">password</property>

<!-- Configuracion del pool interno -->

<property name="connection.pool_size">1</property>

<!-- Dialecto de la base de datos -->

<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>

<!-- Otras propiedades importantes -->

<property name="show_sql">true</property>

<property name="hbm2ddl.auto">create-drop</property>

<!-- Archivos de mapeo -->

<mapping resource="mapeos/Contacto.hbm.xml"/>

</session-factory>

</hibernate-configuration>

Esta es toda la configuración que debemos hacer. Ahora veremos el código necesario para guardar y recuperar objetos "Contacto" de la

base de datos.

Lo primero que haremos es crear una clase ayudante o de utilidad llamada "HibernateUtil", que se hará cargo de inicializar y hacer el

acceso al "org.hibernate.SessionFactory" (el objeto encargado de gestionar las sesiones de conexión a la base de datos que

configuramos en el archivo "hibernate.cfg.xml") más conveniente.

Dentro de esta clase declaramos un atributo static de tipo "SessionFactory", así nos aseguraremos de que solo existe una

Page 10: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

www.javatutoriales.com/2009/05/hibernate-parte-1-persistiendo-objetos.html 10/29

instancia en la aplicación. Además lo declararemos como final para que la referencia no pueda ser cambiada después de que la

hayamos asignado.

private static final SessionFactory sessionFactory;

Después usamos un bloque de inicialización estático para inicializar esta variable en el momento en el que la clase sea cargada en la

JVM.

Para realizar esta inicialización lo primero que se necesita es una instancia de la clase "org.hibernate.cfg.Configuration" que

permite a la aplicación especificar las propiedades y documentos de mapeo que se usarán (es aquí donde indicamos todo si no queremos

usar un archivo XML o de propiedades). Si usamos el método "configure()" que no recibe parámetros entonces Hibernate busca el

archivo "hibernate.cfg.xml" que creamos anteriormente. Una vez que tenemos este objeto, entonces podemos inicializar la instancia

de "SessionFactory" con su método "buildSessionFactory()". Además como este proceso puede lanzar

"org.hibernate.HibernateException" (que extiende de "RuntimeException") la cachamos y lanzamos como un

"ExceptionInInitializarError" (que es lo único que puede lanzarse desde un bloque de inicialización). El bloque de inicialización

queda de la siguiente forma:

static

{

try

{

sessionFactory = new Configuration().configure().buildSessionFactory();

} catch (HibernateException he)

{

System.err.println("Ocurrió un error en la inicialización de la SessionFactory: " + he);

throw new ExceptionInInitializerError(he);

}

}

Finalmente creamos un método static llamado "getSessionFactory()" para recuperar la instancia de la "SessionFactory":

public static SessionFactory getSessionFactory()

{

return sessionFactory;

}

La clase "HibernateUtil" queda de la siguiente forma:

import org.hibernate.HibernateException;

import org.hibernate.SessionFactory;

import org.hibernate.cfg.Configuration;

public class HibernateUtil

{

private static final SessionFactory sessionFactory;

static

{

try

{

sessionFactory = new Configuration().configure().buildSessionFactory();

} catch (HibernateException he)

{

System.err.println("Ocurrió un error en la inicialización de la SessionFactory: " + he)

;

throw new ExceptionInInitializerError(he);

}

}

public static SessionFactory getSessionFactory()

{

return sessionFactory;

}

}

Page 11: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

www.javatutoriales.com/2009/05/hibernate-parte-1-persistiendo-objetos.html 11/29

Bien ha llegado el momento, ahora escribiremos una clase DAO (no seguiremos el patrón al pie de la letra, es solo para mostrar cómo

trabajar con Hibernate) que nos permitirá realizar operaciones de base de datos.

Creamos una clase llamada "ContactosDAO" y agregamos dos atributos, uno llamado "sesion" de tipo "org.hibernate.Session",

y otro llamado "tx" de tipo "org.hibernate.Transaction".

private Session sesion;

private Transaction tx;

Estos atributos nos servirán para mantener la referencia a la sesión a base de datos, y a la transacción actual, respectivamente. Ahora

agregaremos dos métodos de utilidad. El primero nos ayudará a iniciar una sesión y una transacción en la base de datos. Llamaremos a

este método "iniciaOperacion", y la implementación es la siguiente:

private void iniciaOperacion() throws HibernateException

{

sesion = HibernateUtil.getSessionFactory().openSession();

tx = sesion.beginTransaction();

}

En el método anterior obtenemos una referencia a "SessionFactory" usando nuestra clase de utilidad "HibernateUtil". Una vez

que tenemos la "SessionFactory" creamos una conexión a la base de datos e iniciamos una nueva sesión con el método

"openSession()". Una vez teniendo la sesión iniciamos una nueva transacción y obtenemos una referencia a ella con

"beginTransaction()".

Ahora el segundo método de utilidad (llamado "manejaExcepcion") nos ayudará a manejar las cosas en caso de que ocurra una

excepción. Si esto pasa queremos que la transacción que estamos ejecutando se deshaga y se relance la excepción (o podríamos lanzar

una propia). Por lo que el método queda así:

private void manejaExcepcion(HibernateException he) throws HibernateException

{

tx.rollback();

throw new HibernateException("Ocurrió un error en la capa de acceso a datos", he);

}

Ahora crearemos los métodos que nos permitirán realizar las tareas de persistencia de una entidad "Contacto", conocidas en lenguaje

de base de datos como CRUD: guardarla, actualizarla, eliminarla, buscar un entidad "Contacto" y obtener todas los contactos que

existen en la base de datos, así que comencemos.

Afortunadamente Hibernate hace que esto sea fácil ya que proporciona métodos para cada una de estas tareas. Primero veamos como

guardar un objeto "Contacto". Para esto Hibernate proporciona el método "save" en el objeto de tipo "org.hibernate.Session",

que se encarga de generar el "INSERT" apropiado para la entidad que estamos tratando de guardar. El método "guardaContacto"

queda de la siguiente forma:

Page 12: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

www.javatutoriales.com/2009/05/hibernate-parte-1-persistiendo-objetos.html 12/29

public long guardaContacto(Contacto contacto)

{

long id = 0;

try

{

iniciaOperacion();

id = (Long)sesion.save(contacto);

tx.commit();

}catch(HibernateException he)

{

manejaExcepcion(he);

throw he;

}finally

{

sesion.close();

}

return id;

}

Regresamos el "id" generado al guardar el "Contacto" solo por si queremos usarlo más adelante en el proceso (como lo haremos

nosotros), o si queremos mostrarle al usuario, por alguna razón, el identificador del "Contacto".

Ahora veremos cómo actualizar un "Contacto". Para eso usamos el método "update" del objeto "sesion" en nuestro método

"actualizaContacto":

public void actualizaContacto(Contacto contacto) throws HibernateException

{

try

{

iniciaOperacion();

sesion.update(contacto);

tx.commit();

}catch (HibernateException he)

{

manejaExcepcion(he);

throw he;

}finally

{

sesion.close();

}

}

Como podemos ver, el método para actualizar es muy similar al método para guardar la entidad. Lo mismo ocurre con el método para

eliminarla, "eliminaContacto":

public void eliminaContacto(Contacto contacto) throws HibernateException

{

try

{

iniciaOperacion();

sesion.delete(contacto);

tx.commit();

} catch (HibernateException he)

{

manejaExcepcion(he);

throw he;

}finally

{

sesion.close();

}

}

Ahora veremos unos métodos un poco más interesantes.

Cuando queremos buscar una entidad podemos usar varios criterios. La forma más fácil es buscar una entidad particular usando su "id".

La clase "org.hibernate.Session" proporciona dos métodos para esto: "load" y "get". Los dos hacen prácticamente lo mismo: en

base al identificador y tipo de la entidad recuperan la entidad indicada, con la diferencia de que "load" lanza una excepción en caso de

que la entidad indicada no sea encontrada en la base de datos, mientras que "get" simplemente regresa "null". Pueden usar el que

prefieran, en lo personal me gusta más "get", así que lo usaré para el método "obtenContacto":

Page 13: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

www.javatutoriales.com/2009/05/hibernate-parte-1-persistiendo-objetos.html 13/29

public Contacto obtenContacto(long idContacto) throws HibernateException

{

Contacto contacto = null;

try

{

iniciaOperacion();

contacto = (Contacto) sesion.get(Contacto.class, idContacto);

} finally

{

sesion.close();

}

return contacto;

}

Finalmente veremos el método "obtenListaContactos" que recupera todos los Contactos que estén guardados en la base de

datos. Como en este caso regresaremos una lista de elementos deberemos crear una consulta. Cuando tenemos que crear una consulta

con JDBC lo hacemos en SQL, sin embargo con Hibernate tenemos varias opciones:

Usar una query en SQL nativo.

Crear una query en HQL (Hibernate Query Language).

Crear una query usando Criteria que es un API para crear queries de una forma más "orientada a objetos".

Critera es, a mi parecer, la forma más fácil de crear las queries. Sin embargo, también en mi opinión, HQL es más poderosa y tenemos

más control sobre lo que está ocurriendo, así que será esta forma la que usaremos.

Creando la consulta en HQL Hibernate la transformará al SQL apropiado para la base de datos que estemos usando. La consulta en

realidad es muy simple: indicamos que queremos obtener datos (generar un "SELECT"), en este caso la clausula "SELECT" no es

necesaria (aunque si existe) porque vamos a regresar todos los datos del objeto (podemos indicar solo unos cuantos atributos, eso es

llamado proyección, pero se maneja de una forma distinta). Lo que si debemos indicar es de cuál clase queremos recuperar las

instancias, por lo que necesitamos la clausula "FROM" y el nombre de la clase (recuerden que en base al nombre de la clase Hibernate

sabrá en cuál tabla están almacenadas las entidades). Espero que esta explicación se haya entendido, se que es un poco enredado, pero

quedará más claro con el ejemplo:

public List<Contacto> obtenListaContactos() throws HibernateException

{

List<Contacto> listaContactos = null;

try

{

iniciaOperacion();

listaContactos = sesion.createQuery("from Contacto").list();

}finally

{

sesion.close();

}

return listaContactos;

}

Eso es todo, nuestra consulta para recuperar todos los Contactos que tenemos en la base de datos solo debemos usar la clausula "FROM

Contacto". Si quieren una referencia más amplia de HQL pueden encontrarla en el tutorial sobre HQL.

Bien, eso es todo. La clase "ContactosDAO" queda de la siguiente forma (omitiendo los imports):

public class ContactosDAO

{

private Session sesion;

private Transaction tx;

public long guardaContacto(Contacto contacto) throws HibernateException

{

long id = 0;

try

{

iniciaOperacion();

id = (Long) sesion.save(contacto);

tx.commit();

} catch (HibernateException he)

{

manejaExcepcion(he);

throw he;

} finally

Page 14: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

www.javatutoriales.com/2009/05/hibernate-parte-1-persistiendo-objetos.html 14/29

} finally

{

sesion.close();

}

return id;

}

public void actualizaContacto(Contacto contacto) throws HibernateException

{

try

{

iniciaOperacion();

sesion.update(contacto);

tx.commit();

} catch (HibernateException he)

{

manejaExcepcion(he);

throw he;

} finally

{

sesion.close();

}

}

public void eliminaContacto(Contacto contacto) throws HibernateException

{

try

{

iniciaOperacion();

sesion.delete(contacto);

tx.commit();

} catch (HibernateException he)

{

manejaExcepcion(he);

throw he;

} finally

{

sesion.close();

}

}

public Contacto obtenContacto(long idContacto) throws HibernateException

{

Contacto contacto = null;

try

{

iniciaOperacion();

contacto = (Contacto) sesion.get(Contacto.class, idContacto);

} finally

{

sesion.close();

}

return contacto;

}

public List<Contacto> obtenListaContactos() throws HibernateException

{

List<Contacto> listaContactos = null;

try

{

iniciaOperacion();

listaContactos = sesion.createQuery("from Contacto").list();

} finally

{

sesion.close();

}

return listaContactos;

}

private void iniciaOperacion() throws HibernateException

{

sesion = HibernateUtil.getSessionFactory().openSession();

tx = sesion.beginTransaction();

}

private void manejaExcepcion(HibernateException he) throws HibernateException

{

tx.rollback();

throw new HibernateException("Ocurrió un error en la capa de acceso a datos", he);

}

}

Page 15: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

www.javatutoriales.com/2009/05/hibernate-parte-1-persistiendo-objetos.html 15/29

Ahora, y para terminar el ejemplo, haremos uso de "ContactosDAO" para crear y recuperar algunas instancias de "Contacto". Creo

que el código es muy claro así que no lo explicaré - !. La clase "Main" queda de la siguiente forma:

public class Main

{

public static void main(String[] args)

{

ContactosDAO contactosDAO = new ContactosDAO();

Contacto contactoRecuperado = null;

long idAEliminar = 0;

//Creamos tes instancias de Contacto

Contacto contacto1 = new Contacto("Contacto 1", "[email protected]", "12345678");

Contacto contacto2 = new Contacto("Contacto 2", "[email protected]", "87654321");

Contacto contacto3 = new Contacto("Contacto 3", "[email protected]", "45612378");

//Guardamos las tres instancias, guardamos el id del contacto1 para usarlo posteriormente

idAEliminar = contactosDAO.guardaContacto(contacto1);

contactosDAO.guardaContacto(contacto2);

contactosDAO.guardaContacto(contacto3);

//Modificamos el contacto 2 y lo actualizamos

contacto2.setNombre("Nuevo Contacto 2");

contactosDAO.actualizaContacto(contacto2);

//Recuperamos el contacto1 de la base de datos

contactoRecuperado = contactosDAO.obtenContacto(idAEliminar);

System.out.println("Recuperamos a " + contactoRecuperado.getNombre());

//Eliminamos al contactoRecuperado (que es el contacto3)

contactosDAO.eliminaContacto(contactoRecuperado);

//Obtenemos la lista de contactos que quedan en la base de datos y la mostramos

List<Contacto> listaContactos = contactosDAO.obtenListaContactos();

System.out.println("Hay " + listaContactos.size() + "contactos en la base de datos");

for(Contacto c : listaContactos)

{

System.out.println("-> " + c.getNombre());

}

}

}

En el momento que ejecutemos la aplicación Hibernate creará la tabla correspondiente en la base de datos (llamada "contactos") y que

tiene los siguientes campos:

Como podemos ver la tabla y los nombres de las columnas se generan en base al archivo de mapeo. En donde hemos colocado los

nombres de las columnas se han usado estos nombres, en donde no lo hemos hecho se han tomado los mismos nombres que el de los

atributos que estamos mapeando.

Si observamos la salida de la consola podemos ver, que en la parte final, se indica esta creación y además las consultas que Hibernate

Page 16: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

www.javatutoriales.com/2009/05/hibernate-parte-1-persistiendo-objetos.html 16/29

Publicado por Alex en 10:48

Reacciones: divertido (1) interesante (5) increible (1) no me gusta (0)

Etiquetas: base de datos, hibernate, java

ha generado (revueltas con los mensajes que sacamos a consola). He marcado con rojo los mensajes que mandamos a consola para que

sea fácil distinguirlos.

Segun indica la salida en la base de datos solo debemos tener dos entidades, con los nombres "Nuevo Contacto 2" y "Contacto

3". Revisemos la tabla para ver que esto sea correcto:

Como podemos ver, esto es correcto, en la base de datos existen solo los registros indicados, con lo que podemos estar seguros de que

nuestra aplicación funciona!!! - .

Entonces, vimos que con Hibernate fue muy simple hacer la manipulación de los datos de una base de datos relacional sin escribir una

sola línea de SQL. En aplicaciones de tamaño mediano a grande esto nos ahorrará mucho tiempo (que normalmente usaríamos en escribir

el SQL para query, armar algunas de estas de forma dinámica, pasar los parámetros adecuados, recuperar los mismos de un

ResultSet, entre otras cosas) para mejorar algunos otros aspectos de la aplicación, como la interfaz gráfica, la lógica del negocio, o

realizar más pruebas. Además nuestro código es más simple y por lo tanto fácil de mantener.

Lo único malo es que ahora tenemos que crear mapeos de las entidades con las tablas de la base de datos en un archivo XLM. Es por

esto que en el siguiente tutorial veremos cómo realizar este mismo ejemplo pero usando anotaciones en vez del archivo de mapeo - .

Espero que esto les haya sido de utilidad. No olviden dejar sus comentarios, dudas, y sugerencias.

Saludos.

Descarga los archivos de este tutorial desde aquí:

Hibernate Archivos de Mapeo

Entradas Relacionadas:

Parte 2: Persistiendo Objetos Simples usando Anotaciones (Metadatos)

Parte 3: Relaciones / Uno a uno

Parte 4: Relaciones / Uno a muchos

Parte 5: Relaciones / Muchos a uno

Parte 6: Relaciones / Muchos a Muchos

Parte 7: HQL Primera Parte

Parte 8: HQL Segunda Parte

Parte 9: Parámetros en HQL

Parte 10: Herencia

Parte 11: Interceptores y Eventos

+14 Recomendar esto en Google

79 comentarios:

Anónimo 13 de mayo de 2009 05:17

ok programador java , como siempre excelente tutorial ya lo estoy implementando . . .

Responder

janeth 2 de junio de 2009 07:30

Gracias:

quisiera saber que version de Netbeans utiliza, y como queda el ContactosDAO, el main que no me funcionan.

gracias nuevamente

Responder

Alex 2 de junio de 2009 08:49

Hola Janeth;

Page 17: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 2: Persistiendo Objetos Simples usando Anotaciones (metadatos)

www.javatutoriales.com/2009/05/hibernate-parte-2-persistiendo-objetos.html 1/20

Blog dedicado a temas de programación actuales usando el lenguaje de programación Java y las últimas versiones de sus APIs y Herramientas.

Tutoriales de Programacion Java

Página principal Página de Concursos Tutoriales UPAO 2010 Presentaciones Capacitación

MIÉRCOLES, 13 DE MAYO DE 2009

HIbernate - Parte 2: Persistiendo Objetos Simples usando Anotaciones(metadatos)

En el tutorial anterior vimos como crear una pequeña aplicación con Hibernate usando mapeos en archivos XML. Aunque esto es bastante

útil (en algunas situaciones) puede ser bastante engorroso, especialmente en aplicaciones muy grandes.

Afortunadamente existe otra forma de realizar estos mapeos, y es usando anotaciones o metadatos, los cuales se agregan directamente

al código fuente y, por lo tanto, es más fácil estar conscientes de estos datos de mapeos al estar editando el código fuente. Además

usan muchos valores por default, ya no será necesario que escribamos toda la información para los mapeos.

En esta ocasión mostraré como realizar el mismo ejemplo que en el tutorial anterior, pero haciendo uso de anotaciones en lugar de

archivos de mapeo XML.

Para este ejemplo usaremos MySQL 5.1 como base de datos. Si no la tienen pueden descargarla desde aquí. También deben bajar el

conector para Java versión 5.1.7 desde aquí. Además usaremos la biblioteca de Hibernate que creamos también en el tutorial anterior.

Hibernate proporciona un API especial para trabajar usando anotaciones llamada "Hibernate Annotations" que puede ser

descargada desde aquí. Actualmente la última versión es la 3.4.0 GA y es la que usaremos para este tutorial.

Una vez descargado el archivo y descomprimido, veremos que dentro del directorio de "hibernate-annotations" hay un archivo

llamado "hibernate-annotations.jar". Este jar contiene las clases principales de Hibernate annotations. Dentro del directorio

"lib" encontrarán algunos jars de soporte.

Crearemos una biblioteca que contendrá estos archivos para que sea fácil agregarlos cada vez que queramos trabajar con anotaciones de

Hibernate.

Abrimos el NetBeans y vamos al menú "Tools -> Libraries":

En la ventana que se abre seleccionamos la opción "New Library..." con lo que se abre otra ventana. En esta ventana le damos a la

biblioteca el nombre "HibernateAnotaciones" (sin espacios), de tipo "Class Libraries":

DONACIONES

JAVA TUTORIALES EN FACEBOOK

Java Tutoriales

Me gusta

A 7895 personas les gusta

Plug-in social de Facebook

Java Tutoriales

Amigo programador, tú que

estás todo el día picando

código, escucha estas

recomendaciones sólo

hechas para ti..... ¿qué es

esto?... Gracias Jorge

Rubira....

No olviden poner la

sentencia where al hacer

un borraro en la base de

datos..

http://www.youtube.com/

watch?v=i_cVJgIz_Cs

Compartir 0 Más Siguiente blog»

Page 18: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 2: Persistiendo Objetos Simples usando Anotaciones (metadatos)

www.javatutoriales.com/2009/05/hibernate-parte-2-persistiendo-objetos.html 2/20

Presionamos el botón "OK" y ya tendremos creada nuestra biblioteca, ahora hay que agregar los jars que la conformarán. Presionamos el

botón "Add JAR/Folder" y seleccionamos, del directorio de "hibernate-annotations" los siguientes archivos:

hibernate-annotations.jar

y del directorio lib:

ejb3-persistence.jar

hibernate-commons-annotations.jar

Nuestra biblioteca debe verse así:

Ahora cada vez que queramos usar hibernate con anotaciones deberemos agregar la biblioteca "Hibernate" (que creamos en el tutorial

anterior) y la nueva biblioteca "HibernateAnotaciones".

Ahora creamos un nuevo proyecto desde el menú "File -> New Project... -> Java -> Java Application". Llamamos a la

aplicación "HibernateAnotaciones" y nos aseguramos que las opciones "Create Main Class" y "Set as Main Project"

estén seleccionadas. Presionamos el botón "Finish" y veremos la clase "Main" en nuestro editor.

Agregamos las bibliotecas de Hibernate a nuestro proyecto. Hacemos clic derecho sobre el nodo "Libraries" del proyecto y

agregamos las dos bibliotecas de Hibernate que creamos ("Hibernate" y "HibernateAnotaciones"), seleccionando la opción "Add

Library..." del menú contextual que se abre. Aprovechamos para agregar el driver de la base de datos, el archivo "mysql-

connector-java-5.1.7-bin.jar" usando la opción "Add JAR/Folder..." de ese mismo menú contextual. Al final el nodo

"Libraries" debe verse así:

También es buen momento para crear la base de datos "pruebahibernate" si no lo han hecho. Igual que en el tutorial anterior no se

preocupen por crear tablas, ya que será el propio Hibernate quien lo haga.

Creamos una clase simple que servirá como Entidad. De hecho reutilizaremos la clase "Contacto" del tutorial anterior, cuyo código

quedó de esta forma:

Extend JMS to.NET

www.CodeMesh.c…

Let .NET applications

participate in any JMS

messaging system.

Cursos GratisINEM

Curso Onlinede jquery

ProgramadorJava

Cursos deJava

Participar en este sitioGoogle Friend Connect

Miembros (173) Más »

¿Ya eres miembro? Iniciar sesión

SEGUIDORES

► 2012 (2)

► 2011 (11)

► 2010 (10)

▼ 2009 (22)

► septiembre (2)

► agosto (2)

► julio (1)

► junio (3)

▼ mayo (2)

HIbernate - Parte 2: Persistiendo ObjetosSimples ...

HIbernate - Parte 1: Persistiendo ObjetosSimples ...

► abril (5)

► marzo (2)

► febrero (2)

► enero (3)

ARCHIVO DEL BLOG

Page 19: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 2: Persistiendo Objetos Simples usando Anotaciones (metadatos)

www.javatutoriales.com/2009/05/hibernate-parte-2-persistiendo-objetos.html 3/20

public class Contacto implements Serializable

{

private long id;

private String nombre;

private String email;

private String telefono;

public Contacto()

{

}

public Contacto(String nombre, String email, String telefono)

{

this.nombre = nombre;

this.email = email;

this.telefono = telefono;

}

public String getEmail()

{

return email;

}

public void setEmail(String email)

{

this.email = email;

}

public long getId()

{

return id;

}

private void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

public String getTelefono()

{

return telefono;

}

public void setTelefono(String telefono)

{

this.telefono = telefono;

}

}

En el caso anterior habíamos dejado la clase así, y el archivo de mapeo se encargaba del resto. En este caso agregaremos en la clase

las anotaciones para realizar el mapeo.

Antes de comenzar debo decir que en realidad Hibernate no trabaja solo cuando usamos anotaciones. Si recuerdan, cuando creamos la

biblioteca de "HibernateAnotaciones" entre los archivos que agregamos para conformarla se encontraba "ejb3-

persistence.jar". Este archivo contiene las clases de un Framework llamado "JPA" o "Java Persistence API", la cual es el API

"oficial" de persistencia para aplicaciones Java. Trabaja utilizando lo que se conoce como un "proveedor de persistencia" que es quien

se encarga de hacer el verdadero trabajo con la base de datos. En este caso Hibernate será el proveedor de persistencia. Las anotaciones

que usaremos si son de JPA. Hibernate también proporciona algunas anotaciones, pero son por si queremos agregar funcionalidad extra,

lo cual escapa al objetivo de este tutorial.

No usaremos al 100% JPA ya que no realizaremos la configuración como debe ser (en un archivo llamado "persistence.xml") sino

que seguiremos usando Hibernate para configurar todo.

Ahora si, pasemos a anotar nuestra clase. La anotación más importante es "javax.persistence.Entity", la cual indica que la clase

es una "Entidad", por lo que la anotación se coloca a nivel de clase.

Alex

Programador Java con algunos años de

experiencia en múltiples poyectos y con

múltiples APIs y herramientas deseoso de

compartir experiencias con el resto de

programadores.

Ver todo mi perfil

DATOS PERSONALES

Page 20: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 2: Persistiendo Objetos Simples usando Anotaciones (metadatos)

www.javatutoriales.com/2009/05/hibernate-parte-2-persistiendo-objetos.html 4/20

@Entity

public class Contacto implements Serializable

{

}

Cuando Hibernate genere la tabla para esta entidad lo hará en base al nombre de la misma. En este caso creará una tabla llamada

"contacto". Pero, en lo personal, prefiero que los nombres de las tablas estén el plural. Así que usaré la anotación

"javax.persistence.Table" para indicar cuál será el nombre de la tabla generada (o si ya tenemos una base de datos creada, cuál

es el nombre de la tabla en la que se almacenarán estas entidades):

@Entity

@Table(name="contactos")

public class Contacto implements Serializable

{

}

Después debemos indicar cuál atributo de la clase será el identificador. Esto lo hacemos con la anotación "javax.persistence.Id".

Este atributo podemos colocarlo en dos lugares: en el getter del atributo identificador, o directamente en el atributo.

Cuando colocamos la anotación en el atributo decimos que se realiza un acceso por "atributo" (field access), o sea que Hibernate

lee y establece los valores directamente de los atributos. Cuando la colocamos en el getter decimos que se realiza un acceso por

"propiedad" (property access), o sea que Hibernate accesa a los valores mediante los setters y getters. No pueden mezclarse

los tipos de acceso, así que elijan con cuidado. Pueden encontrar un poco más de información acerca de esto aquí (es un tutorial de JPA)

En este caso colocaré la anotación en el atributo:

@Id

private long id;

Podemos personalizar la forma en la que se generará el valor del identificador usando la anotación

"javax.persistence.GeneratedValue" e indicando la "estrategia" que se usará para generarlo. Existen solo 4 estrategias en JPA

(si usamos las anotaciones de Hibernate podemos usar todos los generadores de Hibernate):

AUTO

IDENTITY

SEQUENCE

TABLE

Las más usadas son "AUTO" e "IDENTITY". La primera usa la estrategia por default de la base de datos, mientras que la segunda genera

los identificadores en orden (1, 2, 3, etc.). Pueden encontrar más información aquí.

@Id

@GeneratedValue(strategy=GenerationType.IDENTITY)

private long id;

Esto es todo lo que es necesario hacerle a la clase, el resto de las propiedades que se persistirán serán obtenidas mediante reflexión, la

explicación es la misma que en el tutorial anterior. Todos los atributos que no estén marcados como "transient" o con la anotación

"javax.persistence.Transient" serán persistidas.

Si queremos personalizar la columna de la tabla que se generará de un atributo, podemos hacerlo con la anotación

"javax.persistence.Column". Por ejemplo, yo no quiero que la columna generada del atributo "email" sea "email", sino que sea

"e_mail", por lo que agrego la anotación de la siguiente forma:

@Column(name="e_mail")

private String email;

Al final la clase "Contacto" queda de la siguiente forma (omitiendo los imports):

Page 21: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 2: Persistiendo Objetos Simples usando Anotaciones (metadatos)

www.javatutoriales.com/2009/05/hibernate-parte-2-persistiendo-objetos.html 5/20

@Entity

@Table(name="contactos")

public class Contacto implements Serializable

{

@Id

@GeneratedValue(strategy=GenerationType.IDENTITY)

private long id;

private String nombre;

@Column(name="e_mail")

private String email;

private String telefono;

public Contacto()

{

}

public Contacto(String nombre, String email, String telefono)

{

this.nombre = nombre;

this.email = email;

this.telefono = telefono;

}

public String getEmail()

{

return email;

}

public void setEmail(String email)

{

this.email = email;

}

public long getId()

{

return id;

}

private void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

public String getTelefono()

{

return telefono;

}

public void setTelefono(String telefono)

{

this.telefono = telefono;

}

}

Y esto nos ahorrará el tener que crear el archivo de mapeo para esta clase - . Ahora configuraremos Hibernate. Como expliqué

anteriormente: La configuración de Hibernate puede hacerse en tres lugares:

Un archivo de propiedades llamado "hibernate.properties".

Un archivo XML llamado "hibernate.cfg.xml".

En código dentro de la misma aplicación.

Yo usaré el archivo "hibernate.cfg.xml". Así que crearemos este archivo en nuestro proyecto. Hacemos clic derecho en el nodo

"Source Packages" del proyecto. En el menú contextual que se abre seleccionamos "New -> XML Document...":

Page 22: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 2: Persistiendo Objetos Simples usando Anotaciones (metadatos)

www.javatutoriales.com/2009/05/hibernate-parte-2-persistiendo-objetos.html 6/20

Nombramos al archivo "hibernate.cfg", el IDE se encargará de colocarle la extensión ".xml". Ubicamos el archivo en el directorio

"src":

Con esto lograremos que el archivo quede en la raíz del classpath de la aplicación, también conocido como el paquete default.

Presionamos el botón "Next >" y en la pantalla siguiente indicamos que queremos crear un documento XML bien formado (como en el

caso anterior). Presionamos el botón "Finish" para generar el archivo. Si nos fijamos en el archivo generado, este debe encontrarse en

un paquete llamado "<default package>":

Borramos todo el contenido del archivo generado y colocamos las siguientes líneas:

<?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>

</hibernate-configuration>

El DTD nos ayudará para que el IDE autocomplete las etiquetas.

Como podemos ver, el elemento raíz del archivo de configuración es "<hibernate-configuration>" y, por lo tanto, todos los

elementos los colocaremos entre estas dos etiquetas.

Lo primero que debemos hacer es configurar un "session-factory", que básicamente es lo que le dice a Hibernate cómo conectarse y

manejar la conexión a la base de datos. Podemos tener más de un "session-factory" en el archivo de configuración (por si

quisiéramos conectarnos a más de una base de datos), pero por lo regular solo ponemos uno:

<hibernate-configuration>

<session-factory>

</session-factory>

</hibernate-configuration>

Lo siguiente es configurar la conexión a nuestra base de datos. En este archivo se configuran los parámetros básicos y típicos para una

conexión (la URL, nombre de usuario, contraseña, driver, etc.). Cada uno de estos parámetros se configura dentro de una etiqueta "

<property>" (al igual que casi todos los elementos del archivo de configuración). Como dije antes, usaré una base de datos MySQL

para este ejemplo, así que mi configuración queda de la siguiente forma:

Page 23: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 2: Persistiendo Objetos Simples usando Anotaciones (metadatos)

www.javatutoriales.com/2009/05/hibernate-parte-2-persistiendo-objetos.html 7/20

<property name="connection.driver_class">com.mysql.jdbc.Driver</property>

<property name="connection.url">jdbc:mysql://localhost/pruebahibernate</property>

<property name="connection.username">usuario</property>

<property name="connection.password">password</property>

Después, configuramos el pool de conexiones de Hibernate. En este caso como es un ejemplo muy simple, solo nos interesa tener una

conexión en el pool, por lo que colocamos la propiedad "connection.pool_size" con un valor de "1":

<property name="connection.pool_size">1</property>

*Nota: Este pool de conexiones es solo para efectos de pruebas en desarrollo. Cuando nuestra aplicación para a producción se recomienda usar

un pool de conexiones distinto, como C3P0.

El siguiente paso es muy importante. Debemos indicar el "dialecto" que usará Hibernate para comunicarse con la base de datos. Este

dialecto es la variante de SQL que usa la base de datos para ejecutar queries. Indicamos el dialecto con el "fully qualified class name",

o el nombre completo de la clase incluyendo el paquete. En el caso de MySQL 5 usamos

"org.hibernate.dialect.MySQL5Dialect". En esta página pueden encontrar una lista más o menos completa de los dialectos

soportados por Hibernate, pero siempre es mejor revisar la documentación de la versión que estén usando para estar seguros:

<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>

Otras dos propiedades importantes que podemos configurar son: "show_sql", que indica si queremos que las consultas SQL generadas

sean mostradas en el stdout (normalmente la consola), y "hbm2ddl.auto", que indica si queremos que se genere automáticamente el

esquema de la base de datos (las tablas). "show_sql" puede tomar valores de "true" o "false", yo lo colocaré en "true" (lo que

puede ser bueno mientras estamos en etapas de desarrollo o pruebas, pero querrán cambiar su valor cuando su aplicación pase a

producción). Por otro lado "hbm2ddl.auto" puede tomar los valores, según la documentación oficial, de "validate", "update",

"create", y "create-drop" (falta "none") (aunque no todos los valores funcionan para todas las bases de datos). Yo los colocaré de la

siguiente forma:

<property name="show_sql">true</property>

<property name="hbm2ddl.auto">create-drop</property>

Con el valor "create-drop" hacemos que cada vez que se ejecute la aplicación Hibernate elimine las tablas de la base de datos y las

vuelva a crear, cuando su sistema pase a producción sería bueno que quitaran esta propiedad.

Para terminar la configuración, debemos indicar dónde están las clases entidades. Cuando usamos Hibernate con archivos de mapeo

usamos el atributo "resource" del elemento "<mapping>", en el que indicábamos en dónde se encontraba dicho archivo de mapeo.

Cuando usamos anotaciones (y por lo tanto no hay archivos de mapeo) debemos indicar las clases anotadas usando su atributo "class",

de la siguiente forma:

<mapping class="hibernateanotaciones.Contacto" />

Tal vez encuentren que "<mapping>" tiene otro atributo que es "package". No se confundan, este atributo NO sirve para indicar un

paquete en el que se encuentren las clases entidades anotadas y lograr que Hibernate encuentre automáticamente estas clases para

ahorrarnos el trabajo de indicar cada clase. De hecho este atributo se usa cuando tenemos paquetes anotados. No estoy seguro de para

qué pueden usarse, pero solo no se confundan - !.

El archivo de configuración queda de la siguiente forma:

Page 24: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 2: Persistiendo Objetos Simples usando Anotaciones (metadatos)

www.javatutoriales.com/2009/05/hibernate-parte-2-persistiendo-objetos.html 8/20

<?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>

<property name="connection.driver_class">com.mysql.jdbc.Driver</property>

<property name="connection.url">jdbc:mysql://localhost/pruebahibernate</property>

<property name="connection.username">usuario</property>

<property name="connection.password">password</property>

<property name="connection.pool_size">1</property>

<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>

<property name="show_sql">true</property>

<property name="hbm2ddl.auto">create-drop</property>

<mapping class="hibernateanotaciones.Contacto" />

</session-factory>

</hibernate-configuration>

Esto es todo lo que debemos poner en el archivo de configuración. Ahora veremos cómo crear el código de acceso a la base de datos. Al

igual que en el tutorial anterior crearemos una clase de utilidad llamada "HibernateUtil", que se hará cargo de inicializar y hacer el

acceso al "SessionFactory". La clase será casi idéntica, con una excepción.

Creamos una nueva clase llamada "HibernateUtil" y dentro de esta clase declaramos un atributo static de tipo

"SessionFactory", así nos aseguraremos de que solo existe una instancia en la aplicación. Además lo declararemos como final

para que la referencia no pueda ser cambiada después de que la hayamos asignado.

private static final SessionFactory sessionFactory;

Después usamos un bloque de inicialización estático para inicializar esta variable en el momento en el que la clase sea cargada en la

JVM.

La última vez usamos una instancia de la clase "org.hibernate.cfg.Configuration" para cargar la configuración. Cuando

usamos anotaciones usamos una instancia de "org.hibernate.cfg.AnnotationConfiguration". Si usamos el método

"configure()" que no recibe parámetros entonces Hibernate busca el archivo "hibernate.cfg.xml" que creamos anteriormente.

Una vez que tenemos este objeto, entonces podemos inicializar la instancia de "SessionFactory" con su método

"buildSessionFactory". Además como este proceso puede lanzar "HibernateException" (que extiende de

"RuntimeException") la cachamos y lanzamos como un "ExceptionInInitializarError" (que es lo único que puede lanzarse

desde un bloque de inicialización). El bloque de inicialización queda de la siguiente forma:

static

{

try

{

sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();

} catch (HibernateException he)

{

System.err.println("Ocurrió un error en la inicialización de la SessionFactory: " + he);

throw new ExceptionInInitializerError(he);

}

}

Finalmente creamos un método static para recuperar la instancia de la "SessionFactory":

public static SessionFactory getSessionFactory()

{

return sessionFactory;

}

Page 25: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 2: Persistiendo Objetos Simples usando Anotaciones (metadatos)

www.javatutoriales.com/2009/05/hibernate-parte-2-persistiendo-objetos.html 9/20

La clase "HibernateUtil" queda de la siguiente forma:

import org.hibernate.HibernateException;

import org.hibernate.SessionFactory;

import org.hibernate.cfg.AnnotationConfiguration;

public class HibernateUtil

{

private static final SessionFactory sessionFactory;

static

{

try

{

sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();

} catch (HibernateException he)

{

System.err.println("Ocurrió un error en la inicialización de la SessionFactory: " + he)

;

throw new ExceptionInInitializerError(he);

}

}

public static SessionFactory getSessionFactory()

{

return sessionFactory;

}

}

Ahora escribiremos una clase DAO (nuevamente no seguiremos el patrón al pie de la letra, es solo para mostrar cómo trabajar con

Hibernate) que nos permitirá realizar operaciones de base de datos. De hecho es la misma clase "ContactosDAO" del tutorial anterior.

Copiaré los pasos y la explicación para que no tengan que regresar a revisarla

Creamos una clase llamada "ContactosDAO" y agregamos dos atributos, uno llamado "sesion" de tipo "org.hibernate.Session",

y otro llamado "tx" de tipo "org.hibernate.Transaction".

private Session sesion;

private Transaction tx;

Estos atributos nos servirán para mantener la referencia a la sesión a base de datos, y a la transacción actual, respectivamente. Ahora

agregaremos dos métodos de utilidad. El primero nos ayudará a iniciar una sesión y una transacción en la base de datos. Llamaremos a

este método "iniciaOperacion", y la implementación es la siguiente:

private void iniciaOperacion() throws HibernateException

{

sesion = HibernateUtil.getSessionFactory().openSession();

tx = sesion.beginTransaction();

}

En el método anterior obtenemos una referencia a "SessionFactory" usando nuestra clase de utilidad "HibernateUtil". Una vez

que tenemos la "SessionFactory" creamos una conexión a la base de datos e iniciamos una nueva sesión con el método

"openSession()". Una vez teniendo la sesión iniciamos una nueva transacción y obtenemos una referencia a ella con

"beginTransaction()".

Ahora el segundo método de utilidad (llamado "manejaExcepcion") nos ayudará a manejar las cosas en caso de que ocurra una

excepción. Si esto pasa queremos que la transacción que estamos ejecutando se deshaga y se relance la excepción (o podríamos lanzar

Page 26: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 2: Persistiendo Objetos Simples usando Anotaciones (metadatos)

www.javatutoriales.com/2009/05/hibernate-parte-2-persistiendo-objetos.html 10/20

una propia). Por lo que el método queda así:

private void manejaExcepcion(HibernateException he) throws HibernateException

{

tx.rollback();

throw new HibernateException("Ocurrió un error en la capa de acceso a datos", he);

}

Ahora crearemos los métodos que nos permitirán realizar las tareas de persistencia de una entidad "Contacto", conocidas en lenguaje

de base de datos como CRUD: guardarla, actualizarla, eliminarla, buscar un entidad "Contacto" y obtener todas los contactos que

existen en la base de datos, así que comencemos.

Afortunadamente Hibernate hace que esto sea fácil ya que proporciona métodos para cada una de estas tareas. Primero veamos como

guardar un objeto "Contacto". Para esto Hibernate proporciona el método "save" en nuestro objeto de tipo

"org.hibernate.Session", que se encarga de generar el "INSERT" apropiado para la entidad que estamos tratando de guardar. El

método "guardaContacto" queda de la siguiente forma:

public long guardaContacto(Contacto contacto)

{

long id = 0;

try

{

iniciaOperacion();

id = (Long)sesion.save(contacto);

tx.commit();

}catch(HibernateException he)

{

manejaExcepcion(he);

throw he;

}finally

{

sesion.close();

}

return id;

}

Regresamos el "id" generado al guardar el "Contacto" solo por si queremos usarlo más adelante en el proceso (como lo haremos

nosotros), o si queremos mostrarle al usuario, por alguna razón, el identificador del contacto.

Ahora veremos cómo actualizar un "Contacto". Para eso usamos el método "update", de nuestro objeto "session", en nuestro

método "actualizaContacto":

public void actualizaContacto(Contacto contacto) throws HibernateException

{

try

{

iniciaOperacion();

sesion.update(contacto);

tx.commit();

}catch (HibernateException he)

{

manejaExcepcion(he);

throw he;

}finally

{

sesion.close();

}

}

Como podemos ver, el método para actualizar es muy similar al método para guardar la entidad. Lo mismo ocurre con el método para

eliminarla, "eliminaContacto":

Page 27: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 2: Persistiendo Objetos Simples usando Anotaciones (metadatos)

www.javatutoriales.com/2009/05/hibernate-parte-2-persistiendo-objetos.html 11/20

public void eliminaContacto(Contacto contacto) throws HibernateException

{

try

{

iniciaOperacion();

sesion.delete(contacto);

tx.commit();

} catch (HibernateException he)

{

manejaExcepcion(he);

throw he;

}finally

{

sesion.close();

}

}

Ahora veremos unos métodos un poco más interesantes.

Cuando queremos buscar una entidad podemos usar varios criterios. La forma más fácil es buscar una entidad particular usando su "id".

El objeto "org.hibernate.Session" proporciona dos métodos para esto: "load" y "get". Los dos hacen prácticamente lo mismo. En

base al identificador y tipo de la entidad recuperan la entidad indicada, con la diferencia de que "load" lanza una excepción en caso de

que la entidad indicada no sea encontrada en la base de datos, mientras que "get" simplemente regresa "null". Pueden usar el que

prefieran, en lo personal me gusta más "get", así que lo usaré para el método "obtenContacto":

public Contacto obtenContacto(long idContacto) throws HibernateException

{

Contacto contacto = null;

try

{

iniciaOperacion();

contacto = (Contacto) sesion.get(Contacto.class, idContacto);

} finally

{

sesion.close();

}

return contacto;

}

Finalmente veremos el método "obtenListaContactos" que recupera todos los Contactos que estén guardados en la base de

datos. Como en este caso regresaremos una lista de elementos deberemos crear una consulta. Nuevamente usaremos HQL para generar

dicha consulta:

public List<Contacto> obtenListaContactos() throws HibernateException

{

List<Contacto> listaContactos = null;

try

{

iniciaOperacion();

listaContactos = sesion.createQuery("from Contacto").list();

}finally

{

sesion.close();

}

return listaContactos;

}

Eso es todo, nuestra consulta para recuperar todos los Contactos que tenemos en la base de datos solo debemos usar la clausula

"from Contacto". Si queren una referencia más amplia de HQL pueden encontrarla en esta página.

Bien, eso es todo. La clase "ContactosDAO" queda de la siguiente forma (omitiendo los imports):

public class ContactosDAO

{

private Session sesion;

private Transaction tx;

public long guardaContacto(Contacto contacto) throws HibernateException

Page 28: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 2: Persistiendo Objetos Simples usando Anotaciones (metadatos)

www.javatutoriales.com/2009/05/hibernate-parte-2-persistiendo-objetos.html 12/20

public long guardaContacto(Contacto contacto) throws HibernateException

{

long id = 0;

try

{

iniciaOperacion();

id = (Long) sesion.save(contacto);

tx.commit();

} catch (HibernateException he)

{

manejaExcepcion(he);

throw he;

} finally

{

sesion.close();

}

return id;

}

public void actualizaContacto(Contacto contacto) throws HibernateException

{

try

{

iniciaOperacion();

sesion.update(contacto);

tx.commit();

} catch (HibernateException he)

{

manejaExcepcion(he);

throw he;

} finally

{

sesion.close();

}

}

public void eliminaContacto(Contacto contacto) throws HibernateException

{

try

{

iniciaOperacion();

sesion.delete(contacto);

tx.commit();

} catch (HibernateException he)

{

manejaExcepcion(he);

throw he;

} finally

{

sesion.close();

}

}

public Contacto obtenContacto(long idContacto) throws HibernateException

{

Contacto contacto = null;

try

{

iniciaOperacion();

contacto = (Contacto) sesion.get(Contacto.class, idContacto);

} finally

{

sesion.close();

}

return contacto;

}

public List<Contacto> obtenListaContactos() throws HibernateException

{

List<Contacto> listaContactos = null;

try

{

iniciaOperacion();

listaContactos = sesion.createQuery("from Contacto").list();

} finally

{

sesion.close();

}

return listaContactos;

}

private void iniciaOperacion() throws HibernateException

{

Page 29: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 2: Persistiendo Objetos Simples usando Anotaciones (metadatos)

www.javatutoriales.com/2009/05/hibernate-parte-2-persistiendo-objetos.html 13/20

{

sesion = HibernateUtil.getSessionFactory().openSession();

tx = sesion.beginTransaction();

}

private void manejaExcepcion(HibernateException he) throws HibernateException

{

tx.rollback();

throw new HibernateException("Ocurrió un error en la capa de acceso a datos", he);

}

}

Ahora, y para terminar el ejemplo, haremos uso de "ContactosDAO" en la clase "Main" para crear y recuperar algunas instancias de

"Contactos". Como la clase es la misma que en el tutorial anterior no daré una explicación. La clase "Main" queda de la siguiente

forma:

public class Main

{

public static void main(String[] args)

{

ContactosDAO contactosDAO = new ContactosDAO();

Contacto contactoRecuperado = null;

long idAEliminar = 0;

//Creamos tes instancias de Contacto

Contacto contacto1 = new Contacto("Contacto 1", "[email protected]", "12345678");

Contacto contacto2 = new Contacto("Contacto 2", "[email protected]", "87654321");

Contacto contacto3 = new Contacto("Contacto 3", "[email protected]", "45612378");

//Guardamos las tres instancias, guardamos el id del contacto1 para usarlo posteriormente

idAEliminar = contactosDAO.guardaContacto(contacto1);

contactosDAO.guardaContacto(contacto2);

contactosDAO.guardaContacto(contacto3);

//Modificamos el contacto 2 y lo actualizamos

contacto2.setNombre("Nuevo Contacto 2");

contactosDAO.actualizaContacto(contacto2);

//Recuperamos el contacto1 de la base de datos

contactoRecuperado = contactosDAO.obtenContacto(idAEliminar);

System.out.println("Recuperamos a " + contactoRecuperado.getNombre());

//Eliminamos al contactoRecuperado (que es el contacto3)

contactosDAO.eliminaContacto(contactoRecuperado);

//Obtenemos la lista de contactos que quedan en la base de datos y la mostramos

List<Contacto> listaContactos = contactosDAO.obtenListaContactos();

System.out.println("Hay " + listaContactos.size() + "contactos en la base de datos");

for(Contacto c : listaContactos)

{

System.out.println("-> " + c.getNombre());

}

}

}

En el momento que ejecutemos la aplicación Hibernate creará la tabla correspondiente en la base de datos (llamada "contactos", como

lo indicamos) y con los siguientes campos:

Page 30: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 2: Persistiendo Objetos Simples usando Anotaciones (metadatos)

www.javatutoriales.com/2009/05/hibernate-parte-2-persistiendo-objetos.html 14/20

Publicado por Alex en 17:09

Reacciones: divertido (1) interesante (3) increible (0) no me gusta (0)

Etiquetas: anotaciones, hibernate, java, mapeos

Como podemos ver la tabla se ha generado en base a las indicaciones que hemos hecho con las anotaciones.

Para terminar veamos los datos de la tabla. Según la salida en la consola del NetBeans, debe haber dos contactos en la base de datos

"Nuevo Contacto 2" y "Contacto 3":

Si vemos los datos en la base comprabos que, efectivamente, esto es correcto:

Con lo que comprobamos que el ejemplo funciona!!! - .

Bien, pues en esta ocasión vimos cómo usar Hibernate sin hacer uso de archivos de mapeo en XML, indicando esos datos en el código

fuente de la clase.

Esto tiene sus ventajas y desventajas, así que debe usarse con cuidado. Por ejemplo, como la información está en el código fuente,

cualquier cambio necesitará que recompilemos la aplicación. Además que nuestra entidad ahora ha quedado atada al API de JPA.

Esto puede no ser problema en muchos casos, pero habrá unos en los que si lo sea, como cuando no tenemos el código fuente de las

entidades, o no queremos atarlas a JPA, porque las usaremos posteriormente en otra aplicación que no usará JPA. Pero, nuevamente,

esto depende de la aplicación que estén desarrollando.

Además podemos mezclar ambos métodos en donde, si existe un mapeo en los dos lugares para una misma clase, el mapero del archivo

XML toma precedencia.

Espero que este tutorial les haya sido de utilidad. En los próximos tutoriales mostraré como trabajar con Hibernate para hacer cosas más

interesantes y mostraré como realizar la configuración de las dos formas, con anotaciones y archivos de mapeo.

No olviden dejar sus dudas, comentarios y sugerencias.

Saludos.

Descarga los archivos de este tutorial desde aquí:

Hibernate Anotaciones

Entradas Relacionadas:

Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

Parte 3: Relaciones / Uno a uno

Parte 4: Relaciones / Uno a muchos

Parte 5: Relaciones / Muchos a uno

Parte 6: Relaciones / Muchos a Muchos

Parte 7: HQL Primera Parte

Parte 8: HQL Segunda Parte

Parte 9: Parámetros en HQL

Parte 10: Herencia

Parte 11: Interceptores y Eventos

Recomendar esto en Google

38 comentarios:

nbalike 14 de mayo de 2009 20:59

Page 31: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 3: Relaciones / Uno a uno

www.javatutoriales.com/2009/06/hibernate-parte-3-relaciones-uno-uno.html 1/24

Blog dedicado a temas de programación actuales usando el lenguaje de programación Java y las últimas versiones de sus APIs y Herramientas.

Tutoriales de Programacion Java

Página principal Página de Concursos Tutoriales UPAO 2010 Presentaciones Capacitación

JUEVES, 4 DE JUNIO DE 2009

HIbernate - Parte 3: Relaciones / Uno a uno

En los tutoriales anteriores vimos cómo persistir objetos simples en la base de datos usando Hibernate, tanto con archivos de mapeo XML

como con anotaciones. Gracias a eso no tuvimos que escribir ni una sola línea de código SQL.

Sin embargo solo en raras ocasiones deberemos guardar un objeto simple como lo vimos. Por lo regular guardamos grafos de objetos u

objetos relacionados con otros objetos. En estos casos querremos que los objetos se guarden, actualicen, o eliminen en el momento que

lo haga nuestro objeto principal (o tal vez no - !).

En este tutorial veremos cómo manejar los objetos relacionados (con mapeos y con XML).

En Hibernate (y en general en las bases de datos) existen 4 tipos de relaciones:

Uno a Uno

Uno a Muchos

Muchos a Uno

Muchos a Muchos

Si le ponemos direcciones a estas relaciones (unidireccional, o bidireccional) tendremos 7 tipos de relaciones:

Uno a uno unidireccional

Uno a uno bidireccional

Uno a muchos unidireccional

Uno a muchos bidireccional

Muchos a uno unidireccional

muchos a muchos unidireccional

Muchos a muchos bidireccional

La relación muchos a uno bidireccional es igual que la relación uno a muchos bidireccional, así que la dejamos fuera.

Bien, pues en este y los siguientes tutoriales veremos cómo manejar estos 7 tipo de relaciones usando Hibernate.

En este tutorial solo veremos las relaciones uno a uno y cubriré los siguientes tipos de relaciones en las siguientes entregas, ya que hay

mucho que se puede decir sobre cada uno de los tipos de relaciones.

Crearemos un proyecto en NetBeans para mostrar los ejemplos (menú "File -> New Project... -> Java -> Java

Application"). Le damos un nombre y una ubicación al proyecto y nos aseguramos de que las opciones "Create Main Class" y

"Set as Main Project" estén habilitadas. Presionamos el botón "Finish" y veremos aparecer en el editor nuestra clase "Main".

Agregamos la biblioteca de "Hibernate", que creamos en el primer tutorial de la serie. Hacemos clic derecho en el nodo "Libraries"

del proyecto. En el menú contextual que se abre seleccionamos la opción "Add Library...":

En la ventana que se abre seleccionamos la biblioteca "Hibernate":

DONACIONES

JAVA TUTORIALES EN FACEBOOK

Java Tutoriales

Me gusta

A 7895 personas les gusta

Plug-in social de Facebook

Java Tutoriales

Amigo programador, tú que

estás todo el día picando

código, escucha estas

recomendaciones sólo

hechas para ti..... ¿qué es

esto?... Gracias Jorge

Rubira....

No olviden poner la

sentencia where al hacer

un borraro en la base de

datos..

http://www.youtube.com/

watch?v=i_cVJgIz_Cs

Compartir 0 Más Siguiente blog»

Page 32: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 3: Relaciones / Uno a uno

www.javatutoriales.com/2009/06/hibernate-parte-3-relaciones-uno-uno.html 2/24

Presionamos el botón "Add Library" para que la biblioteca se agregue a nuestro proyecto. Aprovechamos también para agregar el

conector de MySQL. Debemos tener los siguientes archivos en nuestro proyecto:

Ahora creamos dos paquetes, uno con el nombre "modelo", que contendrá las clases entidades, y otro con el nombre "mapeos" que

contendrá los archivos de mapeo XML. Hacemos clic derecho en el nodo del paquete que se creó al generar el proyecto. En el menú

contextual que se abre seleccionamos la opción "New -> Java Package..." y creamos los dos paquetes.

Que deben quedar así:

Aprovecharemos para crear nuestro archivo de configuración de Hibernate, "hibernate.cfg.xml", el cual será muy parecido al del

primer tutorial:

Java filelibraries

www.aspose.com

DOC, XLS, PPT, PDF,

MSG and more APIs

to Manage, Print and

Convert

Participar en este sitioGoogle Friend Connect

Miembros (173) Más »

¿Ya eres miembro? Iniciar sesión

SEGUIDORES

► 2012 (2)

► 2011 (11)

► 2010 (10)

▼ 2009 (22)

► septiembre (2)

► agosto (2)

► julio (1)

▼ junio (3)

Acepto Donaciones

Hibernate - Parte 4: Relaciones / Uno amuchos

HIbernate - Parte 3: Relaciones / Uno auno

► mayo (2)

► abril (5)

► marzo (2)

► febrero (2)

► enero (3)

ARCHIVO DEL BLOG

Page 33: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 3: Relaciones / Uno a uno

www.javatutoriales.com/2009/06/hibernate-parte-3-relaciones-uno-uno.html 3/24

<!DOCTYPE hibernate-configuration PUBLIC

"-//Hibernate/Hibernate Configuration DTD 3.0//EN"

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

<hibernate-configuration>

<session-factory>

<!-- parametros para la conexion a la base de datos -->

<property name="connection.driver_class">com.mysql.jdbc.Driver</property>

<property name="connection.url">jdbc:mysql://localhost/hibernaterelaciones</property>

<property name="connection.username">usuario</property>

<property name="connection.password">password</property>

<!-- Configuracion del pool interno -->

<property name="connection.pool_size">1</property>

<!-- Dialecto de la base de datos -->

<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>

<!-- Otras propiedades importantes -->

<property name="show_sql">true</property>

<property name="hbm2ddl.auto">create-drop</property>

<!-- Aqui iran los archivos de mapeo -->

</session-factory>

</hibernate-configuration>

Para los ejercicios usaremos una base de datos llamada "hibernaterelaciones", en MySQL.

Nota: Yo usaré dos proyectos, uno para usar archivos de mapeo y otro para usar anotaciones y que el código de ambos no se mezcle.

Ahora sí, comencemos con el tema del tutorial.

En las relaciones uno a uno un objeto entidad de una clase A está relacionado con uno y solo un objeto entidad de una clase B. Si la

relación es unidireccional solo el objeto A está consciente de la relación (El objeto A tiene una referencia al objeto B) y el objeto B NO

sabe nada de esta. Por ejemplo, cuando tenemos una relación Persona -> Direccion (es uno a uno porque una Persona, en teoría,

solo puede tener una Direccion). Donde la Persona es la entidad "A" y la Direccion es la entidad "B". O sea, la Persona conoce

su Direccion pero la Direccion no conoce a la Persona a la que pertenece.

En las relaciones bidireccionales las dos entidades están conscientes de la relación. Por ejemplo, si tenemos una relación Pais <->

Presidente (es uno a uno porque un Pais solo debería tener un Presidente y un Presidente solo puede serlo de un Pais). En

este caso ambos lados de la relación conocen el otro lado, o su "inverso".

Veamos estos dos ejemplos. Las clases que usaremos serán muy simples y solo pondré un atributo adicional al "id" y al de la relación

para que esta última sea más clara.

Primero crearemos las clases Persona y Dirección (para la relación unidireccional), dentro del paquete "modelo" que creamos

hace unos momentos. La clase Direccion queda así:

1 - Relaciones Uno a Uno Unidireccionales

Alex

Programador Java con algunos años de

experiencia en múltiples poyectos y con

múltiples APIs y herramientas deseoso de

compartir experiencias con el resto de

programadores.

Ver todo mi perfil

DATOS PERSONALES

Page 34: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 3: Relaciones / Uno a uno

www.javatutoriales.com/2009/06/hibernate-parte-3-relaciones-uno-uno.html 4/24

public class Direccion implements Serializable

{

private long id;

private String calle;

private String codigoPostal;

public Direccion()

{

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getCalle()

{

return calle;

}

public void setCalle(String calle)

{

this.calle = calle;

}

public String getCodigoPostal()

{

return codigoPostal;

}

public void setCodigoPostal(String codigoPostal)

{

this.codigoPostal = codigoPostal;

}

}

Como podemos ver, la clase Direccion es muy simple, solo tiene el "id y dos atributos más. De hecho esta entidad no tiene idea que

será relacionada con una entidad Persona, la cual por cierto queda de esta forma:

Page 35: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 3: Relaciones / Uno a uno

www.javatutoriales.com/2009/06/hibernate-parte-3-relaciones-uno-uno.html 5/24

public class Persona implements Serializable

{

private long id;

private String nombre;

private Direccion direccion;

public Persona()

{

}

public Direccion getDireccion()

{

return direccion;

}

public void setDireccion(Direccion direccion)

{

this.direccion = direccion;

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

}

La clase Persona es un poco más interesante. Esta clase tiene, además del "id" y el atributo "nombre", un atributo de tipo

"Direccion" (con sus correspondientes setters y getters). Por lo tanto la Persona tendrá acceso a los datos de la Direccion.

En este caso se dice que Persona es la "dueña" (owner) de la relación. O sea que ella controla qué es lo que pasa cuando se realiza

una modificación en un objeto entidad (si queremos que al eliminarse el dueño se elimine la entidad relacionada, o se actualice, o no pase

nada, etc.). Esto se configura en el archivo de mapeo XML o en las anotaciones, dependiendo del método que estemos utilizando.

Veamos cómo indicar que existe este relación. Primero veremos cómo indicarlo con archivos de mapeo y después veremos cómo se hace

con anotaciones.

Creamos, en el paquete "mapeos" un nuevo documento XML. Le damos de nombre "Direccion.hbm" (el asistente se encargará de

colocar el ".xml")

Presionamos el botón "Next >" e indicamos que queremos crear un documento XML bién formado (la primer opción) y presionamos el

botón "Finish".

Eliminamos el contenido del archivo creado y lo reemplazamos con el siguiente:

1.1 RELACIONES UNO A UNO UNIDIRECCIONALES CON ARCHIVOS DE MAPEO

Page 36: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 3: Relaciones / Uno a uno

www.javatutoriales.com/2009/06/hibernate-parte-3-relaciones-uno-uno.html 6/24

<?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="hibernate.relaciones.modelo.Direccion" table="DIRECCIONES">

</class>

</hibernate-mapping>

El mapeo de la clase "Dirección" es muy simple, como el que expliqué en el primer tutorial de la serie. El mapeo queda de la siguiente

forma:

<hibernate-mapping>

<class name="hibernate.relaciones.modelo.Direccion" table="DIRECCIONES">

<id name="id" column="ID">

<generator class="identity" />

</id>

<property name="calle" />

<property name="codigoPostal" />

</class>

</hibernate-mapping>

Ahora crearemos el mapeo de la clase "Persona", en un archivo llamado llamado "Persona.hbm.xml" en el paquete "mapeos", la cuál

de hecho será muy parecido al de "Direccion", excepto en el identificador. Queremos que el identificador de la "Persona" y de la

"Direccion" sean el mismo, ya que en las relaciones uno a uno Hibernate supone que ambas entidades tendrán el mismo identificador

(lo cual podría no siempre ser verdad) y de esta forma la recuperación de la Direccion se hará automáticamente al momento de

recuperar la Persona, lo mismo ocurre con la eliminación. Pero como solamente Persona está consciente de la relación, es ella quien

recibirá el mismo identificador que la Direccion.

Para lograr esto usamos un generador especial de id llamado "foreign", indicando que el identificador será tomado de otra entidad.

Decimos que la entidad está referenciada como la propiedad "direccion" de nuestra clase "Persona". Por lo que el identificador queda

así:

<id name="id" column="ID">

<generator class="foreign">

<param name="property">direccion</param>

</generator>

</id>

El archivo, solamente con los mapeos para los atributos "id" y "nombre", se ve así:

<?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="hibernate.relaciones.modelo.Persona" table="PERSONAS">

<id name="id" column="ID">

<generator class="foreign">

<param name="property">direccion</param>

</generator>

</id>

<property name="nombre" />

</class>

</hibernate-mapping>

Page 37: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 3: Relaciones / Uno a uno

www.javatutoriales.com/2009/06/hibernate-parte-3-relaciones-uno-uno.html 7/24

Ahora agregaremos el elemento para indicar la relación uno a uno con la clase "Direccion". Para esto usamos el elemento "<one-to-

one>". Usamos el atributo "name" para indicar cómo se llama el atributo en la clase "Persona" que indica la relación con "Direccion",

que en este caso es "direccion":

<one-to-one name="direccion" />

Queremos que la entidad "Direccion" se guarde en la base de datos en el momento en el que se guarda la "Persona". De la misma

forma, queremos que cuando la "Persona" sea eliminada de la base de datos también se elimine su correspondiente "Direccion".

Para lograr esto usamos el atributo "cascade" del elemento "<one-to-one>".

Las operaciones en cascada son operaciones que se realizan en los hijos al mismo momento que en los padres (o en las entidades

relacionadas con la entidad en la que estamos realizando la operación)

Este atributo puede tener los siguientes valores:

persist

merge

save-update

delete

lock

refresh

evict

replicate

all

none

La mayoría de los valores corresponde con un método, con el mismo nombre, del objeto Session de Hibernate, con excepción de "all" y

"none", que no corresponden con ninguno.

Por default ninguna operación se realiza en cascada, pero nosotros dijimos que queremos que la Direccion se actualice en el momento

en el que guardamos y eliminamos la Persona entonces indicamos como el valor de cascade "persist, delete":

<one-to-one name="direccion" cascade="persist, delete"/>

Al final el mapeo de Persona queda de la siguiente forma:

<hibernate-mapping>

<class name="hibernate.relaciones.modelo.Persona" table="PERSONAS">

<id name="id" column="ID">

<generator class="foreign">

<param name="property">direccion</param>

</generator>

</id>

<property name="nombre" />

<one-to-one name="direccion" cascade="persist, delete"/>

</class>

</hibernate-mapping>

Esto es todo lo que necesitamos para esta relación. No olviden agregar estos dos archivos de mapeo al archivo "hibernate.cfg.xml":

Page 38: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 3: Relaciones / Uno a uno

www.javatutoriales.com/2009/06/hibernate-parte-3-relaciones-uno-uno.html 8/24

<mapping resource="hibernate/relaciones/mapeos/Persona.hbm.xml"/>

<mapping resource="hibernate/relaciones/mapeos/Direccion.hbm.xml"/>

Ahora usaremos la clase "HibernateUtil" que creamos en el primer tutorial para trabajar con nuestras entidades.

En la clase "Main" agregamos el siguiente código autoexplicativo:

public static void main(String[] args)

{

Persona persona1 = new Persona();

persona1.setNombre("Persona que sera borrada");

Persona persona2 = new Persona();

persona2.setNombre("Persona que permanecera");

Direccion direccion1 = new Direccion();

direccion1.setCalle("Calle 1");

direccion1.setCodigoPostal("12345");

Direccion direccion2 = new Direccion();

direccion2.setCalle("Calle 2");

direccion2.setCodigoPostal("54321");

persona1.setDireccion(direccion1);

persona2.setDireccion(direccion2);

Session sesion = HibernateUtil.getSessionFactory().openSession();

/*Esta direccion se agrega para comprobar que las personas tomen el mismo

identificador que las direcciones (ninguna persona debe tener el mismo id de

esta direccion)*/

Direccion d = new Direccion();

d.setCalle("Calle de Prueba de identificadores");

d.setCodigoPostal("21345");

/*En la primer sesion a la base de datos almacenamos los dos objetos Persona

los objetos Direccion se almacenaran en cascada*/

sesion.beginTransaction();

sesion.persist(d);

sesion.persist(persona1);

sesion.persist(persona2);

sesion.getTransaction().commit();

sesion.close();

/*En la segunda sesion eliminamos el objeto persona1,

la direccion1 sera borrada en cascada*/

sesion = HibernateUtil.getSessionFactory().openSession();

sesion.beginTransaction();

sesion.delete(persona1);

sesion.getTransaction().commit();

sesion.close();

}

Como podemos ver, se crean tres Direcciones y dos Personas; dos de las Direcciones se relacionan con las Personas y

finalmente se elimina el objeto "persona1". Esto debería dejarnos solamente con una Persona almacenada y dos Direcciones.

Comprobemos que es así:

Como podemos ver, se eliminaron tanto la Persona como la Direccion con id = 2, comprobando que nuestra configuración funciona -

Page 39: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 3: Relaciones / Uno a uno

www.javatutoriales.com/2009/06/hibernate-parte-3-relaciones-uno-uno.html 9/24

. Como dije anteriormente, en una relación uno a uno Hibernate supondrá que ambas entidades en la relación tienen el mismo id.

Nosotros nos aseguramos de que esto ocurra indicando que el id de Persona, que es la única entidad que sabe de la relación, sea

tomado en base al id de su Direccion asociada. Claro que esto nos obliga a que siempre debe haber una Direccion asociada con

una Persona, de lo contrario esta última no podrá obtener su id de ningún lado, por lo que debemos tener cuidado al usar este tipo de

relación.

Ahora veremos cómo hacer lo mismo pero usando anotaciones:

Recuerden que pasa usar anotaciones debemos agregar al proyecto la biblioteca "HibernateAnotaciones" que creamos en el

segundo tutorial.

Las anotaciones que colocaremos en la clase "Direccion" serán las mismas que expliqué en el segundo tutorial de esta serie, como

son muy sencillas no volveré a explicarlos. La clase "Direccion anotada queda de la siguiente forma:

@Entity

public class Direccion implements Serializable

{

@Id

@GeneratedValue(strategy=GenerationType.IDENTITY)

private long id;

private String calle;

private String codigoPostal;

public Direccion()

{

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getCalle()

{

return calle;

}

public void setCalle(String calle)

{

this.calle = calle;

}

public String getCodigoPostal()

{

return codigoPostal;

}

public void setCodigoPostal(String codigoPostal)

{

this.codigoPostal = codigoPostal;

}

}

Listo, con tan solo tres anotaciones nos ahorramos el crear un archivo de mapeo para esta clase.

La clase "Persona" se anota casi de la misma forma, con excepción de la indicación de la relación uno a uno con la clase

"Direccion". Explicaré primero esto.

Para cada una de los 4 tipos de relaciones que expliqué antes existe una anotación especial. En el caso de las relaciones uno a uno se

usa la anotación "@OneToOne" indicando cuál atributo representa la relación. En este caso la relación está representado por el atributo

"direccion", así que la colocamos así:

@OneToOne

private Direccion direccion;

Es así de simple - . Habiamos dicho que nos interesa que cuando guardamos y eliminamos la "Persona" también se elimine la

1.2 RELACIONES UNO A UNO UNIDIRECCIONALES CON ANOTACIONES

Page 40: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 3: Relaciones / Uno a uno

www.javatutoriales.com/2009/06/hibernate-parte-3-relaciones-uno-uno.html 10/24

"Direccion" asociada, y que esto se logra indicando que esas operaciones se hagan en cascada. Usando anotaciones también

podemos indicar eso usando el atributo "cascade" de la anotación que representa la relación (en este caso "@OneToOne"). Este atributo

recibe un arreglo de objetos tipo "javax.persistence.CascadeType" la cual es una enumeración. Cada elemento de la enumeración

representa una operación que será realizada en cascada. Usando las anotaciones de JPA tenemos un número menor de operaciones que

con los archivos de mapeo. De hecho solo tenemos:

merge

persist

refresh

remove

all

Por default ninguna operación se realiza en cascada, pero nosotros queremos que se realicen en cascada las operaciones de guardar y

eliminar, por lo que colocamos estos valores de la siguiente forma:

@OneToOne(cascade={CascadeType.PERSIST, CascadeType.REMOVE})

private Direccion direccion;

Además, y para terminar con esta parte, había comentado que Hibernate realizará las operaciones en cascada suponiendo que tanto la

Persona como la Direccion tienen el mismo identificador, pero que esto podría no siempre ser verdad (como en el ejemplo que vimos

anteriormente). Por lo que debemos indicar que el identificador de Persona debe ser el mismo que el de Direccion.

Para indicar esto con anotaciones se supone que usamos la anotación "@PrimaryKeyJoinColumn" en el elemento del que se tomará

el id, que en ese caso también es "direccion", pero en la práctica esto no funciona - !, de hecho hay muchos reportes en el sitio de

Hibernate indicando esto. Así que dejamos la anotación de la relación como está. Con esto lograremos que en la tabla en la que se

almacene las entidades "Persona" se agregue una columna más para indicar con cuál "Direccion" está relacionada.

Finalmente la relación queda indicada de esta forma:

@OneToOne(cascade={CascadeType.PERSIST, CascadeType.REMOVE})

private Direccion direccion;

La clase "Persona" anotada queda de la siguiente forma:

Page 41: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 3: Relaciones / Uno a uno

www.javatutoriales.com/2009/06/hibernate-parte-3-relaciones-uno-uno.html 11/24

@Entity public class Persona implements Serializable

{

@Id

private long id;

private String nombre;

@OneToOne(cascade={CascadeType.PERSIST, CascadeType.REMOVE})

private Direccion direccion;

public Persona()

{

}

public Direccion getDireccion()

{

return direccion;

}

public void setDireccion(Direccion direccion)

{

this.direccion = direccion;

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

}

No olviden que debemos colocar en el archivo de configuración de Hibernate estas clases:

<mapping class="hibernate.relacionesanotaciones.modelo.Persona" />

<mapping class="hibernate.relacionesanotaciones.modelo.Direccion" />

En esta ocasión usaremos la clase "HibernateUtil" que creamos en el segundo tutorial para trabajar con nuestras entidades.

La clase "Main" tiene el mismo código auto-explicativo de antes:

Page 42: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 3: Relaciones / Uno a uno

www.javatutoriales.com/2009/06/hibernate-parte-3-relaciones-uno-uno.html 12/24

public static void main(String[] args)

{

Persona persona1 = new Persona();

persona1.setNombre("Persona que sera borrada");

Persona persona2 = new Persona();

persona2.setNombre("Persona que permanecera");

Direccion direccion1 = new Direccion();

direccion1.setCalle("Calle 1");

direccion1.setCodigoPostal("12345");

Direccion direccion2 = new Direccion();

direccion2.setCalle("Calle 2");

direccion2.setCodigoPostal("54321");

persona1.setDireccion(direccion1);

persona2.setDireccion(direccion2);

Session sesion = HibernateUtil.getSessionFactory().openSession();

/*Esta direccion se agrega para comprobar que las personas tomen el mismo

identificador que las direcciones*/

Direccion d = new Direccion();

d.setCalle("Calle de Prueba de identificadores");

d.setCodigoPostal("21345");

/*En la primer sesion a la base de datos almacenamos los dos objetos Persona

los objetos Direccion se almacenaran en cascada*/

sesion.beginTransaction();

sesion.persist(d);

sesion.persist(persona1);

sesion.persist(persona2);

sesion.getTransaction().commit();

sesion.close();

/*En la segunda sesion eliminamos el objeto persona1,

la direccion1 sera borrada en cascada*/

sesion = HibernateUtil.getSessionFactory().openSession();

sesion.beginTransaction();

sesion.delete(persona1);

sesion.getTransaction().commit();

sesion.close();

}

Nuevamente revisamos que se hayan eliminado la persona1 y direccion1:

Como podemos ver también este ejemplo funciona - . Ahora veremos cómo crear relaciones uno a uno pero en esta ocasión

bidireccionales.

El crear relaciones uno a uno bidireccionales no es muy distinto a crearlas unidireccionales. La única diferencia es que en este caso

ambos lados de la relación están conscientes de la misma. Veamos el ejemplo. Para esto usaremos dos clases: "Pais y "Presidente.

Estas clases las pondremos en el paquete "modelo" que creamos anteriormente.

La clase Pais queda así:

2 - Relaciones Uno a Uno Bidireccionales

Page 43: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 3: Relaciones / Uno a uno

www.javatutoriales.com/2009/06/hibernate-parte-3-relaciones-uno-uno.html 13/24

public class Pais implements Serializable

{

private int id;

private String nombre;

private Presidente presidente;

public Pais()

{

}

public int getId()

{

return id;

}

protected void setId(int id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

public Presidente getPresidente()

{

return presidente;

}

public void setPresidente(Presidente presidente)

{

this.presidente = presidente;

}

}

Vemos que la clase "Pais" tiene una referencia a un objeto de tipo "Presidente" y por lo tanto sabe que existe la relación y puede

acceder a la misma.

Ahora veamos cómo queda la clase "Presidente":

Page 44: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 3: Relaciones / Uno a uno

www.javatutoriales.com/2009/06/hibernate-parte-3-relaciones-uno-uno.html 14/24

public class Presidente implements Serializable

{

private int id;

private String nombre;

private Pais pais;

public Presidente()

{

}

public int getId()

{

return id;

}

protected void setId(int id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

public Pais getPais()

{

return pais;

}

public void setPais(Pais pais)

{

this.pais = pais;

}

}

Como podemos ver, "Presidente" también sabe de la relación por lo que tiene una referencia a un objeto "Pais". Ahora veremos cómo

realizar el mapeo de las relaciones uno a uno bidireccionales de estas dos clases que, como podremos ver, es igual que para las

relaciones unidireccionales. Comencemos usando los archivos de mapeo:

Creamos, en el paquete "mapeos" un nuevo documento XML. Le damos de nombre "Pais.hbm" (el asistente se encargará de colocar el

".xml"):

Presionamos el botón "Next >" e indicamos que queremos crear un documento XML bien formado (la primer opción) y presionamos el

botón "Finish".

Eliminamos el contenido del archivo creado y lo reemplazamos con el siguiente:

2.1 RELACIONES UNO A UNO BIDIRECCIONALES CON ARCHIVOS DE MAPEO

Page 45: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 3: Relaciones / Uno a uno

www.javatutoriales.com/2009/06/hibernate-parte-3-relaciones-uno-uno.html 15/24

<?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="hibernate.relaciones.modelo.Pais" table="Paises">

<id name="id" column="ID">

<generator class="identity" />

</id>

<property name="nombre" />

<one-to-one name="presidente" cascade="persist,delete"/>

</class>

</hibernate-mapping>

Como podemos ver, el mapeo de "Pais" es prácticamente el mismo que para la relación uno a uno.

Ahora crearemos el mapeo para la clase "Presidente". Creamos, en el paquete "mapeos" un nuevo documento XML. Le damos de

nombre "Presidente.hbm" (el asistente se encargará de colocar el ".xml)

Presionamos el botón "Next >" e indicamos que queremos crear un documento XML bien formado (la primer opción) y presionamos el

botón "Finish".

Eliminamos el contenido del archivo creado y lo reemplazamos con el siguiente:

<?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="hibernate.relaciones.modelo.Presidente" table="Presidentes">

<id name="id" column="ID">

<generator class="foreign">

<param name="property">pais</param>

</generator>

</id>

<property name="nombre" />

<one-to-one name="pais" constrained="true" />

</class>

</hibernate-mapping>

Page 46: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 3: Relaciones / Uno a uno

www.javatutoriales.com/2009/06/hibernate-parte-3-relaciones-uno-uno.html 16/24

Podemos ver que nuevamente usamos la estrategia de colocar "foreign" como generador para el "id" del Presidente, para que sea

tomado el mismo que el Pais con el que está asociado. Aquí estoy haciendo una suposición para que eso funcione: No puede existir

un Presidente si antes no existe el Pais que gobernará. O sea que la entidad "Pais" debe ser guardado en la base de datos

antes, o al menos al mismo tiempo, que el "Presidente".

En esta ocasión en el elemento "<one-to-one>" agregué el elemento "constrained". Esto colocará una restricción, evitando que se

almacene el Presidente si antes no existe el Pais (para evitar que tratemos de almacenar un Presidente sin Pais, o de un Pais

que no exista).

Eso es todo para mapear las relaciones. No olviden agregar los dos mapeos nuevos al archivo "hibernate.cfg.xml":

<mapping resource="hibernate/relaciones/mapeos/Pais.hbm.xml"/>

<mapping resource="hibernate/relaciones/mapeos/Presidente.hbm.xml"/>

Ahora en nuestra clase "Main" colocamos el siguiente código que, nuevamente, es auto-explicativo:

public static void main(String[] args)

{

Pais pais1 = new Pais();

pais1.setNombre("China");

Pais pais2 = new Pais();

pais2.setNombre("Corea");

Presidente presidente1 = new Presidente();

presidente1.setNombre("Jiang Zemin");

Presidente presidente2 = new Presidente();

presidente2.setNombre("Kim Dae-Jung");

pais1.setPresidente(presidente1);

pais2.setPresidente(presidente2);

presidente1.setPais(pais1);

presidente2.setPais(pais2);

Session sesion = HibernateUtil.getSessionFactory().openSession();

/*Este pais se agrega para comprobar que los presidentes tomen el mismo

identificador que los paises*/

Pais p = new Pais();

p.setNombre("Chipre");

/*En la primer sesion a la base de datos almacenamos los dos objetos Pais

los objetos Presidente se almacenaran en cascada*/

sesion.beginTransaction();

sesion.persist(p);

sesion.persist(pais1);

sesion.persist(pais2);

sesion.getTransaction().commit();

sesion.close();

/*En la segunda sesion eliminamos el objeto pais1,

el presidente1 sera borrado en cascada*/

sesion = HibernateUtil.getSessionFactory().openSession();

sesion.beginTransaction();

sesion.delete(pais1);

sesion.getTransaction().commit();

sesion.close();

}

Page 47: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 3: Relaciones / Uno a uno

www.javatutoriales.com/2009/06/hibernate-parte-3-relaciones-uno-uno.html 17/24

En esta ocasión debemos agregar los dos objetos de los dos lados de la relación, es decir, debemos agregar el Pais al Presidente y

el Presidente al Pais haciendo:

pais.setPresidente(presidente);

presidente.setPais(pais);

Una forma más elegante sería llamar al método "setPais" del objeto Presidente al momento de establecer su Pais, o sea, el método

"setPresidente" de la clase Pais quedaría así:

public void setPresidente(Presidente presidente)

{

this.presidente = presidente;

presidente.setPais(this);

}

Así solo tendremos que hacer:

pais.setPresidente(presidente);

Pero es cuestión de gustos.

Ahora pasemos a ver los resultados de las operaciones realizadas por Hibernate. Segun el código de la clase Main, debemos haber

guardado 3 Paises y 2 Presidentes y, posteriormente, haber eliminado un Pais (y por lo tanto un Presidente), y quedarnos con 2

Paises y 1 Presidente. Vemos qué es lo que tenemos en la base de datos:

Como podemos ver, una vez más - , todo ha salido bien, por lo que podemos pasar a la última parte de este tutorial:

Nuevamente las anotaciones que hay que usar son muy simples y similares a las que ya hemos usado, por lo que las colocaré sin mucha

explicación. La clase Pais queda así:

2.2 RELACIONES UNO A UNO BIDIRECCIONALES CON ANOTACIONES

Page 48: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 3: Relaciones / Uno a uno

www.javatutoriales.com/2009/06/hibernate-parte-3-relaciones-uno-uno.html 18/24

@Entity

public class Pais implements Serializable

{

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private int id;

private String nombre;

@OneToOne(cascade={CascadeType.PERSIST,CascadeType.REMOVE})

private Presidente presidente;

public Pais()

{

}

public int getId()

{

return id;

}

protected void setId(int id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

public Presidente getPresidente()

{

return presidente;

}

public void setPresidente(Presidente presidente)

{

this.presidente = presidente;

}

}

La anotación que debemos usar es, nuevamente "@OneToOne" y se usa de la misma forma que en el caso de las relaciones

unidireccionales.

La clase Presidente queda así:

Page 49: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 3: Relaciones / Uno a uno

www.javatutoriales.com/2009/06/hibernate-parte-3-relaciones-uno-uno.html 19/24

@Entity

public class Presidente implements Serializable

{

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private int id;

private String nombre;

@OneToOne

private Pais pais;

public Presidente()

{

}

public int getId()

{

return id;

}

protected void setId(int id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

public Pais getPais()

{

return pais;

}

public void setPais(Pais pais)

{

this.pais = pais;

}

}

Y listo, esto es todo lo que debemos hacer para tener nuestra aplicación con una relación uno a uno bidireccional - . Ahora probemos

que todo funciona correctamente.

No olviden agregar estas dos clases al archivo "hibernate.cfg.xml":

<mapping class="hibernate.relacionesanotaciones.modelo.Pais" />

<mapping class="hibernate.relacionesanotaciones.modelo.Presidente" />

Para probar que todo funciona bien, coloquen el siguiente código en la clase "Main":

Page 50: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: HIbernate - Parte 3: Relaciones / Uno a uno

www.javatutoriales.com/2009/06/hibernate-parte-3-relaciones-uno-uno.html 20/24

public static void main(String[] args)

{

Pais pais1 = new Pais();

pais1.setNombre("China");

Pais pais2 = new Pais();

pais2.setNombre("Corea");

Presidente presidente1 = new Presidente();

presidente1.setNombre("Jiang Zemin");

Presidente presidente2 = new Presidente();

presidente2.setNombre("Kim Dae-Jung");

pais1.setPresidente(presidente1);

pais2.setPresidente(presidente2);

presidente1.setPais(pais1);

presidente2.setPais(pais2);

Session sesion = HibernateUtil.getSessionFactory().openSession();

/*Este pais se agrega para comprobar que los presidentes tomen el mismo

identificador que los paises*/

Pais p = new Pais();

p.setNombre("Chipre");

/*En la primer sesion a la base de datos almacenamos los dos objetos Pais

los objetos Presidente se almacenaran en cascada*/

sesion.beginTransaction();

sesion.persist(p);

sesion.persist(pais1);

sesion.persist(pais2);

sesion.getTransaction().commit();

sesion.close();

/*En la segunda sesion eliminamos el objeto pais1,

el presidente1 sera borrado en cascada*/

sesion = HibernateUtil.getSessionFactory().openSession();

sesion.beginTransaction();

sesion.delete(pais1);

sesion.getTransaction().commit();

sesion.close();

}

Comprobemos que, en la base de datos, solo haya dos Paises y un Presidente:

Como podemos ver, todo ha salido bien - .

Espero que todo haya quedado claro, en caso contrario no duden en dejar sus dudas, comentarios y sugerencias.

En los siguientes tutoriales veremos cómo manejar los otros tipos de relaciones que podemos tener usando Hibernate.

Saludos

Descarga los archivos de este tutorial desde aquí:

Hibernate Relaciones Uno a Uno con Archivos de Mapeo

Hibernate Relaciones Uno a uno con Anotaciones

Entradas Relacionadas:

Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

Parte 2: Persistiendo Objetos Simples usando Anotaciones (Metadatos)

Page 51: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 4: Relaciones / Uno a muchos

www.javatutoriales.com/2009/06/hibernate-parte-4-relaciones-uno-muchos.html 1/27

Blog dedicado a temas de programación actuales usando el lenguaje de programación Java y las últimas versiones de sus APIs y Herramientas.

Tutoriales de Programacion Java

Página principal Página de Concursos Tutoriales UPAO 2010 Presentaciones Capacitación

SÁBADO, 27 DE JUNIO DE 2009

Hibernate - Parte 4: Relaciones / Uno a muchos

En el tutorial anterior dijimos que en las bases de datos existen 7 tipos de relaciones, y vimos cómo crear relaciones uno a uno, tanto

unidireccionales com bidireccionales, con Hibernate usando archivos de mapeo y anotaciones (en ejemplos separados). Cubriendo con

eso 2 de los 7 tipos.

Ahora veremos como trabajar con relaciones Uno a Muchos, unidireccionales y bidireccionales. Con lo que cubriremos otros dos tipos.

Lo primero que haremos es crear un proyecto en NetBeans (menú "File -> New Project... -> Java -> Java

Application"). Le damos un nombre y una ubicación al proyecto y nos aseguramos de que las opciones "Create Main Class" y

"Set as Main Project" estén habilitadas. Presionamos el botón "Finish" y veremos aparecer en el editor nuestra clase "Main".

Agregamos la biblioteca de Hibernate, que creamos en el primer tutorial de la serie. Hacemos clic derecho en el nodo "Libraries" del

proyecto. En el menú contextual que se abre seleccionamos la opción "Add Library...":

En la ventana que se abre seleccionamos la biblioteca "Hibernate":

Presionamos el botón "Add Library" para que la biblioteca se agregue a nuestro proyecto. Aprovechamos también para agregar el

conector de MySQL. Debemos tener los siguientes archivos en nuestro proyecto:

DONACIONES

JAVA TUTORIALES EN FACEBOOK

Java Tutoriales

Me gusta

A 7895 personas les gusta

Plug-in social de Facebook

Java Tutoriales

Amigo programador, tú que

estás todo el día picando

código, escucha estas

recomendaciones sólo

hechas para ti..... ¿qué es

esto?... Gracias Jorge

Rubira....

No olviden poner la

sentencia where al hacer

un borraro en la base de

datos..

http://www.youtube.com/

watch?v=i_cVJgIz_Cs

Compartir 1 Más Siguiente blog»

Page 52: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 4: Relaciones / Uno a muchos

www.javatutoriales.com/2009/06/hibernate-parte-4-relaciones-uno-muchos.html 2/27

Ahora creamos dos paquetes, uno con el nombre "modelo", que contendrá las clases entidades, y otro con el nombre "mapeos" que

contendrá los archivos de mapeo XML. Hacemos clic derecho en el nodo del paquete que se creó al generar el proyecto. En el menú

contextual que se abre seleccionamos la opción "New -> Java Package..." y creamos los dos paquetes

Que deben quedar así:

Aprovecharemos para crear nuestro archivo de configuración de Hibernate, "hibernate.cfg.xml", el cual será muy parecido al del

primer tutorial:

<!DOCTYPE hibernate-configuration PUBLIC

"-//Hibernate/Hibernate Configuration DTD 3.0//EN"

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

<hibernate-configuration>

<session-factory>

<!-- parametros para la conexion a la base de datos -->

<property name="connection.driver_class">com.mysql.jdbc.Driver</property>

<property name="connection.url">jdbc:mysql://localhost/hibernaterelaciones</property>

<property name="connection.username">usuario</property>

<property name="connection.password">password</property>

<!-- Configuracion del pool interno -->

<property name="connection.pool_size">1</property>

<!-- Dialecto de la base de datos -->

<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>

<!-- Otras propiedades importantes -->

<property name="show_sql">true</property>

<property name="hbm2ddl.auto">create-drop</property>

<!-- Aqui iran los archivos de mapeo -->

</session-factory>

</hibernate-configuration>

Para los ejercicios usaremos una base de datos llamada "hibernaterelaciones", en MySQL.

Nota: Yo usaré dos proyectos, uno para usar archivos de mapeo y otro para usar anotaciones y que el código de ambos no se mezcle.

Participar en este sitioGoogle Friend Connect

Miembros (173) Más »

¿Ya eres miembro? Iniciar sesión

SEGUIDORES

► 2012 (2)

► 2011 (11)

► 2010 (10)

▼ 2009 (22)

► septiembre (2)

► agosto (2)

► julio (1)

▼ junio (3)

Acepto Donaciones

Hibernate - Parte 4: Relaciones / Uno amuchos

HIbernate - Parte 3: Relaciones / Uno auno

► mayo (2)

► abril (5)

► marzo (2)

► febrero (2)

► enero (3)

ARCHIVO DEL BLOG

Page 53: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 4: Relaciones / Uno a muchos

www.javatutoriales.com/2009/06/hibernate-parte-4-relaciones-uno-muchos.html 3/27

Pues bien, comencemos.

En las relaciones Uno a Muchos, un objeto de la entidad "A" (el lado uno) está relacionado con muchos objetos de la entidad "B" (el lado

muchos). En el caso de que la relación sea unidireccional, solo la entidad "A" tiene una referencia a los objetos de tipo "B" y esta

relación está representada por una colección (un List o un Set) y la entidad "A" puede acceder a cada uno de los objetos de tipo "B" de

esa colección. Un ejemplo de este tipo de relaciones puede ser una Persona puede tener muchos Libros. Donde la Persona es el

lado Uno de la relación, y los Libros son el lado Muchos.

Si la relación es bidireccional, adicionalmente, las entidades "B" tendrán una referencia a la entidad "A" con la que están relacionados.

Un ejemplo de esto es un Jefe y sus Empleados. En donde el Jefe es el lado Uno y los Empleados el lado Muchos.

Este tipo de relación se vería de forma gráfica más o menos así:

Bien, ahora comencemos con los ejemplos:

Primero crearemos las clases Persona y Libro (para la relación unidireccional), dentro del paquete "modelo" que creamos hace

unos momentos. La clase Libro queda así:

public class Libro

{

private long id;

private String titulo;

public Libro()

{

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getTitulo()

{

return titulo;

}

public void setTitulo(String titulo)

{

this.titulo = titulo;

}

}

Como podemos ver es una clase muy simple, solo tiene dos atributos: id y titulo y, como no tiene ninguna referencia a la clase

Persona, no sabe nada de la existencia de una relación con esta.

Pasemos a ver cómo queda la clase Persona:

3 - Relaciones Uno a Muchos Unidireccionales

¿ProgramadorJava?

InfoJobs.net/Ofert…

Más de mil ofertas de

empleo Java

interesantes

¡Descúbrelas!

Extend JMS to.NET

Cursos GratisINEM

Kick Eclipse tothe Cloud

CursoProgramación

Alex

Programador Java con algunos años de

experiencia en múltiples poyectos y con

múltiples APIs y herramientas deseoso de

compartir experiencias con el resto de

programadores.

Ver todo mi perfil

DATOS PERSONALES

Page 54: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 4: Relaciones / Uno a muchos

www.javatutoriales.com/2009/06/hibernate-parte-4-relaciones-uno-muchos.html 4/27

public class Persona

{

private long id;

private String nombre;

private List<Libro> libros = new ArrayList<Libro>();

public Persona()

{

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

public List getLibros()

{

return libros;

}

public void setLibros(List libros)

{

this.libros = libros;

}

public void addLibro(Libro libro)

{

this.libros.add(libro);

}

}

La clase Persona tiene, como habíamos dicho, una referencia a una colección (en este caso a una lista de objetos Libro, por lo que

puede acceder a cada uno de los datos de los libros. La lista se inicializa con un ArrayList en el momento de la creación del objeto.

Esto nos ayudará a agregar libros tan pronto como creamos un nuevo objeto Persona, sin recibir una NullPointerException.

He agregado además de los setters y los getters para la lista de Libros un método auxiliar llamado "addLibro" que permite

agregar un libro a la lista.

Ahora veremos cómo indicarle a Hibernate que existen estas relaciones, primero usando archivos de mapeo y después usando

anotaciones:

Creamos un nuevo documento XML en el paquete "mapeos". Le damos el nombre de "Libro.hbm" (el asistente se encargará de colocar

el .xml):

Presionamos el botón "Next >" e indicamos que queremos crear un documento XML bien formado (la primer opción) y presionamos el

botón "Finish".

3.1 RELACIONES UNO A MUCHOS UNIDIRECCIONALES CON ARCHIVOS DE MAPEO

Page 55: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 4: Relaciones / Uno a muchos

www.javatutoriales.com/2009/06/hibernate-parte-4-relaciones-uno-muchos.html 5/27

Eliminamos el contenido del archivo creado y lo reemplazamos con el siguiente:

<?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="hibernate.relaciones.unomuchos.modelo.Libro" table="LIBROS">

</class>

</hibernate-mapping>

El mapeo de la clase Libro es similar al que expliqué en el primer tutorial de la serie y, al final, queda de la siguiente forma:

<hibernate-mapping>

<class name="hibernate.relaciones.unomuchos.modelo.Libro" table="LIBROS">

<id name="id">

<generator class="identity" />

</id>

<property name="titulo" />

</class>

</hibernate-mapping>

Ahora crearemos el mapeo para la clase "Persona", llamado "Persona.hbm.xml" en el paquete "mapeos", de la misma forma que lo

hicimos para Libro. El mapeo, excluyendo el elemento que representa la relación debe estar de esta forma:

<?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="hibernate.relaciones.unomuchos.modelo.Persona" table="PERSONAS">

<id name="id" column="ID_PERSONA" >

<generator class="identity" />

</id>

<property name="nombre" />

</class>

</hibernate-mapping>

Noten que en esta ocasión agregue el atributo "column" en el elemento "<id>". Esto es importante ya que deberemos indicar el nombre

de esta columna para poder crear la relación (cuál será la llave foránea que se usará).

Ahora ocupémonos de la relación.

Las relaciones uno a muchos se representan usando el elemento acorde con el tipo de colección que estemos usando (list, set,

map, array, y primitive-array) existe otro elemento llamado "bag" usado de forma menos frecuente y que tal vez veamos en otro

tutorial.

Cada tipo de colección se mapea de forma un poco distinta. Como nosostros estamos usando una lista, veremos cómo mapear esta pero

pueden encontrar como mapear los otros tipos de colecciones en esta página.

Para mapear una lista usamos el elemento "<list>". En este elemento indicamos cuál es el nombre del atributo, dentro de la clase

Persona, que representa la relación. En este caso el atributo se llama "libros". También aquí indicamos cuáles operaciones queremos

que se realicen en cascada. En este caso queremos que todas las operaciones de guardar, actualizar y eliminar que ocurran en el padre

sean pasadas a la colección, o sea que cuando guardemos, actualicemos, o eliminemos una Persona, las operaciones pasen también a

todos sus Libros relacionados, por lo que usamos el valor "all".

Page 56: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 4: Relaciones / Uno a muchos

www.javatutoriales.com/2009/06/hibernate-parte-4-relaciones-uno-muchos.html 6/27

En las relaciones uno a muchos existe dos estilos de cascada especiales llamados "delete-orphan" y "all-delete-orphan" (que

solo existen si usamos archivos de mapeo) los cuales se encargan de que, en el caso de que se elimine el objeto padre (Persona),

todos los objetos hijos (Libros) serán eliminados de la base de datos. Adicionalmente "all-delete-orphan" se encarga de que

todas las otras operaciones que mencionamos antes (guardar, actualizar, y eliminar) también sean realizados en cascada, por lo que

usaremos este valor:

<list name="libros" cascade="all-delete-orphan">

</list>

Ahora indicamos cuál será el valor que se usará como llave foránea para relacionar los Libros con la Persona. ¿Recuerdan que

indicamos el nombre de la columna para el identificador de la Persona? Pues bien, es aquí en donde usaremos este valor:

<key column="ID_PERSONA" />

Con esto indicamos que, en la tabla que se creará para mantener las entidades Libro, se debe crear una llave foránea relacionada con el

identificador de la tabla Persona, identificada en este caso por la columna "ID_PERSONA". Indicamos el nombre de la columna de forma

explícita ya que si dejamos que sea Hibernate quien lo genere, el nombre de la llave foránea (id) será el mismo que el de la llave primara

(id) y ocurrirán errores al tratar de insertar entidades (claro que esto también podría solucionarse dando a los atributos nombres distintos

desde el principio, pero es cuestión de gustos).

Ahora bien, las listas son una estructura de datos con una característica única: tienen un orden. Esto significa que el orden en el que

los elementos entran en la lista es importante e, internamente, se usa un índice para saber el orden de los elementos.

Cuando tratamos de almacenar estos datos nos interesa que en el momento que sean recuperados, los elementos de la lista estén en el

mismo orden en el que los guardamos y es por esta razón que se debe usar una columna extra en la tabla generada para guardar

este índice (el cual comienza en cero). Para indicar el nombre que tendrá esta columna usamos el elemento "index" y colocamos en su

atributo "column" el nombre que tendrá esta columna:

<index column="ORDEN" />

Dejamos lo mejor para el final - ya que ahora debemos indicar qué tipo de relación representa esta colección. Como en este caso

estamos representando una relación "uno a muchos", usamos el elemento "<one-to-many>". En el cual debemos indicar de qué clase

son las entidades que estamos guardando en la lista (ya que este dato no puede ser obtenido usando reflexión):

<one-to-many class="hibernate.relaciones.unomuchos.modelo.Libro" />

Finalmente el mapeo de la relación queda así:

<list name="libros" cascade="all-delete-orphan">

<key column="ID_PERSONA" />

<index column="ORDEN" />

<one-to-many class="hibernate.relaciones.unomuchos.modelo.Libro" />

</list>

Y el archivo de mapeo para la entidad Persona completo así:

Page 57: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 4: Relaciones / Uno a muchos

www.javatutoriales.com/2009/06/hibernate-parte-4-relaciones-uno-muchos.html 7/27

<hibernate-mapping>

<class name="hibernate.relaciones.unomuchos.modelo.Persona" table="PERSONAS">

<id name="id" column="ID_PERSONA">

<generator class="identity" />

</id>

<property name="nombre" />

<list name="libros" cascade="all-delete-orphan">

<key column="ID_PERSONA" />

<index column="ORDEN" />

<one-to-many class="hibernate.relaciones.unomuchos.modelo.Libro" />

</list>

</class>

</hibernate-mapping>

Para este ejemplo, usaremos la clase HibernateUtil que creamos en el primer tutorial de la serie.

Ahora colocaremos el siguiente código (como siempre auto-explicatvo) como nuestro método main:

Page 58: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 4: Relaciones / Uno a muchos

www.javatutoriales.com/2009/06/hibernate-parte-4-relaciones-uno-muchos.html 8/27

public static void main(String[] args)

{

/*Primero creamos una persona y la asociamos con dos libros*/

Libro libro1 = new Libro();

libro1.setTitulo("20000 leguas de viaje submarino");

Libro libro2 = new Libro();

libro2.setTitulo("La maquina del tiempo");

Persona persona1 = new Persona();

persona1.setNombre("Persona que se eliminara");

persona1.addLibro(libro1);

persona1.addLibro(libro2);

/*Creamos una segunda persona, que sera eliminada, y la asociamos con otros

dos libros*/

Libro libro3 = new Libro();

libro3.setTitulo("El ingenioso hidalgo don Quijote de la Mancha");

Libro libro4 = new Libro();

libro4.setTitulo("La Galatea");

Persona persona2 = new Persona();

persona2.setNombre("Alex");

persona2.addLibro(libro3);

persona2.addLibro(libro4);

/*En la primer sesion guardamos las dos personas (los libros correspondientes

seran guardados en cascada*/

Session sesion = HibernateUtil.getSessionFactory().openSession();

sesion.beginTransaction();

sesion.persist(persona1);

sesion.persist(persona2);

sesion.getTransaction().commit();

sesion.close();

/*En la segunda sesion eliminamos la persona1 (los dos primeros libros seran

borrados en cascada)*/

sesion = HibernateUtil.getSessionFactory().openSession();

sesion.beginTransaction();

sesion.delete(persona1);

sesion.getTransaction().commit();

sesion.close();

}

Según el código anterior en la base de datos debería quedar una Persona, la segunda que guardamos ("Alex"), con sus dos libros

respectivos. Comprobemos que esto es verdad.

Podemos ver que efectivamente el resultado es el esperado - .

Ahora veamos cómo hacer lo mismo pero usando anotaciones.

Recuerden que pasa usar anotaciones debemos agregar al proyecto la biblioteca "HibernateAnotaciones" que creamos en el

segundo tutorial.

Las anotaciones (así como los archivos de configuración) de ambas clases (omitiendo la que indica la relación) serán las mismas que

3.2 RELACIONES UNO A MUCHOS UNIDIRECCIONALES CON ANOTACIONES

Page 59: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 4: Relaciones / Uno a muchos

www.javatutoriales.com/2009/06/hibernate-parte-4-relaciones-uno-muchos.html 9/27

expliqué en el segundo tutorial de esta serie, quedan de la siguiente forma:

@Entity

public class Libro implements Serializable

{

@Id

@GeneratedValue(strategy=GenerationType.IDENTITY)

private long id;

private String titulo;

public Libro()

{

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getTitulo()

{

return titulo;

}

public void setTitulo(String titulo)

{

this.titulo = titulo;

}

}

Y esta es la de la clase Persona:

Page 60: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 4: Relaciones / Uno a muchos

www.javatutoriales.com/2009/06/hibernate-parte-4-relaciones-uno-muchos.html 10/27

@Entity

public class Persona implements Serializable

{

@Id

@GeneratedValue(strategy=GenerationType.IDENTITY)

private long id;

private String nombre;

private List<Libro> libros = new ArrayList<Libro>();

public Persona()

{

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

public List getLibros()

{

return libros;

}

public void setLibros(List libros)

{

this.libros = libros;

}

public void addLibro(Libro libro)

{

this.libros.add(libro);

}

}

Ahora explicaré la anotación que usaremos para indicar la relación. Esta anotación es "@OneToMany" y la colocamos en el atributo de

tipo colección de Libros (en este caso la clase Persona será la dueña de la relación ya que es la única que está consciente de esta).

Igual que lo hicimos en la anotación "@OneToOne" aqui definiremos qué operaciones serán realizadas en cascada. Además también

podemos indicar el tipo de "fetch" o recuperación que tendrá la colección. Explico: solo existen dos tipo de recuperación: tardado

(lazy)e inmediato (eager). Si decidimos que la recuperación sea "lazy" entonces las entidades relacionadas (los Libros de la

colección) no serán recuperados de la base de datos al momento que se recupera la Persona dueña, sino hasta que se usen estos

elementos (siempre y cuando estemos dentro de una transacción). Si la recuperación es "eager" entonces los Libros relacionados se

recuperarán al mismo tiempo que la Persona dueña. En este caso yo usaré un fetch de tipo eager.

Al final la relación queda de esta forma:

@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)

private List<Libro> libros = new ArrayList<Libro>();

Y la clase Persona, con todas sus anotaciones, así:

Page 61: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 4: Relaciones / Uno a muchos

www.javatutoriales.com/2009/06/hibernate-parte-4-relaciones-uno-muchos.html 11/27

@Entity

public class Persona implements Serializable

{

@Id

@GeneratedValue(strategy=GenerationType.IDENTITY)

private long id;

private String nombre;

@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)

private List&ltLibro> libros = new ArrayList<Libro>();

public Persona()

{

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

public List getLibros()

{

return libros;

}

public void setLibros(List libros)

{

this.libros = libros;

}

public void addLibro(Libro libro)

{

this.libros.add(libro);

}

}

Y listo, con esto ya tenemos una relación Uno a Muchos unidireccional con la Persona como dueña de la relación y varios Libros

como entidad inversa.

No olviden que debemos colocar en el archivo de configuración de Hibernate estas clases:

<mapping class="hibernate.relaciones.unomuchos.anotaciones.modelo.Libro" />

<mapping class="hibernate.relaciones.unomuchos.anotaciones.modelo.Persona" />

Usaremos la clase HibernateUtil que creamos en el segundo tutorial de la serie para implementar el código de prueba y el siguiente

código dentro la clase main:

Page 62: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 4: Relaciones / Uno a muchos

www.javatutoriales.com/2009/06/hibernate-parte-4-relaciones-uno-muchos.html 12/27

public static void main(String[] args)

{

/*Primero creamos una persona y la asociamos con dos libros*/

Libro libro1 = new Libro();

libro1.setTitulo("20000 leguas de viaje submarino");

Libro libro2 = new Libro();

libro2.setTitulo("La maquina del tiempo");

Persona persona1 = new Persona();

persona1.setNombre("Persona que se eliminara");

persona1.addLibro(libro1);

persona1.addLibro(libro2);

/*Creamos una segunda persona, que sera eliminada, y la asociamos

con otros dos libros*/

Libro libro3 = new Libro();

libro3.setTitulo("El ingenioso hidalgo don Quijote de la Mancha");

Libro libro4 = new Libro();

libro4.setTitulo("La Galatea");

Persona persona2 = new Persona();

persona2.setNombre("Alex");

persona2.addLibro(libro3);

persona2.addLibro(libro4);

/*En la primer sesion guardamos las dos personas (los libros

correspondientes seran guardados en cascada*/

Session sesion = HibernateUtil.getSessionFactory().openSession();

sesion.beginTransaction();

sesion.persist(persona1);

sesion.persist(persona2);

sesion.getTransaction().commit();

sesion.close();

/*En la segunda sesion eliminamos la persona1 (los dos primeros

libros seran borrados en cascada)*/

sesion = HibernateUtil.getSessionFactory().openSession();

sesion.beginTransaction();

sesion.delete(persona1);

sesion.getTransaction().commit();

sesion.close();

}

Según el código anterior, debería haber quedado una sola Persona en la base de datos con sus dos Libros relacionados, vemos que

esto sea así:

Como podemos ver, todo ha salido bien - . Ahora podemos pasar a ver cómo funciona la relación Uno a Muchos bidireccional.

Las relaciones bidireccionales son muy parecidas a las unidireccionales, con la diferencia que el lado inverso de la relación también

sabe de esta, por lo que tiene una referencia al dueño. Veamos esto con un ejemplo para que quede más claro. Nuevamente

comenzaremos con un ejemplo usando archivos de mapeo y posteriormente usaremos anotaciones.

Modificaremos un poco nuestra clase "Libro" para agregar una referencia a la "Persona" (con sus correspondientes setters y

getters):

4 - Relaciones Uno a Muchos Bidireccionales

4.1 RELACIONES UNO A MUCHOS BIDIRECCIONALES CON ARCHIVOS DE MAPEO

Page 63: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 4: Relaciones / Uno a muchos

www.javatutoriales.com/2009/06/hibernate-parte-4-relaciones-uno-muchos.html 13/27

private Persona persona;

public Persona getPersona()

{

return persona;

}

public void setPersona(Persona persona)

{

this.persona = persona;

}

Por lo que la clase queda de la siguiente forma:

public class Libro

{

private long id;

private String titulo;

private Persona persona;

public Libro()

{

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getTitulo()

{

return titulo;

}

public void setTitulo(String titulo)

{

this.titulo = titulo;

}

public Persona getPersona()

{

return persona;

}

public void setPersona(Persona persona)

{

this.persona = persona;

}

}

La clase "Persona" queda intacta.

Ahora modificaremos el archivo "Libro.hbm.xml" para que refleje los campos que acabamos de realizar. Pero primero veamos como

quedo el archivo de mapeo de la ("PersonaPersona.hbm.xml"):

Page 64: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 4: Relaciones / Uno a muchos

www.javatutoriales.com/2009/06/hibernate-parte-4-relaciones-uno-muchos.html 14/27

<hibernate-mapping>

<class name="hibernate.relaciones.unomuchos.modelo.Persona" table="PERSONAS">

<id name="id" column="ID_PERSONA">

<generator class="identity" />

</id>

<property name="nombre" />

<list name="libros" cascade="all-delete-orphan">

<key column="ID_PERSONA" />

<index column="ORDEN" />

<one-to-many class="hibernate.relaciones.unomuchos.modelo.Libro" />

</list>

</class>

</hibernate-mapping>

Como podemos ver la relación de Persona a Libros esta mapeada usando el elemento "one-to-many" (una Persona puede tener

muchos Libros), por lo que para indicar la relación de Libros a Persona usamos el elemento contrario: "many-to-one" (muchos

Libros pertenecen a una Persona).

En este elemento debemos indicar el nombre del elemento que representa la relación (en la clase Libro), que en este caso es

"persona", y el nombre de la columna donde se almacena el identificador de la Persona ("ID_PERSONA"). Por lo que el elemento

queda así:

<many-to-one name="persona" column="ID_PERSONA" />

Y al final el archivo "Libro.hbm.xml" completo así:

<hibernate-mapping>

<class name="hibernate.relaciones.unomuchos.modelo.Libro" table="LIBROS">

<id name="id" column="id">

<generator class="identity" />

</id>

<property name="titulo" />

<many-to-one name="persona" column="ID_PERSONA" />

</class>

</hibernate-mapping>

Y eso es todo - . Probémoslo usando el mismo código que ya teníamos en la clase Main:

Page 65: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 4: Relaciones / Uno a muchos

www.javatutoriales.com/2009/06/hibernate-parte-4-relaciones-uno-muchos.html 15/27

public static void main(String[] args)

{

/*Primero creamos una persona y la asociamos con dos libros*/

Libro libro1 = new Libro();

libro1.setTitulo("20000 leguas de viaje submarino");

Libro libro2 = new Libro();

libro2.setTitulo("La maquina del tiempo");

Persona persona1 = new Persona();

persona1.setNombre("Persona que se eliminara");

persona1.addLibro(libro1);

persona1.addLibro(libro2);

/*Creamos una segunda persona, que sera eliminada, y la asociamos con otros dos libros*/

Libro libro3 = new Libro();

libro3.setTitulo("El ingenioso hidalgo don Quijote de la Mancha");

Libro libro4 = new Libro();

libro4.setTitulo("La Galatea");

Persona persona2 = new Persona();

persona2.setNombre("Alex");

persona2.addLibro(libro3);

persona2.addLibro(libro4);

/*En la primer sesion guardamos las dos personas (los libros correspondientes seran guardados

en cascada*/

Session sesion = HibernateUtil.getSessionFactory().openSession();

sesion.beginTransaction();

sesion.persist(persona1);

sesion.persist(persona2);

sesion.getTransaction().commit();

sesion.close();

/*En la segunda sesion eliminamos la persona1 (los dos primeros libros seran borrados en casca

da)*/

sesion = HibernateUtil.getSessionFactory().openSession();

sesion.beginTransaction();

sesion.delete(persona1);

sesion.getTransaction().commit();

sesion.close();

}

Ahora comprobemos que en la base de datos solo quedan los datos de la persona2, junto con sus libros correspondientes:

Como podemos ver, todo ha salido bien - .

Para terminar este tutorial veamos cómo trabajar con relaciones Uno a Muchos usando anotaciones.

Como ya teníamos una relación uno a muchos unidireccional con anotaciones, representada en el atributo "libros" de la clase

"Persona". Siendo la clase Persona la única que esta consciente de la relación (por eso es unidireccional).

Para transformar esta relación en bidireccional lo único que debemos hacer es agregar un atributo que represente la relación con la

clase "Persona" en la clase "Libro". De la misma forma que ocurre cuando usamos archivos de mapeo: Como la relación uno a

muchos está representada en la clase "Persona" usando la anotación "@OneToMany" (una Persona puede tener muchos Libros), en

el lado inverso de la relación debe usarse la anotación inversa "@ManyToOne" (muchos Libros pueden pertenecer a una Persona).

Esta anotación es un poco distinta a la anotación "@OneToOne" que vimos anteriormente. En este caso es importante quién es el dueño

de la relación, ya que es el dueño quien determina cómo es que el motor de persistencia hace actualizaciones en la base de datos.

En el caso de las relaciones bidireccionales, usando anotaciones, el dueño SIEMPRE es el lado muchos (esta es una regla). Por lo que

en este caso el dueño de la relación es la clase "Libro".

El lado inverso debe saber cuál es el nombre que lo representa en el lado dueño (esto se aclarará en el ejemplo). Para esto se usa el

4.2 RELACIONES UNO A MUCHOS BIDIRECCIONALES CON ANOTACIONES

Page 66: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 4: Relaciones / Uno a muchos

www.javatutoriales.com/2009/06/hibernate-parte-4-relaciones-uno-muchos.html 16/27

elemento "mappedBy".

Para ver cómo funciona lo dicho vayamos al ejemplo que teníamos. Primero modificaremos la clase "Libro" para agregar el atributo de

tipo Persona que representará la relación (recuerden que debemos usar la aniotación "@ManyToOne"):

@ManyToOne

private Persona persona;

Y eso es todo lo que hay que modificar en la clase Libro. Como ven es un cambio muy pequeño. Ahora veamos la clase Persona.

Hace unos momentos dije que la clase Libro es la clase dueña de la relación y la clase Persona es la clase inversa. También dije

que la clase inversa debe indicar en la anotación que representa la relación el nombre con el que el lado dueño la representa (que en este

caso es "persona") y que esto se hace en el elemento "mappedBy" de la anotación. Por lo que la relación queda representada en la

clase "Persona" de la siguiente forma:

@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER, mappedBy="persona")

private List libros = new ArrayList();

Y listo - . Con estos pequeños cambios ya tenemos una relación bidireccional. Al final la clase Persona queda de la siguiente forma:

@Entity

public class Persona implements Serializable

{

@Id

@GeneratedValue(strategy=GenerationType.IDENTITY)

private long id;

private String nombre;

@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER, mappedBy="persona")

private List libros = new ArrayList();

public Persona()

{

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

public List getLibros()

{

return libros;

}

public void setLibros(List libros)

{

this.libros = libros;

}

public void addLibro(Libro libro)

{

this.libros.add(libro);

}

}

Y la clase Libro así:

Page 67: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 4: Relaciones / Uno a muchos

www.javatutoriales.com/2009/06/hibernate-parte-4-relaciones-uno-muchos.html 17/27

@Entity

public class Libro implements Serializable

{

@Id

@GeneratedValue(strategy=GenerationType.IDENTITY)

private long id;

private String titulo;

@ManyToOne

private Persona persona;

public Libro()

{

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getTitulo()

{

return titulo;

}

public void setTitulo(String titulo)

{

this.titulo = titulo;

}

public Persona getPersona()

{

return persona;

}

public void setPersona(Persona persona)

{

this.persona = persona;

}

}

Probemos que todo funciona correctamente usando el código que teníamos anteriormente en la clase Main:

Page 68: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 4: Relaciones / Uno a muchos

www.javatutoriales.com/2009/06/hibernate-parte-4-relaciones-uno-muchos.html 18/27

public static void main(String[] args)

{

/*Primero creamos una persona y la asociamos con dos libros*/

Libro libro1 = new Libro();

libro1.setTitulo("20000 leguas de viaje submarino");

Libro libro2 = new Libro();

libro2.setTitulo("La maquina del tiempo");

Persona persona1 = new Persona();

persona1.setNombre("Persona que se eliminara");

persona1.addLibro(libro1);

persona1.addLibro(libro2);

libro1.setPersona(persona1);

libro2.setPersona(persona1);

/*Creamos una segunda persona, que sera eliminada, y la asociamos con otros dos libros*/

Libro libro3 = new Libro();

libro3.setTitulo("El ingenioso hidalgo don Quijote de la Mancha");

Libro libro4 = new Libro();

libro4.setTitulo("La Galatea");

Persona persona2 = new Persona();

persona2.setNombre("Alex");

persona2.addLibro(libro3);

persona2.addLibro(libro4);

libro3.setPersona(persona2);

libro4.setPersona(persona2);

/*En la primer sesion guardamos las dos personas (los libros correspondientes seran guardados

en cascada*/

Session sesion = HibernateUtil.getSessionFactory().openSession();

sesion.beginTransaction();

sesion.persist(persona1);

sesion.persist(persona2);

sesion.getTransaction().commit();

sesion.close();

/*En la segunda sesion eliminamos la persona1 (los dos primeros libros seran borrados en casca

da)*/

sesion = HibernateUtil.getSessionFactory().openSession();

sesion.beginTransaction();

sesion.delete(persona1);

sesion.getTransaction().commit();

sesion.close();

}

Nuevamente solo deben haber quedado en la base de datos los datos de la persona2 y sus libros relacionados. Comprobemos que

efectivamente esto es así:

Como podemos ver todo ha salido como esperábamos - .

Bien, esto es todo lo que respecta a las relaciones uno a muchos. Con este tutorial ya llevamos 4 de los 7 tipos de relaciones que

podemos tener en Hibernate. En los siguientes dos tutoriales veremos los 3 tipos restantes.

Cualquier duda, comentario, o sugerencia por favor no duden en colocarlo.

Saludos.

Descarga los archivos de este tutorial desde aquí:

Page 69: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 5: Relaciones / Muchos a Uno

www.javatutoriales.com/2009/08/hibernate-parte-5-relaciones-muchos-uno_02.html 1/11

Blog dedicado a temas de programación actuales usando el lenguaje de programación Java y las últimas versiones de sus APIs y Herramientas.

Tutoriales de Programacion Java

Página principal Página de Concursos Tutoriales UPAO 2010 Presentaciones Capacitación

DOMINGO, 2 DE AGOSTO DE 2009

Hibernate - Parte 5: Relaciones / Muchos a Uno

Ya hemos visto, en los tutoriales anteriores, cómo generar 4 de los 7 tipos de relaciones que podemos crear con Hibernate.

En esta ocasión veremos cómo crear relaciones uno a muchos unidireccionales (ya que, como dije en el tercer tutorial, las relaciones uno

a muchos bidireccionales son iguales a las relaciones muchos a uno bidireccionales - ).

Lo primero que haremos es crear un proyecto en NetBeans (menú "File -> New Project... -> Java -> Java

Application"). Le damos un nombre y una ubicación al proyecto y nos aseguramos de que las opciones "Create Main Class" y

"Set as Main Project" estén habilitadas. Presionamos el botón "Finish" y veremos aparecer nuestra clase en el editor."Main".

Agregamos la biblioteca de Hibernate, que creamos en el primer tutorial de la serie. Hacemos clic derecho en el nodo "Libraries" del

proyecto. En el menú contextual que se abre seleccionamos la opción "Add Library...":

En la ventana que se abre seleccionamos la biblioteca "Hibernate":

Presionamos el botón "Add Library" para que la biblioteca se agregue a nuestro proyecto. Aprovechamos también para agregar el

conector de MySQL. Debemos tener los siguientes archivos en nuestro proyecto:

Ahora creamos dos paquetes, uno con el nombre "modelo", que contendrá las clases entidades, y otro con el nombre "mapeos" que

DONACIONES

JAVA TUTORIALES EN FACEBOOK

Java Tutoriales

Me gusta

A 7895 personas les gusta

Plug-in social de Facebook

Java Tutoriales

Amigo programador, tú que

estás todo el día picando

código, escucha estas

recomendaciones sólo

hechas para ti..... ¿qué es

esto?... Gracias Jorge

Rubira....

No olviden poner la

sentencia where al hacer

un borraro en la base de

datos..

http://www.youtube.com/

watch?v=i_cVJgIz_Cs

Compartir 0 Más Siguiente blog»

Page 70: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 5: Relaciones / Muchos a Uno

www.javatutoriales.com/2009/08/hibernate-parte-5-relaciones-muchos-uno_02.html 2/11

contendrá los archivos de mapeo XML. Hacemos clic derecho en el nodo del paquete que se creó al generar el proyecto. En el menú

contextual que se abre seleccionamos la opción "New -> Java Package..." y creamos los dos paquetes.

Que deben quedar así:

Aprovecharemos para crear nuestro archivo de configuración de Hibernate, "hibernate.cfg.xml", el cual será muy parecido al del

primer tutorial:

<!DOCTYPE hibernate-configuration PUBLIC

"-//Hibernate/Hibernate Configuration DTD 3.0//EN"

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

<hibernate-configuration>

<session-factory>

<!-- parametros para la conexion a la base de datos -->

<property name="connection.driver_class">com.mysql.jdbc.Driver</property>

<property name="connection.url">jdbc:mysql://localhost/hibernaterelaciones</property>

<property name="connection.username">usuario</property>

<property name="connection.password">password</property>

<!-- Configuracion del pool interno -->

<property name="connection.pool_size">1</property>

<!-- Dialecto de la base de datos -->

<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>

<!-- Otras propiedades importantes -->

<property name="show_sql">true</property>

<property name="hbm2ddl.auto">create-drop</property>

<!-- Aqui iran los archivos de mapeo -->

</session-factory>

</hibernate-configuration>

Para los ejercicios usaremos una base de datos llamada "hibernaterelaciones", en MySQL.

Nota: Yo usaré dos proyectos, uno para usar archivos de mapeo y otro para usar anotaciones y que el código de ambos no se mezcle.

Pues bien, comencemos con este quinto tutorial:

Las relaciones muchos a uno en realidad son muy parecidas a las relaciones uno a muchos, con la excepción de que, en el caso de las

relaciones unidireccionales, solo el lado muchos de la relación sabe de la existencia de esta.

Es como si tuviéramos una clase Televidente, que tiene una relación muchos a uno unidireccional con una clase

Participar en este sitioGoogle Friend Connect

Miembros (173) Más »

¿Ya eres miembro? Iniciar sesión

SEGUIDORES

► 2012 (2)

► 2011 (11)

► 2010 (10)

▼ 2009 (22)

► septiembre (2)

▼ agosto (2)

Hibernate - Parte 6: Relaciones / Muchosa Muchos

Hibernate - Parte 5: Relaciones / Muchosa Uno

► julio (1)

► junio (3)

► mayo (2)

► abril (5)

► marzo (2)

► febrero (2)

► enero (3)

ARCHIVO DEL BLOG

Page 71: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 5: Relaciones / Muchos a Uno

www.javatutoriales.com/2009/08/hibernate-parte-5-relaciones-muchos-uno_02.html 3/11

CadenaTelevisiva. Donde el Televidente (el lado muchos de la relación) tiene una CadenaTelevisiva favorita (el lado uno de

la relación). Pero CadenaTelevisiva no tiene una referencia hacia cada uno de sus televidentes (desde los Televidentes podemos

llegar a una CadenaTelevisiva, pero desde la CadenaTelevisiva no podemos llegar a los Televidentes).

Comencemos con los ejemplos para que esto quede más claro.

Primero crearemos la clase CadenaTelevisiva, dentro del paquete “modelo”. Esta será una clase muy simple y solamente tendrá

dos atributos: id y nombre. La clase CadenaTelevisiva queda así:

public class CadenaTelevisiva

{

private long id;

private String nombre;

public CadenaTelevisiva()

{

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

}

Como dije antes: esta clase no tiene ninguna referencia a la clase Televidente (aunque si fuera una relación bidireccional debería

tener un arreglo de Televidentes), ya que será esta última la que este consciente de la relación.

La clase Televidente, también colocada en el paquete “modelo”, queda de esta forma:

5 - Relaciones Muchos a Uno Unidireccionales

File APIs forProgrammers

www.aspose.com

DOC, XLS, PPT, PDF,

MSG and more .NET,

Java, SSRS &

SharePoint

Alex

Programador Java con algunos años de

experiencia en múltiples poyectos y con

múltiples APIs y herramientas deseoso de

compartir experiencias con el resto de

programadores.

Ver todo mi perfil

DATOS PERSONALES

Page 72: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 5: Relaciones / Muchos a Uno

www.javatutoriales.com/2009/08/hibernate-parte-5-relaciones-muchos-uno_02.html 4/11

public class Televidente

{

private long id;

private String nombre;

private CadenaTelevisiva cadenaFavorita;

public Televidente()

{

}

public CadenaTelevisiva getCadenaFavorita()

{

return cadenaFavorita;

}

public void setCadenaFavorita(CadenaTelevisiva cadenaFavorita)

{

this.cadenaFavorita = cadenaFavorita;

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

}

Como podemos ver, en este caso Televidente tiene una referencia a CadenaTelevisiva, la cual usa el identificador

cadenaFavorita.

Ahora veamos cómo representar esta relación. Comenzaremos, como siempre haciendo uso de archivos de mapeo:

Creamos un nuevo documento XML en el paquete “mapeos”. Le damos el nombre “CadenaTelevisiva.hbm” (el asistente se encargará

de colocar el .xml):

Presionamos el botón “Next >” e indicamos que queremos crear un documento XML bien formado (la primer opción) y presionamos el

botón “Finish”.

Eliminamos en el contenido del archivo creado y lo reemplazamos con el siguiente:

5.1 RELACIONES MUCHOS A UNO UNIDIRECCIONALES CON ARCHIVOS DE MAPEO

Page 73: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 5: Relaciones / Muchos a Uno

www.javatutoriales.com/2009/08/hibernate-parte-5-relaciones-muchos-uno_02.html 5/11

<?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="hibernate.relaciones.modelos.CadenaTelevisiva" table="CADENAS_TELEVISIVAS">

</class>

</hibernate-mapping>

El mapeo de la clase CadenaTelevisiva el similar al que expliqué en el primer tutorial de la serie y, al final, queda de la siguiente

forma:

<hibernate-mapping>

<class name="hibernate.relaciones.modelos.CadenaTelevisiva" table="CADENAS_TELEVISIVAS">

<id name="id">

<generator class="identity" />

</id>

<property name="nombre" />

</class>

</hibernate-mapping>

Ahora crearemos el mapeo de la clase “Televidente”, llamado “Televidente.hbm.xml” en el paquete “mapeos”, de la misma forma

que lo hicimos para CadenaTelevisiva. El mapeo, excluyendo el elemento que representa la relación, debe estar de esta forma:

<hibernate-mapping>

<class name="hibernate.relaciones.modelos.Televidente" table="TELEVIDENTES">

<id name="id">

<generator class="identity" />

</id>

<property name="nombre" />

</class>

</hibernate-mapping>

Para representar la relación uno a muchos usamos el elemento “<many-to-one>”, que ya habíamos usado en el tutorial anterior. En

esta etiqueta colocamos, haciendo uso de se atributo name, el nombre de la propiedad de la clase Televidente que hace referencia a

la CadenaTelevisiva, que en este caso es cadenaFavorita:

<many-to-one name="cadenaFavorita" />

Por lo que, finalmente, el mapeo de la clase Televidente, queda de la siguiente forma:

Page 74: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 5: Relaciones / Muchos a Uno

www.javatutoriales.com/2009/08/hibernate-parte-5-relaciones-muchos-uno_02.html 6/11

<hibernate-mapping>

<class name="hibernate.relaciones.modelos.Televidente" table="TELEVIDENTES">

<id name="id">

<generator class="identity" />

</id>

<property name="nombre" />

<many-to-one name="cadenaFavorita" />

</class>

</hibernate-mapping>

Ahora probemos que la configuración funciona. Para que la prueba sea más fácil usaremos la clase HibernateUtil que creamos en el

primer tutorial.

No olviden que, además, debemos colocar en el archivo hibernate.cfg.xml las indicaciones de dónde están los archivos de mapeo

que acabamos de crear:

<mapping resource="hibernate/relaciones/mapeos/CadenaTelevisiva.hbm.xml" />

<mapping resource="hibernate/relaciones/mapeos/Televidente.hbm.xml" />

En la prueba crearemos tres objetos CadenaTelevisiva, cadena 1, cadena 2, y cadena 3, respectivamente, y dos objetos

Televidente, televidente 1, y televidente 2, y relacionaremos la cadena 1 con el televidente 1 y la cadena 3 con el

televidente 2.

Veamos cómo queda el código en el método main de nuestra clase Main:

public static void main(String[] args)

{

/* Creamos los tres objetos CadenaTelevisiva */

CadenaTelevisiva cadena1 = new CadenaTelevisiva();

cadena1.setNombre("Cadena 1");

CadenaTelevisiva cadena2 = new CadenaTelevisiva();

cadena2.setNombre("Cadena 2");

CadenaTelevisiva cadena3 = new CadenaTelevisiva();

cadena3.setNombre("Cadena 3");

/* Guardamos estos tres objetos CadenaTelevisiva en la base de datos */

Session sesion = HibernateUtil.getSessionFactory().openSession();

sesion.beginTransaction();

sesion.save(cadena1);

sesion.save(cadena2);

sesion.save(cadena3);

sesion.getTransaction().commit();

sesion.close();

/* Creamos dos objetos Televidente */

Televidente televidente1 = new Televidente();

televidente1.setNombre("Televidente 1");

televidente1.setCadenaFavorita(cadena1);

Televidente televidente2 = new Televidente();

televidente2.setNombre("Televidente 2");

televidente2.setCadenaFavorita(cadena3);

/* Guardamos los dos objetos Televidente en la base de Datos */

sesion = HibernateUtil.getSessionFactory().openSession();

sesion.beginTransaction();

sesion.save(televidente1);

sesion.save(televidente2);

sesion.getTransaction().commit();

sesion.close();

}

Listo, esto es todo lo que necesitamos para probar nuestro ejemplo.

Page 75: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 5: Relaciones / Muchos a Uno

www.javatutoriales.com/2009/08/hibernate-parte-5-relaciones-muchos-uno_02.html 7/11

Ahora veamos que, en la base de datos, estén las 3 cadenas televisivas y los 2 televidentes que creamos y que, las referencias de los

televidentes a las cadenas estén correctos:

Como podemos ver, los datos se han guardado de forma correcta - .

Ahora veamos cómo podemos hacer lo mismo usando anotaciones.

Recuerden que pasa usar anotaciones debemos agregar al proyecto la biblioteca "HibernateAnotaciones" que creamos en el

segundo tutorial.

Las anotaciones (así como los archivos de configuración) de ambas clases (omitiendo la que indica la relación) serán las mismas que

expliqué en el segundo tutorial de esta serie, quedan de la siguiente forma.

Esta es la clase CadenaTelevisiva:

@Entity

public class CadenaTelevisiva implements Serializable

{

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private long id;

private String nombre;

public CadenaTelevisiva()

{

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

}

Y esta es la clase Televidente:

5.2 RELACIONES MUCHOS A UNO UNIDIRECCIONALES ANOTACIONES

Page 76: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 5: Relaciones / Muchos a Uno

www.javatutoriales.com/2009/08/hibernate-parte-5-relaciones-muchos-uno_02.html 8/11

@Entity

public class Televidente implements Serializable

{

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private long id;

private String nombre;

private CadenaTelevisiva cadenaFavorita;

public Televidente()

{

}

public CadenaTelevisiva getCadenaFavorita()

{

return cadenaFavorita;

}

public void setCadenaFavorita(CadenaTelevisiva cadenaFavorita)

{

this.cadenaFavorita = cadenaFavorita;

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

}

Para representar la relación usamos la anotación @ManyToOne, que también vimos en el tutorial anterior. Así que el atributo

cadenaFavorita queda anotado de esta forma:

@ManyToOne

private CadenaTelevisiva cadenaFavorita;

Y la clase Televidente queda, por lo tanto, de esta forma:

Page 77: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 5: Relaciones / Muchos a Uno

www.javatutoriales.com/2009/08/hibernate-parte-5-relaciones-muchos-uno_02.html 9/11

@Entity

public class Televidente implements Serializable

{

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private long id;

private String nombre;

@ManyToOne

private CadenaTelevisiva cadenaFavorita;

public Televidente()

{

}

public CadenaTelevisiva getCadenaFavorita()

{

return cadenaFavorita;

}

public void setCadenaFavorita(CadenaTelevisiva cadenaFavorita)

{

this.cadenaFavorita = cadenaFavorita;

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

}

Ahora usaremos la clase HibernateUtil que creamos en el segundo tutorial para comprobar que todo funcione correctamente. No

olviden agregar al archivo hibernate.cfg.xml la ubicación de las clases que acabamos de anotar:

<mapping class="hibernate.relaciones.muchos.uno.anotaciones.modelo.CadenaTelevisiva" />

<mapping class="hibernate.relaciones.muchos.uno.anotaciones.modelo.Televidente" />

En este ejemplo haremos lo mismo que hace unos instantes:

Crearemos tres objetos CadenaTelevisiva, cadena 1, cadena 2, y cadena 3, respectivamente, y dos objetos Televidente,

televidente 1, y televidente 2, y relacionaremos la cadena 1 con el televidente 1 y la cadena 3 con el televidente

2.

Para lo cual colocaremos el siguiente código en el método main de nuestra clase Main:

Page 78: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 5: Relaciones / Muchos a Uno

www.javatutoriales.com/2009/08/hibernate-parte-5-relaciones-muchos-uno_02.html 10/11

public static void main(String[] args)

{

/* Creamos los tres objetos CadenaTelevisiva */

CadenaTelevisiva cadena1 = new CadenaTelevisiva();

cadena1.setNombre("Cadena 1");

CadenaTelevisiva cadena2 = new CadenaTelevisiva();

cadena2.setNombre("Cadena 2");

CadenaTelevisiva cadena3 = new CadenaTelevisiva();

cadena3.setNombre("Cadena 3");

/* Guardamos estos tres objetos CadenaTelevisiva en la base de datos */

Session sesion = HibernateUtil.getSessionFactory().openSession();

sesion.beginTransaction();

sesion.save(cadena1);

sesion.save(cadena2);

sesion.save(cadena3);

sesion.getTransaction().commit();

sesion.close();

/* Creamos dos objetos Televidente */

Televidente televidente1 = new Televidente();

televidente1.setNombre("Televidente 1");

televidente1.setCadenaFavorita(cadena1);

Televidente televidente2 = new Televidente();

televidente2.setNombre("Televidente 2");

televidente2.setCadenaFavorita(cadena3);

/* Guardamos los dos objetos Televidente en la base de Datos */

sesion = HibernateUtil.getSessionFactory().openSession();

sesion.beginTransaction();

sesion.save(televidente1);

sesion.save(televidente2);

sesion.getTransaction().commit();

sesion.close();

}

Ahora comprobemos que en la base de datos tengamos las 3 cadenas televisivas y los 2 televidentes:

Como podemos observar todo ha salido como lo esperábamos - .

Esto es todo con respecto a las relaciones unidireccionales. Como les decía, las relaciones Muchos a Uno bidireccionales, son igual

a las relaciones Uno a Muchos bidireccionales que vimos en el tutorial anterior por lo que hemos terminado con este tutorial.

Si tienen alguna duda, comentario o sugerencia no duden en preguntar.

Saludos y gracias.

Descarga los archivos de este tutorial desde aquí:

Hibernate Relaciones Muchos a Uno con archivos de Mapeo

Hibernate Relaciones Muchos a Uno con Anotaciones

Entradas Relacionadas:

Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

Parte 2: Persistiendo Objetos Simples usando Anotaciones (Metadatos)

Parte 3: Relaciones / Uno a uno

Parte 4: Relaciones / Uno a muchos

Parte 6: Relaciones / Muchos a Muchos

Parte 7: HQL Primera Parte

Parte 8: HQL Segunda Parte

Parte 9: Parámetros en HQL

Parte 10: Herencia

Page 79: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 6: Relaciones / Muchos a Muchos

www.javatutoriales.com/2009/08/hibernate-parte-6-relaciones-muchos.html 1/23

Blog dedicado a temas de programación actuales usando el lenguaje de programación Java y las últimas versiones de sus APIs y Herramientas.

Tutoriales de Programacion Java

Página principal Página de Concursos Tutoriales UPAO 2010 Presentaciones Capacitación

LUNES, 24 DE AGOSTO DE 2009

Hibernate - Parte 6: Relaciones / Muchos a Muchos

En ocasiones necesitamos relacionar muchas entidades de un tipo con muchas entidades de otro tipo. Este tipo de relaciones es muy

común cuando trabajamos con bases de datos relacionales, por eso es importante saber cómo trabajar con ellas.

En este último tutorial de la serie veremos cómo crear los últimos dos tipos de relaciones el framework de Hibernate: relaciones muchos

a muchos unidireccionales y bidireccionales.

Este tipo de relaciones es especial ya que, como mencione antes, tenemos que muchos registros de la entidad tipo A están

relacionadas con muchos registros de la entidad tipo B.

Normalmente los registros en base de datos se relacionan usando una llave foránea de una tabla con el identificador de otra tabla,

como en la siguiente imagen:

Sin embargo, como en las relaciones muchos a muchos necesitamos relacionar muchos registros de la entidad A, con muchos

registros de la entidad B, y es por esto que NO nos basta con una llave foránea en una de las tablas ya que, por ejemplo, el registro con

ID 1 de la entidad A podría estar relacionado con los registros con IDs 1, 3, y 5 de la entidad B (hasta aquí sería una relación uno a

muchos). Sin embargo el registro 3 de la entidad B puede estas relacionado con los registros 1, 2, y 4 de la entidad A, como lo muestra la

siguiente figura:

Esto no puede ser logrado utilizando simplemente llaves foráneas en las tablas de las entidades A y B. En estos casos se utiliza una

tercera tabla conocida como tabla de join, o tabla de enlace, o tabla de unión.

Esta tercera tabla lo único que hace es mantener las relaciones de las entidades A que están relacionadas con las entidades B, y las

entidades B que están relacionadas con las entidades A. Como lo único que necesita para esto son los identificadores de ambas tablas

como llaves foráneas, esto es lo que mantiene esta tabla de unión:

Afortunadamente con Hibernate no debemos preocuparnos de esta tabla de unión, ya que es el propio framework el que se encarga de

hacerlo. De lo único que debemos preocuparnos es de hacer de forma correcta los mapeos para representar esta relación (ya que tiene

algunos trucos).

Comencemos con el tutorial para ver cómo crear estas relaciones. Para los ejemplos usaremos dos clases: "Estudiante" y

"Materia", que veremos un poco más adelante.

Lo primero que haremos es crear un proyecto en NetBeans (menú "File -> New Project... -> Java -> Java

Application"). Le damos un nombre y una ubicación al proyecto y nos aseguramos de que las opciones "Create Main Class" y

"Set as Main Project" estén habilitadas. Presionamos el botón "Finish" y veremos aparecer nuestra clase "Main" en el editor.

Agregamos la biblioteca de Hibernate, que creamos en el primer tutorial de la serie. Hacemos clic derecho en el nodo "Libraries" del

proyecto y en el menú contextual que se abre seleccionamos la opción "Add Library...":

DONACIONES

JAVA TUTORIALES EN FACEBOOK

Java Tutoriales

Me gusta

A 7895 personas les gusta

Plug-in social de Facebook

Java Tutoriales

Amigo programador, tú que

estás todo el día picando

código, escucha estas

recomendaciones sólo

hechas para ti..... ¿qué es

esto?... Gracias Jorge

Rubira....

No olviden poner la

sentencia where al hacer

un borraro en la base de

datos..

http://www.youtube.com/

watch?v=i_cVJgIz_Cs

Compartir 0 Más Siguiente blog»

Page 80: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 6: Relaciones / Muchos a Muchos

www.javatutoriales.com/2009/08/hibernate-parte-6-relaciones-muchos.html 2/23

En la ventana que se abre seleccionamos la biblioteca "Hibernate":

Presionamos el botón "Add Library" para que la biblioteca se agregue a nuestro proyecto. Aprovechamos también para agregar el

conector de MySQL. Debemos tener los siguientes archivos en nuestro proyecto:

Ahora creamos dos paquetes, uno con el nombre "modelo", que contendrá las clases entidades, y otro con el nombre "mapeos" que

contendrá los archivos de mapeo XML. Hacemos clic derecho en el nodo del paquete que se creó al generar el proyecto, en el menú

contextual que se abre seleccionamos la opción "New -> Java Package..." y creamos los dos paquetes.

Que deben quedar así:

Java filelibraries

www.aspose.com

DOC, XLS, PPT, PDF,

MSG and more APIs

to Manage, Print and

Convert

Participar en este sitioGoogle Friend Connect

Miembros (173) Más »

¿Ya eres miembro? Iniciar sesión

SEGUIDORES

► 2012 (2)

► 2011 (11)

► 2010 (10)

▼ 2009 (22)

► septiembre (2)

▼ agosto (2)

Hibernate - Parte 6: Relaciones / Muchosa Muchos

Hibernate - Parte 5: Relaciones / Muchosa Uno

► julio (1)

► junio (3)

► mayo (2)

► abril (5)

► marzo (2)

► febrero (2)

► enero (3)

ARCHIVO DEL BLOG

Page 81: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 6: Relaciones / Muchos a Muchos

www.javatutoriales.com/2009/08/hibernate-parte-6-relaciones-muchos.html 3/23

Aprovecharemos para crear nuestro archivo de configuración de Hibernate, "hibernate.cfg.xml", el cual será muy parecido al del

primer tutorial:

<!DOCTYPE hibernate-configuration PUBLIC

"-//Hibernate/Hibernate Configuration DTD 3.0//EN"

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

<hibernate-configuration>

<session-factory>

<!-- parametros para la conexion a la base de datos -->

<property name="connection.driver_class">com.mysql.jdbc.Driver</property>

<property name="connection.url">jdbc:mysql://localhost/hibernaterelaciones</property>

<property name="connection.username">usuario</property>

<property name="connection.password">password</property>

<!-- Configuracion del pool interno -->

<property name="connection.pool_size">1</property>

<!-- Dialecto de la base de datos -->

<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>

<!-- Otras propiedades importantes -->

<property name="show_sql">true</property>

<property name="hbm2ddl.auto">create-drop</property>

<!-- Aqui iran los archivos de mapeo -->

</session-factory>

</hibernate-configuration>

Para los ejercicios usaremos una base de datos llamada "hibernaterelaciones", en MySQL.

Nota: Yo usaré dos proyectos, uno para usar archivos de mapeo y otro para usar anotaciones y que el código de ambos no se mezcle.

Comencemos con el tutorial.

Primero crearemos la clase "Materia", dentro del paquete “modelo”. Esta clase solo contendrá los elementos básicos: id y nombre.

Por lo que la clase "Materia" queda así:

6 - Relaciones Muchos a Muchos Unidireccionales

Alex

Programador Java con algunos años de

experiencia en múltiples poyectos y con

múltiples APIs y herramientas deseoso de

compartir experiencias con el resto de

programadores.

Ver todo mi perfil

DATOS PERSONALES

Page 82: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 6: Relaciones / Muchos a Muchos

www.javatutoriales.com/2009/08/hibernate-parte-6-relaciones-muchos.html 4/23

public class Materia

{

private long id;

private String nombre;

public Materia()

{

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

}

Materia no tiene alguna referencia a la clase Estudiante, por lo que no sabe que existe una relación entre ellas.

La clase "Estudiante", colocada también en el paquete “modelo”, será la dueña de la relación, así que contendrá, además de tus

atributos básicos (id y nombre), una lista de referencias a "Materia". La clase Estudiante queda de esta forma:

public class Estudiante

{

private long id;

private String nombre;

private List<Materia> materias = new ArrayList<Materia>();

public Estudiante()

{

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public List<Materia> getMaterias()

{

return materias;

}

public void setMaterias(List<Materia> materias)

{

this.materias = materias;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

}

Agregaré a la clase Estudiante un método auxiliar, addMateria, que nos permitirá agregar una materia nueva a la lista de materias

Page 83: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 6: Relaciones / Muchos a Muchos

www.javatutoriales.com/2009/08/hibernate-parte-6-relaciones-muchos.html 5/23

de este Estudiante:

public void addMateria(Materia materia)

{

this.materias.add(materia);

}

Por lo que la clase Estudiante completa queda así:

public class Estudiante

{

private long id;

private String nombre;

private List<Materia> materias = new ArrayList<Materia>();

public Estudiante()

{

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public List<Materia> getMaterias()

{

return materias;

}

public void setMaterias(List<Materia> materias)

{

this.materias = materias;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

public void addMateria(Materia materia)

{

this.materias.add(materia);

}

}

La lista de Materias que tiene el Estudiante será la referencia que usaremos para la relación muchos a muchos unidireccional.

Ahora vemos cómo representar esta relación, como de costumbre, primero veremos cómo hacerlo con archivos de mapeo en XML y

después veremos cómo hacerlo con anotaciones:

Creamos un nuevo documento XML en el paquete “mapeos”. Le damos el nombre “Materia.hbm” (el asistente se encargará de colocar

el .xml):

6.1 RELACIONES MUCHOS A MUCHOS UNIDIRECCIONALES CON ARCHIVOS DE MAPEO

Page 84: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 6: Relaciones / Muchos a Muchos

www.javatutoriales.com/2009/08/hibernate-parte-6-relaciones-muchos.html 6/23

Presionamos el botón “Next >” e indicamos que queremos crear un documento XML bien formado (la primer opción) y presionamos el

botón “Finish”.

Eliminamos el contenido del archivo creado y lo reemplazamos por el siguiente:

<?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="hibernate.relaciones.muchos.muchos.modelo.Materia" table="MATERIAS">

</class>

</hibernate-mapping>

El mapeo de la clase Materia es similar al que expliqué en el primer tutorial de la serie y, al final, queda de la siguiente forma:

<hibernate-mapping>

<class name="hibernate.relaciones.muchos.muchos.modelo.Materia" table="MATERIAS">

<id name="id" column="ID_MATERIA">

<generator class="identity" />

</id>

<property name="nombre" />

</class>

</hibernate-mapping>

Noten que he agregado el atributo column=”ID_MATERIA” del elemento <id>. Esto no es necesario para las relaciones

unidireccionales, pero es vital para las relaciones bidireccionales (si, como yo, le ponen a todos los identificadores de sus entidades

el mismo nombre, id), por lo que lo colocamos desde el principio.

Ahora crearemos el mapeo para la clase Estudiante. Creamos, en el paquete “mapeos”, el archivo “Estudiante.hbm”. Eliminamos el

contenido del archivo y lo reemplazamos por el siguiente:

<?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="hibernate.relaciones.muchos.muchos.modelo.Estudiante" table="ESTUDIANTES">

</class>

</hibernate-mapping>

Page 85: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 6: Relaciones / Muchos a Muchos

www.javatutoriales.com/2009/08/hibernate-parte-6-relaciones-muchos.html 7/23

Colocamos en el archivo el mapeo de los atributos “id” y “nombre” de la clase Estudiante antes de pasar a ver el mapeo de la

relación. Hasta ahora el mapeo debe verse así:

<hibernate-mapping>

<class name="hibernate.relaciones.muchos.muchos.modelo.Estudiante" table="ESTUDIANTES">

<id name="id" column="ID_ESTUDIANTE">

<generator class="identity" />

</id>

<property name="nombre" />

</class>

</hibernate-mapping>

Ahora podemos centrarnos en mapear la relación.

Al igual que ocurrió cuando vimos las relaciones uno a muchos en el cuarto tutorial, representamos las relaciones muchos a muchos

usando el elemento acorde al tipo de colección que estemos usando (list, set, map, array, y primitive-array).

En este caso estamos usando una lista, por lo que veremos cómo hacer el mapeo usando esta colección, pero si están usando otra

pueden encontrar más información en esta página.

Para mapear una lista usamos el elemento "<list>". En este elemento indicamos cuál es el nombre del atributo, dentro de la clase

Estudiante, que representa la realción. En este caso el atributo se llama “materias”. También aquí indicamos cuáles operaciones

queremos que se realicen en cascada. En este caso queremos que todas las operaciones de guardar, actualizar y eliminar que

ocurran en el padre sean pasadas a la colección, o sea que cuando guardemos, actualicemos, o eliminemos un Estudiante, las

operaciones pasen también a todas sus Materias relacionadas, por lo que usamos el valor "all".

En las relaciones muchos a muchos, igual que en las relaciones uno a muchos, existen dos estilos de cascada especiales llamados

"delete-orphan" y "all-delete-orphan" (que solo pueden usarse con archivos de mapeo) los cuales se encargan de que, en el

caso de que se elimine el objeto padre ("Estudiante"), todos los objetos hijos ("Materia") serán eliminados de la base de datos.

Adicionalmente "all-delete-orphan" se encarga de que todas las otras operaciones que mencionamos antes (guardar, actualizar,

y eliminar) también sean realizados en cascada, por lo que usaremos este valor.

Finalmente, si recuerdan, al principio del tutorial dije que cuando tenemos relaciones muchos a muchos, se usa una “tabla de unión” o

“tabla join” para mantener los datos de qué objetos de la entidad A (Estudiante) están relacionados con qué objetos de la entidad B

(Materia). En este caso debemos especificar cuál será el nombre de esta tabla de unión, usando el atributo "table" del elemento

<list>. Por lo que este elemento queda, por el momento, así:

<list name="materias" table="ESTUDIANTES_MATERIAS" cascade="all-delete-orphan">

</list>

La tabla de unión generada ("ESTUDIANTES_MATERIAS") tendrá como columnas "id", que es la llave foránea de la tabla estudiantes, y

una segunda columna llamada "elt" (elt significa element, que es el nombre que da por default hibérnate si no especificamos uno) que es

la llave foránea de la tabla "materias".

Ahora indicamos cuál será el valor que se usará como llave foránea para relacionar las Materias con el Estudiante. Siempre, lo que

usaremos será la llave primaria de la entidad que estamos mapeando (en este caso Estudiante), por lo que colocamos aquí el nombre

de la columna que mantiene este valor, que en este caso es "ID_ESTUDIANTE":

<key column="ID_ESTUDIANTE " />

Ahora bien, las listas son una estructura de datos con una característica única: tienen un orden. Esto significa que el orden en el que

los elementos entran en la lista es importante e, internamente, se usa un índice para saber el orden de los elementos.

Cuando tratamos de almacenar estos datos nos interesa que en el momento que sean recuperados, los elementos de la lista estén en el

mismo orden en el que los guardamos y es por esta razón que se debe usar una columna extra en la tabla de unión generada para

guardar este índice (el cual comienza en cero). Para indicar el nombre que tendrá esta columna usamos el elemento "index" y

colocamos en su atributo "column" el nombre que tendrá esta columna:

<index column="ORDEN" />

Ahora si, por fin ha llego el momento de usar el elemento que representa la relación - .

Page 86: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 6: Relaciones / Muchos a Muchos

www.javatutoriales.com/2009/08/hibernate-parte-6-relaciones-muchos.html 8/23

Las relaciones muchos a muchos las representamos usando el elemento <many-to-many>. Este elemento se coloca dentro del

elemento <list> (o el elemento que esten usando para representar su relación) que acabo de explicar, y lo único que debemos indicarle

de qué clase son las entidades que estamos guardando en la lista (ya que este dato no puede ser obtenido usando reflexión), y cuál es la

columna que se usa para almacenar el id de esta entidad:

<many-to-many class="hibernate.relaciones.muchos.muchos.modelo.Materia" column="ID_MATERIA" />

Finalmente, el mapeo del a relación queda así:

<list name="materias" table="ESTUDIANTES_MATERIAS" cascade="all-delete-orphan" >

<key column="ID_ESTUDIANTE" />

<list-index column="ORDEN" />

<many-to-many class="hibernate.relaciones.muchos.muchos.modelo.Materia" column="ID_MATERIA" />

</list>

Y el archivo de mapeo para la entidad Estudiante queda así:

<hibernate-mapping>

<class name="hibernate.relaciones.muchos.muchos.modelo.Estudiante" table="ESTUDIANTES">

<id name="id" column="ID_ESTUDIANTE">

<generator class="identity" />

</id>

<property name="nombre" />

<list name="materias" table="ESTUDIANTES_MATERIAS" cascade="all-delete-orphan" >

<key column="ID_ESTUDIANTE" />

<list-index column="ORDEN" />

<many-to-many class="hibernate.relaciones.muchos.muchos.modelo.Materia" column="ID_MAT

ERIA" />

</list>

</class>

</hibernate-mapping>

Ahora agregamos la ruta a los dos archivos de mapeo que acabamos de crear al archivo “hibernate.cfg.xml”:

<mapping resource="hibernate/relaciones/muchos/muchos/mapeos/Materia.hbm.xml" />

<mapping resource="hibernate/relaciones/muchos/muchos/mapeos/Estudiante.hbm.xml" />

Para este ejemplo usaremos la clase HibernateUtil que creamos en el primer tutorial.

Crearemos dos objetos Estudiante, “estudiante1” y “estudiante2”. Después crearemos 6 Materias “materia1” a “materia6”

y asociaremos 3 Materias con cada Estudiante. Posteriormente guardaremos en la base de datos a los 2 Estudiantes, con lo que

las 6 Materias serán almacenadas en la base de datos por las operaciones en cascada que definimos. Finalmente eliminaremos al

“estudiante1”, con lo que esperamos que se eliminen además “materia1”, “materia2”, y “materia3”.

Colocaremos el siguiente código, auto-explicativo, en el método main de nuestra clase Main:

Page 87: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 6: Relaciones / Muchos a Muchos

www.javatutoriales.com/2009/08/hibernate-parte-6-relaciones-muchos.html 9/23

public static void main(String[] args)

{

Estudiante estudiante1 = new Estudiante();

estudiante1.setNombre("estudiante1");

Materia materia1 = new Materia();

materia1.setNombre("materia1");

Materia materia2 = new Materia();

materia2.setNombre("materia2");

Materia materia3 = new Materia();

materia3.setNombre("materia3");

estudiante1.addMateria(materia1);

estudiante1.addMateria(materia2);

estudiante1.addMateria(materia3);

Estudiante estudiante2 = new Estudiante();

estudiante2.setNombre("estudiante2");

Materia materia4 = new Materia();

materia4.setNombre("materia4");

Materia materia5 = new Materia();

materia5.setNombre("materia5");

Materia materia6 = new Materia();

materia6.setNombre("materia6");

estudiante2.addMateria(materia4);

estudiante2.addMateria(materia5);

estudiante2.addMateria(materia6);

Session sesion = HibernateUtil.getSessionFactory().openSession();

sesion.beginTransaction();

sesion.save(estudiante1);

sesion.save(estudiante2);

sesion.getTransaction().commit();

sesion.close();

sesion = HibernateUtil.getSessionFactory().openSession();

sesion.beginTransaction();

sesion.delete(estudiante1);

sesion.getTransaction().commit();

sesion.close();

}

Probemos que todo funciona correctamente. Según lo anterior solo debe estar el “estudiante2” en la base de datos y sus 3 Materias

asociadas “materia1”, “materia2”, y “materia3”:

En la imagen anterior podemos comprobar que el la base de datos están los datos correctos - . También podemos ver que,

efectivamente, se creó una tercera tabla que mantiene las relaciones entre Estudiante y Materia.

Ahora veremos cómo hacer lo mismo pero usando anotaciones:

Recuerden que pasa usar anotaciones debemos agregar al proyecto la biblioteca "HibernateAnotaciones" que creamos en el

segundo tutorial.

Las anotaciones (así como los archivos de configuración) de ambas clases (omitiendo el atributo que indica la relación) serán las mismas

que expliqué en el segundo tutorial de esta serie, quedan de la siguiente forma:

Esta es la clase Materia:

6.2 RELACIONES MUCHOS A MUCHOS UNIDIRECCIONALES CON ANOTACIONES

Page 88: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 6: Relaciones / Muchos a Muchos

www.javatutoriales.com/2009/08/hibernate-parte-6-relaciones-muchos.html 10/23

@Entity

public class Materia implements Serializable

{

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private long id;

private String nombre;

public Materia()

{

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

}

Y esta es la clase Estudiante:

Page 89: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 6: Relaciones / Muchos a Muchos

www.javatutoriales.com/2009/08/hibernate-parte-6-relaciones-muchos.html 11/23

@Entity

public class Estudiante implements Serializable

{

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private long id;

private String nombre;

private List<Materia> materias = new ArrayList<Materia>();

public Estudiante()

{

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public List<Materia> getMaterias()

{

return materias;

}

public void setMaterias(List<Materia> materias)

{

this.materias = materias;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

public void addMateria(Materia materia)

{

this.materias.add(materia);

}

}

La anotación que nos interesa, para representar la relación muchos a muchos es @ManyToMany. Esta anotación debemos colocarla en

el atributo que representa la relación (en este caso “materias”).

Igual que lo hicimos en la anotación "@OneToOne", y con "@OneToMany", aquí definiremos qué operaciones serán realizadas en

cascada. Además también podemos indicar el tipo de "fetch" o recuperación que tendrá la colección, de la misma forma que lo expliqué

en el tutorial anterior.

Al final, la relación queda de esta forma:

@ManyToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)

private List<Materia> materias = new ArrayList<Materia>();

Y la clase Estudiante, con todas sus anotaciones, queda así:

Page 90: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 6: Relaciones / Muchos a Muchos

www.javatutoriales.com/2009/08/hibernate-parte-6-relaciones-muchos.html 12/23

@Entity

public class Estudiante implements Serializable

{

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private long id;

private String nombre;

@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)

private List<Materia> materias = new ArrayList<Materia>();

public Estudiante()

{

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public List<Materia> getMaterias()

{

return materias;

}

public void setMaterias(List<Materia> materias)

{

this.materias = materias;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

public void addMateria(Materia materia)

{

this.materias.add(materia);

}

}

Y eso es todo lo que necesitamos para tener una relación muchos a muchos unidireccional usando anotaciones.

Recuerden que debemos colocar estas dos clases en el archivo de configuración de Hibernate:

<mapping class="hibernate.relaciones.muchos.muchos.anotaciones.modelo.Estudiante" />

<mapping class="hibernate.relaciones.muchos.muchos.anotaciones.modelo.Materia" />

Ahora usaremos la clase HibernateUtil del segundo tutorial para implementar nuestra prueba. La prueba será la misma que describí

anteriormente: Crearemos 2 objetos Estudiante, “estudiante1” y “estudiante2”. Después crearemos 6 Materias “materia1” a

“materia6” y asociaremos 3 materias con cada estudiante. Posteriormente guardaremos en la base de datos a los 2 estudiantes, con lo

que las 6 materias serán almacenadas en la base de datos por las operaciones en cascada que definimos. Finalmente eliminaremos al

“estudiante1”, con lo que esperamos que se eliminen además “materia1”, “materia2”, y “materia3”.

Colocamos el siguiente código como el método main de nuestra clase Main:

Page 91: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 6: Relaciones / Muchos a Muchos

www.javatutoriales.com/2009/08/hibernate-parte-6-relaciones-muchos.html 13/23

public static void main(String[] args)

{

Estudiante estudiante1 = new Estudiante();

estudiante1.setNombre("estudiante1");

Materia materia1 = new Materia();

materia1.setNombre("materia1");

Materia materia2 = new Materia();

materia2.setNombre("materia2");

Materia materia3 = new Materia();

materia3.setNombre("materia3");

estudiante1.addMateria(materia1);

estudiante1.addMateria(materia2);

estudiante1.addMateria(materia3);

Estudiante estudiante2 = new Estudiante();

estudiante2.setNombre("estudiante2");

Materia materia4 = new Materia();

materia4.setNombre("materia4");

Materia materia5 = new Materia();

materia5.setNombre("materia5");

Materia materia6 = new Materia();

materia6.setNombre("materia6");

estudiante2.addMateria(materia4);

estudiante2.addMateria(materia5);

estudiante2.addMateria(materia6);

Session sesion = HibernateUtil.getSessionFactory().openSession();

sesion.beginTransaction();

sesion.save(estudiante1);

sesion.save(estudiante2);

sesion.getTransaction().commit();

sesion.close();

sesion = HibernateUtil.getSessionFactory().openSession();

sesion.beginTransaction();

sesion.delete(estudiante1);

sesion.getTransaction().commit();

sesion.close();

}

Ahora comprobemos que el ejemplo funciona:

Como podemos observar todo ha funcionado correctamente - .

Ahora, veamos cómo crear relaciones muchos a muchos bidireccionales. Primero veamos cómo hacerlo mediante archivos de mapeo.

Las relaciones muchos a muchos bidireccionales son muy parecidas a las relaciones unidireccionales, pero con la diferencia de que,

como en el caso de todas las relaciones bidireccionales, ambos lados de la relación saben de la existencia de esta. Veamos cómo

representar estas relaciones, nuevamente usando primero archivos de mapeo y después con anotaciones.

Las relaciones muchos a muchos bidireccionales son muy parecidas a las relaciones unidireccionales, por lo que los cambios que

deberemos hacer son mínimos.

Modificaremos un poco nuestra clase “Materia” para que contenga una referencia a todos los Estudiantes que estén tomando esa

7 - Relaciones Muchos a Muchos Bidireccionales

7.1 RELACIONES MUCHOS A MUCHOS BIDIRECCIONALES CON ARCHIVOS DE MAPEO

Page 92: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 6: Relaciones / Muchos a Muchos

www.javatutoriales.com/2009/08/hibernate-parte-6-relaciones-muchos.html 14/23

Materia. Como muchos Estudiantes pueden tomar una Materia, deberemos agregar una lista de objetos “Estudiante” a la clase

Materia, de esta forma:

private List<Estudiante> estudiantes = new ArrayList<Estudiante>();

También agregamos los setters y los getters para este atributo.

public List<Estudiante> getEstudiantes()

{

return estudiantes;

}

public void setEstudiantes(List<Estudiante> estudiantes)

{

this.estudiantes = estudiantes;

}

Además pondré un método auxiliar extra que nos permitirá que se agregue un Estudiante a la lista de Estudiantes para esta

Materia. Además, al mismo tiempo, agregará esta Materia a la lista de Materias para el Estudiante:

public void addEstudiante(Estudiante estudiante)

{

this.estudiantes.add(estudiante);

estudiante.addMateria(this);

}

La clase Materia queda de esta forma:

Page 93: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 6: Relaciones / Muchos a Muchos

www.javatutoriales.com/2009/08/hibernate-parte-6-relaciones-muchos.html 15/23

public class Materia

{

private long id;

private String nombre;

private List<Estudiante> estudiantes = new ArrayList<Estudiante>();

public Materia()

{

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

public List<Estudiante> getEstudiantes()

{

return estudiantes;

}

public void setEstudiantes(List<Estudiante> estudiantes)

{

this.estudiantes = estudiantes;

}

public void addEstudiante(Estudiante estudiante)

{

this.estudiantes.add(estudiante);

estudiante.addMateria(this);

}

}

La clase Estudiante queda igual, por lo que no es necesario modificarle nada.

Ahora hay que indicar esta relación en el archivo de mapeo “Materia.hbm.xml”.

Esta relación la indicamos exactamente como lo hicimos en el archivo “Estudiante.hbm.xml”: Agregamos un elemento <list>, con

el nombre del atributo que representa la relación en la clase Materia, que en este caso es “estudiantes”, y, de la misma forma que

hicimos antes, indicamos el nombre de la tabla de unión para estas dos entidades.

El siguiente paso es muy importante. ¿Recuerdan que cuando hablé sobre las relaciones uno a muchos bidireccionales dije que existía

un lado dueño de la relación y un lado inverso. Pues bien, en este caso ocurre lo mismo, una de las entidades es la dueña de la

relación y el otro es el lado inverso. Quién debe ser el dueño y quién el inverso depende de la lógica de su aplicación. Para este

ejemplo, supondré que “Estudiante” es el dueño de la relación (para no tener que modificar su archivo de mapeo), y que “Materias”

es el lado inverso. Esto lo indicamos usando el atributo “inverse” con valor “true” en el elemento <list>.

El resto de los elementos que representan el mapeo de la relación son exactamente los mismos que expliqué en el mapeo de la relación

unidireccional, por lo que el mapeo queda de esta forma:

<list name="estudiantes" table="ESTUDIANTES_MATERIAS" inverse="true" >

<key column="ID_MATERIA" />

<list-index column="ORDEN" />

<many-to-many class="hibernate.relaciones.muchos.muchos.modelo.Estudiante" column="ID_ESTUDIAN

TE" />

</list>

Y el archivo completo de mapeo así:

Page 94: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 6: Relaciones / Muchos a Muchos

www.javatutoriales.com/2009/08/hibernate-parte-6-relaciones-muchos.html 16/23

<hibernate-mapping>

<class name="hibernate.relaciones.muchos.muchos.modelo.Materia" table="MATERIAS">

<id name="id" column="ID_MATERIA">

<generator class="identity" />

</id>

<property name="nombre" />

<list name="estudiantes" table="ESTUDIANTES_MATERIAS" inverse="true" >

<key column="ID_MATERIA" />

<list-index column="ORDEN" />

<many-to-many class="hibernate.relaciones.muchos.muchos.modelo.Estudiante" column="ID_

ESTUDIANTE" />

</list>

</class>

</hibernate-mapping>

El archivo “Estudiante.hbm.xml” queda exactamente igual.

Modificaremos un poco el código del método main de la clase Main para usar el método auxiliar “addEstudiante” de la clase

“Materia”, dejándolo así:

public static void main(String[] args)

{

Estudiante estudiante1 = new Estudiante();

estudiante1.setNombre("estudiante1");

Materia materia1 = new Materia();

materia1.setNombre("materia1");

Materia materia2 = new Materia();

materia2.setNombre("materia2");

Materia materia3 = new Materia();

materia3.setNombre("materia3");

materia1.addEstudiante(estudiante1);

materia2.addEstudiante(estudiante1);

materia3.addEstudiante(estudiante1);

Estudiante estudiante2 = new Estudiante();

estudiante2.setNombre("estudiante2");

Materia materia4 = new Materia();

materia4.setNombre("materia4");

Materia materia5 = new Materia();

materia5.setNombre("materia5");

Materia materia6 = new Materia();

materia6.setNombre("materia6");

materia4.addEstudiante(estudiante2);

materia5.addEstudiante(estudiante2);

materia6.addEstudiante(estudiante2);

Session sesion = HibernateUtil.getSessionFactory().openSession();

sesion.beginTransaction();

sesion.save(estudiante1);

sesion.save(estudiante2);

sesion.getTransaction().commit();

sesion.close();

sesion = HibernateUtil.getSessionFactory().openSession();

sesion.beginTransaction();

sesion.delete(estudiante1);

sesion.getTransaction().commit();

sesion.close();

}

Page 95: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 6: Relaciones / Muchos a Muchos

www.javatutoriales.com/2009/08/hibernate-parte-6-relaciones-muchos.html 17/23

Debe ocurrir lo mismo que en la ocasión anterior: se crean 2 Estudiantes y 6 Materias, relacionando 3 Materias con cada

Estudiante. Después se almacenan los Estudiantes, y sus Materias en cascada, y finalmente se elimina el primer Estudiante.

Por lo que solo deben quedar en la base el “estudiante2” y las materias 4 a 6:

Podemos comprobar que todo salió como esperábamos - . Por lo que solo queda que veamos cómo crear relaciones bidireccionales con

anotaciones:

Finalmente, y para terminar este tutorial, modificaremos el ejemplo de la relación muchos a muchos unidireccional con anotaciones para

transformarlo en bidireccional.

Para hacer eso, debemos agregar un atributo que represente la relación dentro de la clase “Materia”. Como la relación es muchos a

muchos, esta relación estará representada por un atributo de tipo lista de Estudiantes, de esta forma:

private List<Estudiante> estudiantes = new ArrayList<Estudiante>();

A este atributo lo marcamos con la anotación @ManyToMany, de la misma forma que lo está el atributo “materias” de la clase

Estudiante:

@ManyToMany

private List<Estudiante> estudiantes = new ArrayList<Estudiante>();

Agregamos además los setters y los getters de este atributo y el método auxiliar addEstudiante, igual que lo hicimos en el caso de

los archivos de mapeo:

public List<Estudiante> getEstudiantes()

{

return estudiantes;

}

public void setEstudiantes(List<Estudiante> estudiantes)

{

this.estudiantes = estudiantes;

}

public void addEstudiante(Estudiante estudiante)

{

this.estudiantes.add(estudiante);

estudiante.addMateria(this);

}

Bien, con esto ya casi hemos terminado. Si recuerdan, cuando hable de las relaciones uno a muchos bidireccionales con anotaciones

dije que uno de los dos lados de la relación debe ser el dueño y el otro el inverso, y que existe una regla: el lado muchos

SIEMPRE es el lado dueño. Pues bien, esto también aplica para las relaciones muchos a muchos. Claro que como aquí ambos lados

son el lado mucho, podemos elegir el que nosotros queramos para que sea el dueño.

En mi caso escogeré la entidad Estudiante como el dueño, solo para no modificar la clase, y por lo tanto, la entidad Materia será el

lado inverso, por lo que debemos indicarlo colocando, en la anotación @ManyToMany el atributo “mappedBy”, como expliqué en el cuarto

tutorial, cuyo valor será el nombre del atributo que representa la lista de Materias en la clase Estudiante, que en este caso es

“materias”. Por lo que el atributo queda de esta forma:

7.2 RELACIONES MUCHOS A MUCHOS BIDIRECCIONALES CON ANOTACIONES

Page 96: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 6: Relaciones / Muchos a Muchos

www.javatutoriales.com/2009/08/hibernate-parte-6-relaciones-muchos.html 18/23

@ManyToMany(mappedBy="materias")

private List<Estudiante> estudiantes = new ArrayList<Estudiante>();

Y la clase Estudiante así:

@Entity

public class Materia implements Serializable

{

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private long id;

private String nombre;

@ManyToMany(mappedBy = "materias")

private List<Estudiante> estudiantes = new ArrayList<Estudiante>();

public Materia()

{

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

public List<Estudiante> getEstudiantes()

{

return estudiantes;

}

public void setEstudiantes(List<Estudiante> estudiantes)

{

this.estudiantes = estudiantes;

}

public void addEstudiante(Estudiante estudiante)

{

this.estudiantes.add(estudiante);

estudiante.addMateria(this);

}

}

Ahora comprobemos que todo esté bien configurado colocando el siguiente código en el método main de nuestra clase Main:

Page 97: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 6: Relaciones / Muchos a Muchos

www.javatutoriales.com/2009/08/hibernate-parte-6-relaciones-muchos.html 19/23

public static void main(String[] args)

{

Estudiante estudiante1 = new Estudiante();

estudiante1.setNombre("estudiante1");

Materia materia1 = new Materia();

materia1.setNombre("materia1");

Materia materia2 = new Materia();

materia2.setNombre("materia2");

Materia materia3 = new Materia();

materia3.setNombre("materia3");

materia1.addEstudiante(estudiante1);

materia2.addEstudiante(estudiante1);

materia3.addEstudiante(estudiante1);

Estudiante estudiante2 = new Estudiante();

estudiante2.setNombre("estudiante2");

Materia materia4 = new Materia();

materia4.setNombre("materia4");

Materia materia5 = new Materia();

materia5.setNombre("materia5");

Materia materia6 = new Materia();

materia6.setNombre("materia6");

materia4.addEstudiante(estudiante2);

materia5.addEstudiante(estudiante2);

materia6.addEstudiante(estudiante2);

Session sesion = HibernateUtil.getSessionFactory().openSession();

sesion.beginTransaction();

sesion.save(estudiante1);

sesion.save(estudiante2);

sesion.getTransaction().commit();

sesion.close();

sesion = HibernateUtil.getSessionFactory().openSession();

sesion.beginTransaction();

sesion.delete(estudiante1);

sesion.getTransaction().commit();

sesion.close();

}

El cual hace lo mismo que en el caso anterior: se crean 2 Estudiantes y 6 Materias, relacionando 3 Materias con cada

Estudiante. Después se almacenan los Estudiantes, y sus Materias en cascada, y finalmente se elimina el primer Estudiante.

Por lo que solo deben quedar en la base el “estudiante2” y las materias 4 a 6:

Como podemos ver todo ha salido bien - (¿Notaron que cuando usamos anotaciones, en la tabla de unión no se genera una columna de

orden?), por lo que podemos dar por terminado este tutorial, y lo que respecta a las relaciones con hibérnate - .

En el siguiente post mostraré cómo hacer consultas con Hibernate usando un lenguaje especial de consultas llamado HQL (Hibernate

Query Language) para ver cómo podemos recuperar las entidades con las distintas relaciones que hemos visto.

Si tienen alguna duda, comentario o sugerencia no duden en escribir un comentario.

Saludos.

Descarga los archivos de este tutorial desde aquí:

Hibernate Relaciones Muchos a Muchos con archivos de Mapeo

Hibernate Relaciones Muchos a Muchos con Anotaciones

Entradas Relacionadas:

Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

Page 98: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 7: HQL Primera Parte

www.javatutoriales.com/2009/09/hibernate-parte-7-hql-primera-parte.html 1/25

Blog dedicado a temas de programación actuales usando el lenguaje de programación Java y las últimas versiones de sus APIs y Herramientas.

Tutoriales de Programacion Java

Página principal Página de Concursos Tutoriales UPAO 2010 Presentaciones Capacitación

LUNES, 21 DE SEPTIEMBRE DE 2009

Hibernate - Parte 7: HQL Primera Parte

Hibernate proporciona un lenguaje de consultas muy poderoso llamado "Hibernate Query Language" (HQL).

HQL es muy parecido al SQL estándar, con la diferencia de que es completamente orientado a objetos (usamos nombres de clases y

sus atributos en lugar de nombres de tablas y columnas), por lo que podemos usar cosas como herencia, polimorfismo y asociaciones.

Nosotros escribimos las consultas en HQL y Hibernate se encarga de convertirlas al SQL usado por la base de datos con la que estemos

trabajando y ejecutarla para realizar la operación indicada.

En este tutorial veremos cómo usar este lenguaje para hacer algunas consultas típicas dentro de nuestras aplicaciones para poder

recuperar objetos simples, y arboles de objetos.

Lo primero que hay que saber sobre HQL es que es case-insensitive, o sea que sus sentencias pueden escribirse en mayúsculas y

minúsculas. Por lo tanto "SeLeCt", "seleCT", "select", y "SELECT" se entienden como la misma cosa.

Lo único con lo que debemos tener cuidado es con los nombres de las clases que estamos recuperando y con sus propiedades, ahí si se

distinguen mayúsculas y minúsculas. O sea, en este caso "pruebas.hibernate.Usuario" NO ES LO MISMO que

"PrueBAs.HibernatE.UsuArio".

En este tutorial colocaré las clausulas HQL (SELECT, WHERE, ORDER BY, etc.) en mayúsculas.

Comencemos con el tutorial creando un nuevo proyecto en NetBeans.

Lo primero que haremos es crear un proyecto en NetBeans (menú "File -> New Project... -> Java -> Java

Application"). Le damos un nombre y una ubicación al proyecto y nos aseguramos de que las opciones "Create Main Class" y

"Set as Main Project" estén habilitadas. Presionamos el botón "Finish" y veremos aparecer nuestra clase "Main" en el editor.

En el tutorial usaremos anotaciones en vez de archivos de mapeo para que sea un poco más claro, así que agregamos las bibliotecas de

"Hibernate" y "HibernateAnotaciones", que creamos en el primer y segundo tutoriales. Hacemos clic derecho en el nodo

"Libraries" del proyecto y en el menú contextual que se abre seleccionamos la opción "Add Library...":

En la ventana que se abre seleccionamos las bibliotecas "Hibernate" y “HibernateAnotaciones”:

DONACIONES

JAVA TUTORIALES EN FACEBOOK

Java Tutoriales

Me gusta

A 7895 personas les gusta

Plug-in social de Facebook

Java Tutoriales

Amigo programador, tú que

estás todo el día picando

código, escucha estas

recomendaciones sólo

hechas para ti..... ¿qué es

esto?... Gracias Jorge

Rubira....

No olviden poner la

sentencia where al hacer

un borraro en la base de

datos..

http://www.youtube.com/

watch?v=i_cVJgIz_Cs

Compartir 5 Más Siguiente blog»

Page 99: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 7: HQL Primera Parte

www.javatutoriales.com/2009/09/hibernate-parte-7-hql-primera-parte.html 2/25

Presionamos el botón "Add Library" para que la biblioteca se agregue a nuestro proyecto. Aprovechamos también para agregar el

conector de MySQL. Debemos tener los siguientes archivos en nuestro proyecto:

Ahora creamos un paquete con el nombre “modelo” que contendrá las clases entidad. Hacemos clic derecho en el nodo del paquete que

se creó al generar el proyecto, en el menú contextual que se abre seleccionamos la opción "New -> Java Package..." y creamos el

paquete.

Que debe quedar así:

Ahora crearemos el archivo de configuración, el cual será muy parecido al del segundo tutorial. Recuerden que este archivo debe llamarse

"hibernate.cfg.xml" y debe encontrarse en la raíz del classpath del proyecto (el paquete default)

Participar en este sitioGoogle Friend Connect

Miembros (173) Más »

¿Ya eres miembro? Iniciar sesión

SEGUIDORES

► 2012 (2)

► 2011 (11)

► 2010 (10)

▼ 2009 (22)

▼ septiembre (2)

Hibernate - Parte 7: HQL Primera Parte

Día del programados

► agosto (2)

► julio (1)

► junio (3)

► mayo (2)

► abril (5)

► marzo (2)

► febrero (2)

► enero (3)

ARCHIVO DEL BLOG

Page 100: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 7: HQL Primera Parte

www.javatutoriales.com/2009/09/hibernate-parte-7-hql-primera-parte.html 3/25

<!DOCTYPE hibernate-configuration PUBLIC

"-//Hibernate/Hibernate Configuration DTD 3.0//EN"

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

<hibernate-configuration>

<session-factory>

<!-- parametros para la conexion a la base de datos -->

<property name="connection.driver_class">com.mysql.jdbc.Driver</property>

<property name="connection.url">jdbc:mysql://localhost:3306/hibernatehql</property>

<property name="connection.username">root</property>

<property name="connection.password">123</property>

<!-- Configuracion del pool interno -->

<property name="connection.pool_size">1</property>

<!-- Dialecto de la base de datos -->

<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>

<!-- Otras propiedades importantes -->

<property name="show_sql">true</property>

<property name="hbm2ddl.auto">none</property>

<!-- Aqui iran las clases entidades -->

</session-factory>

</hibernate-configuration>

Por ahora dejaremos el archivo de configuración como está mientras creamos nuestras clases entidades:

Para este ejemplo crearemos tres clases entidad: "Usuario", "Direccion" y "Permiso". La relación que existirá sobre estas tres

clases es una relación uno a uno entre "Usuario" y "Dirección" y una relación uno a muchos de "Usuario" con "Permiso"; o

sea, un "Usuario" puede tener una "Dirección" y un "Usuario" puede tener muchos "Permisos".

Aunque estas relaciones son simples, nos ayudaran a mostrar las consultas que realizamos con mayor frecuencia en las aplicaciones.

Primero, crearemos la clase “Permiso”. Creamos una nueva clase en el paquete “modelo” con el siguiente contenido:

NUESTRAS ENTIDADESAlex

Programador Java con algunos años de

experiencia en múltiples poyectos y con

múltiples APIs y herramientas deseoso de

compartir experiencias con el resto de

programadores.

Ver todo mi perfil

DATOS PERSONALES

Page 101: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 7: HQL Primera Parte

www.javatutoriales.com/2009/09/hibernate-parte-7-hql-primera-parte.html 4/25

@Entity

@Table(name="permisos")

public class Permiso implements Serializable

{

public enum Estatus {PENDIENTE, ACTIVO, INACTIVO};

@Id

@GeneratedValue(strategy=GenerationType.IDENTITY)

private long id;

private String nombre;

private Estatus estatus = Estatus.PENDIENTE;

public Permiso()

{

}

public Estatus getEstatus()

{

return estatus;

}

public void setEstatus(Estatus estatus)

{

this.estatus = estatus;

}

public long getId()

{

return id;

}

public void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

}

La entidad “Permiso” es muy sencilla pero nos permitirá observar el comportamiento de las consultas HQL. Usa las anotaciones que

expliqué en el segundo tutorial.

Noten que agregué una enum para restringir los valores de los estatus solo para ejemplificar en uso de las mismas dentro de las

entidades.

La siguiente clase que crearemos es “Dirección”, creamos esta clase en el paquete “modelo”. Este clase mantendrá una relación uno

a uno bidireccional con "Usuario". La clase queda de esta forma:

Page 102: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 7: HQL Primera Parte

www.javatutoriales.com/2009/09/hibernate-parte-7-hql-primera-parte.html 5/25

@Entity

@Table(name="direcciones")

public class Direccion implements Serializable

{

@Id

@GeneratedValue(strategy=GenerationType.IDENTITY)

private long id;

private String calle;

private String codigoPostal;

@OneToOne(mappedBy="direccion")

private Usuario usuario;

public Direccion()

{

}

public String getCalle()

{

return calle;

}

public void setCalle(String calle)

{

this.calle = calle;

}

public String getCodigoPostal()

{

return codigoPostal;

}

public void setCodigoPostal(String codigoPostal)

{

this.codigoPostal = codigoPostal;

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public Usuario getUsuario()

{

return usuario;

}

public void setUsuario(Usuario usuario)

{

this.usuario = usuario;

}

}

Como dije antes: la clase “Direccion” mantiene una relación uno a uno bidireccional con la clase “Usuario” y, como lo indica el

elemento “mappedBy” de la anotación @OneToOne, la entidad “Usuario” es la dueña de la relación.

Ahora veamos la clase “Usuario”.

Creamos la clase “Usuario” en el paquete “modelo”. Esta clase mantendrá la relación uno a muchos con “Permiso” y la relación uno

a uno con “Direccion”; y queda de la siguiente forma:

Page 103: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 7: HQL Primera Parte

www.javatutoriales.com/2009/09/hibernate-parte-7-hql-primera-parte.html 6/25

@Entity

@Table(name="usuarios")

public class Usuario implements Serializable

{

@Id

@GeneratedValue(strategy=GenerationType.IDENTITY)

private long id;

private String nombre;

private String username;

private String password;

@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY)

private List<Permiso> permisos = new ArrayList<Permiso>();

@OneToOne(cascade=CascadeType.ALL, fetch=FetchType.EAGER)

private Direccion direccion;

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

public String getPassword()

{

return password;

}

public void setPassword(String password)

{

this.password = password;

}

public List<Permiso> getPermisos()

{

return permisos;

}

public void setPermisos(List<Permiso> permisos)

{

this.permisos = permisos;

}

public String getUsername()

{

return username;

}

public void setUsername(String username)

{

this.username = username;

}

public Direccion getDireccion()

{

return direccion;

}

public void setDireccion(Direccion direccion)

{

this.direccion = direccion;

direccion.setUsuario(this);

}

}

“Usuario” es una entidad muy similar a las que hemos visto a lo largo de estos tutoriales, así que no debería haber problemas para

entenderla. Lo único interesante sobre son las relaciones que mantiene con las otras entidades.

Page 104: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 7: HQL Primera Parte

www.javatutoriales.com/2009/09/hibernate-parte-7-hql-primera-parte.html 7/25

Ahora que tenemos las entidades no olvidemos que debemos indicarlas en el archivo “hibernate.cfg.xml”:

<mapping class="hql.modelo.Permiso" />

<mapping class="hql.modelo.Usuario" />

<mapping class="hql.modelo.Direccion" />

Ahora que tenemos todo configurado comencemos propiamente con el tutorial.

Para los ejercicios usaremos una base de datos llamada "hibernatehql", en MySQL.

Lo siguiente que debemos saber sobre las consultas en HQL es que, como mencione antes, trabajamos usando los nombres de las

clases; esto quiere decir que si tenemos una clase entidad llamada “Usuario” en el paquete “hql.modelo”, cuyos registros se guardan

en una tabla “usuarios”, NO colocamos el nombre de la tabla en las consultas, sino el nombre de la clase. O sea en vez de hacer algo

asi:

SELECT u.col_username FROM usuarios u…

Haríamos

SELECT u.username FROM hql.modelo.Usuario u…

O también podemos hacer:

SELECT u.username FROM Usuario u…

De la misma forma si el valor del atributo “username” del usuario se guarda en la columna “col_username” hacemos referencia a este

valor con el nombre del atributo: “username” y no con el nombre de la columna.

Ahora veremos las clausulas con las que podemos formar una sentencia:

Estas son algunas de las características más importantes que nos proporciona HQL:

Soporte completo para operaciones relacionales: HQL permite representar consultas SQL en forma de objetos. HQL usa

clases y atributos o propiedades en vez de tablas y columnas.

Regresa sus resultados en forma de objetos: Las consultas realizadas usando HQL regresan los resultados de las mismas

en la forma de objetos o listas de objetos, que son más fáciles de usar, ya que eliminan la necesidad de crear un objeto y

llenarlo con los datos obtenidos de un ResultSet (como hacemos normalmente cuando trabajamos con JDBC).

Consultas Polimórficas: Podemos declarar el resultado usando el tipo de la superclase y Hibernate se encargara de crear los

objetos adecuados de las subclases correctas de forma automática.

Fácil de Aprender: Es muy similar a SQL estándar, así que si has trabajado con SQL, HQL te resultará muy fácil.

Soporte para características avanzadas: HQL contiene muchas características avanzadas que son muy útiles y que no

siempre están presentes en todas las bases de datos, o no es fácil usarlas, como paginación, fetch joins con perfiles

dinámicos, inner y outer joins, etc. Además soporta proyecciones, funciones de agregación (max, avg), y agrupamientos,

ordenamientos, y subconsultas.

Independiente del manejador de base de datos: Las consultas escritas en HQL son independientes de la base de datos

(siempre que la base de dats soporte la característica que estamos intentando utilizar - ).

Bueno, ahora que sabemos un poco de HQL veamos cómo usarlo. Para ver los resultados arrojados por las consultas no necesitaremos

crear una aplicación, ya que NetBeans nos permite ejecutar estas directamente desde un editor especial. Pero para usarlo debemos

configurar algunas otras cosas, que explicaré a continuación.

Lo primero que debemos hacer es crear nuestra base de datos en MySQL con las tablas y datos de prueba necesarios. Creamos una

base de datos llamada “hibernatehql” con cuatro tablas: “direcciones”, “permisos”, “usuarios”, y “usuarios_permisos”.

CARACTERÍSTICAS DE HQL

CONFIGURANDO EL NETBEANS PARA LOS EJEMPLOS

Page 105: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 7: HQL Primera Parte

www.javatutoriales.com/2009/09/hibernate-parte-7-hql-primera-parte.html 8/25

Esta última tabla mantendrá las relaciones entre usuarios y permisos. Pueden usar el siguiente script para crear y poblar la base de

datos, o si lo desean pueden descargarlo desde aquí:

CREATE DATABASE IF NOT EXISTS hibernatehql;

USE hibernatehql;

/*Estructura de la tabla direcciones */

DROP TABLE IF EXISTS direcciones;

CREATE TABLE direcciones (

id BIGINT(20) NOT NULL AUTO_INCREMENT,

calle VARCHAR(255) DEFAULT NULL,

codigoPostal VARCHAR(255) DEFAULT NULL,

PRIMARY KEY (id)

) ENGINE=INNODB DEFAULT CHARSET=latin1;

/*Datos de la tabla direcciones */

insert into direcciones (id ,calle ,codigoPostal) values (1,'Calle1','12345'),(2,'Calle2','5

4321');

/*Estructura de la tabla permisos */

DROP TABLE IF EXISTS permisos;

CREATE TABLE permisos (

id bigint(20) NOT NULL AUTO_INCREMENT,

estatus int(11) DEFAULT NULL,

nombre varchar(255) DEFAULT NULL,

PRIMARY KEY (id)

) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1;

/*Datos de la tabla permisos */

insert into permisos (id ,estatus ,nombre) values (1,0,'Lectura Archivos'),(2,0,'Creacion Ar

chivos'),(3,1,'Eliminacion Archivos'),(4,1,'Moficacion Archivos'),(5,2,'Sin Permisos');

/*Estructura de la tabla usuarios */

DROP TABLE IF EXISTS usuarios;

CREATE TABLE usuarios (

id bigint(20) NOT NULL AUTO_INCREMENT,

nombre varchar(255) DEFAULT NULL,

password varchar(255) DEFAULT NULL,

username varchar(255) DEFAULT NULL,

direccion_id BIGINT(20) DEFAULT NULL,

PRIMARY KEY (id),

KEY FK_DIRECCIONES (direccion_id),

CONSTRAINT FK_DIRECCIONES FOREIGN KEY (direccion_id) REFERENCES direcciones (id)

) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;

/*Datos de la tabla usuarios */

insert into usuarios (id ,nombre ,password ,username ,direccion_id) values (1,'Usuario 1',

'abcdefg','usr001',1),(2,'Usuario1','hijklm','usr456',2),(3,'Usuario3','alex','alex',null);

/*Estructura de la tabla usuarios_permisos */

DROP TABLE IF EXISTS usuarios_permisos;

CREATE TABLE usuarios_permisos (

usuarios_id bigint(20) NOT NULL,

permisos_id bigint(20) NOT NULL,

UNIQUE KEY permisos_id (permisos_id),

KEY FK_USUARIOS (usuarios_id),

KEY FK_PERMISOS (permisos_id),

CONSTRAINT FK_PERMISOS FOREIGN KEY (permisos_id) REFERENCES permisos (id),

CONSTRAINT FK_USUARIOS FOREIGN KEY (usuarios_id) REFERENCES usuarios (id)

) ENGINE=InnoDB DEFAULT CHARSET=latin1;

/*Datos de la tabla usuarios_permisos */

INSERT INTO usuarios_permisos (usuarios_id ,permisos_id) VALUES (1,1),(1,2),(2,3),(2,4),(3,5)

;

Ahora vayamos al NetBeans.

Anteriormente, cuando instalamos plugins en el NetBeans y lo configuramos para usar el tomcat, usamos un panel llamado “Services”,

el cual nos permite agregar servicos y herramientas externas (servidores web y de aplicaciones, bases de datos, servicios web, etc.) para

Page 106: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 7: HQL Primera Parte

www.javatutoriales.com/2009/09/hibernate-parte-7-hql-primera-parte.html 9/25

tener acceso a ellos dentro de NetBeans.

En esta ocasión agregaremos un servicio que nos permitirá tener una conexión a nuestra base de datos de prueba.

En el panel “Services” hacemos clic derecho sobre el nodo “Databases” y en el menú contextual que se abre seleccionamos la opción

“New Connection…”

Con lo que se abrirá la ventana “New Database Connection”. En esta ventana podemos escribir directamente la URL de la conexión

(jdbc:mysql://…), o escribir algunos parámetros y dejar que la URL se genere automáticamente. Nosotros haremos este último.

Lo primero que hacemos es seleccionar, en el campo “Name” la opción “MySQL (Connector/J driver)”. El resto de los datos se

obtienen de su conexión a su servidor de base de datos.

Usen los datos propios de su servidor para conectarse. Al final, con los datos de mi conexión, la ventana queda así:

Les recomiendo que habiliten la opción “Remember password” y “Show JDBC URL”. Hacemos clic en el botón “OK” y veremos que en

el panel “Services”, en el nodo “Databases” aparecerá la conexión a nuestra base de datos. En caso de que haya un error aparecerá

el mensaje correspondiente para que podamos corregirlo.

Page 107: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 7: HQL Primera Parte

www.javatutoriales.com/2009/09/hibernate-parte-7-hql-primera-parte.html 10/25

Gracias a esta configuración ahora podremos ejecutar sentencias de SQL directamente en NetBeans:

Pero, lo más importante, es que también podemos ejecutar sentencias HQL desde el NetBeans, sin necesidad de crear una aplicación:

Será esta forma en la que ejecutaremos las clausulas durante este tutorial.

Veremos las clausulas más comunes de este lenguaje y haremos ejemplos usando nuestras entidades.

La clausula más simple que existe en Hibernate es “FROM”. Esta clausula regresa todas las instancias de la clase indicada que se

encuentran en la base de datos, es como si hiciéramos un “SELECT *” en MySQL. Por ejemplo, para recuperar todos los usuarios de la

base de datos ejecutamos la consulta de esta forma:

FROM hql.modelo.Usuario

Normalmente no necesitamos el “fully qualified name” de la clase, así que esta misma sentencia podríamos escribirla de esta forma:

FROM Usuario

Al ejecutar esta sentencia en NetBeans, con los datos de la base de pruebas que creamos, obtendremos el siguiente resultado:

LA CLAUSULA FROM

Page 108: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 7: HQL Primera Parte

www.javatutoriales.com/2009/09/hibernate-parte-7-hql-primera-parte.html 11/25

Dentro de esta clausula FROM podemos colocar el nombre de más de una clase, lo que resultará en un producto cartesiano entre las

entidades que coloquemos. Podemos colocar el nombre de dos de nuestras entidades en la clausula, de esta forma:

FROM Usuario, Permiso

Resultando en la siguiente salida:

Aquí se muestra un todos contra todos (producto cartesiano) ya que no colocamos ninguna restricción en cuanto a las condiciones que

deben cumplir los objetos.

Cuando tenemos que referirnos a nuestras clases en otras partes de la consulta, como en las restricciones, debemos asignarle un alias y

hacer referencia a la clase a través de este alias. No podemos usar directamente el nombre del objeto ya que resultaría en una excepción.

Por ejemplo, continuando con la consulta anterior, si quisiéramos hacer referencia al “id” del “Usuario”, si lo hacemos de esta forma:

FROM Usuario, Permiso WHERE Usuario.id = 1

Obtendríamos la siguiente excepción:

En vez de esto debemos asignarle un alias a la clase entidad "Usuario" y hacer referencia a los atributos de esta entidad usando el

alias, de esta forma:

Page 109: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 7: HQL Primera Parte

www.javatutoriales.com/2009/09/hibernate-parte-7-hql-primera-parte.html 12/25

FROM Usuario as u, Permiso WHERE u.id = 1

O así:

FROM Usuario u, Permiso WHERE u.id = 1

Resultando en la salida esperada:

HQL también permite los siguientes cuatro tipos de joins entre entidades:

Inner Join

Left Outer Join

Right Outer Join

Full Join (siempre que la base de datos que estemos usando los soporte)

Además también podemos asignar alias a las entidades asociadas o a los elementos de una colección de valores usando un join, por

ejemplo:

FROM Usuario u inner join u.permisos as p

Con su correspondiente salida

También podemos proporcionar condiciones extra al join con la palabra reservada WITH:

FROM Usuario u inner join u.permisos as p WITH p.estatus = 1

ASOCIACIONES Y JOINS

Page 110: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 7: HQL Primera Parte

www.javatutoriales.com/2009/09/hibernate-parte-7-hql-primera-parte.html 13/25

La parte que voy a explicar es muy importante y nos resultará muy útil cuando trabajemos con colecciones de objetos. ¿Recuerdan que

cuando vimos las relaciones uno a muchos, dije que en la relación podemos indicar la forma de fetch o recuperación de los elementos

de la colección? ¿Y que estos tipos solo son 2: EAGER y LAZY?

Cuando tenemos una colección con tipo de fetch EAGER no hay problema, al recuperar la entidad recuperaremos también todos los

objetos de la colección. Pero ¿qué ocurre cuando el tipo de fetch es LAZY? En ese caso necesitamos recuperar los elementos de la

colección de otra forma.

Pues bien, esto puede hacerse usando un tipo especial de join llamado "fetch" join, el cual nos permite inicializar los elementos de

estas asociaciones o colecciones. Este fetch join “sobreescribe” la declaración de fetch LAZY de la colección (solo para la consulta que

se está realizando).

Por ejemplo, nosotros tenemos en nuestra entidad "Usuario" declarada la lista de permisos de la siguiente forma:

@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY)

private List<Permiso> permisos = new ArrayList<Permiso>();

Esto quiere decir que cuando recuperemos, ya dentro de una aplicación, al Usuario, sus permisos no serán recuperados e

inicializados en ese momento. Esto es útil si estamos, por ejemplo, mostrando una lista de usuarios y no nos interesa mostrar nada

relacionado con sus permisos en ese momento. Pero ¿qué ocurre cuando el usuario ingresa a la aplicación y nos interesa validar los

permisos que tiene? En ese caso si debemos recuperar la lista de permisos. Pues bien, es para esto que nos sirve el fetch join, y se usa

de la siguiente forma:

FROM Usuario u inner join fetch u.permisos

Recuerden que aunque la instrucción se llama fetch join la usamos al revés ("join fetch"). Y con esto se recuperarán los permisos del

usuario al mismo tiempo que el usuario. En el editor de HQL de NetBeans esto no se logra apreciar bien, pero créanme, cuando estén

realizando una aplicación esto puede salvarnos la vida - .

HQL soporta dos sintaxis para los joins: implícita y explícita.

En los ejemplos anteriores hemos estado usando la forma explícita, o sea, que colocamos la palabra join dentro de la clausula. Esta

es la forma recomendada ya que es más clara, sin embargo si queremos ahorrar un poco de espacio podemos usar la forma implícita

de esta manera:

SINTAXIS DE LOS JOINS

Page 111: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 7: HQL Primera Parte

www.javatutoriales.com/2009/09/hibernate-parte-7-hql-primera-parte.html 14/25

Como vemos, ocurre un join implícito entre "Usuario" y "Dirección", usando el atributo dirección de la clase Usuario. Lo malo de

este tipo de joins es que solo puede hacerse cuando tenemos una relación uno a uno o muchos a uno, o sea, no

funciona cuando la propiedad es una colección:

En HQL existen dos formas de referirnos a la propiedad que sirve como identificador de la entidad:

La propiedad especial “id” (así con minúsculas). No importa si la clase entidad no tiene una propiedad llamada id.

Si la entidad define una propiedad identificador, podemos referirnos a ella a través del nombre de dicha propiedad (en el caso

de estos ejemplos la propiedad identificadora es llamada id, así que no me será posible ejemplificar esto claramente).

Importante: Esto quiere decir que si nuestra entidad tiene un atributo llamado “id”, pero esto no es el identificador de la entidad, no

habrá forma de hacer referencia a él.

La clausula SELECT indica cuáles objetos y propiedades se regresarán en el conjunto de resultados de la consulta (así es, podemos

regresar solamente algunas propiedades de los objetos y no todo el objeto, pero esto lo veremos más adelante).

Supongamos que tenemos la siguiente consulta:

SELECT dir FROM Usuario as u inner join u.direccion as dir

Esta consulta regresará la direccion de los usuarios. Podemos expresar esta misma consulta de una forma más compacta haciendo

uso de los joins implícitos:

SELECT u.direccion FROM Usuario as u

Como dije hace un momento: podemos recuperar solo algunos atributos o valores del objeto que estemos regresando. Por ejemplo, la

siguiente consulta regresa solo el nombre del usuario:

SELECT u.nombre FROM Usuario as u

En la ventana de ejecución del HQL en el NetBeans, vemos el resultado de esta forma:

PREGUNTANDO POR EL IDENTIFICADOR

LA CLAUSULA SELECT

Page 112: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 7: HQL Primera Parte

www.javatutoriales.com/2009/09/hibernate-parte-7-hql-primera-parte.html 15/25

Intentaré dar una explicación al porqué se ven de esta forma los datos. Si me equivoco en la explicación por favor corríjanme.

Cuando recuperamos solo un atributo de un objeto, recuperamos una lista de objetos del tipo del atributo que queremos recuperar (si es

String recuperamos Strings, si es int recuperamos ints, etc.).

Sin embargo, si recuperamos más de un atributo de un objeto, Hibernate NO nos regresa una lista del tipo de objeto (en este caso

"Usuario"), si no que nos regresa una lista de arreglos de objetos, o sea, una lista en la que cada uno de los elementos es un arreglo

de objetos. Cada uno de los objetos del arreglo representa uno de los valores recuperados. Como nosotros solo estamos recuperando

el valor de una propiedad (el nombre del usuario) vemos que en el NetBeans solo tenemos un valor en cada fila (no tengo idea de porqué

muestra varias filas). Como Hibernate no sabe (o no le importa) el tipo del objeto que es el valor del atributo cuando lo regresa, solo que es

un objeto, nos regresa un arreglo de bytes (finalmente todo objeto es un arreglo de bytes) y es esto lo que vemos en los valores

regresados (cuando tenemos que un valor empieza solo con un corchete que abre ([) significa que el valor es un arreglo.

Como supongo que algunos de ustedes no me creerán que esto funciona, tendré que demostrarlo en código _ .

Como siempre, usaremos la clase HibernateUtil desarrollada antes, la del segundo tutorial, ya que estamos trabajando con

anotaciones.

Como vamos a hacer varios pequeños ejemplos, crearemos un método para cada uno y lo invocaremos en el constructor de la clase

Main. Después iremos comentando y des-comentando las invocaciones a los métodos para ver su funcionamiento individual.

Además crearemos un atributo de instancia en nuestra clase Main que nos ayudará a manejar las conexiones en todos los métodos.

Esta variable será de tipo “org.hibernate.Session”, la declararemos de esta forma:

private Session sesion;

Y dos métodos de utilidad. El primero creará una nueva sesión a la base de datos e iniciar una transacción. El segundo método terminará

la transacción y cerrará la conexión.

Este es el método que inicia la sesión:

private void inciaOperacion()

{

SessionFactory sessionFactory = new AnnotationConfiguration().configure().buildSessionFact

ory();

sesion = sessionFactory.openSession();

sesion.getTransaction().begin();

}

Y este es el método que la termina:

private void terminaOperacion()

{

sesion.getTransaction().commit();

sesion.close();

}

Ahora crearemos el método que recuperará solo el nombre del usuario haciendo uso de la consulta anterior.

Hasta ahora no habíamos realizado consultas con HQL, así que tendré que explicar cómo usarlas en código.

Con Hibernate normalmente haremos consultas el HQL (también podemos hacer consultas en el SQL de la base de datos que estemos

usando, pero no hablaré de eso). Para ejecutar una consulta en HQL necesitamos un objeto de tipo “org.hibernate.Query”. Query

es una interface, por lo que necesitamos una clase que la implemente para usarla.

Afortunadamente el objeto “org.hibernate.Session” que estamos usando (sesion) tiene un método que nos permite obtener un

Page 113: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 7: HQL Primera Parte

www.javatutoriales.com/2009/09/hibernate-parte-7-hql-primera-parte.html 16/25

objeto de este tipo:

Query query = sesion.createQuery("Aquí ira nuestra consulta");

En el lugar que he indicado con la cadena “Aquí ira nuestra consulta” es donde colocaremos el HQL que estemos usando.

Estas consultas pueden ser básicamente de tres tipos:

Búsquedas de listas de resultados (este es el tipo de consulta que hemos estado haciendo hasta el momento)

Búsquedas de un objeto único

Actualizaciones de datos (actualizaciones o eliminaciones)

Cada una de estas operaciones la realizamos con un método específico de la interface Query:

query.list(); //Para las listas de resultados

query.uniqueResult(); //Para los objetos unicos

query.executeUpdate(); //Para las actualizaciones de datos

Nosotros usaremos el primer método (list()) en los siguientes ejemplos.

Bien, ahora si veamos el método.

Crear este método será muy sencillo. Básicamente lo que haremos es crear un nuevo método, colocar los dos métodos auxiliares que

creamos anteriormente, obtener un objeto query, colocarle la consulta anterior, e invocar a su método list.

El método, para no hacer la explicación más aburrida, queda de esta forma:

private void obtenNombres()

{

iniciaOperacion();

Query query = sesion.createQuery("SELECT u.nombre FROM Usuario as u");

List<String> listaResultados = query.list();

for (int i = 0; i < listaResultados.size(); i++)

{

System.out.println("Nombre " + i + ": " + listaResultados.get(i));

}

terminaOperacion();

}

Ahora, solo nos queda colocar la siguiente llamada en el método main:

new Main();

y esto en el constructor de Main:

obtenNombres();

La clase Main debe quedar así:

Page 114: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 7: HQL Primera Parte

www.javatutoriales.com/2009/09/hibernate-parte-7-hql-primera-parte.html 17/25

public class Main

{

private Session sesion;

public Main()

{

obtenNombres();

}

public static void main(String[] args)

{

new Main();

}

private void iniciaOperacion()

{

SessionFactory sessionFactory = new AnnotationConfiguration().configure().buildSessionFact

ory();

sesion = sessionFactory.openSession();

sesion.getTransaction().begin();

}

private void terminaOperacion()

{

sesion.getTransaction().commit();

sesion.close();

}

private void obtenNombres()

{

iniciaOperacion();

Query query = sesion.createQuery("SELECT u.nombre FROM Usuario as u");

List<String> listaResultados = query.list();

for (int i = 0; i < listaResultados.size(); i++)

{

System.out.println("Nombre " + i + ": " + listaResultados.get(i));

}

terminaOperacion();

}

}

Ahora, al ejecutar la aplicación vemos la siguiente salida:

Como vemos, obtuvimos solamente los nombres, justo como esperábamos - .

La clausula SELECT también nos permite recuperar múltiples objetos y/o propiedades de objetos. Es este caso ocurre lo que expliqué

anteriormente: recuperamos una lista de arreglos de objetos. Para comprobar esto, crearemos una consulta que recuperará el

nombre y el password de un usuario.

Coloquemos la siguiente clausula en el ejecutor de HQL de NetBeans:

SELECT u.nombre, u.password FROM Usuario as u

Con lo que obtenemos la siguiente salida:

Page 115: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 7: HQL Primera Parte

www.javatutoriales.com/2009/09/hibernate-parte-7-hql-primera-parte.html 18/25

Como esta salida también es poco clara, crearemos un nuevo método para ver que, efectivamente, estemos recuperando una lista de

arreglos de objetos con los valores indicados.

Como en este caso estamos recuperando dos valores (nombre y password), el primer elemento del arreglo (el elemento 0) será el

nombre, y el segundo (el elemento 1) será el password:

private void obtenNombresYPasswords()

{

iniciaOperacion();

Query query = sesion.createQuery("SELECT u.nombre, u.password FROM Usuario as u ");

List<Object[]> listaResultados = query.list();

for (int i = 0; i < listaResultados.size(); i++)

{

System.out.println("Nombre " + i + ": " + listaResultados.get(i)[0] + ", password: " +

listaResultados.get(i)[1]);

}

terminaOperacion();

}

Cuando ejecutamos este código obtenemos la siguiente salida:

Como vemos, al hacer

listaResultados.get(i)[0]

Obtenemos el nombre que se encuentra en el primer elemento del arreglo, y al hacer

listaResultados.get(i)[1]

Obtenemos el password que se encuentra en el segundo elemento del arreglo, justo como habíamos predicho - .

Aunque esto que acabamos de ver nos resultará bastante útil cuando no necesitemos todo el objeto (ya que nos ahorrará mucha

memoria) puede parecernos un poco raro trabajar con arreglos de objetos; más a los que estamos acostumbrados solo a las listas ( - !).

Para personas como yo, Hibernate nos permite recuperar estos valores dentro de una lista, en vez del arreglo de objetos, usando una

notación especial. A partir de aquí veremos algunas sintaxis extrañas, pero no se preocupen, que con el tiempo nos acostumbraremos a

verlas.

Page 116: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 7: HQL Primera Parte

www.javatutoriales.com/2009/09/hibernate-parte-7-hql-primera-parte.html 19/25

Para recuperar los mismos datos que en el ejemplo anterior, pero con una lista en vez de un arreglo de objetos, debemos escribir la

consulta de la siguiente forma:

SELECT new list(u.nombre, u.password) FROM Usuario as u

Como vemos la consulta es muy similar a la anterior, pero con ese extraño elemento “new list”. Coloquémonos en el NetBeans:

Como podemos ver el NetBeans tampoco nos ayuda mucho cuando trabajamos con este tipo de consultas u.u, así que tendremos que

crear nuevamente un método:

private void obtenNombresYPasswordsComoLista()

{

iniciaOperacion();

Query query = sesion.createQuery("SELECT new list(u.nombre, u.password) FROM Usuario as u ");

List<List> listaResultados = query.list();

for (int i = 0; i < listaResultados.size(); i++)

{

System.out.println("Nombre " + i + ": " + listaResultados.get(i).get(0) + ", password: " +

listaResultados.get(i).get(1));

}

terminaOperacion();

}

Con lo que obtenemos la siguiente salida:

Como podemos ver, obtuvimos el mismo resultado que en la ocasión anterior, pero ahora en vez de hacer esto:

listaResultados.get(i)[0]

listaResultados.get(i)[1]

Hacemos esto:

Page 117: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 7: HQL Primera Parte

www.javatutoriales.com/2009/09/hibernate-parte-7-hql-primera-parte.html 20/25

Publicado por Alex en 21:18

Reacciones: divertido (0) interesante (0) increible (0) no me gusta (0)

Etiquetas: hibernate, hql, java

listaResultados.get(i).get(0)

listaResultados.get(i).get(1)

Podemos incluso recuperar un objeto propio pasando los valores a su constructor o recuperar un Map. Pero eso escapa al propósito de

este tutorial.

Y como me he tardado más de lo esperado en escribir este tutorial, lo dejaré hasta aquí en este momento y lo terminaré en una segunda

parte. Falta ver un poco de la sentencia WHERE, funciones de agregación, y sub-consultas, así que espero sea un poco más corto.

No olviden dejar todas sus dudas y comentarios. También pongan lo que les gustaría ver en el siguiente tutorial.

Saludos.

Entradas Relacionadas:

Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

Parte 2: Persistiendo Objetos Simples usando Anotaciones (Metadatos)

Parte 3: Relaciones / Uno a uno

Parte 4: Relaciones / Uno a muchos

Parte 5: Relaciones / Muchos a uno

Parte 6: Relaciones / Muchos a Muchos

Parte 8: HQL Segunda Parte

Parte 9: Parámetros en HQL

Parte 10: Herencia

Parte 11: Interceptores y Eventos

+5 Recomendar esto en Google

33 comentarios:

erictwo2 7 de octubre de 2009 16:38

Que tal Alex. Te doy las gracias por esforzarte y ayudarnos a todos los que nos iniciamos en hibernate! Eh bajado un monton de

tutoriales pero ninguno tiene tu calidad y claridad. De verdad muchas Gracias! Lo que me gustaria ver mas adelante es si podes dar una

pasadita en como usar Hibernmate junto con Spring ya que por lo que eh visto es muy interesante, pero todavia no tengo claro ciertas

cosas! Eh logrado hacer andar un programa usando esta tecnologia con un par de relaciones medias complejas y la verdad que Spring

simplifica bastante ciertas cosas y me gustaria mucho ver tu explicacion! Desde ya muchas gracias por todo. Saludos Eric Hidalgo

Responder

Alex 7 de octubre de 2009 20:06

Hola Eric;

Muchas gracias por tus comentarios, hago lo mejor que puedo.

Me gustaría pronto poder escribir un tutorial de Spring ya que me gusta mucho; y como dices simplifica mucho las cosas.

Será cosa de que me haga un tiempo y lo escriba, tal vez terminando esta serie sobre Hibernate - .

Saludos

Responder

Jorge Luis 14 de octubre de 2009 16:06

Hola Amigo Gracias por tus tutoriales. Mi pregunta sale un poco del tema de HQl, lo que quiero saber es como creo reportes en ireport

con hibernate.

el ireport lo tengo como plugin en netbeans, mis clases son con anotaciones.

lo que hago es agrego el jar de mi aplicacion al classpach de ireport. Al realizar un nuevo reporte y creo la coneccion con hibernate y al

hacer el test obtengo como resultado que se realizo correctamente, pero para realizar mi consulta no carga ninguna de mis clases, y

cuando escribo la consulta directa osea escribo "From com.MegaSoft.endrago.Clases.Clientes" esa es la direccion de mi clase, obtengo

como resultado null. y no se como hacerlo, agradeceria mucho si me ayudaras con mi problema gracias.

Responder

Alex 14 de octubre de 2009 21:19

Hola Jorge;

Muchas gracias por tus comentarios, hago lo que puedo para que se encientan lo mejor posible.

Lamentablemente creo que no puedo ayudarte con tu pregunta ya que nunca he utilizado Hibernate junto con JasperReports como me

parece que tu lo estas usando, así que no se que es lo que pueda estar ocurriendo.

Trataré de hacer un ejemplo para darme una idea de cómo funcionan estas dos tecnologías juntas para ver si puedo ayudarte.

Page 118: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 8: HQL Segunda Parte

www.javatutoriales.com/2010/05/hibernate-parte-8-hql-segunda-parte.html 1/19

Blog dedicado a temas de programación actuales usando el lenguaje de programación Java y las últimas versiones de sus APIs y Herramientas.

Tutoriales de Programacion Java

Página principal Página de Concursos Tutoriales UPAO 2010 Presentaciones Capacitación

SÁBADO, 22 DE MAYO DE 2010

Hibernate - Parte 8: HQL Segunda Parte

En el tutorial anterior vimos una introducción al lenguaje de consultas de Hibernate, el Hibernate Query Language (HQL) y vimos

algunas de sus características.

También vimos como ejecutar consultas desde el editor del NetBeans, y desde código dentro de una aplicación de ejemplo.

En esta segunda parte veremos el resto de las clausulas que componen este lenguaje y seguiremos con ejemplos en código.

Lo último que vimos fue como recuperar una lista con solo algunos valores de un objeto y colocarlos en un objeto “java.util.List”

usando una sentencia SELECT con la sintaxis “SELECT NEW LIST”.

Esta no es la única forma en la que podemos recuperar valores de un objeto dentro de una colección. Además tenemos la opción de

recuperar una lista de objetos tipo “java.util.Map”, en donde las llaves son las columnas que queremos consultar, y los valores

son los valores recuperados de la base de datos.

Por ejemplo, si queremos recuperar el "identificador", "nombre", y "password" de los usuarios, dejando estos datos en un

“java.util.Map” usamos la siguiente sentencia:

SELECT new map(u.id AS identificador, u.nombre AS nombre, u.password AS pass) FROM Usuario as u

En donde vemos que ahora usamos la clausula SELECT seguido de la sentencia NEW MAP, y los valores que deseamos recuperar dentro

del mapa. Dentro de esta clausula colocamos como alias lo que serán las llaves, y como valores las columnas que queremos

recuperar. Así tendremos como llaves “identificador”, “nombre”, y “pass”, y como valores el “id”, “nombre”, y “password” del

Usuario.

Creamos un método para comprobar la salida de esta consulta:

private void obtenNombresYPasswordsComoMapa()

{

iniciaOperacion();

Query query = sesion.createQuery("SELECT new map(u.id as identificador, u.nombre as nombre, u.

password as pass) FROM Usuario as u ");

List<Map> listaResultados = query.list();

for (int i = 0; i < listaResultados.size(); i++)

{

Map mapa = listaResultados.get(i);

System.out.println("Datos del mapa " + i);

Set llaves = mapa.keySet();

for(Iterator<String> it = llaves.iterator(); it.hasNext();)

{

String llaveActual = it.next();

System.out.println("\tLlave: " + llaveActual + ", valor: " + mapa.get(llaveActual));

}

}

terminaOperacion();

}

Cuando ejecutamos este método obtenemos la siguiente salida:

DONACIONES

JAVA TUTORIALES EN FACEBOOK

Java Tutoriales

Me gusta

A 7895 personas les gusta

Plug-in social de Facebook

Java Tutoriales

Amigo programador, tú que

estás todo el día picando

código, escucha estas

recomendaciones sólo

hechas para ti..... ¿qué es

esto?... Gracias Jorge

Rubira....

No olviden poner la

sentencia where al hacer

un borraro en la base de

datos..

http://www.youtube.com/

watch?v=i_cVJgIz_Cs

Compartir 0 Más Siguiente blog»

Page 119: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 8: HQL Segunda Parte

www.javatutoriales.com/2010/05/hibernate-parte-8-hql-segunda-parte.html 2/19

Podemos ver que, efectivamente, obtenemos como llave lo que colocamos como alias, y los valores de las columnas que solicitamos, de

los tres Usuarios que existen en la base de datos.

El recuperar los valores dentro de listas o dentro de mapas es algo muy útil y que nos ahorrará mucho esfuerzo en nuestras aplicaciones.

Sin embargo Hibernate nos proporciona una forma aún más útil que podemos usar. De hecho podemos recuperar una lista de objetos

propios, el cual reciba en su constructor los valores que estamos recuperando. Esto es muy útil cuando tenemos que recuperar datos

que posteriormente deberán ser mostrados en un reporte. Podríamos, por ejemplo, querer recuperar los datos de un usuario y la cantidad

total de las compras que ha realizado en un sitio y colocar estos datos en objeto para luego solo extraer los datos de estos objetos en el

reporte.

Crearemos una clase “UsuarioDireccion”, en el paquete modelo, que mantendrá el nombre del Usuario, su calle y código

postal, y el número de permisos que tiene (aún no hemos visto las funciones de agregación, de las que COUNT forma parte, pero

nos servirá para ir practicando).

La clase "UsuarioDirección" se muestra a continuación:

Participar en este sitioGoogle Friend Connect

Miembros (173) Más »

¿Ya eres miembro? Iniciar sesión

SEGUIDORES

► 2012 (2)

► 2011 (11)

▼ 2010 (10)

► diciembre (1)

► octubre (3)

► septiembre (2)

► agosto (2)

► julio (1)

▼ mayo (1)

Hibernate - Parte 8: HQL Segunda Parte

► 2009 (22)

ARCHIVO DEL BLOG

Page 120: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 8: HQL Segunda Parte

www.javatutoriales.com/2010/05/hibernate-parte-8-hql-segunda-parte.html 3/19

public class UsuarioDireccion

{

private String nombre;

private String calle;

private String codigoPostal;

private long numeroPermisos;

public UsuarioDireccion(String nombre, String calle, String codigoPostal, long numeroPermisos)

{

this.nombre = nombre;

this.calle = calle;

this.codigoPostal = codigoPostal;

this.numeroPermisos = numeroPermisos;

}

public String getCalle()

{

return calle;

}

public void setCalle(String calle)

{

this.calle = calle;

}

public String getCodigoPostal()

{

return codigoPostal;

}

public void setCodigoPostal(String codigoPostal)

{

this.codigoPostal = codigoPostal;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

public long getNumeroPermisos()

{

return numeroPermisos;

}

public void setNumeroPermisos(long numeroPermisos)

{

this.numeroPermisos = numeroPermisos;

}

}

Como podemos ver, esta clase solo tiene un constructor que recibe los cuatro parámetros que mencioné antes.

Regresaremos una lista con los valores de Usuario – Dirección, además del número de Permisos que tiene cada uno de los

Usuarios, que hay en la base de datos. Estos valores serán regresados en objetos de la clase “UsuarioDireccion”. Para lograr esto

usamos la siguiente sentencia:

SELECT NEW hql.modelo.UsuarioDireccion(u.nombre, d.calle, d.codigoPostal, COUNT(p)) FROM Usuario u

left outer join u.direccion as d left outer join u.permisos as p GROUP BY u.id

Como vemos ahora usamos la sentencia SELECT seguida de algo que parece la invocación del constructor de la clase

“hql.modelo.UsuarioDireccion”, de esta forma:

hql.modelo.UsuarioDireccion(u.nombre, d.calle, d.codigoPostal, COUNT(p))

A la cual le pasamos los cuatro parámetros que recibe el siguiente constructor:

Alex

Programador Java con algunos años de

experiencia en múltiples poyectos y con

múltiples APIs y herramientas deseoso de

compartir experiencias con el resto de

programadores.

Ver todo mi perfil

DATOS PERSONALES

Page 121: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 8: HQL Segunda Parte

www.javatutoriales.com/2010/05/hibernate-parte-8-hql-segunda-parte.html 4/19

public UsuarioDireccion(String nombre, String calle, String codigoPostal, long numeroPermisos)

En la sentencia anterior es necesario usar la clausula GROUP BY, con el identificador del Usuario, ya que estamos usando una función

de agregación (COUNT). Más adelante me adentraré un poco más en esto.

Además podemos ver que estamos usando un LEFT OUTER JOIN para que nos regrese los datos de los Usuarios aunque estos no

tengan ni Direcciones ni Permisos. Pueden encontrar más información de cómo funcionan los distintos joins en esta página.

Si ejecutamos esta sentencia en el NetBeans obtendremos el siguiente resultado:

Con lo que podemos ver que los Usuarios 1 y 2 tienen sus datos de Direccion y 2 Permisos cada uno, mientras que el Usuario 3

no tiene datos de Direccion, y tiene un Permiso.

Creemos un método para poder recuperar estos mismos datos desde nuestra aplicación:

private void obtenUsuariDireccion()

{

iniciaOperacion();

Query query = sesion.createQuery("SELECT NEW hql.modelo.UsuarioDireccion(u.nombre, d.calle, d.

codigoPostal, COUNT(p)) FROM Usuario u left outer join u.direccion as d left outer join u.permisos

as p GROUP BY u.nombre");

List<UsuarioDireccion> listaResultados = query.list();

for (int i = 0; i < listaResultados.size(); i++)

{

UsuarioDireccion usuarioDireccion = listaResultados.get(i);

System.out.println("->" + usuarioDireccion.getNombre() + ", permisos: " + usuarioDireccion

.getNumeroPermisos());

}

terminaOperacion();

}

Con lo que obtenemos la siguiente salida:

La cual podemos observar que es la misma que obtuvimos del editor HQL del NetBeans.

Con esto terminamos todo lo relativo a la clausula SELECT que Hibernate nos proporciona. Ahora veremos cómo funcionan las funciones

de agregación.

En el mundo de SQL las funciones de agregación trabajan sobre conjuntos de filas para dar un resultado correspondiente a ese

grupo. Afortunadamente para nosotros en HQL las funciones de agregación funcionan exactamente igual que en SQL. De hecho HQL

soporte las mismas funciones que SQL:

Las funciones de Agregación

Page 122: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 8: HQL Segunda Parte

www.javatutoriales.com/2010/05/hibernate-parte-8-hql-segunda-parte.html 5/19

AVG(…), SUM(…), MIN(…), MAX(…)

COUNT(*)

COUNT(…), COUNT(DISTINCT …), COUNT(ALL …)

En el ejemplo anterior hicimos uso de la función COUNT para obtener el número de Permisos que cada Usuario tiene relacionado.

Ahora bien, como se menciona en el párrafo anterior, las funciones de agregación operan sobre grupos de datos o resultados (en el

caso anterior era el grupo de Usuarios que se encuentran en la base de datos) por lo tanto, cada vez que usemos una función de

agregación obtendremos como resultado una única fila. Si no queremos que esto ocurra deberemos agrupar nuestros resultados

(usando la clausula GROUP BY) usando el criterio apropiado.

Explicaré esto usando el ejemplo anterior. En él recuperamos la lista de Usuarios de la base de datos, obteniendo además su

Direccion y el número de permisos que tienen asociados. Para recuperar este número de permisos usamos la función de agregación

COUNT. Sin embargo si no hubiéramos agrupado el resultado habría sido el siguiente:

Como podemos ver solo obtuvimos una fila como resultado, en el cual se muestra el total de permisos de los Usuarios que cumplen con

el criterio de búsqueda (2 del primero + 2 del segundo + 1 del tercero). Además muestra los datos de Usuario y Direccion de uno de

los Usuarios, seleccionado usando algún criterio del manejador de base de datos que estamos usando.

Como podemos ver esto difiere del resultado que queríamos obtener, ya que la intención era obtener los datos de cada uno de los

Usuarios y, adicional a esto, en número de permisos que tenía. Esto nos demuestra que debemos tener cuidado cuando hacemos

uso de las funciones de agregación.

¿Qué podemos hacer para obtener los datos que deseamos? Pues lo que hicimos en el ejemplo anterior, o sea, agrupar los resultados

obtenidos mediante algún criterio que nos parezca adecuado. En este caso como lo que queremos es obtener los datos de cada uno de

los Usuarios, debemos agrupar por algún atributo de Usuario que nos parezca adecuado. En este caso podría ser el "nombre" o el

"identificador". Como la vez pasada usamos el "identificador", ahora usemos el "nombre" para ver que también nos da el

resultado esperado:

El resto de las funciones de agregación funcionan de forma similar, así que dejaremos está parte hasta aquí para saltar a la explicación

referente a la clausula WHERE.

Esta clausula nos permite reducir o limitar el número de registros que recuperamos al hacer una consulta a nuestra base de datos,

seleccionando aquellos registros que cumplan con la condición establecida dentro de esta clausula.

Dentro de esta clausula podemos colocar condiciones como que un valor sea igual a otro, que este dentro de ciertos rangos, que un

objeto sea de cierta clase, etc. Básicamente lo mismo que podemos hacer con una sentencia WHERE de SQL estándar, pero agregándole

algunos detalles extra para el trabajo con objetos.

El trabajo con la clausula WHERE es en realidad muy sencillo, solo debemos colocar el nombre del atributo que queremos restringir,

y la restricción que tendrá. Podemos colocar más de una restricción, separando cada una con la palabra reservada AND.

Si queremos limitar el resultado de la consulta para regresar los usuarios de la base de datos, limitándonos a los que su “username” sea

“usr456” restringiremos la consulta de la siguiente forma:

FROM Usuario u WHERE u.username='usr456'

Como el atributo que estamos restringiendo es una cadena colocamos el valor esperado entre comillas simples.

Al ejecutar esta consulta en el editor HQL de NetBeans obtenemos el siguiente resultado:

La clausula WHERE

Page 123: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 8: HQL Segunda Parte

www.javatutoriales.com/2010/05/hibernate-parte-8-hql-segunda-parte.html 6/19

Como podemos ver, la restricción funcionó sin problemas.

Ahora supongamos que queremos restringir para recuperar los permisos cuyo “id” sea mayor a 2 y menor a 5 (o sea, 3 y 4 - ). La

consulta tendría que ser de la siguiente forma:

FROM Permiso p WHERE p.id> 2 AND p.id < 5

Obteniendo el siguiente resultado:

Estas restricciones pueden extenderse para valores de propiedades de objetos relacionados (componentes), como por ejemplo:

FROM Usuario u WHERE u.direccion.codigoPostal = ‘123456’

También podemos obtener los valores de los Usuarios que tienen una Direccion asociada, o sea, cuya Direccion no está nula:

FROM Usuario u WHERE u.direccion IS NOT NULL

Page 124: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 8: HQL Segunda Parte

www.javatutoriales.com/2010/05/hibernate-parte-8-hql-segunda-parte.html 7/19

Algo interesante en HQL es que el operador "=" no solo sirve para comparar valores de atributos, sino que también nos sirva para

comparar instancias:

FROM Usuario u1, Usuario u2 WHERE u1.direccion = u2.direccion

En el ejemplo anterior se recuperan las instancias de Usuario donde usuario 1 tiene la misma Dirección que usuario 2, este ejemplo

es poco útil pero sirve para probar el punto de la comparación de instancias. Si tuviéramos una base de datos más compleja tal vez

podríamos encontrar un uso más adecuado a esta funcionalidad.

Otra cosa interesante es que Hibernate proporciona una propiedad especial llamada “class”, que nos ayuda cuando trabajamos con

persistencia polimórfica (o sea cuando tenemos clases persistentes que heredan de otras clases persistentes, hablaremos de esto en un

futuro tutorial ya que es un tema un poco amplio como para tocarlo aquí mismo).

Por ejemplo, imaginemos que tenemos una estructura de clases de esta forma:

Donde tenemos una clase Animal de la cual extienden dos clases: Domestico y Salvaje. En código sería algo más o menos así:

public abstract class Animal

{

}

public class Domestico extends Animal

{

}

public class Salvaje extends Animal

{

}

Si quisiéramos recuperar todas los Animales guardados en nuestra base de datos, sin importar si son Domesticos o Salvajes lo

haríamos a través de una consulta como la siguiente:

FROM Animal a

Y listo, con esto recuperaríamos todos los Animales, dentro de una lista de referencias de tipo Animal, de la siguiente forma:

Page 125: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 8: HQL Segunda Parte

www.javatutoriales.com/2010/05/hibernate-parte-8-hql-segunda-parte.html 8/19

List<Animal> listaAnimales;

¿Qué pasaría si quisiéramos seguir usando la referencia anterior pero solo recuperar los Animales Domesticos de la base de datos?

Es ahí donde entra en juego la propiedad especial “class” que nos proporciona Hibernate. Por lo que la consulta quedaría de la siguiente

forma:

FROM Animal a WHERE a.class = Domestico

Tendrán que confiar un poco en mí sobre el funcionamiento de esto ya que, como mencioné antes, explicar las consultas polimórficas

requiere un tutorial completo ya que tiene bastantes detalles que explicar.

Las expresiones se usan dentro de la clausula WHERE e incluyen las siguientes:

matemáticas: “+”, “-“, “*”, “/”

comparación binaria: “=”, “>=”, “<=”, “<>”, “!=”, “LIKE”

lógicos: “AND”, “OR”, “NOT”

“IN”, “NOT IN”, “BETWEEN”, “IS NULL”, “IS NOT NULL”, “IS EMPTY”, “IS NOT EMPTY”, “MEMBER OF”, y “NOT MEMBER

OF”

case simple: “CASE… WHEN… THEN… ELSE… END”

case searched: “CASE WHEN… THEN… ELSE… END”

concatenación de cadenas: “… || …” o “CONCAT(…,…)”

“CURRENT_DATE()”,”CURRENT_TIME()”, y “CURRENT_TIMESTAMP()”

“SECOND(…)”, “MINUTE(…)”, “HOUR(…)”, “DAY(…)” , “MONTH(…)”, y “YEAR(…)”

“SUBSTRING()”, “TRIM()”, “LOWER()”, “UPPER()”, “LENGTH()”, “LOCATE()”, “ABS()”, “SQRT()”, “BIT_LENGTH()”,

“MOD()”

“COALESCE()” y “NULLIF()”

“STR()”

“CAST(… AS …)”, y “EXTRACT(… FROM …)”

Funciones HQL que toman expresiones con valores de tipo colección: “SIZE()”, “MINELEMENT()”, “MAXELEMENT()”,

“MININDEX()”, “MAXINDEX()”, junto con las funciones especiales “ELEMENTS()” e “INDICES()” que pueden ser

cuantificadas usando “SOME”, “ALL”, “EXISTS”, “ANY”, e “IN”

Cualquier función escalar soportado por el SQL del manejador de base de datos como “SIGN()”, “TRUNC()”, “RTRIM()”, y

“SIN()”

Parámetros posicionales estilo PreparedStatement de JDBC (usando “?”)

Parámetros con nombre (usando “:”)

Literales SQL: ‘foo’, 123, 6.66E+2, ‘1970-01-01 10:00:01.0’

Constantes java: (public static final)

La lista regresada por una consulta puede ser ordenada por una propiedad de uno de los componentes de las clases regresadas usando

esta clausula. Podemos indicar si queremos que los resultados sean ordenados de forma ascendente (de menor a mayor) o descendente

(de mayor a menor) con “asc” (la default) o “desc”, respectivamente.

Por ejemplo, si recuperamos la lista de todos los Permisos existentes en la base de datos, con la siguiente clausula:

FROM Permiso p

El resultado que obtenemos queda de la siguiente forma:

EXPRESIONES

LA CLAUSULA ORDER BY

Page 126: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 8: HQL Segunda Parte

www.javatutoriales.com/2010/05/hibernate-parte-8-hql-segunda-parte.html 9/19

Como vemos los resultados son ordenados por su "id", pero ¿y si quisiéramos regresarlos ordenamos por "nombre" para mostrarlos a

nuestros clientes? En ese caso tendríamos que usar la clausula “ORDER BY” en el atributo “nombre” de la clase “Permiso”, de la

siguiente forma:

FROM Permiso p ORDER BY p.nombre

Obteniendo el siguiente resultado:

Como vemos ahora los resultados están ordenamos por el "nombre" del Permiso de forma ascendente que, como mencioné, es la

forma por default. Si quisiéramos ordenarlos de forma descendente nuestra consulta quedaría así:

Si queremos ordenar por más de un atributo, colocamos la lista de atributos en esta clausula separados por comas: ORDER BY

atributo1, atributo2, atributo3, … atributo n.

Un query que regresa valores de agregación puede ser agrupado por cualquier propiedad de una clase o componente regresado por dicho

query.

Si por ejemplo, queremos obtener en número de Permisos de cada tipo que tenemos en la base de datos, haríamos una consulta como

la siguiente:

SELECT p.estatus, COUNT(p.estatus) FROM Permiso p GROUP BY p.estatus

Si colocamos esta consulta en el NetBeans obtenemos el siguiente resultado:

LA CLAUSULA GROUP BY

Page 127: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 8: HQL Segunda Parte

www.javatutoriales.com/2010/05/hibernate-parte-8-hql-segunda-parte.html 10/19

Como este resultado no nos dice mucho - ! Crearemos un método que ejecute esta consulta:

private void cuentaPermisos()

{

iniciaOperacion();

Query query = sesion.createQuery("SELECT p.estatus, COUNT(p.estatus) FROM Permiso p GROUP BY p

.estatus");

List<Object[]> datos = query.list();

for (int i = 0; i < datos.size(); i++)

{

Object[] datoActual = datos.get(i);

System.out.println(datoActual[0] + "(" + datoActual[1] + ")");

}

terminaOperacion();

}

Al ejecutar este método obtenemos la siguiente salida:

Que como podemos ver nos muestra el número de permisos de cada tipo que tenemos en la base de datos.

De la misma forma que con la clausula “ORDER BY”, si queremos agrupar por más de un atributo, colocamos la lista de atributos en esta

clausula separados por comas: GROUP BY atributo1, atributo2, atributo3, … atributo n.

Si queremos usar la clausula ORDER BY en una consulta en la que también usemos la clausula GROUP BY, esta ultima debe aparecer

antes que ORDER BY, de la siguiente forma:

SELECT … FROM … WHERE … GROUP BY… ORDER BY…

De lo contrario no obtendremos algún mensaje de error, pero podemos obtener resultados inesperados.

En las bases de datos que soportan subselects, Hibernate soporta subconsultas dentro de consultas. Las subconsultas deben estar

encerradas entre paréntesis. Aún las subconsultas correlacionadas (subconsultas que hacen referencia a un alias en la consulta

externa) son permitidas siempre que la base de datos lo haga.

Por ejemplo, si quisiéramos obtener todos los Usuarios de la base de datos que tengan al menos un Permiso en estado ACTIVO

(cuyo "estatus" sea igual a 1) lo haríamos con la siguiente consulta:

SELECT DISTINCT(u) FROM Usuario u inner join u.permisos as p WHERE p in (SELECT p FROM Permiso p W

HERE p.estatus = 1)

Si colocamos esta consulta en el editor HQL de NetBeans obtenemos el siguiente resultado:

SUBCONSULTAS

Page 128: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 8: HQL Segunda Parte

www.javatutoriales.com/2010/05/hibernate-parte-8-hql-segunda-parte.html 11/19

Como podemos ver solo el Usuario con id = 2 tiene Permisos con el estatus de ACTIVO.

Para terminar esta sección diré que las subconsultas solo pueden estar dentro de clausulas SELECT o WHERE.

HQL soporta el uso de la sintaxis de SQL ANSI “row value constructor”, algunas veces llamado sintaxis “AS tuple”, aun cuando

el manejador de base de datos que estemos usando podría no soportar dicha notación. Aquí, por lo general nos referimos a

comparaciones multi-valuadas, típicamente asociadas con componentes. Por ejemplo, consideremos nuestra entidad “Usuario”, la cual

define un componente “Direccion”:

FROM Usuario u WHERE u.direccion.calle = 'Calle1' AND u.direccion.codigoPostal = '12345'

La anterior es una consulta valida aunque un poco larga. La documentación de Hibernate dice que podemos hacerla más corta con la

sintaxis row value constructor, de la siguiente forma:

SELECT u.direccion FROM Usuario u WHERE u.direccion=('Calle1', '12345')

Sin embargo no he logrado hacerlo funcionar, ni al ejecutarlo desde NetBeans, ni desde una aplicación. Obteniendo el siguiente error:

Afortunadamente (para este ejemplo _ !) la sintaxis row value constructor también puede ser usada en subconsultas que necesitan

comparar multiples valores. Por ejemplo, si queremos obtener los datos del Usuario cuya Direccion tiene la misma "calle" y

"codigoPostal" que la Direccion con "id" = 2 podemos usar la siguiente consulta:

FROM Usuario u WHERE (u.direccion.calle, u.direccion.codigoPostal) in (SELECT d.calle, d.codigoPos

tal FROM Direccion d WHERE d.id = 2)

Con la cual obtenemos el siguiente resultado:

Lo que nos indica que el Usuario con id = 2 es el dueño de la Direccion con id = 2 - .

SINTAXIS ROW VALUE CONSTRUCTOR

COMPONENTES

Page 129: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 8: HQL Segunda Parte

www.javatutoriales.com/2010/05/hibernate-parte-8-hql-segunda-parte.html 12/19

La mayoría de las veces que creamos un modelo de datos, nuestras clases entidad están construidas haciendo uso de la composición de

objetos, teniendo una relación de tipo HAS-A (objetos construidos en base a otros objetos) como en el caso de la relación Usuario-

Direccion; en donde un Usuario tiene una Direccion y una Direccion tiene un Usuario.

Cuando tenemos una relación de este tipo, y esta relación es de Uno a Uno o de Muchos a Muchos al Objeto incluido dentro de

nuestra clase le llamamos “componente”. En este Caso si estamos dentro de la clase Usuario a la referencia que tenemos a un objeto

de la clase Direccion (private Direccion direccion;) la llamamos el componente “dirección” del Usuario.

HQL nos permite hacer consultas a propiedades de los componentes de nuestras clases de la misma forma en la que lo hacemos con las

propiedades de nuestro objeto principal. Estos valores pueden aparecen en la clausula SELECT de la siguiente forma:

SELECT u.nombre, u.direccion.calle, u.direccion.codigoPostal FROM Usuario u

Crearemos un método llamado "obtenNombreDireccion" en nuestra clase "Main" para asegurarnos que la consulta anterior

efectivamente nos devuelve los valores que hemos pedido:

private void obtenNombreDireccion()

{

iniciaOperacion();

Query query = sesion.createQuery("SELECT u.nombre, u.direccion.calle, u.direccion.codigoPostal

FROM Usuario u");

List<Object[]> listaResultados = query.list();

for(int i = 0; i < listaResultados.size(); i++)

{

Object[] resultadoActual = listaResultados.get(i);

System.out.println("Nombre: " + resultadoActual[0] + ", calle: " + resultadoActual[1] + ",

codigo postal: " + resultadoActual[2]);

}

terminaOperacion();

}

Recuerden que cuando hacemos un SELECT con solo algunas propiedades de nuestra entidad estas se obtienen en una lista de arreglos

de objetos, donde cada uno de los valores pedidos está almacenado en los objetos recuperados en el mismo orden en el que aparecen en

la clausula SELECT; esto lo aprendimos en el tutorial anterior.

Al ejecutar el código anterior obtenemos el siguiente resultado:

El cual, como podemos ver, obtiene además del nombre del Usuario la calle y el codigoPostal de su componente "direccion".

De esta misma forma podemos usar los atributos de los componentes en las clausulas WHERE y ORDER BY:

FROM Usuario u WHERE u.direccion.codigoPostal = '12345'

Page 130: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 8: HQL Segunda Parte

www.javatutoriales.com/2010/05/hibernate-parte-8-hql-segunda-parte.html 13/19

SELECT u, u.direccion FROM Usuario u ORDER BY u.direccion.codigoPostal desc

Es importante recordar que, como dije antes, esto solo podemos usarlo cuando tenemos relaciones de tipo Uno a Uno o Muchos a

Muchos, o sea cuando nuestro componente es una referencia simple (no es un arreglo o una lista o algún otro tipo de colección).

Para terminar este tutorial daré una serie de trucos que pueden facilitarnos un poco la vida al trabajar con HQL en nuestras aplicaciones

del día a día.

Contar en número de resultados:

Podemos contar en número de resultados que regresará una consulta sin tener que ejecutarla. Esto nos permitirá evitar obtener un

OutOfMemoryError si es que pensamos que nuestra consulta regresará muchos resultados.

Por ejemplo, tenemos la siguiente consulta, que es el producto cartesiano de todos los Usuarios y todos los Permisos que tenemos

en la base de datos (si, sé que no debemos hacer eso, que debemos usar joins en vez de productos cartesianos, pero no se preocupen,

es solo para ejemplificar u_u):

FROM Usuario u, Permiso p

Solo para que nos demos una idea, si ejecutamos esta consulta obtendremos lo siguiente:

Para obtener el número de resultados que regresará esta consulta sin regresar los resultados, podemos usar la siguiente consulta:

SELECT COUNT(*) FROM Usuario u, Permiso p

Crearemos un método llamado cuentaResultadosConsulta para poder ejecutar la consulta anterior:

TIPS Y TRUCOS

Page 131: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 8: HQL Segunda Parte

www.javatutoriales.com/2010/05/hibernate-parte-8-hql-segunda-parte.html 14/19

public void cuentaResultadosConsulta()

{

iniciaOperacion();

int numeroResultados = ((Long) sesion.createQuery("SELECT COUNT(*) FROM Usuario u, Permiso p")

.iterate().next()).intValue();

System.out.println("Numero de resultados que regresara la consulta: " + numeroResultados);

terminaOperacion();

}

El método anterior nos da el siguiente resultado:

Como podemos ver, si ejecutamos la consulta esta nos regresará 15 resultados. Podemos comprobar que esto es correcto si contamos

el número de resultados que nos regresó la consulta cuando la ejecutamos hace unos momentos.

Resultados en base al tamaño de una colección:

Si queremos ordenar los resultados que nos regresa una consulta en base al número de elementos que tiene una colección

podemos hacerlo usando la función de agregación COUNT dentro de la clausula ORDER BY. Por ejemplo, si queremos ordenar los

Usuarios obtenidos de la base de datos, en base al número de Permisos que tiene cada uno, podemos hacerlo con la siguiente

consulta:

SELECT u FROM Usuario u left join u.permisos p GROUP BY u ORDER BY COUNT(p) asc

Si ejecutamos esta consulta en el NetBeans obtendremos el siguiente resultado:

También, si nuestra base de datos soporte subconsultas, podemos colocar una condición sobre el tamaño de la condición en la clausula

SELECT de la consulta. Por ejemplo si queremos obtener todos los Usuarios que tienen menos de dos Permisos, utilizamos la

siguiente consulta:

FROM Usuario u WHERE size(u.permisos) < 2

Si ejecutamos esta consulta obtendremos el siguiente resultado:

Page 132: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 8: HQL Segunda Parte

www.javatutoriales.com/2010/05/hibernate-parte-8-hql-segunda-parte.html 15/19

Publicado por Alex en 16:05

Reacciones: divertido (0) interesante (0) increible (0) no me gusta (0)

Etiquetas: funciones de agregacion, group by, hibernate, hql, order by, row value constructor

Si nuestra base de datos NO soporta subconsultas podemos usar la siguiente consulta para obtener el mismo resultado que con la

consulta anterior (¿alguien ha contado cuántas veces he escrito la palabra consulta en este post?):

SELECT u FROM Usuario u join u.permisos p GROUP BY u HAVING COUNT(p) < 2

Obteniendo el mismo resultado que con la consulta anterior:

Con esto terminamos por fin este tutorial sobre HQL _ , espero que les sea de utilidad.

Ahora que sabemos cómo hacer consultas con HQL veremos, en el siguiente tutorial, como utilizar todo lo que hemos aprendido dentro

de nuestras aplicaciones java.

Saludos

Descarga los archivos de este tutorial desde aquí:

HQL parte 2

Entradas Relacionadas:

Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

Parte 2: Persistiendo Objetos Simples usando Anotaciones (Metadatos)

Parte 3: Relaciones / Uno a uno

Parte 4: Relaciones / Uno a muchos

Parte 5: Relaciones / Muchos a uno

Parte 6: Relaciones / Muchos a Muchos

Parte 7: HQL Primera Parte

Parte 9: Parámetros en HQL

Parte 10: Herencia

Parte 11: Interceptores y Eventos

Recomendar esto en Google

12 comentarios:

luchonet 29 de mayo de 2010 19:41

Gracias Alex son un maestro en esto desde el primer tutorial te voy siguiendo..

Aqui un pequeño aporte por si no conocias esta pagina de recursos gratuitos me imagino que es muy conocida por todos pero aqui esta

por si acaso es http;//www.illasaron.com/ tiene muy buenos recursos de varios lenguajes!!!

Responder

Alex 29 de mayo de 2010 19:50

Hola luchonet;

Gracias por tu comentario, y muy interesante el sitio que comentas de http://www.illasaron.com/ de videotutoriales, lo voy a revisar

Responder

JUAN CARLOS 15 de junio de 2010 21:58

Page 133: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 9: Parámetros en HQL

www.javatutoriales.com/2010/07/hibernate-parte-9-parametros-en-hql.html 1/19

Blog dedicado a temas de programación actuales usando el lenguaje de programación Java y las últimas versiones de sus APIs y Herramientas.

Tutoriales de Programacion Java

Página principal Página de Concursos Tutoriales UPAO 2010 Presentaciones Capacitación

DOMINGO, 25 DE JULIO DE 2010

Hibernate - Parte 9: Parámetros en HQL

En los dos tutoriales anteriores aprendimos cómo crear sentencias en el Hibernate Query Language (HQL) para poder hacer

consultas más específicas, que las que por default nos proporciona Hibernate, a objetos almacenados en nuestra base de datos.

Hasta ahora nuestras consultas siempre han sido estáticas, pero en nuestras aplicaciones usualmente deberemos pasarles parámetros

para obtener los datos correctos, como por ejemplo una cadena que represente un criterio de búsqueda, o el username y password de

un usuario que queramos buscar.

Ahora veremos cómo poder pasar parámetros a estas consultas de una forma “correcta”; y digo correcta porque siempre podríamos

concatenar los parámetros al String que usemos para generar nuestra consulta.

Afortunadamente existen dos formas de pasar los parámetros sin tener que recurrir a la concatenación. Y como solo existen dos formas

este tutorial será corto, se los prometo _ !

Comencemos recordando un poco cómo hacemos, desde nuestro código Java, una consulta en HQL.

Nota: Aunque uso el término general consulta, no solo me refiero a pedir datos de la base, también uso este término para referirme a

inserciones o actualizaciones de datos.

Hibernate proporciona la interface “org.hibernate.Query”, la cual representa una consulta a la base de datos. Para obtener una

instancia de “Query” usamos el método “createQuery” del objeto “org.hibernate.Session”, que a su vez obtenemos de la clase

“org.hibernate.SessionFactory” (si no recuerdan cómo obtener Session pueden revisarlo en la clase “HibernateUtil” que

creamos en el primer y en el segundo tutorial).

El método “createQuery” recibe como parámetro una cadena que es nuestra consulta HQL, sin embargo, como mencioné antes, hasta

ahora solo hemos usado consultas estáticas. Sin embargo solo en raras ocasiones estas consultas estáticas nos serán del todo útiles.

Hibernate proporciona dos formas de pasar parámetros a nuestras consultas:

Parámetros con nombre (named query parameters).

Parámetros posicionales estilo JDBC.

Antes de entrar en el detalle de cada una de ellas veamos el modelo de datos que usaremos para los ejemplos.

La aplicación que crearemos será un mini sistema de ventas. Tendremos un “Usuario” que puede realizar “Compras” de “Productos”.

Este “Usuario” tendrá una “Direccion” de entrega a la que llegarán los “Productos” que compre.

Por lo tanto, tendremos una clase entidad “Usuario” que tendrá una relación uno a uno con la clase entidad “Direccion”. El

“Usuario” además tendrá una relación uno a muchos con la clase entidad “Compra” que a su vez tendrá una relación muchos a

muchos con la clase entidad “Producto”.

Como podemos ver el ejemplo es pequeño pero podremos usar varias de las cosas que hemos aprendido a lo largo de estos tutoriales.

En los tutoriales solamente usaré anotaciones para el ejemplo, sin embargo pueden descargar también la versión con archivos de mapeo

desde la liga que se encuentra al final del tutorial.

Creamos un nuevo proyecto en NetBeans (menú “File -> New Project… -> Java -> Java Application”). Le damos un

nombre y una ubicación, y nos aseguramos de que las opciones "Create Main Class" y "Set as Main Project" estén

habilitadas. Presionamos el botón "Finish" y veremos aparecer en el editor nuestra clase "Main".

Agregamos la biblioteca de "Hibernate" que creamos en el primer tutorial de la serie. Hacemos clic derecho en el nodo "Libraries"

del proyecto. En el menú contextual que se abre seleccionamos la opción "Add Library...":

DONACIONES

JAVA TUTORIALES EN FACEBOOK

Java Tutoriales

Me gusta

A 7896 personas les gusta

Plug-in social de Facebook

Java Tutoriales

Amigo programador, tú que

estás todo el día picando

código, escucha estas

recomendaciones sólo

hechas para ti..... ¿qué es

esto?... Gracias Jorge

Rubira....

No olviden poner la

sentencia where al hacer

un borraro en la base de

datos..

http://www.youtube.com/

watch?v=i_cVJgIz_Cs

Compartir 0 Más Siguiente blog»

Page 134: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 9: Parámetros en HQL

www.javatutoriales.com/2010/07/hibernate-parte-9-parametros-en-hql.html 2/19

En la ventana que se abre seleccionamos la biblioteca "Hibernate":

Presionamos el botón "Add Library" para que la biblioteca se agregue a nuestro proyecto. De la misma forma agregamos la biblioteca

“HibernateAnotaciones” que creamos en el segundo tutorial. Aprovechamos también para agregar el conector de MySQL. Debemos

tener los siguientes archivos en nuestro proyecto:

Ahora creamos dos paquetes, uno con el nombre "modelo", que contendrá las clases entidades, y otro con el nombre "dao" que

contendrá las clases que se encargarán del manejo de los datos que almacenaremos en la base de datos.

Hacemos clic derecho en el nodo del paquete que se creó al generar el proyecto. En el menú contextual que se abre seleccionamos la

opción "New -> Java Package..." y creamos los dos paquetes.

Que deben quedar así:

Participar en este sitioGoogle Friend Connect

Miembros (173) Más »

¿Ya eres miembro? Iniciar sesión

SEGUIDORES

► 2012 (2)

► 2011 (11)

▼ 2010 (10)

► diciembre (1)

► octubre (3)

► septiembre (2)

► agosto (2)

▼ julio (1)

Hibernate - Parte 9: Parámetros en HQL

► mayo (1)

► 2009 (22)

ARCHIVO DEL BLOG

Page 135: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 9: Parámetros en HQL

www.javatutoriales.com/2010/07/hibernate-parte-9-parametros-en-hql.html 3/19

También crearemos el archivo de configuración “hibernate.cfg.xml” que será prácticamente igual que el de los últimos 8 tutoriales:

<!DOCTYPE hibernate-configuration PUBLIC

"-//Hibernate/Hibernate Configuration DTD 3.0//EN"

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

<hibernate-configuration>

<session-factory>

<!-- parametros para la conexion a la base de datos -->

<property name="connection.driver_class">com.mysql.jdbc.Driver</property>

<property name="connection.url">jdbc:mysql://localhost/ hibernateparametros </property>

<property name="connection.username">usuario</property>

<property name="connection.password">password</property>

<!-- Configuracion del pool interno -->

<property name="connection.pool_size">1</property>

<!-- Dialecto de la base de datos -->

<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>

<!-- Otras propiedades importantes -->

<property name="show_sql">true</property>

<property name="hbm2ddl.auto">create-drop</property>

<!-- Aqui iran las clases mapeadas -->

</session-factory>

</hibernate-configuration>

Para los tutoriales usaremos una base de datos llamada “hibernateparametros”. Las tablas serán creadas de forma automática en

base a nuestras entidades anotadas.

Comencemos con la creación de nuestro modelo de datos, dentro del paquete “modelo”, con la clase “Direccion” que queda de esta

forma:

Alex

Programador Java con algunos años de

experiencia en múltiples poyectos y con

múltiples APIs y herramientas deseoso de

compartir experiencias con el resto de

programadores.

Ver todo mi perfil

DATOS PERSONALES

Page 136: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 9: Parámetros en HQL

www.javatutoriales.com/2010/07/hibernate-parte-9-parametros-en-hql.html 4/19

@Entity

public class Direccion implements Serializable

{

@Id

@GeneratedValue(strategy=GenerationType.IDENTITY)

private int id;

private String calle;

private String codigoPostal;

public Direccion()

{

}

public Direccion(String calle, String codigoPostal)

{

this.calle = calle;

this.codigoPostal = codigoPostal;

}

public String getCalle()

{

return calle;

}

public void setCalle(String calle)

{

this.calle = calle;

}

public String getCodigoPostal()

{

return codigoPostal;

}

public void setCodigoPostal(String codigoPostal)

{

this.codigoPostal = codigoPostal;

}

public int getId()

{

return id;

}

private void setId(int id)

{

this.id = id;

}

}

No hay mucho que explicar sobre esta clase, es prácticamente la misma que hemos visto en los tutoriales anteriores: una clase marcada

con la anotación @Entity e indicando cuál de sus atributos servirá como identificador usando la anotación @Id.

La clase “Direccion” es muy parecida a la clase “Producto” que queda así:

Page 137: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 9: Parámetros en HQL

www.javatutoriales.com/2010/07/hibernate-parte-9-parametros-en-hql.html 5/19

@Entity

public class Producto implements Serializable

{

public static enum Estatus{ACTIVO, INACTIVO};

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private int id;

private String nombre;

private String codigoBarras;

private float precio;

private Estatus estatus = Estatus.ACTIVO;

public Producto()

{

}

public Producto(String nombre, String codigoBarras, float precio)

{

this.nombre = nombre;

this.codigoBarras = codigoBarras;

this.precio = precio;

}

public String getCodigoBarras()

{

return codigoBarras;

}

public void setCodigoBarras(String codigoBarras)

{

this.codigoBarras = codigoBarras;

}

public Estatus getEstatus()

{

return estatus;

}

public void setEstatus(Estatus estatus)

{

this.estatus = estatus;

}

public int getId()

{

return id;

}

public void setId(int id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

public float getPrecio()

{

return precio;

}

public void setPrecio(float precio)

{

this.precio = precio;

}

}

Esta clase es un poco más interesante ya que, como podemos ver arriba, incluye una enumeración llamada “Estatus” que nos indica

si el producto está “ACTIVO” o “INACTIVO”. Esta enumeración es public y static para que podamos acceder a ella desde afuera de

la clase Producto de esta forma: Producto.Estatus.ACTIVO y Producto.Estatus.INACTIVO, y podamos usarlos en nuestras

consultas.

También, la clase Producto incluye un atributo de tipo Estatus, llamado “estatus” y que inicializamos con el valor

“Estatus.ACTIVO”, o sea, cada vez que se cree un producto este estará activo por default.

Page 138: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 9: Parámetros en HQL

www.javatutoriales.com/2010/07/hibernate-parte-9-parametros-en-hql.html 6/19

Ahora comenzaremos a ver las clases entidad que tienen relación con otras entidades. La entidad “Usuario” queda de la siguiente

manera:

@Entity

public class Usuario implements Serializable

{

@Id

@GeneratedValue(strategy=GenerationType.IDENTITY)

private long id;

private String nombre;

private String username;

private String password;

@OneToOne(cascade = CascadeType.ALL)

private Direccion direccion;

@OneToMany(cascade = CascadeType.ALL)

private List<Compra> compras = new ArrayList<Compra>();

public Usuario()

{

}

public Usuario(String nombre, String username, String password)

{

this.nombre = nombre;

this.username = username;

this.password = password;

}

public List<Compra> getCompras()

{

return compras;

}

public void setCompras(List<Compra> compras)

{

this.compras = compras;

}

public void addCompra(Compra compra)

{

this.compras.add(compra);

compra.setUsuario(this);

}

public Direccion getDireccion()

{

return direccion;

}

public void setDireccion(Direccion direccion)

{

this.direccion = direccion;

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

public String getPassword()

{

return password;

}

public void setPassword(String password)

{

this.password = password;

}

Page 139: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 9: Parámetros en HQL

www.javatutoriales.com/2010/07/hibernate-parte-9-parametros-en-hql.html 7/19

}

public String getUsername()

{

return username;

}

public void setUsername(String username)

{

this.username = username;

}

}

La entidad “Usuario” es un poco más interesante que las dos que vimos anteriormente, ya que, además de indicar cuál es su atributo

identificador y mostrar algunos atributos más, vemos que tiene las anotaciones que indican las relaciones que tiene con las otras

entidades.

La relación uno a uno que tiene con “Direccion” la representamos mediante la anotación “@OneToOne”, que como podemos ver

realizará todas las operaciones en cascada con esta entidad, en su atributo direccion.

También podemos ver que tiene una relación uno a muchos, representada mediante la anotación “@OneToMany”, con la entidad “Compra”

en su atributo “compras”.

Finalmente, la entidad “Compra” queda de esta manera:

Page 140: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 9: Parámetros en HQL

www.javatutoriales.com/2010/07/hibernate-parte-9-parametros-en-hql.html 8/19

@Entity

public class Compra implements Serializable

{

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private long id;

@ManyToMany(fetch=FetchType.EAGER)

private List<Producto> productos = new ArrayList<Producto>();

private double importeTotal;

@ManyToOne

private Usuario usuario;

public Usuario getUsuario()

{

return usuario;

}

public void setUsuario(Usuario usuario)

{

this.usuario = usuario;

}

public double getImporteTotal()

{

return importeTotal;

}

public void setImporteTotal(double importeTotal)

{

this.importeTotal = importeTotal;

}

public List<Producto> getProductos()

{

return productos;

}

public void setProductos(List<Producto> productos)

{

this.productos = productos;

}

public void addProducto(Producto producto)

{

this.productos.add(producto);

importeTotal += producto.getPrecio();

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

}

Como vemos, la entidad “Compra” tiene una relación muchos a muchos con la entidad “Producto”, a través de su atributo

“listaProductos”, en la cual colocamos la anotación “@ManyToMany” que representa la relación. En esta anotación también hemos

colocado el valor “FetchType.EAGER” al atributo “fetch” ya que queremos que al momento de recuperar una compra, se recuperen de

forma automática todos los productos que la conforman.

Como dije antes, esta relación será de tipo muchos a muchos, por lo que, además de saber qué Productos están en una Compra,

también podremos saber quién compró qué Productos, a través de la relación muchos a uno que “Compra” tiene con la entidad

“Usuario”.

Ahora ya tenemos las cuatro entidades que usaremos para este ejemplo. No olviden indicar estas clases en el archivo

“hibernate.cfg.xml”, que queda de esta forma:

Page 141: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 9: Parámetros en HQL

www.javatutoriales.com/2010/07/hibernate-parte-9-parametros-en-hql.html 9/19

<?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>

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

<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>

<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernateparametros<

/property>

<property name="hibernate.connection.username">root</property>

<property name="hibernate.connection.password">123</property>

<!-- Configuracion del pool interno -->

<property name="connection.pool_size">1</property>

<!-- Dialecto de la base de datos -->

<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>

<!-- Otras propiedades importantes -->

<property name="show_sql">true</property>

<property name="hbm2ddl.auto">create-drop</property>

<!-- Aqui iran las clases mapeadas -->

<mapping class="hibernate.parametros.modelo.Direccion" />

<mapping class="hibernate.parametros.modelo.Compra" />

<mapping class="hibernate.parametros.modelo.Producto" />

<mapping class="hibernate.parametros.modelo.Usuario" />

</session-factory>

</hibernate-configuration>

Para continuar esta parte de configuración crearemos la clase “HibernateUtil” como lo hicimos en el segundo tutorial de la serie.

A continuación crearemos una clase de utilidad que nos ayudará a para realizar las operaciones de consultas a la base de datos y de

paso nos ayudará a mostrar un poco como debe ser la separación de capas de lógica y de persistencia en nuestra aplicación. Esta será

una clase abstracta que contendrá unos métodos de utilidad para las operaciones de base de datos y de las cuales extenderá el resto de

nuestras clases de persistencia.

Hacemos clic derecho sobre nuestro paquete “dao” y en el menú contextual que aparece seleccionamos las opciones “New -> Java

Class”. En la ventana que se abre colocamos como nombre de la clase “AbstractDAO” y presionamos el botón “Finish”.

Como dije antes, esta clase servirá de base para el resto de nuestras clases de persistencia. Pero no queremos que alguien pueda crear

una instancia de esta clase directamente, así que la marcamos como “abstract”, de la siguiente forma:

public abstract class AbstractDAO

{

}

No entraré mucho en los detalles de lo que hace esta clase porque es básicamente lo mismo que hemos venido viendo a lo largo de los

tutoriales, así que solo colocaré el código final, que queda de esta forma:

public abstract class AbstractDAO

{

private Session sesion;

protected void iniciaOperacion()

{

sesion = HibernateUtil.getSessionFactory().openSession();

sesion.getTransaction().begin();

}

Page 142: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 9: Parámetros en HQL

www.javatutoriales.com/2010/07/hibernate-parte-9-parametros-en-hql.html 10/19

protected void terminaOperacion()

{

sesion.getTransaction().commit();

sesion.close();

}

protected void manejaExcepcion(HibernateException he) throws HibernateException

{

sesion.getTransaction().rollback();

throw he;

}

protected Session getSession()

{

return sesion;

}

public static void almacenaEntidad(Object entidad) throws HibernateException

{

AbstractDAO dummy = new AbstractDAO(){};

try

{

dummy.iniciaOperacion();

dummy.getSession().saveOrUpdate(entidad);

dummy.getSession().flush();

}

catch (HibernateException he)

{

dummy.manejaExcepcion(he);

}

finally

{

dummy.terminaOperacion();

}

}

public static <T> T getEntidad(Serializable id, Class<T> claseEntidad) throws HibernateExcepti

on

{

AbstractDAO dummy = new AbstractDAO(){};

T objetoRecuperado = null;

try

{

dummy.iniciaOperacion();

objetoRecuperado = (T) dummy.getSession().get(claseEntidad, id);

}

catch (HibernateException he)

{

dummy.manejaExcepcion(he);

}

finally

{

dummy.terminaOperacion();

}

return objetoRecuperado;

}

public static <T> List<T> getListaEntidades(Class<T> claseEntidad) throws HibernateException

{

AbstractDAO dummy = new AbstractDAO(){};

List<T> listaResultado = null;

try

{

dummy.iniciaOperacion();

listaResultado = dummy.getSession().createQuery("FROM " + claseEntidad.getSimpleName()

).list();

}

catch (HibernateException he)

{

dummy.manejaExcepcion(he);

}

finally

{

dummy.terminaOperacion();

}

return listaResultado;

}

}

Page 143: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 9: Parámetros en HQL

www.javatutoriales.com/2010/07/hibernate-parte-9-parametros-en-hql.html 11/19

Como podemos ver, los métodos “iniciaOperacion”, “terminaOperacion”, y “manejaExcepcion” son los mismos que hemos

venido viendo hasta ahora. El resto de los métodos podrían parecer un poco extraños así que los explicaré sin entrar mucho en detalles.

El método “almacenaEntidad” es un método estático que nos permite almacenar cualquier tipo de objeto entidad. El tener este

método aquí nos evitará el tener que crear métodos específicos para cada una de las entidades, y como el método es estático ni siquiera

tendremos que crear una instancia de una clase para guardar una entidad. Créanme que esto resulta muy útil en los proyectos - .

Como podemos ver, es necesario crear un objeto “dummy”, el cual es una clase que extiende a la misma “AbstractDAO” para poder

acceder a los métodos “iniciaOperacion”, “terminaOperacion”, y “manejaOperacion” que están marcados como protected.

El método “getEntidad” nos permite recuperar cualquier objeto entidad del cual sepamos su identificador. Como vemos, este recibe

como parámetros un “Serializable” que es el identificador (que puede ser un Long, String, Integer, en fin, casi cualquier clase

que queramos), y la clase a la cual pertenece la entidad que queremos recuperar.

El método “getListaEntidades” es muy parecido al anterior con la diferencia que aquí se recuperan todas las entidades de un tipo

que tengamos almacenadas.

Tanto en “getEntidad” y “getListaEntidades” podemos ver que se hace uso de generics (indicado por <T>) para recuperar los

objetos de una clase determinada.

Pues bien, comencemos viendo cómo pasar parámetros a nuestras consultas usando parámetros con nombre.

Es la forma más simple de pasar parámetros a una consulta en HQL ya que, como su nombre lo indica, se le da un nombre al

parámetro y posteriormente se hace uso de este nombre para hacer referencia al parámetro.

El nombre del parámetro debe ir dentro del String de la consulta, este debe ir precedido por “:”.

Como primer ejemplo haremos una consulta para buscar a un Usuario que nos proporciona su nombre de usuario y contraseña (como

en un login). Esta consulta estará dentro de un método de la clase “UsuariosDAO” que extenderá a la clase “AbstractDAO” que

creamos hace un momento.

Hacemos clic derecho en el paquete “dao”. En el menú que se abre seleccionamos “New -> Java Class”, ponemos como nombre de

la clase “UsuariosDAO” y presionamos el botón “Finish”. La clase que se crea será una subclase de “AbstractDAO” por lo que lo

indicamos con la palabla clave extends.

public class UsuariosDAO extends AbstractDAO

{

}

Ahora pasaremos a ver la consulta. Primero explicaré cada una de las partes y posteriormente veremos cómo queda el método completo.

Si recuerdan, en el tutorial de HQL vimos cómo hacer una consulta simple para recuperar un objeto. En este caso la consulta quedaría

inicialmente de la siguiente forma:

FROM Usuario u

La consulta anterior nos regresará a todos los Usuarios que tengamos en la base de datos. Sin embargo esto no es lo que queremos,

lo que queremos es recuperar a un Usuario específico cuyos nombres de usuario y contraseña coincidan con los valores que le

proporcionaremos. Para esto debemos usar una restricción con la clausula “WHERE”, y después indicarle los parámetros que usaremos.

Como dije hace un momento: para indicar los parámetros lo hacemos con el símbolo “:” seguido del nombre que tendrá el parámetro,

este nombre puede ser cualquiera que nosotros queramos. De esta forma:

FROM Ususario u WHERE u. username = :nombreUsuario AND u. password = :password

La consulta anterior está esperando recibir dos parámetros “nombreUsuario” y “password”, una vez que reciba estos dos parámetros

reemplazará las cadenas “:nombreUsuario” y “:password” con los valores correspondientes.

Ahora la pregunta es: ¿Cómo establecemos estos parámetros? Pues bien, la respuesta a la pregunta anterior es que la interface

“org.hibernate.Query” tiene un método llamado “setParameter” que recibe como primer argumento el nombre del parámetro que

queremos establecer, y como segundo argumento el valor de dicho parámetro. El hecho de que el segundo parámetro sea un objeto

nos permite que le pasemos valores de tipos arreglo, colecciones, bytes, booleanos, o prácticamente cualquier cosa que necesitemos,

como enumeraciones (lo cual haremos en unos momentos). La forma de usar este método es la siguiente:

Parámetros con nombre

Page 144: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 9: Parámetros en HQL

www.javatutoriales.com/2010/07/hibernate-parte-9-parametros-en-hql.html 12/19

query.setParameter(“nombreUsuario”, username);

query.setParameter(“password”, password);

Finalmente, para recuperar el valor que regresa esta consulta usamos el método “uniqueResult()” de la interface

“org.hibernate.Query”, como en los tutoriales anteriores:

Usuario usuario = (Usuario) query.uniqueResult();

Ahora pondremos todo lo anterior en un método llamado “getUsuario” que queda de la siguiente forma (ya agregándole los métodos de

utilidad que obtenemos por herencia de la clase “AbstractDAO”.

public Usuario getUsuario(String username, String password) throws HibernateException

{

Usuario usuario = null;

try

{

iniciaOperacion();

Query query = getSession().createQuery("FROM Ususario u WHERE u. username = :nombreUsu

ario AND u. password = :password");

query.setParameter("nombreUsuario", username);

query.setParameter("password", password);

usuario = (Usuario)query.uniqueResult();

}

catch (HibernateException he)

{

manejaExcepcion(he);

}

finally

{

terminaOperacion();

}

return usuario;

}

Y listo, con esto podremos recuperar a un Usuario dado su nombre de usuario y contraseña (username y password). Probemos que

este método funciona. Para esto, en nuestra clase “Main” crearemos un método llamado “creaUsuarios” que se encargará de llenar

nuestra tabla de Usuarios con algunos datos. Colocamos el método como sigue:

private void creaUsuarios()

{

try

{

AbstractDAO.almacenaEntidad(new Usuario("Usuario de Prueba numero 1", "estudioso", "de

svelado"));

AbstractDAO.almacenaEntidad(new Usuario("Usuario de Prueba numero 2", "caperucita", "l

oboFeroz"));

AbstractDAO.almacenaEntidad(new Usuario("Usuario de Prueba numero 3", "empleado", "inf

elicidad"));

AbstractDAO.almacenaEntidad(new Usuario("Usuario de Prueba numero 4", "usuarioComun",

"password"));

}

catch (HibernateException he)

{

System.err.println("Ocurrió un error al agregar los usuarios...");

he.printStackTrace();

}

}

Como vemos, aquí usamos el método estático “almacenaEntidad” de clase “AbstractDAO” para guardar 4 Usuarios en la base de

datos. Mandamos llamar a este método en el constructor de nuestra clase “Main”.

Ahora creamos un método para probar la recuperación de Usuarios, el cual intentará recuperar al "Usuario de prueba 2", usando

su nombre de usuario “caperucita” y su contraseña “loboFeroz”. Si el Usuario se encuentra mostraremos su nombre en la

Page 145: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 9: Parámetros en HQL

www.javatutoriales.com/2010/07/hibernate-parte-9-parametros-en-hql.html 13/19

consola, si no mostraremos un mensaje de error. El método queda de la siguiente forma:

private void buscaUsuario()

{

Usuario usuario = null;

UsuariosDAO usuariosDAO = new UsuariosDAO();

try

{

usuario = usuariosDAO.getUsuario("caperucita", "loboFeroz");

}catch (HibernateException e)

{

System.err.println("Ocurrió un error al recuperar usuario");

e.printStackTrace();

}

if(usuario == null)

{

System.out.println("No se encontró al usuario");

}

else

{

System.out.println("El usuario es: " + usuario.getNombre());

}

}

Invocamos a este método en el constructor de la clase “Main”, justo después de la llamada a “creaUsuarios”.

public Main()

{

creaUsuarios();

buscaUsuario();

}

Ejecutamos la aplicación y vemos que la salida generada nos indica que, efectivamente, el Usuario recuperado es el Usuario 2, con

nombre “Usuario de Prueba numero 2”:

En realidad creo que no hay mucho más que decir en este punto sobre parámetros con nombre, así que lo dejaré hasta aquí para pasar a

la siguiente y última parte de este tutorial en la que veremos cómo pasar parámetros a una consulta por si posición.

Este estilo de paso de parámetros les será muy conocido a los qué han trabajado con PreparedStatement en JDBC ya que, igual que

en estos últimos establecemos en qué parte de la consulta deseamos que se coloquen los parámetros, haciendo uso de signos de cierre

de interrogación “?”, que establecemos después usando la posición de los signos de interrogación (iniciando en 0).

Para establecer los parámetros usando esta forma, nuevamente hacemos uso del método “setParameter” de la interface

“org.hibernate.Query” que hemos venido usando hasta el momento.

Para este segundo ejemplo haremos algo un poco más complejo. En primer lugar lo que intentaremos hacer es recuperar todos los

Usuarios que han comprado un Producto que ya ha sido dado de baja del catálogo (que su estado sea “INACTIVO”) en el catálogo de

Productos y que además, queremos que el código postal de su “Direccion” sea igual al que le pasemos como parámetro a la

función.

Como lo que queremos hacer es recuperar una lista de Usuarios la primera parte de nuestra consulta queda de la siguiente manera:

Parámetros Posicionales estilo JDBC

Page 146: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 9: Parámetros en HQL

www.javatutoriales.com/2010/07/hibernate-parte-9-parametros-en-hql.html 14/19

SELECT u FROM Usuario u

En este caso el “SELECT u” es necesario ya que haremos joins con varias otras entidades. Ahora, recordemos que cada “Usuario”

está relacionado con la entidad “Producto” mediante las “Compras” que ha realizado, por lo que el siguiente paso será hacer un join

entre estas dos entidades. El join será de tipo fetch join ya que queremos que la Compra del Usuario sea recuperada junto con

los datos del Usuario, de la siguiente forma:

SELECT u FROM Usuario u JOIN FETCH u.compras c

Y después hacemos nuevamente un join entre las Compras y los Productos:

SELECT u FROM Usuario u JOIN FETCH u.compras c JOIN c.productos p

El paso anterior es necesario ya que tanto las Compras del lado de del Usuario como los Productos del lado de la Compra son

colecciones.

Finalmente, colocaremos las dos restricciones que mencionamos anterioremente: que el estatus sea igual a un valor (que

posteriormente estableceremos como inactivo y que el código postal sea igual al que le pasemos como parámetros). Recordemos que

colocaremos signos de “?” en los lugares en los que queremos que posteriormente sean colocados nuestros parámetros, por lo que la

consulta final queda de la siguiente forma:

SELECT u FROM Usuario u JOIN FETCH u.compras c JOIN c.productos p WHERE p.estatus = ? AND u.direcc

ion.codigoPostal = ?

Ahora estableceremos los valores de los parámetros. Como dije antes, para eso usaremos un número que representa la posición de cada

uno de los signos de “?”, iniciando en 0. Por lo tanto, la posición del parámetro asociado a “p.estatus” será “0” y la posición del

parámetro asociado a “u.direccion.codigoPostal” será "1".

Establecemos los valores de la siguiente forma:

query.setParameter(0, Producto.Estatus.INACTIVO);

query.setParameter(1, codigoPostal);

Finalmente, como lo que queremos hacer es obtener una lista de Usuarios usamos el método “list” de la interface

“org.hibernate.Query”:

List<Usuario> listaUsuarios = query.list();

Y ya está. Con esto tenemos todo lista la consulta. Pondremos todo lo anterior en un método llamado

“getUsuariosConComprasInactivas” en la clase “UsuadiosDAO”:

Page 147: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 9: Parámetros en HQL

www.javatutoriales.com/2010/07/hibernate-parte-9-parametros-en-hql.html 15/19

public List<Usuario> getUsuariosConComprasInactivas(String codigoPostal) throws HibernateExcep

tion

{

List<Usuario> listaUsuarios = null;

try

{

iniciaOperacion();

Query query = getSession().createQuery("FROM Usuario u JOIN u.compras c JOIN c.product

os p WHERE p.estatus = ? AND u.direccion.codigoPostal = ?");

query.setParameter(0, Producto.Estatus.INACTIVO);

query.setParameter(1, codigoPostal);

listaUsuarios = query.list();

}catch(HibernateException he)

{

he.printStackTrace();

manejaExcepcion(he);

}finally

{

terminaOperacion();

}

return listaUsuarios;

}

Ahora probemos que este método funciona correctamente. Primero coloquemos algunos datos en la base creando un método llamado

“creaCompras” en la clase “Main”. El método queda de la siguiente forma:

Page 148: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 9: Parámetros en HQL

www.javatutoriales.com/2010/07/hibernate-parte-9-parametros-en-hql.html 16/19

private void creaCompras()

{

Producto libros = new Producto("Libro", "ABC123456", 120.0F);

Producto iPads = new Producto("iPad", "RAF755576", 1315.45F);

Producto televisor = new Producto("T.V.", "AOF765984", 379.64F);

Producto postales = new Producto("Postal", "ELF", 15.65F);

Producto juegos = new Producto("Videojuego", "MEN", 158.24F);

libros.setEstatus(Producto.Estatus.INACTIVO);

postales.setEstatus(Producto.Estatus.INACTIVO);

Usuario usuario1 = new Usuario("Usuario de Prueba numero 1", "estudioso", "desvelado");

usuario1.setDireccion(new Direccion("calle principal", "12345"));

Usuario usuario2 = new Usuario("Usuario de Prueba numero 2", "caperucita", "loboFeroz");

usuario2.setDireccion(new Direccion("primera avenida", "AVR-175"));

Usuario usuario3 = new Usuario("Usuario de Prueba numero 3", "empleado", "infelicidad");

usuario3.setDireccion(new Direccion("puesta del sol", "12345"));

Usuario usuario4 = new Usuario("Usuario de Prueba numero 4", "usuarioComun", "password");

usuario4.setDireccion(new Direccion("Este 145", null));

Compra compraUsuario1 = new Compra();

compraUsuario1.addProducto(libros);

usuario1.addCompra(compraUsuario1);

compraUsuario1 = new Compra();

compraUsuario1.addProducto(televisor);

compraUsuario1.addProducto(juegos);

usuario1.addCompra(compraUsuario1);

Compra compraUsuario2 = new Compra();

compraUsuario2.addProducto(iPads);

compraUsuario2.addProducto(televisor);

compraUsuario2.addProducto(juegos);

usuario2.addCompra(compraUsuario2);

Compra compraUsuario3 = new Compra();

compraUsuario3.addProducto(iPads);

compraUsuario3.addProducto(televisor);

usuario3.addCompra(compraUsuario3);

compraUsuario3 = new Compra();

compraUsuario3.addProducto(postales);

compraUsuario3.addProducto(juegos);

usuario3.addCompra(compraUsuario3);

Compra compraUsuario4 = new Compra();

compraUsuario4.addProducto(libros);

usuario4.addCompra(compraUsuario4);

try

{

AbstractDAO.almacenaEntidad(libros);

AbstractDAO.almacenaEntidad(iPads);

AbstractDAO.almacenaEntidad(televisor);

AbstractDAO.almacenaEntidad(postales);

AbstractDAO.almacenaEntidad(juegos);

AbstractDAO.almacenaEntidad(usuario1);

AbstractDAO.almacenaEntidad(usuario2);

AbstractDAO.almacenaEntidad(usuario3);

AbstractDAO.almacenaEntidad(usuario4);

}

catch (HibernateException he)

{

System.err.println("Ocurrió un error al agregar los usuarios...");

he.printStackTrace();

}

}

El método anterior se encarga de crear algunos Productos, Usuarios y Compras para estos Usuarios. Ahora crearemos el método

que se encargará de ejecutar la consulta que hemos realizado y mostrar en la salida el nombre de los Usuarios que cumplan con la

condición y los Productos que compraron:

Page 149: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 9: Parámetros en HQL

www.javatutoriales.com/2010/07/hibernate-parte-9-parametros-en-hql.html 17/19

Publicado por Alex en 19:18

Reacciones: divertido (0) interesante (0) increible (0) no me gusta (0)

Etiquetas: hibernate, hql, parametros, parametros por nombre, parametros posicionales

private void buscaUsuariosProductosInactivos()

{

List<Usuario> listaUsuarios = new UsuariosDAO().getUsuariosConComprasInactivas("12345");

for(int i = 0; i < listaUsuarios.size(); i++)

{

Usuario usuarioActual = listaUsuarios.get(i);

System.out.println("\nUsuario: " + usuarioActual.getNombre());

List<Compra> listaCompras = usuarioActual.getCompras();

for(int numeroCompra = 0; numeroCompra < listaCompras.size(); numeroCompra++)

{

List<Producto> listaProductos = listaCompras.get(numeroCompra).getProductos();

for(int numeroProducto = 0; numeroProducto < listaProductos.size(); numeroProducto

++)

{

Producto producto = listaProductos.get(numeroProducto);

System.out.println("\t" + producto.getNombre());

}

}

}

}

El método anterior solo muestra en la salida el nombre del Usuario que cumple con las condiciones que establecimos, y los

Productos que compró.

Coloquemos los dos métodos anteriores en el constructor de nuestra clase “Main” y ejecutemos la aplicación para ver cuál es la salida

generada:

Como podemos ver, hay dos Usuarios que cumplen con la condición de habar comprado un Producto inactivo (Libro y Postal) y

que tienen como código postal “12345”.

Pues bien, con esto concluye este tutorial sobre paso de parámetros a consultas HQL. Espero que les sea de utilidad. En el próximo

tutorial mostraré cómo usar Hibernate cuando tenemos clases entidad que hacen uso de Herencia (clases que extienden de otras clases).

No olviden dejar sus dudas, comentarios y sugerencias en la sección de comentarios.

Saludos.

Descarga los archivos de este tutorial desde aquí:

Parámetros en HQL con Anotaciones

Parámetros en HQL con Archivos de Mapeo

Entradas Relacionadas:

Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

Parte 2: Persistiendo Objetos Simples usando Anotaciones (Metadatos)

Parte 3: Relaciones / Uno a uno

Parte 4: Relaciones / Uno a muchos

Parte 5: Relaciones / Muchos a uno

Parte 6: Relaciones / Muchos a Muchos

Parte 7: HQL Primera Parte

Parte 8: HQL Segunda Parte

Parte 10: Herencia

Parte 11: Interceptores y Eventos

Recomendar esto en Google

Page 150: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 1/35

Blog dedicado a temas de programación actuales usando el lenguaje de programación Java y las últimas versiones de sus APIs y Herramientas.

Tutoriales de Programacion Java

Página principal Página de Concursos Tutoriales UPAO 2010 Presentaciones Capacitación

MARTES, 3 DE AGOSTO DE 2010

Hibernate - Parte 10: Herencia

La herencia es uno de los mecanismos que nos proporciona la orientación a objetos más poderosos, ya que gracias a ella surgen cosas

como la sobreescritura, la reutilización y extensibilidad de código, etc.

Ya que le herencia facilita la creación de clases a partir de otras clases ya existentes en algunas ocasiones nuestras clases entidad

necesitarán extender de otras clases, entidades o no. Como por ejemplo, podemos tener una clase "Usuario" de las cuales extiendan

las clases "Administrador", "Operador" y "SuperUsuario".

Mapear una jerarquía de clases a tablas relacionales puede ser un problema un poco complejo. Afortunadamente Hibernate nos

proporciona 3 formas o estrategias de manejar la herencia con nuestras clases entidad. En este tutorial veremos cómo usar estas tres

formas de herencia y las implicaciones que tienen el uso de una u otra. Veremos cómo hacer esto tanto con anotaciones como con

archivos de mapeo.

Las tres estrategias de herencia que soporta Hibernate son:

Una tabla por toda la jerarquía de clases

Una tabla por cada subclase (joins)

Una tabla por cada clase concreta (uniones)

A continuación veremos cómo funciona cada una de estas estrategias, pero primero veamos el modelo de datos que usaremos para este

tutorial.

Crearemos un proyecto en NetBeans para mostrar los ejemplos (menú "File -> New Project... -> Java -> Java

Application"). Le damos un nombre y una ubicación al proyecto y nos aseguramos de que las opciones "Create Main Class" y

"Set as Main Project" estén habilitadas. Presionamos el botón "Finish" y veremos aparecer en el editor nuestra clase "Main".

Agregamos la biblioteca de "Hibernate", que creamos en el primer tutorial de la serie. Hacemos clic derecho en el nodo "Libraries"

del proyecto. En el menú contextual que se abre seleccionamos la opción "Add Library...":

En la ventana que se abre seleccionamos la biblioteca "Hibernate":

Presionamos el botón "Add Library" para que la biblioteca se agregue a nuestro proyecto. Si van a usar anotaciones además deben

agregar la biblioteca "HibernateAnotaciones" que creamos en el segundo tutorial. Aprovechamos también para agregar el conector

de MySQL. Debemos tener los siguientes archivos en nuestro proyecto:

DONACIONES

JAVA TUTORIALES EN FACEBOOK

Java Tutoriales

Me gusta

A 7896 personas les gusta

Plug-in social de Facebook

Java Tutoriales

Amigo programador, tú que

estás todo el día picando

código, escucha estas

recomendaciones sólo

hechas para ti..... ¿qué es

esto?... Gracias Jorge

Rubira....

No olviden poner la

sentencia where al hacer

un borraro en la base de

datos..

http://www.youtube.com/

watch?v=i_cVJgIz_Cs

Compartir 1 Más Siguiente blog»

Page 151: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 2/35

Ahora creamos dos paquetes, uno con el nombre "modelo", que contendrá las clases entidades, y otro con el nombre "dao" que

contendrá nuestras clases para operaciones de persistencia. Adicionalmente si van a trabajar con archivos de mapeo agreguen el paquete

"mapeos" que contendrá los archivos de mapeo XML. Hacemos clic derecho en el nodo del paquete que se creó al generar el proyecto.

En el menú contextual que se abre seleccionamos la opción "New -> Java Package..." y creamos los paquetes.

Que deben quedar así:

Aprovecharemos para crear nuestro archivo de configuración de Hibernate, "hibernate.cfg.xml", el cual será muy parecido al del

primer tutorial:

<?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>

<!-- parametros para la conexion a la base de datos -->

<property name="connection.driver_class">com.mysql.jdbc.Driver</property>

<property name="connection.url">jdbc:mysql://localhost/hibernateherencia</property>

<property name="connection.username">usuario</property>

<property name="connection.password">password</property>

<!-- Configuracion del pool interno -->

<property name="connection.pool_size">1</property>

<!-- Dialecto de la base de datos -->

<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>

<!-- Otras propiedades importantes -->

<property name="show_sql">true</property>

<property name="hbm2ddl.auto">create-drop</property>

<!-- Clases o Archivos de mapeo -->

</session-factory>

</hibernate-configuration>

Java filelibraries

www.aspose.com

DOC, XLS, PPT, PDF,

MSG and more APIs

to Manage, Print and

Convert

Participar en este sitioGoogle Friend Connect

Miembros (173) Más »

¿Ya eres miembro? Iniciar sesión

SEGUIDORES

► 2012 (2)

► 2011 (11)

▼ 2010 (10)

► diciembre (1)

► octubre (3)

► septiembre (2)

▼ agosto (2)

Hibernate - Parte 11: Interceptores yEventos

Hibernate - Parte 10: Herencia

► julio (1)

► mayo (1)

► 2009 (22)

ARCHIVO DEL BLOG

Page 152: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 3/35

También agregaremos nuestra clase "HibernateUtil" al proyecto. Si quieren usar archivos de mapeo deben usar la que creamos en

el primer tutorial; si van a usar anotaciones deben usar la que creamos en el segundo tutorial.

También agregaremos la clase "AbstractDAO", que creamos en el tutorial anterior, en el paquete "dao".

Para los ejercicios usaremos una base de datos llamada "hibernateherencia", en MySQL.

Nota: Yo usaré dos proyectos para cada una de las estrategias de herencia, uno para usar archivos de mapeo y otro para usar anotaciones y que

el código de ambos no se mezcle.

Para los ejemplos usaremos un modelo de datos sencillo, que se muestra en la siguiente imagen:

Tenemos una clase base abstracta llamada "Persona", de la cual extienden dos clases: "Tecnologo" y "Normal" (porque por ahí

dicen que los tecnólogos no somos normales ;)). Finalmente de la clase "Tecnologo" extienden "Programador" y "Tester".

Como podemos ver en el diagrama, cada una de las clases tiene una serie de atributos propios, lo que nos ayudará a ver cómo se

comportan estos en las distintas estrategias de herencia que veremos en un momento.

Las clases quedan de la siguiente manera:

La clase "Persona":

Alex

Programador Java con algunos años de

experiencia en múltiples poyectos y con

múltiples APIs y herramientas deseoso de

compartir experiencias con el resto de

programadores.

Ver todo mi perfil

DATOS PERSONALES

Page 153: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 4/35

public abstract class Persona implements Serializable

{

private long id;

private String nombre;

private int edad;

public Persona()

{

}

public Persona(String nombre, int edad)

{

this.nombre = nombre;

this.edad = edad;

}

public int getEdad()

{

return edad;

}

public void setEdad(int edad)

{

this.edad = edad;

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

}

La clase "Normal" queda así:

public class Normal extends Persona

{

private String ocupacion;

public Normal()

{

}

public Normal(String nombre, int edad, String ocupacion)

{

super(nombre, edad);

this.ocupacion = ocupacion;

}

public String getOcupacion()

{

return ocupacion;

}

public void setOcupacion(String ocupacion)

{

this.ocupacion = ocupacion;

}

}

La clase "Tecnologo" queda de la siguiente forma:

Page 154: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 5/35

public class Tecnologo extends Persona

{

private int aniosDeEstudios;

public Tecnologo()

{

}

public Tecnologo(String nombre, int edad, int aniosDeEstudios)

{

super(nombre, edad);

this.aniosDeEstudios = aniosDeEstudios;

}

public int getAniosDeEstudios()

{

return aniosDeEstudios;

}

public void setAniosDeEstudios(int aniosDeEstudios)

{

this.aniosDeEstudios = aniosDeEstudios;

}

}

La clase "Programador" se ve así:

public class Programador extends Tecnologo

{

private String lenguajeFavorito;

private int aniosDeExperiencia;

public Programador()

{

}

public Programador(String nombre, int edad, int aniosDeEstudios, String lenguajeFavorito, int

aniosDeExperiencia)

{

super(nombre, edad, aniosDeEstudios);

this.lenguajeFavorito = lenguajeFavorito;

this.aniosDeExperiencia = aniosDeExperiencia;

}

public int getAniosDeExperiencia()

{

return aniosDeExperiencia;

}

public void setAniosDeExperiencia(int aniosDeExperiencia)

{

this.aniosDeExperiencia = aniosDeExperiencia;

}

public String getLenguajeFavorito()

{

return lenguajeFavorito;

}

public void setLenguajeFavorito(String lenguajeFavorito)

{

this.lenguajeFavorito = lenguajeFavorito;

}

}

Finalmente la clase "Tester" queda de la siguiente forma:

Page 155: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 6/35

public class Tester extends Tecnologo

{

private String herramientaDeTesteo;

public Tester()

{

}

public Tester(String nombre, int edad, int aniosDeEstudios, String herramientaDeTesteo)

{

super(nombre, edad, aniosDeEstudios);

this.herramientaDeTesteo = herramientaDeTesteo;

}

public String getHerramientaDeTesteo()

{

return herramientaDeTesteo;

}

public void setHerramientaDeTesteo(String herramientaDeTesteo)

{

this.herramientaDeTesteo = herramientaDeTesteo;

}

}

Bien, ya que tenemos las clases entidades que estaremos usando, comencemos con el tutorial.

En esta estrategia se genera una sola tabla en la que se guardan todas las instancias del árbol completo de herencia. O sea, que se

genera una sola tabla, y en esta tiene una columna para cada una de las propiedades de cada una de las clases que confirman la

jerarquía.

Para saber a qué clase concreta pertenece una fila de esta tabla se usa un valor en una columna discriminadora. Esta columna

discriminadora es muy importante ya que gracias a ella sabemos a qué clase pertenece cada una de las filas de nuestra tabla. El valor

contenido puede ser cualquiera que nosotros indiquemos para cada una de nuestras entidades, y puede ser una cadena, un carácter, o un

entero.

En este caso nuestra base de datos estaría formada por una sola tabla, la cual quedaría de la siguiente forma:

Donde "DISC" es nuestra columna discriminadora, o sea que en esta columna se guardará el valor que nos indicará a cuál de nuestras

clases pertenece cada una de las instancias que almacenemos en la base de datos.

Como podemos ver en la imagen anterior, la tabla de nuestra base de datos tiene las propiedades de todas las clases que conforman la

jerarquía de "Persona".

Esta estrategia es buena en el sentido de que, como solo tenemos una tabla, no es necesario hacer joins o uniones para recuperar

los datos de uno a varios objetos, por lo que es ideal en situaciones en las que necesitamos un rendimiento muy alto, como para la

generación de reportes.

Sin embargo no todo siempre es color de rosa, ya que las columnas que representan a los atributos de las subclases deben declararse

con la posibilidad de tener valores nulos, ya que como solo guardaremos un objeto de una clase por fila, la columnas que representen a

los atributos que no tenga ese objeto quedarán vacías. Debido a esto puede ser que se tengan problemas de integridad.

Ahora veremos cómo implementar esta estrategia de herencia en nuestras aplicaciones.

Creamos, en el paquete "mapeos" un nuevo documento XML. Le damos el nombre de "Persona.hbm" (el asistente se encargará de

colocar el ".xml")

Tabla por Jerarquía de Clases

TABLA POR JERARQUÍA DE CLASES USANDO ARCHIVOS DE MAPEO

Page 156: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 7/35

Presionamos el botón "Next >" e indicamos que queremos crear un documento XML bien formado (la primera opción) y presionamos el

botón "Finish".

Eliminamos el contenido del archivo creado y colocamos el siguiente:

<?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="hibernate.herencia.modelo.Persona" table="personas">

</class>

</hibernate-mapping>

El mapeo de la clase "Persona" es muy simple, como el del primer tutorial de la serie, con la única diferencia de que debemos indicarle

cuál será el nombre y el tipo de la columna discriminadora de la siguiente forma:

<discriminator column="DISC" type="string" />

En donde indicamos que el nombre de nuestra columna discriminaora será "DISC" (aunque puede tener el nombre que ustedes elijan) y

que será de tipo "string". Este elemento debe ir después del elemento "id" en el archivo de mapeo de esa forma:

<?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="hibernate.herencia.modelo.Persona" table="personas">

<id name="id">

<generator class="identity" />

</id>

<discriminator column="DISC" type="string" />

</class>

</hibernate-mapping>

El mapeo completo de "Persona" queda de la siguiente forma:

<?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="hibernate.herencia.modelo.Persona" table="personas">

<id name="id">

<generator class="identity" />

</id>

<discriminator column="DISC" type="string" />

<property name="nombre" />

<property name="edad" />

</class>

</hibernate-mapping>

Como podemos ver, es prácticamente igual al que vimos en el primer tutorial. Pasemos ahora a ver cómo comenzar a mapear cada una

de sus subclases.

Para mapear una subclase en la estrategia de una sola tabla por jerarquía de clases usamos el elemento "<subclass>" en vez de "

<class>" en el archivo de mapeo. Por ejemplo, para mapear la clase "Normal" creamos un nuevo archivo de mapeo llamado

Page 157: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 8/35

"Normal.hbm.xml" en el paquete "mapeos". Eliminamos el contenido del archivo generado y colocamos el siguiente:

<?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>

<subclass name="hibernate.herencia.modelo.Normal" >

</subclass>

</hibernate-mapping>

Como vemos, ahora en vez de colocar el elemento "<class>" colocamos "<subclass>". En este elemento indicamos, como se

muestra arriba, el nombre de la clase que estamos mapeando (que en este caso es nuestra clase "Normal"); pero además debemos

indicar otras cosas. ¿Recuerdan que en el mapeo de la clase base indicamos una columna discriminadora? ¿Y recuerdan que cuando lo

colocamos dijimos que cada subclase debe indicar su valor tener un valor para esa columna discriminadora? Pues bien, el elemento "

<subclass>" es el lugar para indicar este valor. Para el caso de la clase "Normal" indicamos que su valor para la columna

discriminadora será "nrm" usando el atributo "discriminator-value":

<subclass name="hibernate.herencia.modelo.Normal" discriminator-value="nrm">

Finalmente, indicamos de cuál de nuestras clases extiende, con el atributo "extends":

<subclass name="hibernate.herencia.modelo.Normal" discriminator-value="nrm" hibernate.herencia.mod

elo.Persona">

Y listo, esto es todo lo que necesitamos hacer para indicar en nuestro mapeo que usaremos la estrategia de una sola tabla.

El resto del archivo de mapeo de la clase "Normal" es igual a los que ya hemos visto, y queda de la siguiente forma:

<?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>

<subclass name="hibernate.herencia.modelo.Normal" discriminator-value="nrm" extends="hibernate

.herencia.modelo.Persona">

<property name="ocupacion" />

</subclass>

</hibernate-mapping>

Como podemos ver, no es muy diferente a los mapeos que hemos hecho hasta ahora, solo hay que recordar cambiar el elemento "

<class>" por "<subclass>" y agregar algunos atributos.

Fijense que en la clase "Normal" no fué necesario colocar un "id", ya que este será heredado de su clase padre "Persona".

El resto de los mapeos son prácticamente iguales, solo hay que tener cuidado en cambiar el valor del discriminador en cada una de

nuestras subclases, viendo que no haya dos iguales, y qué clase extiende cuál subclase. Para el resto de los mapeos los

discriminadores quedarán de esta forma:

Clase Disc

Normal nrm

Tecnologo tec

Programador pro

Tester tes

Y los archivos de mapeo quedan de la siguiente forma:

Para la clase "Tecnlologo":

Page 158: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 9/35

<?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>

<subclass name="hibernate.herencia.modelo.Tecnologo" discriminator-value="tec" extends="hibern

ate.herencia.modelo.Persona">

<property name="aniosDeEstudios" />

</subclass>

</hibernate-mapping>

Para la clase "Programador":

<?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>

<subclass name="hibernate.herencia.modelo.Programador" discriminator-value="pro" extends="hibe

rnate.herencia.modelo.Tecnologo">

<property name="lenguajeFavorito" />

<property name="aniosDeExperiencia" />

</subclass>

</hibernate-mapping>

Y para la clase "Tester":

<?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>

<subclass name="hibernate.herencia.modelo.Tester" discriminator-value="tes" extends="hibernate

.herencia.modelo.Tecnologo">

<property name="herramientaDeTesteo" />

</subclass>

</hibernate-mapping>

Como vemos, esta forma de indicar la herencia es realmente simple. No olviden indicar todos estos archivos de mapeo en el archivo

"hibernate.cfg.xml":

<mapping resource="hibernate/herencia/mapeos/Persona.hbm.xml" />

<mapping resource="hibernate/herencia/mapeos/Normal.hbm.xml" />

<mapping resource="hibernate/herencia/mapeos/Tecnologo.hbm.xml" />

<mapping resource="hibernate/herencia/mapeos/Programador.hbm.xml" />

<mapping resource="hibernate/herencia/mapeos/Tester.hbm.xml" />

Adicionalmente también es posible indicar toda la jerarquía en un solo archivo de mapeo de esta forma:

Page 159: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 10/35

<?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="hibernate.herencia.modelo.Persona" table="personas">

<id name="id">

<generator class="identity" />

</id>

<discriminator column="DISC" type="string" />

<property name="nombre" />

<property name="edad" />

<subclass name="hibernate.herencia.modelo.Normal" discriminator-value="nrm">

<property name="ocupacion" />

</subclass>

<subclass name="hibernate.herencia.modelo.Tecnologo" discriminator-value="tec">

<property name="aniosDeEstudios" />

</subclass>

<subclass name="hibernate.herencia.modelo.Programador" discriminator-value="pro">

<property name="lenguajeFavorito" />

<property name="aniosDeExperiencia" />

</subclass>

<subclass name="hibernate.herencia.modelo.Tester" discriminator-value="tes">

<property name="herramientaDeTesteo" />

</subclass>

</class>

</hibernate-mapping>

En el archivo de mapeo anterior tenemos los elementos "<subclass>", que representan cada una de las subclases que extienden de

"Persona", dentro del elemento "<class>" que mapea a esta clase. Además aquí no es necesario indicar de cuál clase extiende cada

una de las subclases, ya que esta información se infiere del elemento "<class>" que las contiene.

Esta forma de mapeo me parece ideal si nuestras subclases no tendrán muchos atributos, así no es necesario crear un archivo de mapeo

para cada una de ellas.

Probemos cómo es generada nuestra tabla y como se guardan los mismos en nuestra base de datos. En el método "main" de nuestra

clase "Main" guardemos un elemento de cada uno de los tipos de objetos, y adicionalmente guardemos otro "Programador":

public static void main(String[] args)

{

Normal normal = new Normal("normal", 21, "Empleado");

Tecnologo tecnologo = new Tecnologo("tecnologo", 24, 4);

Programador programador1 = new Programador("primer programador", 25, 4, "java", 4);

Programador programador2 = new Programador("segundo programador", 25, 5, "java", 2);

Tester tester = new Tester("tester", 18, 3, "JUnit");

AbstractDAO.almacenaEntidad(normal);

AbstractDAO.almacenaEntidad(tecnologo);

AbstractDAO.almacenaEntidad(programador1);

AbstractDAO.almacenaEntidad(programador2);

AbstractDAO.almacenaEntidad(tester);

}

En el código anterior usamos nuestra clase "AbstractDAO" para almacenar los 5 objetos que creamos. Ahora ejecutemos la aplicación

y veamos la tabla generada y los datos que se almacenaron en ella.

Vemos qué tablas están en nuestra base de datos "hibernateherencia" y que, efectivamente, solo tenemos una tabla llamada

"personas". Si vemos cómo está conformada esta tabla nos encontramos con que tiene una columna para cada uno de los atributos de

cada una de las clases dentro de la jerarquía que hemos usado para el ejemplo:

Page 160: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 11/35

Ahora veamos los datos que contiene:

Como vemos, se han guardado los 5 objetos en esta tabla, justamente como lo estábamos esperando. En la imagen podemos ver los

valores que se han guardado en la columna discriminadora y que los valores de las columnas que no tiene una clase se ha establecido a

"NULL" en todos los casos.

Ahora veamos cómo hacer esto mismo pero usando archivos de mapeo.

Nuestras clases entidad serán anotadas prácticamente igual que en el segundo tutorial, con unas pequeñas diferencias para indicar el tipo

de herencia que queremos usar.

Para indicar que nuestras entidades harán uso de herencia en nuestra base de datos usamos la anotación "@Inheritance" en la clase

que representa la raíz de la jerarquía, en este caso en la clase "Persona". Para indicar cuál de las estrategias de herencia queremos

usar usamos su atributo "strategy" de esta forma:

@Entity

@Inheritance(strategy=InheritanceType.SINGLE_TABLE)

public abstract class Persona implements Serializable

{

}

Sin embargo cómo el valor por default del atributo "strategy" es "InheritanceType.SINGLE_TABLE" podríamos no poner nada

dejándolo así:

@Entity

@Inheritance

public abstract class Persona implements Serializable

{

}

Solo con eso ya estamos indicando que la clase "Persona" es una entidad y que ella y todas las clases que extiendan de ella usarán la

estrategia de herencia de una sola tabla. Sin embargo aún podemos indicar algo más. ¿Recuerdan la columna discriminadora que se usa

en la tabla? Pues también podemos indicar cuál será el nombre y el tipo de esta columna. Si no indicamos nada (como arriba) el nombre

de la columna será "DTYPE" y el tipo será "String". Si queremos indicar alguna otra cosa podemos hacerlo mediante la anotación

"@DiscriminatorColumn". Esta anotación tiene dos atributos importantes: "name" que indica cuál será el nombre de la columna, y

"discriminatorType" que indica de qué tipo será el discriminador. Este último atributo puede tomar uno de tres valores:

DiscriminatorType.STRING (este es el valor por default)

DiscriminatorType.CHAR

DiscriminatorType.INTEGER

Cambiemos el nombre que Hibernate le da por default a nuestra columna discriminadora para usar "DIS":

TABLA POR JERARQUÍA DE CLASES USANDO ANOTACIONES

Page 161: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 12/35

@Entity

@Inheritance(strategy=InheritanceType.SINGLE_TABLE)

@DiscriminatorColumn(name="DIS", discriminatorType=DiscriminatorType.STRING)

public abstract class Persona implements Serializable

{

}

Y el resto de las anotaciones que usamos en "Persona" son las que ya habíamos visto en el segundo tutorial. Al final la clase

"Persona" queda así:

@Entity

@Inheritance(strategy=InheritanceType.SINGLE_TABLE)

@DiscriminatorColumn(name="DIS", discriminatorType=DiscriminatorType.STRING)

public abstract class Persona implements Serializable

{

@Id

@GeneratedValue(strategy=GenerationType.IDENTITY)

private long id;

private String nombre;

private int edad;

public Persona()

{

}

public Persona(String nombre, int edad)

{

this.nombre = nombre;

this.edad = edad;

}

public int getEdad()

{

return edad;

}

public void setEdad(int edad)

{

this.edad = edad;

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

}

Page 162: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 13/35

}

Ahora veremos cómo se hace el mapeo del resto de nuestras clases que, en realidad, es algo bastante simple.

Para hacer que nuestra clase "Normal" quede lista para ser usada en nuestra aplicación solo es necesario hacer dos cosas. La primera

es indicar, usando la anotación "@Entity" que se trata de una entidad, de esta forma:

@Entity

public class Normal extends Persona

{

}

Hasta aquí todo sigue siendo lo que ya conocemos. El cambio viene solamente en que hay que indicar, al igual que lo hicimos con los

archivos de mapeo, cuál será el valor que identificará a los registros de esta entidad en la columna discriminadora. Y esto lo hacemos con

la anotación "@DiscriminatorValue" en cuyo atributo "value" indicamos cuál será el valor que tendrá nuestra columna. En el caso

de "Normal" pondremos que el valor para la columna discriminadora que lo representará será "NM":

@Entity

@DiscriminatorValue(value="NM")

public class Normal extends Persona

{

}

Y listo. El resto de la clase, en este caso, no lleva ninguna anotación, y al final queda de la siguiente manera:

@Entity

@DiscriminatorValue(value="NM")

public class Normal extends Persona

{

private String ocupacion;

public Normal()

{

}

public Normal(String nombre, int edad, String ocupacion)

{

super(nombre, edad);

this.ocupacion = ocupacion;

}

public String getOcupacion()

{

return ocupacion;

}

public void setOcupacion(String ocupacion)

{

this.ocupacion = ocupacion;

}

}

Para el resto de las clases hacemos exactamente lo mismo, indicamos con "@Entity" que nuestra clase será una entidad, y usamos

"@DiscriminatorValue" para indicar cuál será el valor de la clase en la columna discriminadora. Para el resto de las clases los

discriminadores quedarán de esta forma:

Clase Disc

Normal NM

Tecnologo TC

Programador PG

Tester TS

Y las clases de la siguiente forma:

La clase "Tecnologo" queda así:

Page 163: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 14/35

@Entity

@DiscriminatorValue(value="TC")

public class Tecnologo extends Persona

{

private int aniosDeEstudios;

public Tecnologo()

{

}

public Tecnologo(String nombre, int edad, int aniosDeEstudios)

{

super(nombre, edad);

this.aniosDeEstudios = aniosDeEstudios;

}

public int getAniosDeEstudios()

{

return aniosDeEstudios;

}

public void setAniosDeEstudios(int aniosDeEstudios)

{

this.aniosDeEstudios = aniosDeEstudios;

}

}

La clase "Programador" queda así:

@Entity

@DiscriminatorValue(value="PG")

public class Programador extends Tecnologo

{

private String lenguajeFavorito;

private int aniosDeExperiencia;

public Programador()

{

}

public Programador(String nombre, int edad, int aniosDeEstudios, String lenguajeFavorito, int

aniosDeExperiencia)

{

super(nombre, edad, aniosDeEstudios);

this.lenguajeFavorito = lenguajeFavorito;

this.aniosDeExperiencia = aniosDeExperiencia;

}

public int getAniosDeExperiencia()

{

return aniosDeExperiencia;

}

public void setAniosDeExperiencia(int aniosDeExperiencia)

{

this.aniosDeExperiencia = aniosDeExperiencia;

}

public String getLenguajeFavorito()

{

return lenguajeFavorito;

}

public void setLenguajeFavorito(String lenguajeFavorito)

{

this.lenguajeFavorito = lenguajeFavorito;

}

}

Y Finalmente la clase "Tester" queda de esta forma:

Page 164: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 15/35

@Entity

@DiscriminatorValue(value="TS")

public class Tester extends Tecnologo

{

private String herramientaDeTesteo;

public Tester()

{

}

public Tester(String nombre, int edad, int aniosDeEstudios, String herramientaDeTesteo)

{

super(nombre, edad, aniosDeEstudios);

this.herramientaDeTesteo = herramientaDeTesteo;

}

public String getHerramientaDeTesteo()

{

return herramientaDeTesteo;

}

public void setHerramientaDeTesteo(String herramientaDeTesteo)

{

this.herramientaDeTesteo = herramientaDeTesteo;

}

}

No olviden agregar estas clases al archivo de configuración "hibernate.cfg.xml" de la siguiente forma:

<mapping class="hibernate.herencia.modelo.Persona"/>

<mapping class="hibernate.herencia.modelo.Normal"/>

<mapping class="hibernate.herencia.modelo.Tecnologo"/>

<mapping class="hibernate.herencia.modelo.Programador"/>

<mapping class="hibernate.herencia.modelo.Tester"/>

Probemos que lo anterior funciona usando, nuevamente, el siguiente código en el método "main" de nuestra clase "Main".

public static void main(String[] args)

{

Normal normal = new Normal("normal", 21, "Empleado");

Tecnologo tecnologo = new Tecnologo("tecnologo", 24, 4);

Programador programador1 = new Programador("primer programador", 25, 4, "java", 4);

Programador programador2 = new Programador("segundo programador", 25, 5, "java", 2);

Tester tester = new Tester("tester", 18, 3, "JUnit");

AbstractDAO.almacenaEntidad(normal);

AbstractDAO.almacenaEntidad(tecnologo);

AbstractDAO.almacenaEntidad(programador1);

AbstractDAO.almacenaEntidad(programador2);

AbstractDAO.almacenaEntidad(tester);

}

Al ejecutar la aplicación podemos ver que se genera una sola tabla en la base de datos llamada "persona" constituida por las siguientes

columnas:

Si vemos qué datos contiene esta tabla podemos observar que se han guardado los datos de la forma que esperábamos y que, al igual

que con los archivos de mapeo, en la columna discriminadora se han guardado los valores indicados y que en las columnas que

representan los atributos de una clase distinta a la del registro se ha colocado el valor "NULL" en todos los casos:

Page 165: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 16/35

Como podemos ver, con esta estrategia pasamos de nuestra jerarquía de clases a una sola tabla, como se ilustra en la siguiente imagen:

Ahora que ya hemos visto cómo trabajar con la estrategia de una sola tabla, veamos la siguiente de las estrategias de herencia

proporcionadas por hibérnate.

En esta estrategia de herencia se creará una tabla por cada una de las clases que conformen nuestro árbol de herencia. Cada una de

las clases y subclases que declaren atributos persistentes, incluyendo clases abstractas e interfaces, tendrá su propia tabla.

La representación de la relación de herencia entra estas tablas se hace mediante una llave foránea.

A diferencia de la estrategia de una tabla por jerarquía de clases, aquí cada tabla contiene solamente las columnas que representan los

atributos declarados en la clase, junto con una columna para la llave primaria que es también una llave foránea de la super clase. Por lo

tanto nuestra base de datos quedará de la siguiente forma:

Las tablas que representan a las clases padre y las que representan a las clases hijas son unidas por el valor de sus llaves primarias

compartidas. Gracias a esto es posible recuperar de la base de datos las instancias de una subclase haciendo un "join" entre la tabla

de la subclase con la de la superclase.

Esta estrategia tiene la ventaja de que las modificaciones y actualizaciones de una clase no afectan a los datos almacenados de las otras

clase, además de que nuestro esquema de base de datos está normalizado.

Su desventaja es que la búsqueda de cuál clase concreta pertenece una fila debe realizarse en cada una de las tablas de las clases que

componen la jerarquía, que en este caso serían 5. Debido a esto mismo, si debemos escribir nosotros mismos algo de SQL (digamos,

para hacer reportes) la tarea se complica.

Además de esto el rendimiento puede ser muy malo si tenemos jerarquías complejas de clases.

Veamos cómo implementar esta estrategia en nuestra aplicación.

Creamos, en el paquete "mapeos" un nuevo documento XML. Le damos el nombre de "Persona.hbm" (el asistente se encargará de

colocar el ".xml")

Presionamos el botón "Next >" e indicamos que queremos crear un documento XML bien formado (la primera opción) y presionamos el

botón "Finish".

Una Tabla para cada Subclase (joins)

UNA TABLA PARA CADA SUBCLASE (JOINS) USANDO ARCHIVOS DE MAPEO

Page 166: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 17/35

En esta estrategia la clase base no necesita tener algo especial. Por lo que el mapeo de clase "Persona" queda de forma simple.

Eliminamos el contenido del archivo creado y colocamos el siguiente:

<?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="hibernate.herencia.modelo.Persona" table="personas">

<id name="id">

<generator class="identity" />

</id>

<property name="nombre" />

<property name="edad" />

</class>

</hibernate-mapping>

Ahora comienza la parte interesante que es crear el mapeo de las subclases. Para indicar que usaremos la estrategia de una tabla por

subclase usamos, en el archivo de mapeo de las subclases, en vez del elemento "<class>", el elemento "<joined-subclass>". En

este elemento indicamos el nombre de la clase que estamos mapeando, usando el atributo "name", junto con la tabla en la que se

guardarán, usando el atributo "table", de la siguiente forma:

<hibernate-mapping>

<joined-subclass name="hibernate.herencia.modelo.Normal" table="normales">

</joined-subclass>

</hibernate-mapping>

También, dentro del elemento "<joined-subclass>" indicamos de cuál clase extiende la clase que estamos mapeando, de la

siguiente forma:

<joined-subclass name="hibernate.herencia.modelo.Normal" table="normales" extends="hibernate.heren

cia.modelo.Persona">

Ahora debemos indicar cuál de las columnas de la tabla que almacenará los datos de la entidad padre es la que almacena su

identificador, ya que está columna será usada como llave foránea/primaria de la tabla que se generará para almacenar los datos de la

entidad que estamos mapeando. Para eso hacemos uso del elemento "<key>", y en su atributo "column" indicamos el nombre de la

columna que almacena el identificador de su entidad padre, de la siguiente forma:

<key column="id" />

En este caso la columna que se generará para almacenar el identificador de "Persona" es la mismo nombre que el de la propiedad ("id")

ya que no hemos indicado ninguna otra cosa, pero si, por ejemplo, este se estuviera guardando en una columna llamada "ID_PERSONA"

tendríamos que colocar "ID_PERSONA" en el atributo "column" del elemento "<key>".

Esto es lo único que tenemos que hacer para indicar que queremos usar esta estrategia de herencia. El resto del archivo es el mapeo

normal de la clase, y al final queda de esta forma:

<?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>

<joined-subclass name="hibernate.herencia.modelo.Normal" table="normales" extends="hibernate.h

erencia.modelo.Persona">

<key column="id" />

<property name="ocupacion" />

</joined-subclass>

</hibernate-mapping>

Page 167: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 18/35

El resto de los mapeos se hace exactamente igual, quedando al final de la siguiente forma:

Para la clase "Tecnologo" el mapeo queda de esta forma:

<?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>

<joined-subclass name="hibernate.herencia.modelo.Tecnologo" table="tecnologos" extends="hibern

ate.herencia.modelo.Persona">

<key column="id" />

<property name="aniosDeEstudios" />

</joined-subclass>

</hibernate-mapping>

El mapeo para la clase "Programador" queda así:

<?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>

<joined-subclass name="hibernate.herencia.modelo.Programador" table="programadores" extends="h

ibernate.herencia.modelo.Tecnologo">

<key column="id" />

<property name="lenguajeFavorito" />

<property name="aniosDeExperiencia" />

</joined-subclass>

</hibernate-mapping>

Finalmente, el mapeo de "Tester" queda de esta forma:

<?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>

<joined-subclass name="hibernate.herencia.modelo.Tester" table="testers" extends="hibernate.he

rencia.modelo.Tecnologo">

<key column="id" />

<property name="herramientaDeTesteo" />

</joined-subclass>

</hibernate-mapping>

No olviden poner estos archivos de mapeo en el archivo "hibernate.cfg.xml":

<mapping resource="hibernate/herencia/mapeos/Persona.hbm.xml" />

<mapping resource="hibernate/herencia/mapeos/Normal.hbm.xml" />

<mapping resource="hibernate/herencia/mapeos/Tecnologo.hbm.xml" />

<mapping resource="hibernate/herencia/mapeos/Programador.hbm.xml" />

<mapping resource="hibernate/herencia/mapeos/Tester.hbm.xml" />

Igual que en la estrategia anterior, aquí podemos colocar todos los mapeos en un mismo archivo y así ya no es necesario indicar de cuál

clase extienden las clases que estamos mapeando:

Page 168: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 19/35

<?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="hibernate.herencia.modelo.Persona" table="personas">

<id name="id">

<generator class="identity" />

</id>

<property name="nombre" />

<property name="edad" />

<joined-subclass name="hibernate.herencia.modelo.Normal" table="normales">

<key column="id" />

<property name="ocupacion" />

</joined-subclass>

<joined-subclass name="hibernate.herencia.modelo.Tecnologo" table="tecnologos">

<key column="id" />

<property name="aniosDeEstudios" />

</joined-subclass>

<joined-subclass name="hibernate.herencia.modelo.Programador" table="programadores">

<key column="id" />

<property name="lenguajeFavorito" />

<property name="aniosDeExperiencia" />

</joined-subclass>

<joined-subclass name="hibernate.herencia.modelo.Tester" table="testers">

<key column="id" />

<property name="herramientaDeTesteo" />

</joined-subclass>

</class>

</hibernate-mapping>

Probemos que nuestra aplicación funciona correctamente usando el código que teníamos anteriormente en el método "main" de nuestra

clase "Main":

public static void main(String[] args)

{

Normal normal = new Normal("normal", 21, "Empleado");

Tecnologo tecnologo = new Tecnologo("tecnologo", 24, 4);

Programador programador1 = new Programador("primer programador", 25, 4, "java", 4);

Programador programador2 = new Programador("segundo programador", 25, 5, "java", 2);

Tester tester = new Tester("tester", 18, 3, "JUnit");

AbstractDAO.almacenaEntidad(normal);

AbstractDAO.almacenaEntidad(tecnologo);

AbstractDAO.almacenaEntidad(programador1);

AbstractDAO.almacenaEntidad(programador2);

AbstractDAO.almacenaEntidad(tester);

}

Comprobemos qué, en nuestra base de datos, efectivamente se haya creada una sola tabla para cada una de nuestras clases:

Ahora comprobemos los datos que contiene cada una de las tablas:

Page 169: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 20/35

Como se ve en la imagen anterior, los datos comunes de todas las clases se guardan en la tabla "personas" y los datos particulares de

cada una de las subclases se almacena en su tabla correspondiente.

Veamos cómo hacer esto mismo con anotaciones.

Las anotaciones para esta estrategia de herencia también son muy parecidas a las que ya estamos acostumbrados a usar.

En nuestra clase base, la clase "Persona", usamos la anotación "@Inheritance". Para indicar que queremos usar esta estrategia, lo

indicamos en el atributo "strategy" de esa anotación con el valor "InheritanceType.JOINED", de la siguiente forma:

@Entity

@Inheritance(strategy=InheritanceType.JOINED)

public abstract class Persona implements Serializable

{

}

Y listo, es todo lo que demos hacer en nuestra clase base. El resto de las anotaciones de la clase "Persona" son las que ya

conocemos, al final queda así:

UNA TABLA PARA CADA SUBCLASE (JOINS) USANDO ANOTACIONES

Page 170: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 21/35

@Entity

@Inheritance(strategy=InheritanceType.JOINED)

public abstract class Persona implements Serializable

{

@Id

@GeneratedValue(strategy=GenerationType.IDENTITY)

private long id;

private String nombre;

private int edad;

public Persona()

{

}

public Persona(String nombre, int edad)

{

this.nombre = nombre;

this.edad = edad;

}

public int getEdad()

{

return edad;

}

public void setEdad(int edad)

{

this.edad = edad;

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

}

En las subclases no es necesario que hagamos algo especial en esta estrategia de herencia. Por ejemplo, para la clase "Normal" basta

con que la indiquemos de la siguiente forma:

@Entity

public class Normal extends Persona

{

}

Simplemente con indicar que "Normal" es una entidad y que extiende de "Persona" es suficiente en esta estrategia de herencia. El

resto de la clase queda de la siguiente forma:

Page 171: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 22/35

@Entity

public class Normal extends Persona

{

private String ocupacion;

public Normal()

{

}

public Normal(String nombre, int edad, String ocupacion)

{

super(nombre, edad);

this.ocupacion = ocupacion;

}

public String getOcupacion()

{

return ocupacion;

}

public void setOcupacion(String ocupacion)

{

this.ocupacion = ocupacion;

}

}

El resto de las clases queda exactamente igual, así que no las colocaré aquí. Pero no olviden ponerlas en el archivo

"hibernate.cfg.xml" de la siguiente forma:

<mapping class="hibernate.herencia.modelo.Persona"/>

<mapping class="hibernate.herencia.modelo.Normal"/>

<mapping class="hibernate.herencia.modelo.Tecnologo"/>

<mapping class="hibernate.herencia.modelo.Programador"/>

<mapping class="hibernate.herencia.modelo.Tester"/>

Probemos que todo funciona correctamente con el código que hemos estado usando en nuestro método "main":

public static void main(String[] args)

{

Normal normal = new Normal("normal", 21, "Empleado");

Tecnologo tecnologo = new Tecnologo("tecnologo", 24, 4);

Programador programador1 = new Programador("primer programador", 25, 4, "java", 4);

Programador programador2 = new Programador("segundo programador", 25, 5, "java", 2);

Tester tester = new Tester("tester", 18, 3, "JUnit");

AbstractDAO.almacenaEntidad(normal);

AbstractDAO.almacenaEntidad(tecnologo);

AbstractDAO.almacenaEntidad(programador1);

AbstractDAO.almacenaEntidad(programador2);

AbstractDAO.almacenaEntidad(tester);

}

Veamos las tablas generadas en nuestra base de datos:

Y los datos que contiene cada una de ellas:

Page 172: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 23/35

Comprobamos nuevamente que todo salió como lo planeamos _ .

Ahora veamos un poco el SQL generado con esta estrategia. Para esto modifiquemos un poco nuestra aplicación para recuperar el primer

"Programador" que guardamos. Agreguemos esta línea al final del método "main":

AbstractDAO.getEntidad(programador1.getId(), Programador.class);

No nos interesa trabajar con la entidad recuperada, solo ver el sql generado por Hibernate para recuperarla:

Si vemos el SQL que resalté en la imagen anterior, veremos que tenemos lo siguiente:

select programado0_.id as id0_0_, programado0_2_.nombre as nombre0_0_, programado0_2_.edad as edad

0_0_, programado0_1_.aniosDeEstudios as aniosDeE2_2_0_, programado0_.lenguajeFavorito as lenguaje2

_3_0_, programado0_.aniosDeExperiencia as aniosDeE3_3_0_ from programadores programado0_ inner joi

n tecnologos programado0_1_ on programado0_.id=programado0_1_.id inner join personas programado0_2

_ on programado0_.id=programado0_2_.id where programado0_.id=?

Lo modificaré un poco para hacerlo más claro:

SELECT

prog.id as id0_0_, pers.nombre as nombre0_0_, pers.edad as edad0_0_, tec.aniosDeEstudios as anio

sDeE2_2_0_, prog.lenguajeFavorito as lenguaje2_3_0_, prog.aniosDeExperiencia as aniosDeE3_3_0_

FROM

programadores prog

INNER JOIN

tecnologos tec on prog.id=tec.id

INNER JOIN

personas pers on prog.id=pers.id

WHERE prog.id=?

Podemos observar que, efectivamente, se crea un join para unir cada una de las tres tablas que forman la jerarquía de "Programador",

que en este caso son "programadores", "tecnologos", y "personas". Sin embargo en este caso nosotros indicamos a cuál clase

pertenece la instancia que queremos recuperar (con "Programador.class"). ¿Pero qué ocurriría si no sabemos exactamente a cuál de

las subclases de "Persona" pertenece la entidad? En ese caso nuestro código tendría que ser así:

Page 173: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 24/35

AbstractDAO.getEntidad(programador1.getId(), Persona.class);

Ahora no estamos seguros de a cuál clase pertenece el objeto que queremos recuperar (bueno, nosotros sabemos que buscamos un

"Pogramador", pero Hibernate solo sabe que busca una sublcase de "Persona"). Por lo tanto, si ejecutamos la aplicación de esta

forma, Hibernate genera la siguiente consulta para saber cuál clase recuperar:

select persona0_.id as id0_0_, persona0_.nombre as nombre0_0_, persona0_.edad as edad0_0_, persona

0_1_.ocupacion as ocupacion1_0_, persona0_2_.aniosDeEstudios as aniosDeE2_2_0_, persona0_3_.lengua

jeFavorito as lenguaje2_3_0_, persona0_3_.aniosDeExperiencia as aniosDeE3_3_0_, persona0_4_.herram

ientaDeTesteo as herramie2_4_0_, case when persona0_3_.id is not null then 3 when persona0_4_.id i

s not null then 4 when persona0_1_.id is not null then 1 when persona0_2_.id is not null then 2 wh

en persona0_.id is not null then 0 end as clazz_0_ from personas persona0_ left outer join normale

s persona0_1_ on persona0_.id=persona0_1_.id left outer join tecnologos persona0_2_ on persona0_.i

d=persona0_2_.id left outer join programadores persona0_3_ on persona0_.id=persona0_3_.id left out

er join testers persona0_4_ on persona0_.id=persona0_4_.id where persona0_.id=?

Nuevamente lo acomodaré para hacerlo un poco más claro:

SELECT

pers.id as id0_0_, pers.nombre as nombre0_0_, pers.edad as edad0_0_, norm.ocupacion as ocupacion

1_0_, tecs.aniosDeEstudios as aniosDeE2_2_0_, prog.lenguajeFavorito as lenguaje2_3_0_, prog.aniosD

eExperiencia as aniosDeE3_3_0_, tetr.herramientaDeTesteo as herramie2_4_0_,

CASE

WHEN prog.id IS NOT NULL THEN 3

WHEN tetr.id IS NOT NULL THEN 4

WHEN norm.id IS NOT NULL THEN 1

WHEN tecs.id IS NOT NULL THEN 2

WHEN pers.id IS NOT NULL THEN 0

END as clazz_0_

FROM personas pers

LEFT OUTER JOIN

normales norm on pers.id=norm.id

LEFT OUTER JOIN

tecnologos tecs on pers.id=tecs.id

LEFT OUTER JOIN

programadores prog on pers.id=prog.id

LEFT OUTER JOIN

testers tetr on pers.id=tetr.id

WHERE pers.id=?

Ahora la conuslta consulta es "compleja", y entre más entidades tengamos más compleja se volverá. Es por eso que esta estrategia es

muy lenta si tenemos consulta en los que no sabemos el tipo concreto de la clase que buscamos, solo la clase base. A estas consultas

se les conoce como "Polimórficas".>

Entonces, tenemos que con esta estrategia pasamos de tener un conjunto de clases a tener una tabla por cada una de las clases, como

lo ilustra la siguiente figura:

En esta estrategia no fue necesario usar un discriminador, aunque en caso de que lo quisiéramos agregar también es posible (aunque

Page 174: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 25/35

esto no agiliza las consultas polimórficas). Con esto logramos una mezcla entre las dos estrategias anteriores.

Para hacer eso con archivos de mapeo debemos colocar el elemento "<discriminator>" en el archivo de mapeo de la clase base

(indicando el nombre y tipo de la columna discriminadora) y, en los archivos de mapeo de las subclases, debemos usar nuevamente el

elemento "<subclass>" indicando, mediante su atributo "discriminator-value" el valor que tendrá la columna discriminadora para

cada una de las subclases. Como hijo del elemento "<subclass>" debemos colocar el elemento "<join>" indicando el nombre de la

tabla que se usará para almacenar los atributos de los objetos de ese tipo, y como hijo de este el elemento "<key>" indicando la

columna de la tabla padre que almacena su identificador. El archivo de mapeo (colocando los mapeos de todo el árbol de herencia de

"Persona" en un solo archivo) quedaría de la siguiente forma:

<?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="hibernate.herencia.modelo.Persona" table="personas">

<id name="id">

<generator class="identity" />

</id>

<discriminator column="DISC" type="string" />

<property name="nombre" />

<property name="edad" />

<subclass name="hibernate.herencia.modelo.Normal" discriminator-value="nrm">

<join table="normales">

<key column="id" />

<property name="ocupacion" />

</join>

</subclass>

<subclass name="hibernate.herencia.modelo.Tecnologo" discriminator-value="tcn">

<join table="tecnologos">

<key column="id" />

<property name="aniosDeEstudios" />

</join>

</subclass>

<subclass name="hibernate.herencia.modelo.Programador" discriminator-value="prm">

<join table="programadores" >

<key column="id" />

<property name="lenguajeFavorito" />

<property name="aniosDeExperiencia" />

</join>

</subclass>

<subclass name="hibernate.herencia.modelo.Tester" discriminator-value="tst">

<join table="testers">

<key column="id" />

<property name="herramientaDeTesteo" />

</join>

</subclass>

</class>

</hibernate-mapping>

Con anotaciones parece que no es posible lograr esto. Pero como dije anteriormente: esto no ayuda en realidad a optimizar las consultas

polimórficas.

Pasemos a ver la última de las estrategias de herencia que nos proporciona Hibernate.

Esta última estrategia podría parecer la más extraña ya que en este caso se generará una tabla por cada una de las entidades no-

abstractas que tenga nuestra aplicación (en este caso NO se generará una tabla para "Persona"). Sin embargo cada tabla tendrá una

columna para cada uno de los atributos de la clase de la entidad que almacena, propios y heredados. O sea, que la tabla mantendrá los

atributos de la clase que mapea, junto con los atributos que hereda de su clase padre.

En este caso las tablas no están relacionadas de ninguna forma, por lo que terminaremos con un conjunto de tablas independientes una

de otras.

El esquema de la base de datos en esta ocasión quedará de la siguiente forma:

Una Tabla por cada Clase Concreta (uniones)

Page 175: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 26/35

Como vemos, ahora no hay ninguna tabla para "Persona", sin embargo las tablas de "normal" y "tecnologo" tienen las columnas

"edad" y "nombre", que son atributos de "Persona", además de las columnas para sus propios atributos. De la misma forma

"programador" y "tester" tienen columnas para los atributos de "tecnólogo", además de columnas para sus atributos propios.

Además, estas tablas no tienen relación una con otra de ninguna forma.

Esta estrategia tiene la ventaja de que no es necesario hacer un grupo de joins para obtener todos los datos de una entidad, lo que

nuevamente nos funciona si haremos consultas SQL a mano.

Ahora veamos cómo usar esta estrategia en nuestras aplicaciones.

Creamos, en el paquete "mapeos" un nuevo documento XML. Le damos el nombre de "Persona.hbm" (el asistente se encargará de

colocar el ".xml")

Presionamos el botón "Next >" e indicamos que queremos crear un documento XML bien formado (la primera opción) y presionamos el

botón "Finish".

En esta estrategia la clase base no necesita cambiar mucho. Por lo que el mapeo de clase "Persona" queda de forma simple.

Eliminamos el contenido del archivo creado y colocamos el siguiente:

<?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="hibernate.herencia.modelo.Persona">

</class>

</hibernate-mapping>

En este caso en el mapeo de la clase "Persona" (la cual recordemos que es "abstracta) debemos indicar de forma explícita que

estamos mapeando una clase abstracta, con el atributo ""abstract" del elemento ""<class>", de la siguiente forma:

<class name="hibernate.herencia.modelo.Persona" abstract="true">

También si se han dado cuenta, en esta ocasión NO usamos el atributo "table" en este elemento. Estas dos cosas son para que no se

genere una tabla para las tablas abstractas ya que, como podrán imaginar, estas tablas solo estarían ocupando espacio en la base de

datos ya que nunca se usarían.

El siguiente paso es mapear el atributo que se usará como identificador de todas las subclases que extiendan de esta clase en la base

de datos. Solo que ahora no podemos usar el generador "identity" como lo hemos estado haciendo hasta ahora. En realidad no

entiendo el por qué de esta restricción, pero por el momento tendremos que ajustarnos a esta regla. De hecho los valores que podemos

colocar son los siguientes:

increment

hilo

uuid

guid

assigned

Yo usaré "increment" ya que es el que más se parece a "identity". Si quieren encontrar más información sobre los generadores

UNA TABLA POR CLASE CONCRETA USANDO ARCHIVOS DE MAPEO

Page 176: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 27/35

pueden hacerlo en esta página.

El resto del mapeo de "Persona" no tiene nada de especial, y al final queda de la siguiente forma:

<?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="hibernate.herencia.modelo.Persona" abstract="true">

<id name="id">

<generator class="assigned" />

</id>

<property name="nombre" />

<property name="edad" />

</class>

</hibernate-mapping>

En el mapeo de las subclases, en esta ocasión en vez del elemento "<class>" usaremos "<union-subclass>". Indicamos en cuál

tabla se almacenan los datos de esta subclase usando el atributo "table", y de cuál clase extiende usando el atributo "extends". Por

ejemplo en la clase "Normal" el mapeo quedaría de la siguiente forma:

<?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>

<union-subclass name="hibernate.herencia.modelo.Normal" table="normales" extends="hibernate.he

rencia.modelo.Persona">

<property name="ocupacion" />

</union-subclass>

</hibernate-mapping>

El de "Tecnologo" quedaría de esta forma:

<?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>

<union-subclass name="hibernate.herencia.modelo.Tecnologo" table="tecnologos" extends="hiberna

te.herencia.modelo.Persona">

<property name="aniosDeEstudios" />

</union-subclass>

</hibernate-mapping>

El mapeo de "Programador" queda así:

<?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>

<union-subclass name="hibernate.herencia.modelo.Programador" table="programadores" extends="hi

bernate.herencia.modelo.Tecnologo">

<property name="lenguajeFavorito" />

<property name="aniosDeExperiencia" />

</union-subclass>

</hibernate-mapping>

Y el de la clase "Tester" queda de la siguiente forma:

Page 177: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 28/35

<?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>

<union-subclass name="hibernate.herencia.modelo.Tester" table="testers" extends="hibernate.her

encia.modelo.Tecnologo">

<property name="herramientaDeTesteo" />

</union-subclass>

</hibernate-mapping>

No olviden colocar los mapeos en el archivo de configuración "hibernate.cfg.xml" de la siguiente forma:

<mapping resource="hibernate/herencia/mapeos/Persona.hbm.xml" />

<mapping resource="hibernate/herencia/mapeos/Normal.hbm.xml" />

<mapping resource="hibernate/herencia/mapeos/Tecnologo.hbm.xml" />

<mapping resource="hibernate/herencia/mapeos/Programador.hbm.xml" />

<mapping resource="hibernate/herencia/mapeos/Tester.hbm.xml" />

Igual que en los casos anteriores, podemos poner todos estos mapeos en un solo archivo, de la siguiente forma:

<?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="hibernate.herencia.modelo.Persona" abstract="true">

<id name="id">

<generator class="assigned" />

</id>

<property name="nombre" />

<property name="edad" />

<union-subclass name="hibernate.herencia.modelo.Normal" table="normales">

<property name="ocupacion" />

</union-subclass>

<union-subclass name="hibernate.herencia.modelo.Tecnologo" table="tecnologos">

<property name="aniosDeEstudios" />

</union-subclass>

<union-subclass name="hibernate.herencia.modelo.Programador" table="programadores">

<property name="lenguajeFavorito" />

<property name="aniosDeExperiencia" />

</union-subclass>

<union-subclass name="hibernate.herencia.modelo.Tester" table="testers">

<property name="herramientaDeTesteo" />

</union-subclass>

</class>

</hibernate-mapping>

Probemos que la configuración ha quedado bien usando nuevamente el siguiente código en nuestro método "main":

Page 178: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 29/35

public static void main(String[] args)

{

Normal normal = new Normal("normal", 21, "Empleado");

Tecnologo tecnologo = new Tecnologo("tecnologo", 24, 4);

Programador programador1 = new Programador("primer programador", 25, 4, "java", 4);

Programador programador2 = new Programador("segundo programador", 25, 5, "java", 2);

Tester tester = new Tester("tester", 18, 3, "JUnit");

AbstractDAO.almacenaEntidad(normal);

AbstractDAO.almacenaEntidad(tecnologo);

AbstractDAO.almacenaEntidad(programador1);

AbstractDAO.almacenaEntidad(programador2);

AbstractDAO.almacenaEntidad(tester);

}

Al ejecutar nuestra aplicación podemos ver que se generan las siguientes tablas:

Como vemos, solo se crearon tablas para las entidades no-abstractas de nuestra aplicación. Ahora veamos los datos que contiene cada

una de estas tablas:

Nuevamente todo ha salido como esperabamos - . Ahora veamos cómo usar esta estrategia usando anotaciones.

En esta estrategia nuevamente hacemos uso de la anotación "@Inheritance" en la clase base (en este caso "Persona") para indicar

el mecanismo de herencia que queremos usar. En este caso colocaremos en su atributo "strategy" el valor

"InheritanceType.TABLE_PER_CLASS", de la siguiente forma:

@Entity

@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)

public abstract class Persona implements Serializable

{

}

Lo que sigue es indicar cuál de los atributos de la clase servirá como identificador. Igual que ocurre en el caso de usar archivos de mapeo,

en esta ocasión no podemos usar el valor "GenerationType.IDENTITY" como la estrategia de generación del identificador. De hecho

en esta ocasión el único valor que podemos elegir es:

GenerationType.TABLE

Este tipo de generación lo que hace es crear una tabla especial para generar los identificadores (una especie de secuencia) por lo que en

nuestra base de datos terminaremos con una tabla de más.

Fuera de esta pequeña diferencia el resto del mapeo de nuestra clase no tiene ninguna otra particularidad. El mapeo completo de la clase

"Persona" se muestra a continuación:

UNA TABLA POR CLASE CONCRETA USANDO ANOTACIONES

Page 179: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 30/35

@Entity

@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)

public abstract class Persona implements Serializable

{

@Id

@GeneratedValue(strategy=GenerationType.TABLE)

private long id;

private String nombre;

private int edad;

public Persona()

{

}

public Persona(String nombre, int edad)

{

this.nombre = nombre;

this.edad = edad;

}

public int getEdad()

{

return edad;

}

public void setEdad(int edad)

{

this.edad = edad;

}

public long getId()

{

return id;

}

protected void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

}

El mapeo de las subclases no tiene ninguna cosa extraña, por lo que solo colocaré el mapeo de "Normal", que queda de la siguiente

forma:

@Entity

public class Normal extends Persona

{

private String ocupacion;

public Normal()

{

}

public Normal(String nombre, int edad, String ocupacion)

{

super(nombre, edad);

this.ocupacion = ocupacion;

}

public String getOcupacion()

{

return ocupacion;

}

public void setOcupacion(String ocupacion)

{

this.ocupacion = ocupacion;

}

}

Page 180: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 31/35

El mapeo de las demás clases es exactamente igual que el de "Normal". No olviden colocar estas clases en el archivo de configuración

"hibernate.cfg.xml":

<mapping class="hibernate.herencia.modelo.Persona"/>

<mapping class="hibernate.herencia.modelo.Normal"/>

<mapping class="hibernate.herencia.modelo.Tecnologo"/>

<mapping class="hibernate.herencia.modelo.Programador"/>

<mapping class="hibernate.herencia.modelo.Tester"/>

Ahora, probemos que nuestra aplicación funciona usando nuevamente el siguiente código en nuestro método "main":

public static void main(String[] args)

{

Normal normal = new Normal("normal", 21, "Empleado");

Tecnologo tecnologo = new Tecnologo("tecnologo", 24, 4);

Programador programador1 = new Programador("primer programador", 25, 4, "java", 4);

Programador programador2 = new Programador("segundo programador", 25, 5, "java", 2);

Tester tester = new Tester("tester", 18, 3, "JUnit");

AbstractDAO.almacenaEntidad(normal);

AbstractDAO.almacenaEntidad(tecnologo);

AbstractDAO.almacenaEntidad(programador1);

AbstractDAO.almacenaEntidad(programador2);

AbstractDAO.almacenaEntidad(tester);

}

Al ejecutar nuestra aplicación podemos ver que se generan las siguientes tablas:

En la imagen anterior podemos ver que se generó, además de las tablas esperadas, una tabla llamada "hibernate_sequences". Esta

tabla es la que usa para generara los identificadores de cada una de las subclases de "Persona" que guardemos en la base de datos.

Ahora veamos los datos almacenados en cada uno de nuestras tablas:

Podemos ver que, nuevamente, los datos se han almacenado correctamente y que todo ha salido como lo planeamos _ .

Veamos, solo por curiosidad, nuevamente el SQL generado por Hibernate en las consultas polimórficas. Si intentamos recuperar una

instancia de la clase "Programador" usando la siguiente línea al final del código anterior:

AbstractDAO.getEntidad(programador1.getId(), Programador.class);

Hibernate genera el siguiente SQL:

Page 181: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 32/35

select programado0_.id as id0_0_, programado0_.edad as edad0_0_, programado0_.nombre as nombre0_0_

, programado0_.aniosDeEstudios as aniosDeE1_2_0_, programado0_.aniosDeExperiencia as aniosDeE1_3_0

_, programado0_.lenguajeFavorito as lenguaje2_3_0_ from Programador programado0_ where programado0

_.id=?

Que acomodado queda de la siguiente forma:

SELECT

prog.id as id0_0_, prog.edad as edad0_0_, prog.nombre as nombre0_0_, prog.aniosDeEstudios as ani

osDeE1_2_0_, prog.aniosDeExperiencia as aniosDeE1_3_0_, prog.lenguajeFavorito as lenguaje2_3_0_

FROM

Programador prog

WHERE prog.id=?

Como podemos ver no hay ningún problema, pedimos un Programador y Hibernate lo busca en la tabla "programador". ¿Pero qué

ocurre si no sabemos a cuál clase pertenece nuestra entidad? En ese caso tendríamos que usar la siguiente línea:

AbstractDAO.getEntidad(programador1.getId(), Persona.class);

Con lo que Hibernate generará la siguiente consulta:

select persona0_.id as id0_0_, persona0_.edad as edad0_0_, persona0_.nombre as nombre0_0_, persona

0_.ocupacion as ocupacion1_0_, persona0_.aniosDeEstudios as aniosDeE1_2_0_, persona0_.aniosDeExper

iencia as aniosDeE1_3_0_, persona0_.lenguajeFavorito as lenguaje2_3_0_, persona0_.herramientaDeTes

teo as herramie1_4_0_, persona0_.clazz_ as clazz_0_ from ( select id, edad, nombre, null as ocupac

ion, aniosDeEstudios, aniosDeExperiencia, lenguajeFavorito, null as herramientaDeTesteo, 3 as claz

z_ from Programador union select id, edad, nombre, null as ocupacion, aniosDeEstudios, null as ani

osDeExperiencia, null as lenguajeFavorito, herramientaDeTesteo, 4 as clazz_ from Tester union sele

ct id, edad, nombre, ocupacion, null as aniosDeEstudios, null as aniosDeExperiencia, null as lengu

ajeFavorito, null as herramientaDeTesteo, 1 as clazz_ from Normal union select id, edad, nombre, n

ull as ocupacion, aniosDeEstudios, null as aniosDeExperiencia, null as lenguajeFavorito, null as h

erramientaDeTesteo, 2 as clazz_ from Tecnologo ) persona0_ where persona0_.id=?

Que un poco acomodada se ve así:

Page 182: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 10: Herencia

www.javatutoriales.com/2010/08/hibernate-parte-10-herencia.html 33/35

SELECT

pers.id as id0_0_, pers.edad as edad0_0_, pers.nombre as nombre0_0_, pers.ocupacion as ocupacion

1_0_, pers.aniosDeEstudios as aniosDeE1_2_0_, pers.aniosDeExperiencia as aniosDeE1_3_0_, pers.leng

uajeFavorito as lenguaje2_3_0_, pers.herramientaDeTesteo as herramie1_4_0_, pers.clazz_ as clazz_0

_

FROM (

SELECT id, edad, nombre, null as ocupacion, aniosDeEstudios, aniosDeExperiencia, lenguajeFavorit

o, null as herramientaDeTesteo, 3 as clazz_

FROM Programador

UNION

SELEC id, edad, nombre, null as ocupacion, aniosDeEstudios, null as aniosDeExperiencia, null as

lenguajeFavorito, herramientaDeTesteo, 4 as clazz_

FROM Tester

UNION

SELECT id, edad, nombre, ocupacion, null as aniosDeEstudios, null as aniosDeExperiencia, null as

lenguajeFavorito, null as herramientaDeTesteo, 1 as clazz_

FROM Normal

UNION

SELECT id, edad, nombre, null as ocupacion, aniosDeEstudios, null as aniosDeExperiencia, null as

lenguajeFavorito, null as herramientaDeTesteo, 2 as clazz_

FROM Tecnologo ) pers

WHERE pers.id=?

Como podemos ver, Hibernate tiene que buscar, nuevamente, a qué clase pertenece el objeto en todas las tablas para la jerarquía de

clases. Esto, como se podrán imaginar, hará nuestras consultas muy lentas en caso de que dicha jerarquía tenga muchas clases.

Hemos podido ver cómo usar las tres estrategias de herencia que Hibernate nos proporciona y las ventajas y desventajas de algunas de

ellas. Aunque no es necesario que usemos solo una estrategia en nuestras aplicaciones, podemos mezclarlas en la forma que más nos

convenga para sacarles el mayor provecho.

Para terminar este tutorial les dejo algunos consejos de cómo elegir una estrategia de herencia para sus aplicaciones.

Si no requerimos asociaciones o consultas polimórficas, lo mejor será elegir "una tabla por clase concreta"; en otras

palabras, si nunca o rara vez haremos una consulta buscando con la clase base (como "Persona"), entonces esta estrategia

es ideal, ya que solo buscaremos en una tabla de nuestra base de datos.

Si requerimos asociaciones o consultas polimórficas y nuestras subclases declaran pocos atributos (esto puede ser debido a

que la diferencia principal entre las subclases sea el comportamiento), entonces lo mejor será elegir "una tabla para la

jerarquía de clases".

Finalmente, si requerimos asociaciones polimórficas y nuestras subclases declaran muchos atributos distintos entonces la

mejor estrategia será "una tabla por subclase". O, dependiendo de de la profundidad de la jerarquía de herencia y el posible

costo de los joins contra las uniones, podríamos elegir "una tabla por clase concreta".

Por default podemos elegir "una tabla por jerarquía de clases" para problemas simples, y en casos más complejos elegir alguna de las

otras dos estrategias, o considerar modificar nuestro modelo de datos - !

Pues bien, esto es todo para este post. En la siguiente y última entrega hablaré sobre cómo trabajar con Interceptores y Eventos.

Saludos.

Descarga los archivos de este tutorial desde aquí:

Hibernate Herencia Tabla por Jerarquia de Clases con Archivos de Mapeo

Hibernate Herencia Tabla por Jerarquia de Clases con Anotaciones

Hibernate Herencia Tabla por Subclase con Archivos de Mapeo

Hibernate Herencia Tabla por Subclase con Anotaciones

Hibernate Herencia Tabla por Clase Concreta con Archivos de Mapeo

Hibernate Herencia Tabla por Clase Concreta con Anotaciones

Entradas Relacionadas:

Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

Parte 2: Persistiendo Objetos Simples usando Anotaciones (Metadatos)

Parte 3: Relaciones / Uno a uno

Parte 4: Relaciones / Uno a muchos

Parte 5: Relaciones / Muchos a uno

Parte 6: Relaciones / Muchos a Muchos

ELIGIENDO UNA ESTRATEGIA DE HERENCIA

Page 183: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 11: Interceptores y Eventos

www.javatutoriales.com/2010/08/hibernate-parte-11-interceptores-y.html 1/20

Blog dedicado a temas de programación actuales usando el lenguaje de programación Java y las últimas versiones de sus APIs y Herramientas.

Tutoriales de Programacion Java

Página principal Página de Concursos Tutoriales UPAO 2010 Presentaciones Capacitación

LUNES, 23 DE AGOSTO DE 2010

Hibernate - Parte 11: Interceptores y Eventos

Algunas veces podemos tener situaciones que demanden la realización de algunas operaciones antes o después de nuestra lógica

funcional (precondiciones y postcondiciones). O tal vez simplemente queremos intervenir antes o después de qué alguna de nuestras

operaciones de persistencia (alta, baja, actualización, lectura, etc.) sea realizada. Ya sea para, por ejemplo, modificar algún valor de

nuestra entidad (como encriptar algún dato antes de ser guardado) o para leer algún valor.

También algunas veces es necesario recibir alguna notificación de algún suceso que esté ocurriendo en nuestro motor de persistencia,

como el estar recuperando o eliminando algún objeto. Esto puede ser útil para propósitos de auditorías, o para obtener estadísticas sobre

las operaciones de persistencia en nuestras aplicaciones.

Hibernate proporciona dos mecanismos para lograr estos dos objetivos: listeners y eventos.

En este tutorial aprenderemos cómo recibir notificaciones cada vez que Hibernate realice alguna operación de persistencia a través de

estos dos mecanismos.

Los interceptores nos proporcionan llamadas, a nivel sesión o a nivel aplicación, permitiendo a la aplicación inspeccionar y/o

manipular propiedades de un objeto persistente antes de ser guardado, actualizado, eliminado, o cargado dentro de nuestro contexto

persistente.

Pueden ser utilizados para monitorear los eventos ocurridos o para sobreescribir la funcionalidad de un módulo. El ejemplo clásico es la

auditoría del sistema, para realizar un log de eventos que indiquen los cambios que realizan sobre nuestras entidades.

En nuestro ejemplo realizaremos un mini-sistema de auditoría, en el que queremos que, cada vez que un Usuario sea almacenado o

eliminado del sistema, se muestre un mensaje en la consola. Por lo tanto comenzaremos nuestro ejemplo creando un nuevo proyecto.

Creamos un proyecto en NetBeans (menú "File -> New Project... -> Java -> Java Application"). Le damos un nombre

y una ubicación al proyecto y nos aseguramos de que las opciones "Create Main Class" y "Set as Main Project" estén

habilitadas. Presionamos el botón "Finish" y veremos aparecer en el editor nuestra clase "Main".

Agregamos la biblioteca de "Hibernate", que creamos en el primer tutorial de la serie. Hacemos clic derecho en el nodo "Libraries"

del proyecto. En el menú contextual que se abre seleccionamos la opción "Add Library...":

En la ventana que se abre seleccionamos la biblioteca "Hibernate":

1. Interceptores

DONACIONES

JAVA TUTORIALES EN FACEBOOK

Java Tutoriales

Me gusta

A 7896 personas les gusta

Plug-in social de Facebook

Java Tutoriales

Amigo programador, tú que

estás todo el día picando

código, escucha estas

recomendaciones sólo

hechas para ti..... ¿qué es

esto?... Gracias Jorge

Rubira....

No olviden poner la

sentencia where al hacer

un borraro en la base de

datos..

http://www.youtube.com/

watch?v=i_cVJgIz_Cs

Compartir 0 Más Siguiente blog»

Page 184: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 11: Interceptores y Eventos

www.javatutoriales.com/2010/08/hibernate-parte-11-interceptores-y.html 2/20

Presionamos el botón "Add Library" para que la biblioteca se agregue a nuestro proyecto. Como vamos a usar anotaciones, además

debemos agregar la biblioteca "HibernateAnotaciones" que creamos en el segundo tutorial. Aprovechamos también para agregar el

conector de MySQL. Debemos tener los siguientes archivos en nuestro proyecto:

Ahora creamos tres paquetes, uno con el nombre "modelo", que contendrá las clases entidades, otro con el nombre "dao" que

contendrá nuestras clases para operaciones de persistencia, y el último con el nombre "interceptores" que contendrá el interceptor

que crearemos para nuestro ejemplo. Hacemos clic derecho en el nodo del paquete que se creó al generar el proyecto. En el menú

contextual que se abre seleccionamos la opción "New -> Java Package..." y creamos los paquetes.

Ahora creamos el archivo de mapeo "hibernate.cfg.xml" en el paquete raíz de nuestra aplicación. Este archivo será muy parecido al

del primer tutorial:

Curso Onlinede jquery

www.teleformacio…

Crea páginas web

dinámicas Descuento

para desempleados

25%

Extend JMS to.NET

Visual JSFFlow Designer

CursoProgramación

EvoPerformanceTuning

Participar en este sitioGoogle Friend Connect

Miembros (173) Más »

¿Ya eres miembro? Iniciar sesión

SEGUIDORES

► 2012 (2)

► 2011 (11)

▼ 2010 (10)

► diciembre (1)

► octubre (3)

► septiembre (2)

▼ agosto (2)

Hibernate - Parte 11: Interceptores yEventos

Hibernate - Parte 10: Herencia

► julio (1)

► mayo (1)

► 2009 (22)

ARCHIVO DEL BLOG

Page 185: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 11: Interceptores y Eventos

www.javatutoriales.com/2010/08/hibernate-parte-11-interceptores-y.html 3/20

<?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>

<!-- parametros para la conexion a la base de datos -->

<property name="connection.driver_class">com.mysql.jdbc.Driver</property>

<property name="connection.url">jdbc:mysql://localhost/hibernateinterceptores</property>

<property name="connection.username">usuario</property>

<property name="connection.password">password</property>

<!-- Configuracion del pool interno -->

<property name="connection.pool_size">1</property>

<!-- Dialecto de la base de datos -->

<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>

<!-- Otras propiedades importantes -->

<property name="show_sql">false</property>

<property name="hbm2ddl.auto">create-drop</property>

<!-- Clases o Archivos de mapeo -->

</session-factory>

</hibernate-configuration>

Aprovechamos para agregar, en el paquete "dao" la clase "HibernateUtil" que creamos en el segundo tutorial. Y la clase

"AbstractDAO" que creamos en el tutorial 9 (parámetros).

Para terminar crearemos una sola clase entidad llamada "Usuario", en el paquete "modelo", que tendrá solo un par de atributos

además del identificador y será anotada con las mismas anotaciones que vimos en el segundo tutorial. La clase queda de la siguiente

forma:

Alex

Programador Java con algunos años de

experiencia en múltiples poyectos y con

múltiples APIs y herramientas deseoso de

compartir experiencias con el resto de

programadores.

Ver todo mi perfil

DATOS PERSONALES

Page 186: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 11: Interceptores y Eventos

www.javatutoriales.com/2010/08/hibernate-parte-11-interceptores-y.html 4/20

@Entity

public class Usuario implements Serializable

{

@Id

@GeneratedValue(strategy=GenerationType.IDENTITY)

private long id;

private String nombre;

private String username;

private String password;

public Usuario()

{

}

public Usuario(String nombre, String username, String password)

{

this.nombre = nombre;

this.username = username;

this.password = password;

}

public long getId()

{

return id;

}

private void setId(long id)

{

this.id = id;

}

public String getNombre()

{

return nombre;

}

public void setNombre(String nombre)

{

this.nombre = nombre;

}

public String getPassword()

{

return password;

}

public void setPassword(String password)

{

this.password = password;

}

public String getUsername()

{

return username;

}

public void setUsername(String username)

{

this.username = username;

}

}

No olviden agregar esta clase en el archivo de configuración "hibernate.cfg.xml".

<mapping class="hibernate.interceptores.modelo.Usuario" />

Para este ejercicio usaremos una base de datos llamada "hibernateinterceptores".

Ahora comencemos a ver cómo implementar nuestro interceptor.

La forma más "directa" de crear un interceptor es implementar la interface "org.hibernate.Interceptor" la cual tiene nada más

y nada menos que 18 métodos. La siguiente tabla muestra una breve descripción de los 13 métodos más importantes de esta interface:

afterTransactionBegin – Este método es llamado inmediatamente después de que una transacción es iniciada.

Page 187: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 11: Interceptores y Eventos

www.javatutoriales.com/2010/08/hibernate-parte-11-interceptores-y.html 5/20

afterTransactionCompletion – Llamado al terminar una transacción.

beforeTransactionCompletion – Llamado antes de que se realice un commit de la transacción (solo de commit, no de

rollback).

findDirty – Llamado en el momento de llamar a "flush()".

onCollectionRecreate – Llamado antes de que una colección se creada o recreada.

onCollectionRemove – Llamado antes de que una colección sea eliminada.

onCollectionUpdate – Llamado antes de que una colección sea actualizada.

onDelete – Llamado antes de que una instancia sea eliminada

onLoad – Llamado antes de que una entidad sea inicializada (o sea antes de establecer los valores de sus atributos).

onPrepareStatement – Llamado cuando se está preparando la cadena con el SQL generado.

onSave – Llamado antes de que una entidad sea almacenada.

postFlush – Llamado después del flush que sincroniza los datos con la base de datos.

preFlush – Llamado antes de un flush.

Como podemos ver, esta interface tiene métodos para prácticamente cualquier cosa que se nos pueda ofrecer (aparentemente). Sin

embargo, como sabemos, si queremos implementar una interface entonces debemos proporcionar la implementación de cada uno de los

métodos que tiene la interface (y con 18 métodos esto no es una tarea nada fácil). Afortunadamente los creadores de Hibernate nos

ofrecen una manera de no tener que implementar, por nosotros mismos, cada uno de estos métodos, a través de la clase

"org.hibernate.EmptyInterceptor", la cual funciona como adapter de la interface "org.hibernate.Interceptor". Esta clase

contiene implementaciones vacías de los 18 métodos de la interface "org.hibernate.Interceptor", por lo que solo deberemos

sobreescribir los métodos que nos interesen.

Será a través de esta última clase que realizaremos nuestro ejemplo.

Creamos, en nuestro paquete "interceptores", una clase llamada "InterceptorAuditoria". Que se encargará de realizar los

procesos de auditoría de los que hablamos. Esta clase debe extender de "EmptyInterceptor", de la siguiente forma:

public class InterceptorAuditoria extends EmptyInterceptor

{

}

Repasemos los dos requerimientos que nuestra clase resolverá:

1. Cada vez que un Usuario sea almacenado se debe mostrar un mensaje en consola.

2. Cada vez que un Usuario sea eliminado se debe mostrar un mensaje en consola.

Si revisamos la lista de métodos de la interface "Interceptor" vemos que tiene 2 que podrían servirnos: "onSave" para el

requerimiento número 1, y "onDelete" para el requerimiento número 2. Por lo que sobreescribimos estos dos método en la clase

"InterceptorAuditoria".

public class InterceptorAuditoria extends EmptyInterceptor

{

@Override

public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames,

Type[] types)

{

}

@Override

public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, T

ype[] types)

{

}

}

Veamos primero cómo trabaja el método "onSave" y cómo lo usaremos para nuestros propósitos de auditoría.

boolean onSave(Object entity,

Serializable id,

Object[] state,

String[] propertyNames,

Type[] types)

throws CallbackException

Como dice en la lista: "onSave" es llamado antes de que un objeto sea guardado. Lo primero que podemos ver en la firma del método

nos indica que este regresa un booleano. Este valor indica si nosotros hemos modificado este objeto de alguna forma, como por

Page 188: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 11: Interceptores y Eventos

www.javatutoriales.com/2010/08/hibernate-parte-11-interceptores-y.html 6/20

ejemplo modificando alguno de los valores del mismo. Como para nuestros propósitos esto no es necesario regresaremos "false":

@Override

public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type

[] types)

{

return false;

}

Dentro de la lista de parámetros que recibe este método podemos ver que solamente nos interesa uno, de hecho el primero: "entity"

que es la entidad que será almacenada en nuestra base de datos. Para poder saber si estamos guardando un "Usuario" o algún otro

tipo de entidad debemos usar el operador "instanceof". Después haremos un cast al tipo "Usuario" para poder obtener de este su

"nombre" y su "username" de la siguiente forma:

if(entity instanceof Usuario)

{

Usuario usuario = (Usuario)entity;

System.out.println("Se ha almacenado al Usuario " + usuario.getNombre() + ", \"" + usuario.get

Username() + "\"");

}

Y listo, con este pequeño fragmento de código hemos cumplido con el primer requerimiento de nuestro sistema de auditoría. Nuestro

método "onSave" queda de la siguiente forma:

public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type

[] types)

{

if(entity instanceof Usuario)

{

Usuario usuario = (Usuario)entity;

System.out.println("Se ha almacenado al Usuario " + usuario.getNombre() + ", \"" + usuario

.getUsername() + "\"");

}

return false;

}

Ahora veamos nuestro segundo requerimiento: "Cada vez que un Usuario sea eliminado se debe mostrar un mensaje en consola". Para

el cual dijimos que usaremos el método "onDelete":

void onDelete(Object entity,

Serializable id,

Object[] state,

String[] propertyNames,

Type[] types)

throws CallbackException

Recordemos que, según la lista, "onDelete" es llamado antes de que la entidad sea eliminada. Podemos ver en la firma del método que,

a diferencia de "onSave", "onDelete" no regresa nada. Esto es debido a que como la entidad va a ser eliminada, no tiene sentido

modificar los atributos del objeto.

Dentro de los parámetros que recibe el método, ahora hay 2 que nos interesan: "entity", que es la entidad que se va a eliminar, e "id",

que es el identificador de dicha entidad en la base de datos.

Nuevamente, dentro del método "onDelete" usaremos el operador "instanceof" para determinar si estamos eliminando un

"Usuario". Después haremos un cast al tipo "Usuario" para poder obtener de este su "nombre" y usaremos el argumento "id" para

mostrar el identificador de esta entidad (el cual también podríamos obtener invocando al método "getId()" de nuestro Usuario):

if(entity instanceof Usuario)

{

Usuario usuario = (Usuario)entity;

System.out.println("Se eliminará al Usuario " + usuario.getNombre() + ", id=" + id);

}

Page 189: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 11: Interceptores y Eventos

www.javatutoriales.com/2010/08/hibernate-parte-11-interceptores-y.html 7/20

Y listo, esto es suficiente para cumplir con nuestro segundo requerimiento. El método "onDelete" queda de la siguiente forma:

public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[

] types)

{

if(entity instanceof Usuario)

{

Usuario usuario = (Usuario)entity;

System.out.println("Se eliminará al Usuario " + usuario.getNombre() + ", id=" + id);

}

}

Y nuestra clase "InterceptorAuditoria" así:

public class InterceptorAuditoria extends EmptyInterceptor

{

@Override

public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames,

Type[] types)

{

if(entity instanceof Usuario)

{

Usuario usuario = (Usuario)entity;

System.out.println("Se ha almacenado al Usuario " + usuario.getNombre() + ", \"" + usu

ario.getUsername() + "\"");

}

return false;

}

@Override

public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, T

ype[] types)

{

if(entity instanceof Usuario)

{

Usuario usuario = (Usuario)entity;

System.out.println("Se eliminará al Usuario " + usuario.getNombre() + ", id=" + id);

}

}

}

Ya teniendo listo nuestro interceptor, lo siguiente que debemos hacer es indicarle a Hibernate que este interceptor existe para que pueda

utilizarlo. Este es el momento indicado para decir que Hibernate maneja dos tipos de Interceptores:

Interceptores de Session (Session-scoped)

Interceptores de SessionFactory (SessionFactory-scoped)

Los interceptores de session son especificados cuando se abre una sesión, usando el método "openSession" de

"SessionFactory" (que en nuestra clase "AbstracDAO" ocurre en el método "iniciaOperacion"), y funcionan únicamente para la

sesión que se está abriendo.

Los interceptores de SessionFactory se registran con el objeto "org.hibernate.cfg.Configuration" que usamos para

construir la "SessionFactory", usando el método "buildSessionFactory" (que en nuestra clase "HibernateUtil" ocurre en el

constructor estático). Estos interceptores son usados en todas las sesiones que se abran en nuestra aplicación (a menos que

especifiquemos interceptores para una sesión específica usando los interceptores de session).

Como en este caso nuestro interceptor solo el útil si estamos trabajando con entidades "Usuario", usaremos interceptores de

session. Por lo que modificaremos un poco nuestra clase "AbstracDAO" para que pueda recibir un interceptor cada vez que vayamos a

almacenar una entidad.

Lo primero que haremos es agregar un nuevo método "iniciaOperacion" que reciba como parámetro nuestro interceptor. He optado

por hacer esto ya que es la opción que requiere menos cambios del código que ya tenemos. Así que nuestro nuevo método

"iniciaOperacion" queda de la siguiente forma:

protected void iniciaOperacion(Interceptor interceptor)

{

sesion = HibernateUtil.getSessionFactory().openSession(interceptor);

sesion.getTransaction().begin();

}

Page 190: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 11: Interceptores y Eventos

www.javatutoriales.com/2010/08/hibernate-parte-11-interceptores-y.html 8/20

Este nuevo método "iniciaOperacion" lo usaremos en el método "almacenaEntidad" de la misma clase "AbstractDAO", de la

siguiente forma:

dummy.iniciaOperacion(new InterceptorAuditoria());

Esto nos resuelve parte referente al primer requerimiento, pero si revisan la clase "AbstractDAO" verán que no existe ningún método

para eliminar entidades, por lo que crearemos uno muy similar a "almacenaEntidad", pero que haga uso del método "delete" en vez

de "saveOrUpdate". Este método se llamará "eliminaEntidad", y queda de esta forma:

public static void eliminaEntidad(Object entidad) throws HibernateException

{

AbstractDAO dummy = new AbstractDAO(){};

try

{

dummy.iniciaOperacion(new InterceptorAuditoria());

dummy.getSession().delete(entidad);

dummy.getSession().flush();

}

catch(HibernateException he)

{

dummy.manejaExcepcion(he);

}

finally

{

dummy.terminaOperacion();

}

}

Ahora que ya tenemos los dos métodos listos para usarlos, solo nos falta usarlos en nuestra clase "Main" en nuestro método "main".

Crearemos 5 Usuarios y después eliminaremos a 2 de ellos, para ver que, efectivamente, tengamos la salida esperada en la consola.

Coloquemos el siguiente código en nuestro método "main":

public static void main(String[] args)

{

Usuario usuario1 = new Usuario("usuario 1", "username usuario 1", "password usuario 1");

Usuario usuario2 = new Usuario("usuario 2", "username usuario 2", "password usuario 2");

Usuario usuario3 = new Usuario("usuario 3", "username usuario 3", "password usuario 3");

Usuario usuario4 = new Usuario("usuario 4", "username usuario 4", "password usuario 4");

Usuario usuario5 = new Usuario("usuario 5", "username usuario 5", "password usuario 5");

AbstractDAO.almacenaEntidad(usuario1);

AbstractDAO.almacenaEntidad(usuario2);

AbstractDAO.almacenaEntidad(usuario3);

AbstractDAO.almacenaEntidad(usuario4);

AbstractDAO.almacenaEntidad(usuario5);

AbstractDAO.eliminaEntidad(usuario2);

AbstractDAO.eliminaEntidad(usuario5);

}

El código es muy simple: creamos 5 Usuarios que posteriormente guardamos en nuestra base de datos usando el método

"almacenaEntidad" de la clase "UsuarioDAO", y posteriormente eliminamos a los Usuarios 2 y 5 usando el método

"eliminaEntidad" de la clase "UsuarioDAO".

Al ejecutar el código anterior genera la siguiente salida en nuestra consola:

Page 191: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 11: Interceptores y Eventos

www.javatutoriales.com/2010/08/hibernate-parte-11-interceptores-y.html 9/20

Como podemos ver hemos obtenido los 7 mensajes esperados en consola. Por lo que nuestro ejemplo ha funcionado correctamente _ .

Ahora veremos cómo funciona y cómo trabajar con el sistema de eventos de Hibernate.

Si queremos que nuestra aplicación reaccione a ciertos eventos de nuestra capa de persistencia, podemos usar la arquitectura de eventos

de Hibernate. El sistema de eventos puede ser usado para complementar, o reemplazar, el uso de interceptores.

Todos los métodos de la interface "Session" están relacionados con un evento del cual podemos recibir una notificación. Cuando

invocamos algún método como "save", "load", "delete", etc. Hibernate lanza un evento y podemos realizar alguna acción en ese

momento. Por cada tipo de evento existe una interface "xxEventListener" que tendremos que implementar para recibir notificaciones

para ese evento. Estas interfaces reciben un parámetro del tipo del evento que la lanzo. Por ejemplo, si esperamos una notificación de un

evento "saveOrUpdate", tendremos que implementar una interface "org.hibernate.event.SaveOrUpdateEventListener" en

cuyo método de escucha recibe un objeto de tipo "org.hibernate.event.SaveOrUpdateEvent".

El paquete "org.hibernate.event" contiene las siguientes interfaces que nos permiten recibir notificaciones de eventos:

AutoFlushEventListener

DeleteEventListener

DirtyCheckEventListener

EvictEventListener

FlushEntityEventListener

FlushEventListener

InitializeCollectionEventListener

LoadEventListener

LockEventListener

MergeEventListener

PersistEventListener

PostCollectionRecreateEventListener

PostCollectionRemoveEventListener

PostCollectionUpdateEventListener

PostDeleteEventListener

PostInsertEventListener

PostLoadEventListener

PostUpdateEventListener

PreCollectionRecreateEventListener

PreCollectionRemoveEventListener

PreCollectionUpdateEventListener

PreDeleteEventListener

PreInsertEventListener

PreLoadEventListener

PreUpdateEventListener

RefreshEventListener

ReplicateEventListener

SaveOrUpdateEventListener

Para este ejemplo crearemos un sistema que reciba notificaciones después de que una entidad "Usuario" sea cargada de la base de

datos, antes y después de eliminarla y antes de actualizarla, y muestre en consola el mensaje correspondiente al eventos que se ha

recibido.

Crearemos un nuevo proyecto en NetBeans siguiendo los mismos pasos que usamos para el proyecto que usa los interceptores. Las

únicas diferencias son que ahora crearemos un paquete llamados "eventos" en lugar de "interceptores" y que usaremos una base

de datos llamada "hibernateeventos".

Usaremos la misma clase "Usuario" del ejemplo anterior, así como las clases "HibernateUtil" del segundo tutorial, y

"AbstractDAO" del noveno tutorial.

Hagamos nuevamente una lista con los requerimientos de nuestra aplicación, en lo que a eventos se refiere.

2. Eventos

Page 192: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 11: Interceptores y Eventos

www.javatutoriales.com/2010/08/hibernate-parte-11-interceptores-y.html 10/20

1. Antes de que un Usuario sea cargado debemos mostrar un mensaje en consola.

2. Antes de eliminar un Usuario debemos mostrar un mensaje en consola.

3. Después de eliminar un Usuario debemos mostrar un mensaje en consola.

4. Antes de actualizar un Usuario debemos mostrar un mensaje en consola.

Si echamos un vistazo a la lista listeners de eventos que nos proporciona Hibernate podemos ver que tenemos 4 interfaces que nos sirven

perfectamente para cada uno de los requerimientos (vaya coincidencia _ !).

Comencemos viendo cómo cubrir el primer requerimiento. Si revisamos la lista de listeners disponibles vemos que hay uno llamado

"PostLoadEventListener" el cual, como su nombre lo indica, es llamado después de que la entidad es cargada en el contexto

persistente de nuestra aplicación. Como la entidad ya se encuentra cargada, es posible acceder a sus atributos para consultarlos o

modificarlos.

Creamos una nueva clase, en el paquete "eventos", llamada "CargaUsuarioListener", la cual implementará la interface

"PostLoadEventListener":

public class CargaUsuarioListener implements PostLoadEventListener

{

}

La interface "PostLoadEventListener" tiene un solo método llamado "onPostLoad", el cual recibe un solo argumento de tipo

"org.hibernate.event.PostLoadEvent", de la siguiente forma:

public void onPostLoad(PostLoadEvent postLoadEvent);

La clase "PostLoadEvent" tiene dos métodos que nos serán de mucha utilidad: "getEntity()", que regresa un "Object" que es la

entidad que se está recuperando, y "getId()", y regesa el identificador de dicha entidad. Primero usaremos "getEntity()" para

recuperar la entidad cargada, de la siguiente forma:

Object entidad = postLoadEvent.getEntity();

Después revisamos, usando el operador "instanceof", si la entidad es de tipo "Usuario". De ser el caso, hacemos un cast al tipo

"Usuario" para poder obtener su "username". Una vez hecho esto, usamos el método "getId()" para obtener el identificador de la

entidad. De la siguiente forma:

if (entidad instanceof Usuario)

{

Usuario usuario = (Usuario) entidad;

System.out.println("Se ha cargado el usuario " + usuario.getUsername() + ", id=\"" + postLoadE

vent.getId() + "\"");

}

Finalmente la clase "CargaUsuarioListener" queda de la siguiente forma:

public class CargaUsuarioListener implements PostLoadEventListener

{

public void onPostLoad(PostLoadEvent postLoadEvent)

{

Object entidad = postLoadEvent.getEntity();

if (entidad instanceof Usuario)

{

Usuario usuario = (Usuario) entidad;

System.out.println("Se ha cargado el usuario " + usuario.getUsername() + ", id=\"" + p

ostLoadEvent.getId() + "\"");

}

}

}

Page 193: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 11: Interceptores y Eventos

www.javatutoriales.com/2010/08/hibernate-parte-11-interceptores-y.html 11/20

Veamos ahora el segundo requerimiento: "Antes de eliminar un Usuario debemos mostrar un mensaje en consola". Nuevamente si

revisamos la lista de listeners vemos que existe una interface llamada "PreDeleteEventListener" que, como su nombre indica, es

llamado antes de que una entidad sea eliminada de la base de datos.

Creamos una nueva clase llamada "PreEliminaUsuarioListener" que implementará la interface "PreDeleteEventListener". De

la siguiente forma:

public class PreEliminaUsuarioListener implements PreDeleteEventListener

{

}

La interface "PreDeleteEventListener" tiene un solo método llamado "onPreDelete", que regresa un booleano que indica la

operación de eliminación debe ser cancelada. También recibe un único argumento de tipo

"org.hibernate.event.PreDeleteEvent". La firma del método es la siguiente:

public boolean onPreDelete(PreDeleteEvent preDeleteEvent);

Lo primero que haremos es regresar un valor de "false", ya que NO queremos que la operación de eliminación sea cancelada.

public boolean onPreDelete(PreDeleteEvent preDeleteEvent)

{

return false;

}

La clase "PreDeleteEvent" tiene prácticamente los mismos métodos que "PostLoadEvent" (al menos los que nos interesan). Por lo

nuevamente usaremos el método "getEntity()" para obtener la entidad que estamos a punto de eliminar, y después revisamos,

usando el operador "instanceof", si la entidad es de tipo "Usuario". De ser el caso, hacemos un cast al tipo "Usuario" para poder

obtener su "username". Una vez hecho esto, usamos el método "getId()" para obtener el identificador de la entidad. Por lo que el

método queda de la siguiente forma:

public boolean onPreDelete(PreDeleteEvent preDeleteEvent)

{

Object entidad = preDeleteEvent.getEntity();

if (entidad instanceof Usuario)

{

Usuario usuario = (Usuario) entidad;

System.out.println("Se eliminará al usuario " + usuario.getUsername() + ", id=\"" + preDel

eteEvent.getId() + "\"");

}

return false;

}

Prosigamos con el tercer requerimiento: "Después de eliminar un Usuario debemos mostrar un mensaje en consola". Para este

requerimiento nuevamente haremos uso de una de las interfaces de la lista: "PostDeleteEventListener", así que crearemos una

nueva clase llamada "PostEliminaUsuarioListener", en el paquete "eventos", que implemente esta interface, de la siguiente

forma:

public class PostEliminaUsuarioListener implements PostDeleteEventListener

{

}

Esta interface tiene un solo método: "onPostDelete", que recibe un parámetro de tipo

"org.hibernate.event.PostDeleteEvent", de la siguiente forma:

public void onPostDelete(PostDeleteEvent pde);

Page 194: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 11: Interceptores y Eventos

www.javatutoriales.com/2010/08/hibernate-parte-11-interceptores-y.html 12/20

Al igual que en los casos anteriores, "PostDeleteEvent" tiene dos métodos que nos interesan "getEntity()", y "getId()". Como

creo que la idea de cómo implementaremos nuestros eventos ya está entendida, solo colocaré el código de la clase

"PostEliminaUsuarioListener" que se encarga del requerimiento 3 y "ActualizaUsuarioListener" que se encarga del

requerimiento 4.

La clase "PostEliminaUsuarioListener" queda de la siguiente forma:

public class PostEliminaUsuarioListener implements PostDeleteEventListener

{

public void onPostDelete(PostDeleteEvent postDeleteEvent)

{

Object entidad = postDeleteEvent.getEntity();

if (entidad instanceof Usuario)

{

Usuario usuario = (Usuario) entidad;

System.out.println("Se ha eliminado al Usuario " + usuario.getUsername() + ", id=\"" +

postDeleteEvent.getId() + "\"");

}

}

}

Y la clase "ActualizaUsuarioListener" queda así:

public class ActualizaUsuarioListener implements PreUpdateEventListener

{

public boolean onPreUpdate(PreUpdateEvent preUploadEvent)

{

Object entidad = preUploadEvent.getEntity();

if(entidad instanceof Usuario)

{

Usuario usuario = (Usuario)entidad;

System.out.println("Se va a actualizar al usuario " + usuario.getUsername() + ", id=\"

" + preUploadEvent.getId() + "\"");

}

return false;

}

}

Ahora debemos decirle a Hibernate que queremos que estas clases reciban notificaciones para los eventos indicados. Para esto existen

dos formas: la primera y que me parece más sencilla es colocando los listeners en el archivo de configuración "hibernate.cfg.xml"

en la configuración del "session-factory", y la segunda es en código, al momento de crear el objeto

"org.hibernate.cfg.Configuration" o "org.hibernate.cfg.AnnotationConfiguration" (que nosotros hacemos en la

clase "HibernateUtil" en nuestro bloque de inicialización estático). Veremos cómo hacerlo de las dos formas, aunque en los archivos

del tutorial dejaré la versión que hace uso del archivo de configuración.

En la primera de las formas debemos colocar, al final del archivo "hibernate.cfg.xml", un elemento "<event>", en cuyo atributo

"type" indicamos el tipo de evento que está esperando recibir el listener que indicaremos posteriormente en el sub-elemento "

<listener>", en cuyo atributo "class" indicaremos la clase que responderá al evento. Por ejemplo, para indicar que la clase

"hibernate.eventos.eventos.CargaUsuarioListener" debe responder al evento de que una entidad haya sido cargada lo

hacemos de la siguiente forma:

<event type="post-load">

<listener class="hibernate.eventos.eventos.CargaUsuarioListener" />

</event>

Los tipos de eventos, como deben ser marcados en el atributo "type" del elemento "<event>" son:

*Nota: Algunos de los listenes pueden tomar más de un valor, en la tabla los he separado usando una coma, pero en su archivo de configuración

solo deben colocar uno de los dos.

Clase Listener Valor de typo

2.1. CONFIGURACIÓN DE LISTENERS DE EVENTOS EN EL ARCHIVO DE CONFIGURACIÓN

Page 195: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 11: Interceptores y Eventos

www.javatutoriales.com/2010/08/hibernate-parte-11-interceptores-y.html 13/20

AutoFlushEventListener auto-flush

DeleteEventListener delete

DirtyCheckEventListener dirty-check

EvictEventListener evict

FlushEntityEventListener flush-entity

FlushEventListener Flush

InitializeCollectionEventListener load-collection

LoadEventListener load

LockEventListener Lock

MergeEventListener merge

PersistEventListener create, create-onflush

PostCollectionRecreateEventListener post-collection-recreate

PostCollectionRemoveEventListener post-collection-remove

PostCollectionUpdateEventListener post-collection-update

PostDeleteEventListener post-delete, post-commit-delete

PostInsertEventListener post-insert, post-commit-insert

PostLoadEventListener post-load

PostUpdateEventListener post-update, post-commit-update

PreCollectionRecreateEventListener pre-collection-recreate

PreCollectionRemoveEventListener pre-collection-remove

PreCollectionUpdateEventListener pre-collection-update

PreDeleteEventListener pre-delete

PreInsertEventListener pre-insert

PreLoadEventListener pre-load

PreUpdateEventListener pre-update

RefreshEventListener refresh

ReplicateEventListener replicate

SaveOrUpdateEventListener save-update, save, update

Por lo tanto, los listeners declarados es nuestro archivo de configuración quedan de la siguiente forma:

<event type="post-load">

<listener class="hibernate.eventos.eventos.CargaUsuarioListener" />

</event>

<event type="pre-delete">

<listener class="hibernate.eventos.eventos.PreEliminaUsuarioListener" />

</event>

<event type="post-delete">

<listener class="hibernate.eventos.eventos.PostEliminaUsuarioListener" />

</event>

<event type="pre-update">

<listener class="hibernate.eventos.eventos.ActualizaUsuarioListener" />

</event>

Y el archivo completo de configuración quedaría de esta forma:

Page 196: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 11: Interceptores y Eventos

www.javatutoriales.com/2010/08/hibernate-parte-11-interceptores-y.html 14/20

<?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>

<!-- parametros para la conexion a la base de datos -->

<property name="connection.driver_class">com.mysql.jdbc.Driver</property>

<property name="connection.url">jdbc:mysql://localhost/hibernateeventos</property>

<property name="connection.username">usuario</property>

<property name="connection.password">password</property>

<!-- Configuracion del pool interno -->

<property name="connection.pool_size">1</property>

<!-- Dialecto de la base de datos -->

<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>

<!-- Otras propiedades importantes -->

<property name="show_sql">false</property>

<property name="hbm2ddl.auto">create-drop</property>

<!-- Clases o Archivos de mapeo -->

<mapping class="hibernate.eventos.modelo.Usuario" />

<event type="post-load">

<listener class="hibernate.eventos.eventos.CargaUsuarioListener" />

</event>

<event type="pre-delete">

<listener class="hibernate.eventos.eventos.PreEliminaUsuarioListener" />

</event>

<event type="post-delete">

<listener class="hibernate.eventos.eventos.PostEliminaUsuarioListener" />

</event>

<event type="pre-update">

<listener class="hibernate.eventos.eventos.ActualizaUsuarioListener" />

</event>

</session-factory>

</hibernate-configuration>

Veamos que nuestra aplicación funciona correctamente colocando el siguiente código en el método "main" de nuestra clase "Main":

public static void main(String[] args)

{

Usuario usuario1 = new Usuario("usuario 1", "username usuario1", "password usuario1");

Usuario usuario2 = new Usuario("usuario 2", "username usuario2", "password usuario2");

Usuario usuario3 = new Usuario("usuario 3", "username usuario3", "password usuario3");

Usuario usuario4 = new Usuario("usuario 4", "username usuario4", "password usuario4");

// Almacenamos las 4 entidades Usuario

AbstractDAO.almacenaEntidad(usuario1);

AbstractDAO.almacenaEntidad(usuario2);

AbstractDAO.almacenaEntidad(usuario3);

AbstractDAO.almacenaEntidad(usuario4);

// Recuperamos 2 Usuarios

AbstractDAO.getEntidad(usuario2.getId(), Usuario.class);

AbstractDAO.getEntidad(usuario3.getId(), Usuario.class);

// Actualizamos 3 Usuarios

usuario1.setNombre("Nuevo nombre del Usuario1");

usuario2.setNombre("Nuevo nombre del Usuario2");

usuario3.setNombre("Nuevo nombre del Usuario3");

AbstractDAO.almacenaEntidad(usuario1);

AbstractDAO.almacenaEntidad(usuario2);

AbstractDAO.almacenaEntidad(usuario3);

// Eliminamos 2 Usuarios

AbstractDAO.eliminaEntidad(usuario1);

AbstractDAO.eliminaEntidad(usuario4);

}

Page 197: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 11: Interceptores y Eventos

www.javatutoriales.com/2010/08/hibernate-parte-11-interceptores-y.html 15/20

Nuevamente el código es muy sencillo y autoexplicativo. Simplemente creamos 4 objetos "Usuario" que posteriormente almacenamos

en la base de datos. Luego recuperamos 2 de estos Usuarios, haciendo uso de su identificador, actualizamos 3, y eliminamos 2, con lo

que esperamos tener en consola 9 mensajes (2 de recuperación, 3 de actualización, 2 de antes de eliminar, y 2 de después de eliminar).

Ejecutemos nuestra aplicación para ver la salida:

Como podemos ver, tenemos los 9 mensajes en consola que esperábamos, por lo que nuestros listeners de eventos han funcionado

correctamente.

Ahora veamos cómo hacer lo mismo en código, directo en nuestra clase "HibernateUtil".

Si recordamos, "HibernateUtil" tiene un inicializador estático en donde se crea un objeto de tipo

"org.hibernate.cfg.Configuration", si usamos archivos de mapeo, o "org.hibernate.cfg.AnnotationConfiguration",

si usamos anotaciones. Actualmente creamos este objeto e inmediatamente, mediante encadenamiento de métodos, creamos el

"SessionFactory" correspondiente:

new AnnotationConfiguration().configure().buildSessionFactory();

Como ahora necesitamos una referencia a este objeto para registrar nuestros listeners tendremos que modificar la línea anterior para

hacer lo mismo, pero en dos pasos, de esta forma:

Configuration cfg = new AnnotationConfiguration();

sessionFactory = cfg.configure().buildSessionFactory();

Así ya tenemos una referencia a nuestro objeto "Configuration" para poder registrar los listeners de eventos que necesitamos. El

objeto "org.hibernate.cfg.Configuration" tiene un método llamado "getEventListeners()", que regresa un objeto de tipo

"org.hibernate.event.EventListeners", el cual mantendrá la lista de listeners de nuestra aplicación. Este objeto tiene una serie

de métodos setters, uno para cada tipo de listeners, los cuales reciben un arreglo con los listeners de eventos que queremos registrar.

Por ejemplo, para registrar el listener "CargaUsuarioListener", que recordemos que es de tipo "PostLoadEventListener", lo

hacemos de la siguiente forma:

Configuration cfg = new AnnotationConfiguration();

EventListeners eventListeners = cfg.getEventListeners();

eventListeners.setPostLoadEventListeners(new PostLoadEventListener[]{new CargaUsuarioListener()});

Donde creamos un arreglo anónimo de tipo "PostLoadEventListener" cuyo único elemento es un objeto de tipo

"CargaUsuarioListener". Su tuviéramos otro listener de tipo "PostLoadEventListener" simplemente lo agregaríamos dentro del

mismo arreglo.

Podemos observar que este arreglo anónimo lo pasamos como parámetro al método "setPostLoadEventListeners" que es que

usamos para registrar los listeners de tipo "PostLoadEventListener". Hacemos lo mismo con el resto de nuestros listeners de la

siguiente forma:

2.2. CONFIGURACIÓN DE LISTENERS DE EVENTOS EN CÓDIGO

Page 198: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 11: Interceptores y Eventos

www.javatutoriales.com/2010/08/hibernate-parte-11-interceptores-y.html 16/20

eventListeners.setPreDeleteEventListeners (new PreDeleteEventListener[] {new PreEliminaUsuarioList

ener()});

eventListeners.setPostDeleteEventListeners(new PostDeleteEventListener[]{new PostEliminaUsuarioLis

tener()});

eventListeners.setPreUpdateEventListeners (new PreUpdateEventListener[] {new ActualizaUsuarioListe

ner()});

Con lo que la nueva clase "HibernateUtil" queda de la siguiente forma:

public class HibernateUtil

{

private static final SessionFactory sessionFactory;

static

{

try

{

Configuration cfg = new AnnotationConfiguration();

EventListeners eventListeners = cfg.getEventListeners();

eventListeners.setPostLoadEventListeners (new PostLoadEventListener[] {new CargaUsua

rioListener()});

eventListeners.setPreDeleteEventListeners (new PreDeleteEventListener[] {new PreElimin

aUsuarioListener()});

eventListeners.setPostDeleteEventListeners(new PostDeleteEventListener[]{new PostElimi

naUsuarioListener()});

eventListeners.setPreUpdateEventListeners (new PreUpdateEventListener[] {new Actualiza

UsuarioListener()});

sessionFactory = cfg.configure().buildSessionFactory();

}

catch (HibernateException he)

{

System.err.println("Ocurrió un error en la inicialización de la SessionFactory: " + he

);

throw new ExceptionInInitializerError(he);

}

}

public static SessionFactory getSessionFactory()

{

return sessionFactory;

}

}

Y listo. Ahora probemos que funciona colocando el siguiente código en el método "main" de nuestra clase "Main":

Page 199: Tutorial Hibernate

10/11/13 Tutoriales de Programacion Java: Hibernate - Parte 11: Interceptores y Eventos

www.javatutoriales.com/2010/08/hibernate-parte-11-interceptores-y.html 17/20

Publicado por Alex en 21:15

Reacciones: divertido (2) interesante (1) increible (1) no me gusta (0)

public static void main(String[] args)

{

Usuario usuario1 = new Usuario("usuario 1", "username usuario1", "password usuario1");

Usuario usuario2 = new Usuario("usuario 2", "username usuario2", "password usuario2");

Usuario usuario3 = new Usuario("usuario 3", "username usuario3", "password usuario3");

Usuario usuario4 = new Usuario("usuario 4", "username usuario4", "password usuario4");

// Almacenamos las 4 entidades Usuario

AbstractDAO.almacenaEntidad(usuario1);

AbstractDAO.almacenaEntidad(usuario2);

AbstractDAO.almacenaEntidad(usuario3);

AbstractDAO.almacenaEntidad(usuario4);

// Recuperamos 2 Usuarios

AbstractDAO.getEntidad(usuario2.getId(), Usuario.class);

AbstractDAO.getEntidad(usuario3.getId(), Usuario.class);

// Actualizamos 3 Usuarios

usuario1.setNombre("Nuevo nombre del Usuario1");

usuario2.setNombre("Nuevo nombre del Usuario2");

usuario3.setNombre("Nuevo nombre del Usuario3");

AbstractDAO.almacenaEntidad(usuario1);

AbstractDAO.almacenaEntidad(usuario2);

AbstractDAO.almacenaEntidad(usuario3);

// Eliminamos 2 Usuarios

AbstractDAO.eliminaEntidad(usuario1);

AbstractDAO.eliminaEntidad(usuario4);

}

Que es el mismo que ya había explicado. Solo recordemos que esperamos obtener en la consola 9 mensajes: 2 de recuperación, 3 de

actualización, 2 de antes de eliminar, y 2 de después de eliminar. Cuando ejecutamos nuestra aplicación obtenemos la siguiente salida:

Como podemos ver, obtuvimos los 9 mensajes esperados. Por lo que nuestro ejemplo ha funcionado correctamente.

Pues bien, este fue el último tutorial oficial de la serie de Hibernate, los cuales espero sirvan para conocer los conceptos básicos de esta

herramienta ORM que es bastante útil.

Cualquier duda, comentario, o sugerencia no duden en dejarlo en la sección correspondiente.

Saludos.

Descarga los archivos de este tutorial desde aquí:

Interceptores con Hibernate

Eventos con Hibernate

Entradas Relacionadas:

Parte 1: Persistiendo Objetos Simples usando Mapeos en XML

Parte 2: Persistiendo Objetos Simples usando Anotaciones (Metadatos)

Parte 3: Relaciones / Uno a uno

Parte 4: Relaciones / Uno a muchos

Parte 5: Relaciones / Muchos a uno

Parte 6: Relaciones / Muchos a Muchos

Parte 7: HQL Primera Parte

Parte 8: HQL Segunda Parte

Parte 9: Parámetros en HQL

Parte 10: Herencia

Page 200: Tutorial Hibernate

Hibernate Annotations

Reference Guide

Version: 3.1 beta 9

Page 201: Tutorial Hibernate

Table of ContentsPreface ............................................................................................................................................ iv1. Setting up an annotations project ................................................................................................ 1

1.1. Requirements ..................................................................................................................... 11.2. Configuration ..................................................................................................................... 1

2. Entity Beans ................................................................................................................................ 32.1. Intro .................................................................................................................................. 32.2. Mapping with EJB3 Annotations ......................................................................................... 3

2.2.1. Declaring an entity bean .......................................................................................... 32.2.1.1. Defining the table ......................................................................................... 32.2.1.2. Versioning for optimistic locking ................................................................... 4

2.2.2. Mapping simple properties ....................................................................................... 42.2.2.1. Declaring basic property mappings ................................................................ 42.2.2.2. Declaring column attributes ........................................................................... 52.2.2.3. Embedded objects (aka components) .............................................................. 62.2.2.4. Non-annotated property defaults .................................................................... 8

2.2.. Mapping identifier properties ..................................................................................... 82.2.4. Mapping inheritance .............................................................................................. 11

2.2.4.1. Table per class ............................................................................................ 112.2.4.2. Single table per class hierarchy .................................................................... 112.2.4.3. Joined subclasses ........................................................................................ 122.2.4.4. Inherit properties from superclasses ............................................................. 12

2.2.5. Mapping entity bean associations/relationships ........................................................ 142.2.5.1. One-to-one ................................................................................................. 142.2.5.2. Many-to-one ............................................................................................... 152.2.5.3. Collections ................................................................................................. 152.2.5.4. Transitive persistence with cascading ........................................................... 212.2.5.5. Association fetching ................................................................................... 21

2.2.6. Mapping composite primary and foreign keys ......................................................... 212.2.7. Mapping secondary tables ...................................................................................... 23

2.3. Mapping Queries .............................................................................................................. 232.3.1. Mapping EJBQL/HQL queries ............................................................................... 232.3.2. Mapping native queries .......................................................................................... 24

2.4. Hibernate Annotation Extensions ...................................................................................... 272.4.1. Entity ................................................................................................................... 282.4.2. Identifier ............................................................................................................... 292.4.3. Property ................................................................................................................ 29

2.4.3.1. Access type ................................................................................................ 292.4.3.2. Formula ..................................................................................................... 312.4.3.3. Type .......................................................................................................... 312.4.3.4. Index ......................................................................................................... 32

2.4.4. Inheritance ............................................................................................................ 322.4.5. Single Association related annotations .................................................................... 322.4.6. Collection related annotations ................................................................................ 32

2.4.6.1. Parameter annotations ................................................................................. 322.4.6.2. Extra collection types .................................................................................. 33

2.4.7. Cache ................................................................................................................... 352.4.8. Filters ................................................................................................................... 352.4.9. Queries ................................................................................................................. 36

Hibernate 3.1 beta 9 ii

Page 202: Tutorial Hibernate

3. Hibernate Validator .................................................................................................................. 373.1. Constraints ...................................................................................................................... 37

3.1.1. What is a constraint? .............................................................................................. 373.1.2. Built in constraints ................................................................................................. 373.1.3. Error messages ...................................................................................................... 383.1.4. Writing your own constraints ................................................................................. 393.1.5. Annotating your domain model .............................................................................. 40

3.2. Using the Validator framework ......................................................................................... 413.2.1. Database schema-level validation ........................................................................... 413.2.2. Hibernate event-based validation ............................................................................ 423.2.3. Application-level validation ................................................................................... 423.2.4. Validation informations ......................................................................................... 42

4. Hibernate Lucene Integration ................................................................................................... 444.1. Using Lucene to index your entities ................................................................................... 44

4.1.1. Annotating your domain model .............................................................................. 444.1.2. Enabling automatic indexing .................................................................................. 44

Hibernate Annotations

Hibernate 3.1 beta 9 iii

Page 203: Tutorial Hibernate

PrefaceHibernate, like all other object/relational mapping tools, requires metadata that governs the transformation ofdata from one representation to the other (and vice versa). In Hibernate 2.x, mapping metadata is most of thetime declared in XML text files. Another option is XDoclet, utilizing Javadoc source code annotations and apreprocessor at compile time. The same kind of annotation support is now available in the standard JDK, al-though more powerful and better supported by tools. IntelliJ IDEA, and Eclipse for example, support auto-completion and syntax highlighting of JDK 5.0 annotations. Annotations are compiled into the bytecode andread at runtime (in Hibernate's case on startup) using reflection, so no external XML files are needed.

The EJB3 specification recognizes the interest and the success of the transparent object/relational mappingparadigm. The EJB3 specification standardizes the basic APIs and the metadata needed for any object/relationalpersistence mechanism. Hibernate EntityManager implements the programming interfaces and lifecycle rulesas defined by the EJB3 persistence specification. Together with Hibernate Annotations, this wrapper imple-ments a complete (and standalone) EJB3 persistence solution on top of the mature Hibernate core. You may usea combination of all three together, annotations without EJB3 programming interfaces and lifecycle, or evenpure native Hibernate, depending on the business and technical needs of your project. You can at all times fallback to Hibernate native APIs, or if required, even to native JDBC and SQL.

Please note that this documentation is based on a preview release of the Hibernate Annotations that follows thepublic final draft of EJB 3.0/JSR-220 persistence annotations. This work is already very close to the final con-cepts in the new specification. Our goal is to provide a complete set of ORM annotations, including EJB3standard annotations as well as Hibernate3 extensions for cases not covered by the specification. Eventuallyyou will be able to create all possible mappings with annotations. See the JIRA road mapsection for more in-formation.

The EJB3 Public final draft has change some annotations, please refer to http://www.hibernate.org/371.html asa migration guide between Hibernate Annotations 3.1beta7 and 3.1beta8.

Hibernate 3.1 beta 9 iv

Page 204: Tutorial Hibernate

Chapter 1. Setting up an annotations project

1.1. Requirements

• Download and unpack the Hibernate Annotations distribution from the Hibernate website.

• This preview release requires Hibernate 3.2.0.CR2 and above. Do not use this release of Hibernate Annota-tions with an older version of Hibernate 3.x!

• This release is known to work on Hibernate core 3.2.0.CR2

• Make sure you have JDK 5.0 installed. You can of course continue using XDoclet and get some of the be-nefits of annotation-based metadata with older JDK versions. Note that this document only describes JDK5.0 annotations and you have to refer to the XDoclet documentation for more information.

1.2. Configuration

First, set up your classpath (after you have created a new project in your favorite IDE):

• Copy all Hibernate3 core and required 3rd party library files (see lib/README.txt in Hibernate).

• Copy hibernate-annotations.jar and lib/ejb3-persistence.jar from the Hibernate Annotations dis-tribution to your classpath as well.

• To use the Chapter 4, Hibernate Lucene Integration, add the lucene jar file.

We also recommend a small wrapper class to startup Hibernate in a static initializer block, known as Hibern-

ateUtil. You might have seen this class in various forms in other areas of the Hibernate documentation. ForAnnotation support you have to enhance this helper class as follows:

package hello;

import org.hibernate.*;import org.hibernate.cfg.*;import test.*;import test.animals.Dog;

public class HibernateUtil {

private static final SessionFactory sessionFactory;

static {try {

sessionFactory = new AnnotationConfiguration().buildSessionFactory();} catch (Throwable ex) {

// Log exception!throw new ExceptionInInitializerError(ex);

}}

public static Session getSession()throws HibernateException {

return sessionFactory.openSession();}

}

Hibernate 3.1 beta 9 1

Page 205: Tutorial Hibernate

Interesting here is the use of AnnotationConfiguration. The packages and annotated classes are declared inyour regular XML configuration file (usually hibernate.cfg.xml). Here is the equivalent of the above declara-tion:

<!DOCTYPE hibernate-configuration PUBLIC"-//Hibernate/Hibernate Configuration DTD 3.0//EN""http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration><session-factory><mapping package="test.animals"/><mapping class="test.Flight"/><mapping class="test.Sky"/><mapping class="test.Person"/><mapping class="test.animals.Dog"/>

</session-factory></hibernate-configuration>

Note that you can mix the hbm.xml use and the new annotation one.

Alternatively, you can define the annotated classes and packages using the programmatic API

sessionFactory = new AnnotationConfiguration().addPackage("test.animals") //the fully qualified package name.addAnnotatedClass(Flight.class).addAnnotatedClass(Sky.class).addAnnotatedClass(Person.class).addAnnotatedClass(Dog.class).buildSessionFactory();

You can also use the Hibernate Entity Manager which has it's own configuration mechanism. Please refer tothis project documentation for more details.

There is no other difference in the way you use Hibernate APIs with annotations, except for this startup routinechange or in the configuration file. You can use your favorite configuration method for other properties ( hi-bernate.properties, hibernate.cfg.xml, programmatic APIs, etc). You can even mix annotated persistentclasses and classic hbm.cfg.xml declarations with the same SessionFactory. You can however not declare aclass several times (whether annotated or through hbm.xml). You cannot mix configuration strategies (hbm vsannotations) in a mapped entity hierarchy either.

To ease the migration process from hbm files to annotations, the configuration mechanism detects the mappingduplication between annotations and hbm files. HBM files are then prioritized over annotated metadata on aclass to class basis. You can change the priority using hibernate.mapping.precedence property. The default ishbm, class, changing it to class, hbm will prioritize the annotated classes over hbm files when a conflict oc-curs.

Setting up an annotations project

Hibernate 3.1 beta 9 2

Page 206: Tutorial Hibernate

Chapter 2. Entity Beans

2.1. Intro

This section covers EJB 3.0 entity bean annotations and Hibernate-specific extensions.

2.2. Mapping with EJB3 Annotations

EJB3 entity beans are plain POJOs. Actually they represent the exact same concept as the Hibernate persistententities. Their mappings are defined through JDK 5.0 annotations (an XML descriptor syntax for overridingwill be defined in the EJB3 specification, but it's not finalized so far). Annotations can be split in two categor-ies, the logical mapping annotations (allowing you to describe the object model, the class associations, etc.) andthe physical mapping annotations (describing the physical schema, tables, columns, indexes, etc). We will mixannotations from both categories in the following code examples.

EJB3 annotations are in the javax.persistence.* package. Most JDK 5 compliant IDE (like Eclipse, IntelliJIDEA and Netbeans) can autocomplete annotation interfaces and attributes for you (even without a specific"EJB3" module, since EJB3 annotations are plain JDK 5 annotations).

For more and runnable concrete examples read the JBoss EJB 3.0 tutorial or review the Hibernate Annotationstest suite. Most of the unit tests have been designed to represent a concrete example and be a inspiration source.

2.2.1. Declaring an entity bean

Every bound persistent POJO class is an entity bean and is declared using the @Entity annotation (at the classlevel):

@Entitypublic class Flight implements Serializable {

Long id;

@Idpublic Long getId() { return id; }

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

@Entity declares the class as an entity bean (i.e. a persistent POJO class), @Id declares the identifier property ofthis entity bean. The other mapping declarations are implicit. This configuration by exception concept is centralto the new EJB3 specification and a major improvement. The class Flight is mapped to the Flight table, usingthe column id as its primary key column.

Depending on whether you annotate fields or methods, the access type used by Hibernate will be field orproperty. The EJB3 spec requires that you declare annotations on the element type that will be accessed, i.e.the getter method if you use property access, the field if you use field access. Mixing EJB3 annotations inboth fields and methods should be avoided. Hibernate will guess the access type from the position of @Id or@EmbeddedId.

Defining the table

Hibernate 3.1 beta 9 3

Page 207: Tutorial Hibernate

@Table is set at the class level; it allows you to define the table, catalog, and schema names for your entity beanmapping. If no @Table is defined the default values are used: the unqualified class name of the entity.

@Entity@Table(name="tbl_sky")public class Sky implements Serializable {...

The @Table element also contains a schema and a catalog attributes, if they need to be defined. You can alsodefine unique constraints to the table using the @UniqueConstraint annotation in conjunction with @Table (fora unique constraint bound to a single column, refer to @Column).

@Table(name="tbl_sky",uniqueConstraints = {@UniqueConstraint(columnNames={"month", "day"})}

)

A unique constraint is applied to the tuple month, day. Note that the columnNames array refers to the logicalcolumn names.The logical column name is defined by the Hibernate NamingStrategy implementation. The default EJB3 nam-ing strategy use the physical column name as the logical column name. Note that this may be different than theproperty name (if the column name is explicit). Unless you override the NamingStrategy, you shouldn't worryabout that.

Versioning for optimistic locking

You can add optimistic locking capability to an entity bean using the @Version annotation:

@Entitypublic class Flight implements Serializable {...

@Version@Column(name="OPTLOCK")public Integer getVersion() { ... }

}

The version property will be mapped to the OPTLOCK column, and the entity manager will use it to detect con-flicting updates (preventing lost updates you might otherwise see with the last-commit-wins strategy).

The version column may be a numeric (the recommended solution) or a timestamp as per the EJB3 spec. Hi-bernate support any kind of type provided that you define and implement the appropriate UserVersionType.

2.2.2. Mapping simple properties

Declaring basic property mappings

Every non static non transient property (field or method) of an entity bean is considered persistent, unless youannotate it as @Transient. Not having an annotation for your property is equivalent to the appropriate @Basic

annotation. The @Basic annotation allows you to declare the fetching strategy for a property:

public transient int counter; //transient property

private String firstname; //persistent property

@TransientString getLengthInMeter() { ... } //transient property

Entity Beans

Hibernate 3.1 beta 9 4

Page 208: Tutorial Hibernate

String getName() {... } // persistent property

@Basicint getLength() { ... } // persistent property

@Basic(fetch = FetchType.LAZY)String getDetailedComment() { ... } // persistent property

@Temporal(TemporalType.TIME)java.util.Date getDepartureTime() { ... } // persistent property

@Enumerated(STRING)Starred getNote() { ... } //enum persisted as String in database

counter, a transient field, and lengthInMeter, a method annotated as @Transient, and will be ignored by theentity manager. name, length, and firstname properties are mapped persistent and eagerly fetched (the defaultfor simple properties). The detailedComment property value will be lazily fetched from the database once alazy property of the entity is accessed for the first time. Usually you don't need to lazy simple properties (not tobe confused with lazy association fetching).

Note

To enable property level lazy fetching, your classes have to be instrumented: bytecode is added to theoriginal one to enable such feature, please refer to the Hibernate reference documentation. If yourclasses are not instrumented, property level lazy loading is silently ignored.

The recommended alternative is to use the projection capability of EJB-QL or Criteria queries.

EJB3 support property mapping of all basic types supported by Hibernate (all basic Java types , their respectivewrappers and serializable classes). Hibernate Annotations support out of the box Enum type mapping either in-to a ordinal column (saving the enum ordinal) or a string based column (saving the enum string representation):the persistence representation, defaulted to ordinal, can be overriden through the @Enumerated annotation asshown in the note property example.

In core Java APIs, the temporal precision is not defined. When dealing with temporal data you might want todescribe the expected precision in database. Temporal data can have DATE, TIME, or TIMESTAMP precision (ie theactual date, only the time, or both). Use the @Temporal annotation to fine tune that.

@Lob indicates that the property should be persisted in a Blob or a Clob depending on the property type:java.sql.Clob, Character[], char[] and java.lang.String will be persisted in a Clob. java.sql.Blob,Byte[], byte[] and serializable type will be persisted in a Blob.

@Lobpublic String getFullText() {

return fullText;}

@Lobpublic byte[] getFullCode() {

return fullCode;}

If the property type implements java.io.Serializable and is not a basic type, and if the property is not annot-ated with @Lob, then the Hibernate serializable type is used.

Declaring column attributes

Entity Beans

Hibernate 3.1 beta 9 5

Page 209: Tutorial Hibernate

The column(s) used for a property mapping can be defined using the @Column annotation. Use it to override de-fault values (see the EJB3 specification for more information on the defaults). You can use this annotation atthe property level for properties that are:

• not annotated at all

• annotated with @Basic

• annotated with @Version

• annotated with @Lob

• annotated with @Temporal

• annotated with @org.hibernate.annotations.CollectionOfElements (for Hibernate only)

@Entitypublic class Flight implements Serializable {...@Column(updatable = false, name = "flight_name", nullable = false, length=50)public String getName() { ... }

The name property is mapped to the flight_name column, which is not nullable, has a length of 50 and is notupdatable (making the property immutable).

This annotation can be applied to regular properties as well as @Id or @Version properties.

@Column(name="columnName"; (1)boolean unique() default false; (2)boolean nullable() default true; (3)boolean insertable() default true; (4)boolean updatable() default true; (5)String columnDefinition() default ""; (6)String table() default ""; (7)int length() default 255; (8)int precision() default 0; // decimal precision (9)int scale() default 0; // decimal scale

(1) name (optional): the column name (default to the property name)(2) unique (optional): set a unique constraint on this column or not (default false)(3) nullable (optional): set the column as nullable (default false).(4) insertable (optional): whether or not the column will be part of the insert statement (default true)(5) updatable (optional): whether or not the column will be part of the update statement (default true)(6) columnDefinition (optional): override the sql DDL fragment for this particular column (non portable)(7) table (optional): define the targeted table (default primary table)(8) length (optional): column length (default 255)(8) precision (optional): column decimal precision (default 0)(10) scale (optional): column decimal scale if useful (default 0)

Embedded objects (aka components)

It is possible to declare an embedded component inside an entity and even override its column mapping. Com-ponent classes have to be annotated at the class level with the @Embeddable annotation. It is possible to overridethe column mapping of an embedded object for a particular entity using the @Embedded and

Entity Beans

Hibernate 3.1 beta 9 6

Page 210: Tutorial Hibernate

@AttributeOverride annotation in the associated property:

@Entitypublic class Person implements Serializable {

// Persistent component using defaultsAddress homeAddress;

@Embedded@AttributeOverrides( {

@AttributeOverride(name="iso2", column = @Column(name="bornIso2") ),@AttributeOverride(name="name", column = @Column(name="bornCountryName") )

} )Country bornIn;...

}

@Embeddablepublic class Address implements Serializable {

String city;Country nationality; //no overriding here

}

@Embeddablepublic class Country implements Serializable {

private String iso2;@Column(name="countryName") private String name;

public String getIso2() { return iso2; }public void setIso2(String iso2) { this.iso2 = iso2; }

public String getName() { return name; }public void setName(String name) { this.name = name; }...

}

A embeddable object inherit the access type of its owning entity (note that you can override that using the Hi-bernate specific @AccessType annotations (see Hibernate Annotation Extensions).

The Person entity bean has two component properties, homeAddress and bornIn. homeAddress property has notbeen annotated, but Hibernate will guess that it is a persistent component by looking for the @Embeddable an-notation in the Address class. We also override the mapping of a column name (to bornCountryName) with the@Embedded and @AttributeOverride annotations for each mapped attribute of Country. As you can see, Coun-try is also a nested component of Address, again using auto-detection by Hibernate and EJB3 defaults. Over-riding columns of embedded objects of embedded objects is currently not supported in the EJB3 spec, however,Hibernate Annotations supports it through dotted expressions.

@Embedded@AttributeOverrides( {

@AttributeOverride(name="city", column = @Column(name="fld_city") )@AttributeOverride(name="nationality.iso2", column = @Column(name="nat_Iso2") ),@AttributeOverride(name="nationality.name", column = @Column(name="nat_CountryName") )//nationality columns in homeAddress are overridden

} )Address homeAddress;

Hibernate Annotations supports one more feature that is not explicitly supported by the EJB3 specification.

Entity Beans

Hibernate 3.1 beta 9 7

Page 211: Tutorial Hibernate

You can annotate a embedded object with the @MappedSuperclass annotation to make the superclass propertiespersistent (see @MappedSuperclass for more informations).

While not supported by the EJB3 specification, Hibernate Annotations allows you to use association annota-tions in an embeddable object (ie @*ToOne nor @*ToMany). To override the association columns you can use@AssociationOverride.

If you want to have the same embeddable object type twice in the same entity, the column name defaulting willnot work: at least one of the columns will have to be explicit. Hibernate goes beyond the EJB3 spec and allowsyou to enhance the defaulting mechanism through the NamingStrategy. DefaultComponentSafeNaming-

Strategy is a small improvement over the default EJB3NamingStrategy that allows embedded objects to be de-faulted even if used twice in the same entity.

Non-annotated property defaults

If a property is not annotated, the following rules apply:

• If the property is of a single type, it is mapped as @Basic

• Otherwise, if the type of the property is annotated as @Embeddable, it is mapped as @Embedded

• Otherwise, if the type of the property is Serializable, it is mapped as @Basic in a column holding the objectin its serialized version

• Otherwise, if the type of the property is java.sql.Clob or java.sql.Blob, it is mapped as @Lob with the ap-propriate LobType

2.2.. Mapping identifier properties

The @Id annotation lets you define which property is the identifier of your entity bean. This property can be setby the application itself or be generated by Hibernate (preferred). You can define the identifier generationstrategy thanks to the @GeneratedValue annotation:

• AUTO - either identity column, sequence or table depending on the underlying DB

• TABLE - table holding the id

• IDENTITY - identity column

• SEQUENCE - sequence

Hibernate provides more id generators than the basic EJB3 ones. Check Hibernate Annotation Extensions formore informations.

The following example shows a sequence generator using the SEQ_STORE configuration (see below)

@Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_STORE")public Integer getId() { ... }

The next example uses the identity generator:

@Id @GeneratedValue(strategy=GenerationType.IDENTITY)

Entity Beans

Hibernate 3.1 beta 9 8

Page 212: Tutorial Hibernate

public Long getId() { ... }

The AUTO generator is the preferred type for portable applications (across several DB vendors). The identifiergeneration configuration can be shared for several @Id mappings with the generator attribute. There are severalconfigurations available through @SequenceGenerator and @TableGenerator. The scope of a generator can bethe application or the class. Class-defined generators are not visible outside the class and can override applica-tion level generators. Application level generators are defined at package level (see package-info.java):

@javax.persistence.TableGenerator(name="EMP_GEN",table="GENERATOR_TABLE",pkColumnName = "key",valueColumnName = "hi"pkColumnValue="EMP",allocationSize=20

)@javax.persistence.SequenceGenerator(

name="SEQ_GEN",sequenceName="my_sequence"

)package org.hibernate.test.metadata;

If package-info.java in the org.hibernate.test.metadata package is used to initialize the EJB configura-tion, EMP_GEN and SEQ_GEN are application level generators. EMP_GEN defines a table based id generator usingthe hilo algorithm with a max_lo of 20. The hi value is kept in a table "GENERATOR_TABLE". The information iskept in a row where pkColumnName "key" is equals to pkColumnValue "EMP" and column valueColumnName "hi"contains the the next high value used.

SEQ_GEN defines a sequence generator using a sequence named my_sequence. Note that this version of Hibern-ate Annotations does not handle initialValue and allocationSize parameters in the sequence generator.

The next example shows the definition of a sequence generator in a class scope:

@[email protected](

name="SEQ_STORE",sequenceName="my_sequence"

)public class Store implements Serializable {

private Long id;

@Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_STORE")public Long getId() { return id; }

}

This class will use a sequence named my_sequence and the SEQ_STORE generator is not visible in otherclasses. Note that you can check the Hibernate Annotations tests in the org.hibernate.test.metadata.id packagefor more examples.

You can define a composite primary key through several syntaxes:

• annotate the component property as @Id and make the component class @Embeddable

• annotate the component property as @EmbeddedId

Entity Beans

Hibernate 3.1 beta 9 9

Page 213: Tutorial Hibernate

• annotate the class as @IdClass and annotate each property of the entity involved in the primary key with@Id

While quite common to the EJB2 developer, @IdClass is likely new for Hibernate users. The compositeprimary key class corresponds to multiple fields or properties of the entity class, and the names of primary keyfields or properties in the primary key class and those of the entity class must match and their types must be thesame. Let's look at an example:

@Entity@IdClass(FootballerPk.class)public class Footballer {

//part of the id key@Id public String getFirstname() {

return firstname;}

public void setFirstname(String firstname) {this.firstname = firstname;

}

//part of the id key@Id public String getLastname() {

return lastname;}

public void setLastname(String lastname) {this.lastname = lastname;

}

public String getClub() {return club;

}

public void setClub(String club) {this.club = club;

}

//appropriate equals() and hashCode() implementation}

@Embeddablepublic class FootballerPk implements Serializable {

//same name and type as in Footballerpublic String getFirstname() {

return firstname;}

public void setFirstname(String firstname) {this.firstname = firstname;

}

//same name and type as in Footballerpublic String getLastname() {

return lastname;}

public void setLastname(String lastname) {this.lastname = lastname;

}

//appropriate equals() and hashCode() implementation}

As you may have seen, @IdClass points to the corresponding primary key class.

While not supported by the EJB3 specification, Hibernate allows you to define associations inside a composite

Entity Beans

Hibernate 3.1 beta 9 10

Page 214: Tutorial Hibernate

identifier. Simply use the regular annotations for that

@Entity@AssociationOverride( name="id.channel", joinColumns = @JoinColumn(name="chan_id") )public class TvMagazin {

@EmbeddedId public TvMagazinPk id;@Temporal(TemporalType.TIME) Date time;

}

@Embeddablepublic class TvMagazinPk implements Serializable {

@ManyToOnepublic Channel channel;public String name;@ManyToOnepublic Presenter presenter;

}

2.2.4. Mapping inheritance

EJB3 supports the three types of inheritance:

• Table per Class Strategy: the <union-class> element in Hibernate

• Single Table per Class Hierarchy Strategy: the <subclass> element in Hibernate

• Joined Subclass Strategy: the <joined-subclass> element in Hibernate

The chosen strategy is declared at the class level of the top level entity in the hierarchy using the @Inheritance

annotation.

Note

Annotating interfaces is currently not supported.

Table per class

This strategy has many drawbacks (esp. with polymorphic queries and associations) explained in the EJB3spec, the Hibernate reference documentation, Hibernate in Action, and many other places. Hibernate workaround most of them implementing this strategy using SQL UNION queries. It is commonly used for the top levelof an inheritance hierarchy:

@Entity@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)public class Flight implements Serializable {

This strategy support one to many associations provided that they are bidirectional. This strategy does not sup-port the IDENTITY generator strategy: the id has to be shared across several tables. Consequently, when usingthis strategy, you should not use AUTO nor IDENTITY.

Single table per class hierarchy

All properties of all super- and subclasses are mapped into the same table, instances are distinguished by a spe-cial discriminator column:

Entity Beans

Hibernate 3.1 beta 9 11

Page 215: Tutorial Hibernate

@Entity@Inheritance(strategy=InheritanceType.SINGLE_TABLE)@DiscriminatorColumn(

name="planetype",discriminatorType=DiscriminatorType.STRING

)@DiscriminatorValue("Plane")public class Plane { ... }

@Entity@DiscriminatorValue("A320")public class A320 extends Plane { ... }

Plane is the superclass, it defines the inheritance strategy InheritanceType.SINGLE_TABLE. It also defines thediscriminator column through the @DiscriminatorColumn annotation, a discriminator column can also definethe discriminator type. Finally, the @DiscriminatorValue annotation defines the value used to differentiate aclass in the hierarchy. All of these attributes have sensible default values. The default name of the discriminatorcolumn is DTYPE. The default discriminator value is the entity name (as defined in @Entity.name) for Discrim-inatorType.STRING. A320 is a subclass; you only have to define discriminator value if you don't want to usethe default value. The strategy and the discriminator type are implicit.

@Inheritance and @DiscriminatorColumn should only be defined at the top of the entity hierarchy.

Joined subclasses

The @PrimaryKeyJoinColumn and @PrimaryKeyJoinColumns annotations define the primary key(s) of thejoined subclass table:

@Entity@Inheritance(strategy=InheritanceType.JOINED)public class Boat implements Serializable { ... }

@Entitypublic class Ferry extends Boat { ... }

@Entity@PrimaryKeyJoinColumn(name="BOAT_ID")public class AmericaCupClass extends Boat { ... }

All of the above entities use the JOINED strategy, the Ferry table is joined with the Boat table using the sameprimary key names. The AmericaCupClass table is joined with Boat using the join condition Boat.id = Amer-

icaCupClass.BOAT_ID.

Inherit properties from superclasses

This is sometimes useful to share common properties through a technical or a business superclass without in-cluding it as a regular mapped entity (ie no specific table for this entity). For that purpose you can map them as@MappedSuperclass.

@MappedSuperclasspublic class BaseEntity {

@Basic@Temporal(TemporalType.TIMESTAMP)public Date getLastUpdate() { ... }public String getLastUpdater() { ... }...

}

Entity Beans

Hibernate 3.1 beta 9 12

Page 216: Tutorial Hibernate

@Entity class Order extends BaseEntity {@Id public Integer getId() { ... }...

}

In database, this hierarchy will be represented as an Order table having the id, lastUpdate and lastUpdater

columns. The embedded superclass property mappings are copied into their entity subclasses. Remember thatthe embeddable superclass is not the root of the hierarchy though.

Note

Properties from superclasses not mapped as @MappedSuperclass are ignored.

Note

The access type (field or methods), is inherited from the root entity, unless you use the Hibernate an-notation @AccessType

Note

The same notion can be applied to @Embeddable objects to persist properties from their superclasses.You also need to use @MappedSuperclass to do that (this should not be considered as a standard EJB3feature though)

Note

It is allowed to mark a class as @MappedSuperclass in the middle of the mapped inheritance hierarchy.

Note

Any class in the hierarchy non annotated with @MappedSuperclass nor @Entity will be ignored.

You can override columns defined in entity superclasses at the root entity level using the @AttributeOverride

annotation.

@MappedSuperclasspublic class FlyingObject implements Serializable {

public int getAltitude() {return altitude;

}

@Transientpublic int getMetricAltitude() {

return metricAltitude;}

@ManyToOnepublic PropulsionType getPropulsion() {

return metricAltitude;}...

}

@Entity@AttributeOverride( name="altitude", column = @Column(name="fld_altitude") )@AssociationOverride( name="propulsion", joinColumns = @JoinColumn(name="fld_propulsion_fk") )public class Plane extends FlyingObject {

...}

Entity Beans

Hibernate 3.1 beta 9 13

Page 217: Tutorial Hibernate

The altitude property will be persisted in an fld_altitude column of table Plane and the propulsion associ-ation will be materialized in a fld_propulsion_fk foreign key column.

You can define @AttributeOverride(s) and @AssociationOverride(s) on @Entity classes,@MappedSuperclass classes and properties pointing to an @Embeddable object.

2.2.5. Mapping entity bean associations/relationships

One-to-one

You can associate entity beans through a one-to-one relationship using @OneToOne. There are two cases for one-to-one associations: either the associated entities share the same primary keys values or a foreign key is held byone of the entities (note that this FK column in the database should be constrained unique to simulate one-to-one multiplicity).

First, we map a real one-to-one association using shared primary keys:

@Entitypublic class Body {

@Idpublic Long getId() { return id; }

@OneToOne(cascade = CascadeType.ALL)@PrimaryKeyJoinColumnpublic Heart getHeart() {

return heart;}...

}

@Entitypublic class Heart {

@Idpublic Long getId() { ...}

}

The one to one is marked as true by using the @PrimaryKeyJoinColumn annotation.

In the following example, the associated entities are linked through a foreign key column:

@Entitypublic class Customer implements Serializable {

@OneToOne(cascade = CascadeType.ALL)@JoinColumn(name="passport_fk")public Passport getPassport() {

...}

@Entitypublic class Passport implements Serializable {

@OneToOne(mappedBy = "passport")public Customer getOwner() {...

}

Entity Beans

Hibernate 3.1 beta 9 14

Page 218: Tutorial Hibernate

A Customer is linked to a Passport, with a foreign key column named passport_fk in the Customer table. Thejoin column is declared with the @JoinColumn annotation which looks like the @Column annotation. It has onemore parameters named referencedColumnName. This parameter declares the column in the targeted entity thatwill be used to the join. Note that when using referencedColumnName to a non primary key column, the associatedclass has to be Serializable. Also note that the referencedColumnName to a non primary key column has to bemapped to a property having a single column (other cases might not work).

The association may be bidirectional. In a bidirectional relationship, one of the sides (and only one) has to bethe owner: the owner is responsible for the association column(s) update. To declare a side as not responsiblefor the relationship, the attribute mappedBy is used. mappedBy refers to the property name of the association onthe owner side. In our case, this is passport. As you can see, you don't have to (must not) declare the joincolumn since it has already been declared on the owners side.

If no @JoinColumn is declared on the owner side, the defaults apply. A join column(s) will be created in theowner table and its name will be the concatenation of the name of the relationship in the owner side, _(underscore), and the name of the primary key column(s) in the owned side. In this example passport_id be-cause the property name is passport and the column id of Passport is id.

Many-to-one

Many-to-one associations are declared at the property level with the annotation @ManyToOne:

@Entity()public class Flight implements Serializable {

@ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )@JoinColumn(name="COMP_ID")public Company getCompany() {

return company;}...

}

The @JoinColumn attribute is optional, the default value(s) is like in one to one, the concatenation of the nameof the relationship in the owner side, _ (underscore), and the name of the primary key column in the ownedside. In this example company_id because the property name is company and the column id of Company is id.

@ManyToOne has a parameter named targetEntity which describes the target entity name. You usually don'tneed this parameter since the default value (the type of the property that stores the association) is good in al-most all cases. However this is useful when you want to use interfaces as the return type instead of the regularentity.

@Entity()public class Flight implements Serializable {

@ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE}, targetEntity=CompanyImpl.class )@JoinColumn(name="COMP_ID")public Company getCompany() {

return company;}...

}

public interface Company {...

Collections

Entity Beans

Hibernate 3.1 beta 9 15

Page 219: Tutorial Hibernate

Overview

You can map Collection, List (ie ordered lists, not indexed lists), Map and Set. The EJB3 specification de-scribes how to map an ordered list (ie a list ordered at load time) using @javax.persistence.OrderBy annota-tion: this annotation takes into parameter a list of comma separated (target entity) properties to order the collec-tion by (eg firstname asc, age desc), if the string is empty, the collection will be ordered by id. @OrderBycurrently works only on collections having no association table. For true indexed collections, please refer to theHibernate Annotation Extensions. EJB3 allows you to map Maps using as a key one of the target entity prop-erty using @MapKey(name="myProperty") (myProperty is a property name in the target entity). When using@MapKey (without property name), the target entity primary key is used. The map key uses the same column asthe property pointed out: there is no additional column defined to hold the map key, and it does make sensesince the map key actually represent a target ptoperty. Be aware that once loaded, the key is no longer kept insync with the property, in other words, if you change the property value, the key will not change automaticallyin your Java model (Map support the way Hibernate 3 does is currently not supported in this release). Manypeople confuse <map> capabilities and @MapKey ones. These are two different features. @MapKey still has somelimitations, please check the forum or the JIRA tracking system for more informations.

Hibernate has several notions of collections.

Table 2.1. Collections semantics

Semantic java representation annotations

Bag semantic java.util.List, java.util.Collection @org.hibernate.annotations.CollectionOfElements, @OneToMany,@ManyToMany

List semantic java.util.List @org.hibernate.annotations.CollectionOfElements, @OneToMany,@ManyToMany + @OrderBy,@org.hibernate.annotations.IndexColumn

Set semantic java.util.Set @org.hibernate.annotations.CollectionOfElements, @OneToMany,@ManyToMany

Map semantic java.util.Map @org.hibernate.annotations.CollectionOfElements, @OneToMany,@ManyToMany + @MapKey

So specifically, java.util.List collections wo @OrderBy nor @org.hibernate.annotations.IndexColumn are go-ing to be considered as bags.

Collection of primitive, core type or embedded objects is not supported by the EJB3 specification. HibernateAnnotations allows them however (see Hibernate Annotation Extensions).

@Entity public class City {@OneToMany(mappedBy="city")@OrderBy("streetName")public List<Street> getStreets() {

return streets;}

...}

Entity Beans

Hibernate 3.1 beta 9 16

Page 220: Tutorial Hibernate

@Entity public class Street {public String getStreetName() {

return streetName;}

@ManyToOnepublic City getCity() {

return city;}...

}

@Entitypublic class Software {

@OneToMany(mappedBy="software")@MapKey(name="codeName")public Map<String, Version> getVersions() {

return versions;}

...}

@Entity@Table(name="tbl_version")public class Version {

public String getCodeName() {...}

@ManyToOnepublic Software getSoftware() { ... }

...}

So City has a collection of Streets that are ordered by streetName (of Street) when the collection is loaded.Software has a map of Versions which key is the Version codeName.

Unless the collection is a generic, you will have to define targetEntity. This is a annotation attribute that takethe target entity class as a value.

One-to-many

One-to-many associations are declared at the property level with the annotation @OneToMany. One to many asso-ciations may be bidirectional.

Bidirectional

Since many to one are (almost) always the owner side of a bidirectional relationship in the EJB3 spec, the oneto many association is annotated by @OneToMany( mappedBy=... )

@Entitypublic class Troop {

@OneToMany(mappedBy="troop")public Set<Soldier> getSoldiers() {...

}

@Entitypublic class Soldier {

@ManyToOne@JoinColumn(name="troop_fk")public Troop getTroop() {...

}

Troop has a bidirectional one to many relationship with Soldier through the troop property. You don't have to

Entity Beans

Hibernate 3.1 beta 9 17

Page 221: Tutorial Hibernate

(must not) define any physical mapping in the mappedBy side.

To map a bidirectional one to many, with the one-to-many side as the owning side, you have to remove themappedBy element and set the many to one @JoinColumn as insertable and updatable to false. This solution isobviously not optimized from the number of needed statements.

@Entitypublic class Troop {

@OneToMany@JoinColumn(name="troop_fk") //we need to duplicate the physical informationpublic Set<Soldier> getSoldiers() {...

}

@Entitypublic class Soldier {

@ManyToOne@JoinColumn(name="troop_fk", insertable=false, updatable=false)public Troop getTroop() {...

}

Unidirectional

A unidirectional one to many using a foreign key column in the owned entity is not that common and not reallyrecommended. We strongly advise you to use a join table for this kind of association (as explained in the nextsection). This kind of association is described through a @JoinColumn

@Entitypublic class Customer implements Serializable {

@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)@JoinColumn(name="CUST_ID")public Set<Ticket> getTickets() {...

}

@Entitypublic class Ticket implements Serializable {

... //no bidir}

Customer describes a unidirectional relationship with Ticket using the join column CUST_ID.

Unidirectional with join table

A unidirectional one to many with join table is much preferred. This association is described through an@JoinTable.

@Entitypublic class Trainer {

@OneToMany@JoinTable(

name="TrainedMonkeys",joinColumns = { @JoinColumn( name="trainer_id") },inverseJoinColumns = @JoinColumn( name="monkey_id")

)public Set<Monkey> getTrainedMonkeys() {...

}

@Entitypublic class Monkey {

... //no bidir

Entity Beans

Hibernate 3.1 beta 9 18

Page 222: Tutorial Hibernate

}

Trainer describes a unidirectional relationship with Monkey using the join table TrainedMonkeys, with a for-eign key trainer_id to Trainer (joinColumns) and a foreign key monkey_id to Monkey

(inversejoinColumns).

Defaults

Without describing any physical mapping, a unidirectional one to many with join table is used. The table nameis the concatenation of the owner table name, _, and the other side table name. The foreign key name(s) referen-cing the owner table is the concatenation of the owner table, _, and the owner primary key column(s) name.The foreign key name(s) referencing the other side is the concatenation of the owner property name, _, and theother side primary key column(s) name. A unique constraint is added to the foreign key referencing the otherside table to reflect the one to many.

@Entitypublic class Trainer {

@OneToManypublic Set<Tiger> getTrainedTigers() {...

}

@Entitypublic class Tiger {

... //no bidir}

Trainer describes a unidirectional relationship with Tiger using the join table Trainer_Tiger, with a foreignkey trainer_id to Trainer (table name, _, trainer id) and a foreign key trainedTigers_id to Monkey (propertyname, _, Tiger primary column).

Many-to-many

Definition

A many-to-many association is defined logically using the @ManyToMany annotation. You also have to describethe association table and the join conditions using the @JoinTable annotation. If the association is bidirectional,one side has to be the owner and one side has to be the inverse end (ie. it will be ignored when updating the re-lationship values in the association table):

@Entitypublic class Employer implements Serializable {

@ManyToMany(targetEntity=org.hibernate.test.metadata.manytomany.Employee.class,cascade={CascadeType.PERSIST, CascadeType.MERGE}

)@JoinTable(

name="EMPLOYER_EMPLOYEE",joinColumns={@JoinColumn(name="EMPER_ID")},inverseJoinColumns={@JoinColumn(name="EMPEE_ID")}

)public Collection getEmployees() {

return employees;}...

}

Entity Beans

Hibernate 3.1 beta 9 19

Page 223: Tutorial Hibernate

@Entitypublic class Employee implements Serializable {

@ManyToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE},mappedBy="employees"targetEntity=Employer.class

)public Collection getEmployers() {

return employers;}

}

We've already shown the many declarations and the detailed attributes for associations. We'll go deeper in the@JoinTable description, it defines a name, an array of join columns (an array in annotation is defined using { A,B, C }), and an array of inverse join columns. The latter ones are the columns of the association table whichrefer to the Employee primary key (the "other side").

As seen previously, the other side don't have to (must not) describe the physical mapping: a simple mappedBy

argument containing the owner side property name bind the two.

Default values

As any other annotations, most values are guessed in a many to many relationship. Without describing anyphysical mapping in a unidirectional many to many the following rules applied. The table name is the concaten-ation of the owner table name, _ and the other side table name. The foreign key name(s) referencing the ownertable is the concatenation of the owner table name, _ and the owner primary key column(s). The foreign keyname(s) referencing the other side is the concatenation of the owner property name, _, and the other sideprimary key column(s). These are the same rules used for a unidirectional one to many relationship.

@Entitypublic class Store {

@ManyToMany(cascade = CascadeType.PERSIST)public Set<City> getImplantedIn() {

...}

}

@Entitypublic class City {

... //no bidirectional relationship}

A Store_Table is used as the join table. The Store_id column is a foreign key to the Store table. The im-

plantedIn_id column is a foreign key to the City table.

Without describing any physical mapping in a bidirectional many to many the following rules applied. The ta-ble name is the concatenation of the owner table name, _ and the other side table name. The foreign keyname(s) referencing the owner table is the concatenation of the other side property name, _, and the ownerprimary key column(s). The foreign key name(s) referencing the other side is the concatenation of the ownerproperty name, _, and the other side primary key column(s). These are the same rules used for a unidirectionalone to many relationship.

@Entitypublic class Store {

@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})public Set<Customer> getCustomers() {

Entity Beans

Hibernate 3.1 beta 9 20

Page 224: Tutorial Hibernate

...}

}

@Entitypublic class Customer {

@ManyToMany(mappedBy="customers")public Set<Store> getStores() {

...}

}

A Store_Customer is used as the join table. The stores_id column is a foreign key to the Store table. Thecustomers_id column is a foreign key to the City table.

Transitive persistence with cascading

You probably have noticed the cascade attribute taking an array of CascadeType as a value. The cascadeconcept in EJB3 is very is similar to the transitive persistence and cascading of operations in Hibernate, butwith slightly different semantics and cascading types:

• CascadeType.PERSIST: cascades the persist (create) operation to associated entities persist() is called or ifthe entity is managed

• CascadeType.MERGE: cascades the merge operation to associated entities if merge() is called or if the en-tity is managed

• CascadeType.REMOVE: cascades the remove operation to associated entities if delete() is called

• CascadeType.REFRESH: cascades the refresh operation to associated entities if refresh() is called

• CascadeType.ALL: all of the above

Please refer to the chapter 6.3 of the EJB3 specification for more information on cascading and create/mergesemantics.

Association fetching

You have the ability to either eagerly or lazily fetch associated entities. The fetch parameter can be set toFetchType.LAZY or FetchType.EAGER. EAGER will try to use an outer join select to retrieve the associated ob-ject, while LAZY is the default and will only trigger a select when the associated object is accessed for the firsttime. EJBQL also has a fetch keyword that allows you to override laziness when doing a particular query. Thisis very useful to improve performance and is decided on a use case to use case basis.

2.2.6. Mapping composite primary and foreign keys

Composite primary keys use a embedded class as the primary key representation, so you'd use the @Id and@Embeddable annotations. Alternatively, you can use the @EmbeddedId annotation. Note that the dependent classhas to be serializable and implements equals()/hashCode(). You can also use @IdClass as described in Map-ping identifier properties.

@Entitypublic class RegionalArticle implements Serializable {

@Id

Entity Beans

Hibernate 3.1 beta 9 21

Page 225: Tutorial Hibernate

public RegionalArticlePk getPk() { ... }}

@Embeddablepublic class RegionalArticlePk implements Serializable { ... }

or alternatively

@Entitypublic class RegionalArticle implements Serializable {

@EmbeddedIdpublic RegionalArticlePk getPk() { ... }

}

public class RegionalArticlePk implements Serializable { ... }

@Embeddable inherit the access type of its owning entity unless the Hibernate specific annotation @AccessType

is used. Composite foreign keys (if not using the default sensitive values) are defined on associations using the@JoinColumns element, which is basically an array of @JoinColumn. It is considered a good practice to expressreferencedColumnNames explicitly. Otherwise, Hibernate will suppose that you use the same order of columnsas in the primary key declaration.

@Entitypublic class Parent implements Serializable {

@Idpublic ParentPk id;public int age;

@OneToMany(cascade=CascadeType.ALL)@JoinColumns ({

@JoinColumn(name="parentCivility", referencedColumnName = "isMale"),@JoinColumn(name="parentLastName", referencedColumnName = "lastName"),@JoinColumn(name="parentFirstName", referencedColumnName = "firstName")

})public Set<Child> children; //unidirectional...

}

@Entitypublic class Child implements Serializable {

@Id @GeneratedValuepublic Integer id;

@ManyToOne@JoinColumns ({

@JoinColumn(name="parentCivility", referencedColumnName = "isMale"),@JoinColumn(name="parentLastName", referencedColumnName = "lastName"),@JoinColumn(name="parentFirstName", referencedColumnName = "firstName")

})public Parent parent; //unidirectional

}

@Embeddablepublic class ParentPk implements Serializable {

String firstName;String lastName;...

Entity Beans

Hibernate 3.1 beta 9 22

Page 226: Tutorial Hibernate

}

Note the explicit usage of the referencedColumnName.

2.2.7. Mapping secondary tables

You can map a single entity bean to several tables using the @SecondaryTable or @SecondaryTables class levelannotations. To express that a column is in a particular table, use the table parameter of @Column or@JoinColumn.

@Entity@Table(name="MainCat")@SecondaryTables({

@SecondaryTable(name="Cat1", pkJoinColumns={@PrimaryKeyJoinColumn(name="cat_id", referencedColumnName="id")

),@SecondaryTable(name="Cat2", uniqueConstraints={@UniqueConstraint(columnNames={"storyPart2"})})

})public class Cat implements Serializable {

private Integer id;private String name;private String storyPart1;private String storyPart2;

@Id @GeneratedValuepublic Integer getId() {

return id;}

public String getName() {return name;

}

@Column(table="Cat1")public String getStoryPart1() {

return storyPart1;}

@Column(table="Cat2")public String getStoryPart2() {

return storyPart2;}

In this example, name will be in MainCat. storyPart1 will be in Cat1 and storyPart2 will be in Cat2. Cat1will be joined to MainCat using the cat_id as a foreign key, and Cat2 using id (ie the same column name, theMainCat id column has). Plus a unique constraint on storyPart2 has been set.

Check out the JBoss EJB 3 tutorial or the Hibernate Annotations unit test suite for more examples.

2.3. Mapping Queries

2.3.1. Mapping EJBQL/HQL queries

You can map EJBQL/HQL queries using annotations. @NamedQuery and @NamedQueries can be defined at theclass or at the package level. However their definitions are global to the session factory/entity manager factoryscope. A named query is defined by its name and the actual query string.

Entity Beans

Hibernate 3.1 beta 9 23

Page 227: Tutorial Hibernate

javax.persistence.NamedQueries(@javax.persistence.NamedQuery(name="plane.getAll", query="select p from Plane p")

)package org.hibernate.test.annotations.query;

...

@Entity@NamedQuery(name="night.moreRecentThan", query="select n from Night n where n.date >= :date")public class Night {

...}

public class MyDao {doStuff() {

Query q = s.getNamedQuery("night.moreRecentThan");q.setDate( "date", aMonthAgo );List results = q.list();...

}...

}

You can also provide some hints to a query through an array of QueryHint through a hints attribute.

The availabe Hibernate hints are

Table 2.2. Query hints

hint description

org.hibernate.cacheable Whether the query should interact with the secondlevel cache (defualt to false)

org.hibernate.cacheRegion Cache region name (default used otherwise)

org.hibernate.timeout Query timeout

org.hibernate.fetchSize resultset fetch size

org.hibernate.flushMode Flush mode used for this query

org.hibernate.cacheMode Cache mode used for this query

org.hibernate.readOnly Entities loaded by this query should be in read onlymode or not (default to false)

org.hibernate.comment Query comment added to the generated SQL

2.3.2. Mapping native queries

You can also map a native query (ie a plain SQL query). To achieve that, you need to describe the SQL result-set structure using @SqlResultSetMapping (or @SqlResultSetMappings if you plan to define several resulsetmappings). Like @NamedQuery, a @SqlResultSetMapping can be defined at both package level or class level.However its scope is global to the application. As we will see, a resultSetMapping parameter is defined the@NamedNativeQuery, it represents the name of a defined @SqlResultSetMapping. The resultset mapping de-clares the entities retrieved by this native query. Each field of the entity is bound to an SQL alias (or column

Entity Beans

Hibernate 3.1 beta 9 24

Page 228: Tutorial Hibernate

name). All fields of the entity including the ones of subclasses and the foreign key columns of related entitieshave to be present in the SQL query. Field definitions are optional provided that they map to the same columnname as the one declared on the class property.

@NamedNativeQuery(name="night&area", query="select night.id nid, night.night_duration, "+ " night.night_date, area.id aid, night.area_id, area.name "+ "from Night night, Area area where night.area_id = area.id", resultSetMapping="joinMapping")

@SqlResultSetMapping(name="joinMapping", entities={@EntityResult(entityClass=org.hibernate.test.annotations.query.Night.class, fields = {

@FieldResult(name="id", column="nid"),@FieldResult(name="duration", column="night_duration"),@FieldResult(name="date", column="night_date"),@FieldResult(name="area", column="area_id"),discriminatorColumn="disc"

}),@EntityResult(entityClass=org.hibernate.test.annotations.query.Area.class, fields = {

@FieldResult(name="id", column="aid"),@FieldResult(name="name", column="name")

})}

)

In the above example, the night&area named query use the joinMapping result set mapping. This mapping re-turns 2 entities, Night and Area, each property is declared and associated to a column name, actually thecolumn name retrieved by the query. Let's now see an implicit declaration of the property / column.

@Entity@SqlResultSetMapping(name="implicit", entities=@EntityResult(entityClass=org.hibernate.test.annotations.query.SpaceShip.class))@NamedNativeQuery(name="implicitSample", query="select * from SpaceShip", resultSetMapping="implicit")public class SpaceShip {

private String name;private String model;private double speed;

@Idpublic String getName() {

return name;}

public void setName(String name) {this.name = name;

}

@Column(name="model_txt")public String getModel() {

return model;}

public void setModel(String model) {this.model = model;

}

public double getSpeed() {return speed;

}

public void setSpeed(double speed) {this.speed = speed;

}}

In this example, we only describe the entity member of the result set mapping. The property / column mappingsis done using the entity mapping values. In this case the model property is bound to the model_txt column. Ifthe association to a related entity involve a composite primary key, a @FieldResult element should be used foreach foreign key column. The @FieldResult name is composed of the property name for the relationship, fol-

Entity Beans

Hibernate 3.1 beta 9 25

Page 229: Tutorial Hibernate

lowed by a dot ("."), followed by the name or the field or property of the primary key.

@Entity@SqlResultSetMapping(name="compositekey",

entities=@EntityResult(entityClass=org.hibernate.test.annotations.query.SpaceShip.class,fields = {

@FieldResult(name="name", column = "name"),@FieldResult(name="model", column = "model"),@FieldResult(name="speed", column = "speed"),@FieldResult(name="captain.firstname", column = "firstn"),@FieldResult(name="captain.lastname", column = "lastn"),@FieldResult(name="dimensions.length", column = "length"),@FieldResult(name="dimensions.width", column = "width")}),

columns = { @ColumnResult(name = "surface"),@ColumnResult(name = "volume") } )

@NamedNativeQuery(name="compositekey",query="select name, model, speed, lname as lastn, fname as firstn, length, width, length * width as surface from SpaceShip",resultSetMapping="compositekey")

} )public class SpaceShip {

private String name;private String model;private double speed;private Captain captain;private Dimensions dimensions;

@Idpublic String getName() {

return name;}

public void setName(String name) {this.name = name;

}

@ManyToOne(fetch= FetchType.LAZY)@JoinColumns( {

@JoinColumn(name="fname", referencedColumnName = "firstname"),@JoinColumn(name="lname", referencedColumnName = "lastname")} )

public Captain getCaptain() {return captain;

}

public void setCaptain(Captain captain) {this.captain = captain;

}

public String getModel() {return model;

}

public void setModel(String model) {this.model = model;

}

public double getSpeed() {return speed;

}

public void setSpeed(double speed) {this.speed = speed;

}

public Dimensions getDimensions() {return dimensions;

}

public void setDimensions(Dimensions dimensions) {this.dimensions = dimensions;

Entity Beans

Hibernate 3.1 beta 9 26

Page 230: Tutorial Hibernate

}}

@Entity@IdClass(Identity.class)public class Captain implements Serializable {

private String firstname;private String lastname;

@Idpublic String getFirstname() {

return firstname;}

public void setFirstname(String firstname) {this.firstname = firstname;

}

@Idpublic String getLastname() {

return lastname;}

public void setLastname(String lastname) {this.lastname = lastname;

}}

Note

If you look at the dimension property, you'll see that Hibernate supports the dotted notation for embed-ded objects (you can even have nested embedded objects). EJB3 implementations do not have to sup-port this feature, we do :-)

If you retrieve a single entity and if you use the default mapping, you can use the resultClass attribute insteadof resultSetMapping:

@NamedNativeQuery(name="implicitSample", query="select * from SpaceShip",resultClass=SpaceShip.class)

public class SpaceShip {

In some of your native queries, you'll have to return scalar values, for example when building report queries.You can map them in the @SqlResultsetMapping through @ColumnResult. You actually can even mix, entitiesand scalar returns in the same native query (this is probably not that common though).

@SqlResultSetMapping(name="scalar", columns=@ColumnResult(name="dimension"))@NamedNativeQuery(name="scalar", query="select length*width as dimension from SpaceShip", resultSetMapping="scalar")

An other query hint specific to native queries has been introduced: org.hibernate.callable which can be trueor false depending on whether the query is a stored procedure or not.

2.4. Hibernate Annotation Extensions

Hibernate 3.1 offers a variety of additional annotations that you can mix/match with your EJB 3 entities. Theyhave been designed as a natural extension of EJB3 annotations.

To empower the EJB3 capabilities, hibernate provides specific annotations that match hibernate features. Theorg.hibernate.annotations package contains all these annotations extensions.

Entity Beans

Hibernate 3.1 beta 9 27

Page 231: Tutorial Hibernate

2.4.1. Entity

You can fine tune some of the actions done by Hibernate on entities beyond what the EJB3 spec offers.

@org.hibernate.annotations.Entity adds additional metadata that may be needed beyond what is defined inthe standard @Entity

• mutable: whether this entity is mutable or not

• dynamicInsert: allow dynamic SQL for inserts

• dynamicUpdate: allow dynamic SQL for updates

• selectBeforeUpdate: Specifies that Hibernate should never perform an SQL UPDATE unless it is certainthat an object is actually modified.

• polymorphism: whether the entity polymorphism is of PolymorphismType.IMPLICIT (default) or Poly-morphismType.EXPLICIT

• persister: allow the overriding of the default persister implementation

• optimisticLock: optimistic locking strategy (OptimisticLockType.VERSION, OptimisticLockType.NONE,OptimisticLockType.DIRTY or OptimisticLockType.ALL)

Note

@javax.persistence.Entity is still mandatory, @org.hibernate.annotations.Entity is not a replacement.

Here are some additional Hibernate annotation extensions

@org.hibernate.annotations.BatchSize allows you to define the batch size when fetching instances of thisentity ( eg. @BatchSize(size=4) ). When loading a given entity, Hibernate will then load all the uninitializedentities of the same type in the persistence context up to the batch size.

@org.hibernate.annotations.Proxy defines the laziness attributes of the entity. lazy (default to true) definewhether the class is lazy or not. proxyClassName is the interface used to generate the proxy (default is the classitself).

@org.hibernate.annotations.Where defines an optional SQL WHERE clause used when instances of thisclass is retrieved.

@org.hibernate.annotations.Check defines an optional check constraints defined in the DDL statetement.

@OnDelete(action=OnDeleteAction.CASCADE) on joined subclasses: use a SQL cascade delete on deletion in-stead of the regular Hibernate mechanism.

@Table(appliesTo="tableName", indexes = { @Index(name="index1", columnNames={"column1",

"column2"} ) } ) creates the defined indexes on the columns of table tableName. This can be applied on theprimary table or any secondary table. The @Tables annotation allows your to apply indexes on different tables.This annotation is expected where @javax.persistence.Table or @javax.persistence.SecondaryTable(s)occurs.

Note

Entity Beans

Hibernate 3.1 beta 9 28

Page 232: Tutorial Hibernate

@org.hibernate.annotations.Table is a complement, not a replacement [email protected]. Especially, if you want to change the default name of a table, you mustuse @javax.persistence.Table, not @org.hibernate.annotations.Table.

@Entity@BatchSize(size=5)@org.hibernate.annotations.Entity(

selectBeforeUpdate = true,dynamicInsert = true, dynamicUpdate = true,optimisticLock = OptimisticLockType.ALL,polymorphism = PolymorphismType.EXPLICIT)

@Where(clause="1=1")@org.hibernate.annotations.Table(name="Forest", indexes = { @Index(name="idx", columnNames = { "name", "length" } ) } )public class Forest { ... }

@Entity@Inheritance(

strategy=InheritanceType.JOINED)public class Vegetable { ... }

@Entity@OnDelete(action=OnDeleteAction.CASCADE)public class Carrot extends Vegetable { ... }

2.4.2. Identifier

@org.hibernate.annotations.GenericGenerator allows you to define an Hibernate specific id generator.

@Id @GeneratedValue(generator="system-uuid")@GenericGenerator(name="system-uuid", strategy = "uuid")public String getId() {

@Id @GeneratedValue(generator="hibseq")@GenericGenerator(name="hibseq", strategy = "seqhilo",

parameters = {@Parameter(name="max_lo", value = "5"),@Parameter(name="sequence", value="heybabyhey")

})public Integer getId() {

strategy is the short name of an Hibernate3 generator strategy or the fully qualified class name of an Identi-

fierGenerator implementation. You can add some parameters through the parameters attribute

2.4.3. Property

Access type

The access type is guessed from the position of @Id or @EmbeddedId in the entity hierarchy. Sub-entities, em-bedded objects and mapped superclass inherit the access type from the root entity.

In Hibernate, you can override the access type to:

• use a custom access type strategy

• fine tune the access type at the class level or at the property level

Entity Beans

Hibernate 3.1 beta 9 29

Page 233: Tutorial Hibernate

An @AccessType annotation has been introduced to support this behavior. You can define the access type on

• an entity

• a superclass

• an embeddable object

• a property

The access type is overriden for the annotated element, if overriden on a class, all the properties of the givenclass inherit the access type. For root entities, the access type is considered to be the default one for the wholehierarchy (overridable at class or property level).

If the access type is marked as "property", the getters are scanned for annotations, if the access type is markedas "field", the fields are scanned for annotations. Otherwise the elements marked with @Id or @embeddedIdare scanned.

You can override an access type for a property, but the element to annotate will not be influenced: for examplean entity having access type field, can annotate a field with @AccessType("property"), the access type willthen be property for this attribute, the the annotations still have to be carried on the field.

If a superclass or an embeddable object is not annotated, the root entity access type is used (even if an accesstype has been define on an intermediate superclass or embeddable object). The russian doll principle does notapply.

@Entitypublic class Person implements Serializable {

@Id @GeneratedValue //access type fieldInteger id;

@Embedded@AttributeOverrides({@AttributeOverride(name = "iso2", column = @Column(name = "bornIso2")),@AttributeOverride(name = "name", column = @Column(name = "bornCountryName"))

})Country bornIn;

}

@Embeddable@AccessType("property") //override access type for all properties in Countrypublic class Country implements Serializable {

private String iso2;private String name;

public String getIso2() {return iso2;

}

public void setIso2(String iso2) {this.iso2 = iso2;

}

@Column(name = "countryName")public String getName() {

return name;}

public void setName(String name) {this.name = name;

}}

Entity Beans

Hibernate 3.1 beta 9 30

Page 234: Tutorial Hibernate

Formula

Sometimes, you want the Database to do some computation for you rather than in the JVM, you might also cre-ate some kind of virtual column. You can use a SQL fragment (aka formula) instead of mapping a property intoa column. This kind of property is read only (its value is calculated by your formula fragment).

@Formula("obj_length * obj_height * obj_width")public long getObjectVolume()

The SQL fragment can be as complex as you want avec even include subselects.

Type

@org.hibernate.annotations.Type overrides the default hibernate type used: this is generally not necessarysince the type is correctly inferred by Hibernate. Please refer to the Hibernate reference guide for more inform-ations on the Hibernate types.

@org.hibernate.annotations.TypeDef and @org.hibernate.annotations.TypeDefs allows you to declaretype definitions. These annotations are placed at the class or package level. Note that these definitions will beglobal for the session factory (even at the class level) and that type definition has to be defined before any us-age.

@TypeDefs({@TypeDef(

name="caster",typeClass = CasterStringType.class,parameters = {

@Parameter(name="cast", value="lower")}

)}

)package org.hibernate.test.annotations.entity;

...public class Forest {

@Type(type="caster")public String getSmallText() {...

}

When using composite user type, you will have to express column definitions. The @Columns has been intro-duced for that purpose.

@Type(type="org.hibernate.test.annotations.entity.MonetaryAmountUserType")@Columns(columns = {

@Column(name="r_amount"),@Column(name="r_currency")

})public MonetaryAmount getAmount() {

return amount;}

public class MonetaryAmount implements Serializable {private BigDecimal amount;private Currency currency;...

}

Entity Beans

Hibernate 3.1 beta 9 31

Page 235: Tutorial Hibernate

Index

You can define an index on a particular column using the @Index annotation on a one column property, thecolumnNames attribute will then be ignored

@Column(secondaryTable="Cat1")@Index(name="story1index")public String getStoryPart1() {

return storyPart1;}

2.4.4. Inheritance

SINGLE_TABLE is a very powerful strategy but sometimes, and especially for legacy systems, you cannot addan additional discriminator column. For that purpose Hibernate has introduced the notion of discriminator for-mula: @DiscriminatorFormula is a replacement of @DiscriminatorColumn and use a SQL fragment as a for-mula for discriminator resolution (no need to have a dedicated column).

@Entity@DiscriminatorForumla("case when forest_type is null then 0 else forest_type end")public class Forest { ... }

2.4.5. Single Association related annotations

By default, when Hibernate cannot resolve the association because the expected associated element is not indatabase (wrong id on the association column), an exception is raised by Hibernate. This might be inconvenientfor lecacy and badly maintained schemas. You can ask Hibernate to ignore such elements instead of raising anexception using the @NotFound annotation. This annotation can be used on a @OneToOne (with FK), @ManyToOne,@OneToMany or @ManyToMany association.

@Entitypublic class Child {

...@ManyToOne@NotFound(action=NotFoundAction.IGNORE)public Parent getParent() { ... }...

}

Sometimes you want to delegate to your database the deletion of cascade when a given entity is deleted.

@Entitypublic class Child {

...@ManyToOne@OnDelete(action=OnDeleteAction.CASCADE)public Parent getParent() { ... }...

}

In this case Hibernate generates a cascade delete constraint at the database level.

2.4.6. Collection related annotations

Parameter annotations

Entity Beans

Hibernate 3.1 beta 9 32

Page 236: Tutorial Hibernate

It is possible to set

• the batch size for collections using @BatchSize

• the where clause, using @Where

• the check clause, using @Check

• the SQL order by clause, using @OrderBy

• the delete cascade strategy through @OnDelete(action=OnDeleteAction.CASCADE)

You can also declare a sort comparator. Use the @Sort annotation. Expressing the comparator type you wantbetween unsorted, natural or custom comparator. If you want to use your own comparator implementation,you'll also have to express the implementation class using the comparator attribute.

@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)@JoinColumn(name="CUST_ID")@Sort(type = SortType.COMPARATOR, comparator = TicketComparator.class)@Where(clause="1=1")@OnDelete(action=OnDeleteAction.CASCADE)public SortedSet<Ticket> getTickets() {

return tickets;}

Please refer to the previous descriptions of these annotations for more informations.

Extra collection types

Beyond EJB3, Hibernate Annotations supports true List and Array. Map your collection the same way as usualand add the @IndexColumn. This annotation allows you to describe the column that will hold the index. Youcan also declare the index value in DB that represent the first element (aka as base index). The usual value is 0

or 1.

@OneToMany(cascade = CascadeType.ALL)@IndexColumn(name = "drawer_position", base=1)public List<Drawer> getDrawers() {

return drawers;}

Note

If you forgot to set @IndexColumn, the bag semantic is applied

Hibernate Annotations also supports collections of core types (Integer, String, Enums, ...), collections of em-beddable objects and even arrays of primitive types. This is known as collection of elements.

A collection of elements as to be annotated as @CollectionOfElements (as a replacement of @OneToMany) Todefine the collection table, the @JoinTable annotation is used on the association property, joinColumns definesthe join columns between the entity primary table and the collection table (inverseJoincolumn is useless andshould be left empty). For collection of core types or array of primitive types, you can override the elementcolumn definition using a @Column on the association property. You can also override the columns of a collec-tion of embeddable object using @AttributeOverride.

@Entitypublic class Boy {

private Integer id;private Set<String> nickNames = new HashSet<String>();

Entity Beans

Hibernate 3.1 beta 9 33

Page 237: Tutorial Hibernate

private int[] favoriteNumbers;private Set<Toy> favoriteToys = new HashSet<Toy>();private Set<Character> characters = new HashSet<Character>();

@Id @GeneratedValuepublic Integer getId() {

return id;}

@CollectionOfElementspublic Set<String> getNickNames() {

return nickNames;}

@CollectionOfElements@JoinTable(

table=@Table(name="BoyFavoriteNumbers"),joinColumns = @JoinColumn(name="BoyId")

)@Column(name="favoriteNumber", nullable=false)@IndexColumn(name="nbr_index")public int[] getFavoriteNumbers() {

return favoriteNumbers;}

@CollectionOfElements@AttributeOverride( name="serial", column=@Column(name="serial_nbr") )public Set<Toy> getFavoriteToys() {

return favoriteToys;}

@CollectionOfElementspublic Set<Character> getCharacters() {

return characters;}...

}

public enum Character {GENTLE,NORMAL,AGGRESSIVE,ATTENTIVE,VIOLENT,CRAFTY

}

@Embeddablepublic class Toy {

public String name;public String serial;

public String getName() {return name;

}

public void setName(String name) {this.name = name;

}

public String getSerial() {return serial;

}

public void setSerial(String serial) {this.serial = serial;

}

public boolean equals(Object o) {if ( this == o ) return true;if ( o == null || getClass() != o.getClass() ) return false;

Entity Beans

Hibernate 3.1 beta 9 34

Page 238: Tutorial Hibernate

final Toy toy = (Toy) o;

if ( !name.equals( toy.name ) ) return false;if ( !serial.equals( toy.serial ) ) return false;

return true;}

public int hashCode() {int result;result = name.hashCode();result = 29 * result + serial.hashCode();return result;

}}

Note

Previous versions of Hibernate Annotations used the @OneToMany to mark a collection of elements. Dueto semantic inconsistencies, we've introduced the annotation @CollectionOfElements. Marking collec-tions of elements the old way still work but is considered deprecated and is going to be unsupported infuture releases

2.4.7. Cache

In order to optimize your database accesses, you can activave the so called second level cache of Hibernate.This cache is configurable on a per entity and per collection basis.

@org.hibernate.annotations.Cache defines the caching strategy and region of a given second level cache.This annotation can be applied on the root entity (not the sub entities), and on the collections.

@Entity@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)public class Forest { ... }

@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)@JoinColumn(name="CUST_ID")@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)public SortedSet<Ticket> getTickets() {

return tickets;}

@Cache(CacheConcurrencyStrategy usage(); (1)String region() default ""; (2)String include() default "all"; (3)

)

(1) usage: the given cache concurrency strategy (NONE, READ_ONLY, NONSTRICT_READ_WRITE,READ_WRITE, TRANSACTIONAL)

(2) region (optional): the cache region (default to the fqcn of the class or the fq role name of the collection)(3) include (optional): all to include all properties, non-lazy to only include non lazy properties (default all).

2.4.8. Filters

Hibernate has the notion of data filter that can be applied at runtime on a given session. Those filters has to bedefined first.

Entity Beans

Hibernate 3.1 beta 9 35

Page 239: Tutorial Hibernate

@org.hibernate.annotations.FilterDef or @FilterDefs define filter definition(s) used by filter(s) using thesame name. A filter definition has a name() and an array of parameters(). A @ParamDef has a name and a type.You can also define a defaultCondition() parameter for a given @filterDef to set the default condition to usewhen none are defined in the @Filter. A @FilterDef(s) can be defined at the class or package level.

We now need to define the SQL filter clause applied to either the entity load or the collection load. @Filter isused and placed either on the entity or the collection element

@Entity@FilterDef(name="minLength", parameters={ @ParamDef( name="minLength", type="integer" ) } )@Filters( {

@Filter(name="betweenLength", condition=":minLength <= length and :maxLength >= length"),@Filter(name="minLength", condition=":minLength <= length")

} )public class Forest { ... }

2.4.9. Queries

Since Hibernate has more features on named queries than the one defined in the EJB3 specification,@org.hibernate.annotations.NamedQuery, @org.hibernate.annotations.NamedQueries,@org.hibernate.annotations.NamedNativeQuery and @org.hibernate.annotations.NamedNativeQueries

have been introduced. They add some attributes to the standard version and can be used as a replacement:

• flushMode: define the query flush mode (Always, Auto, Commit or Never)

• cacheable: whether the query should be cached or not

• cacheRegion: cache region used if the query is cached

• fetchSize: JDBC statement fetch size for this query

• timeout: query time out

• callable: for native queries only, to be set to true for stored procedures

• comment: if comments are activated, the comment seen when the query is sent to the database.

• cacheMode: Cache interaction mode (get, ignore, normal, put or refresh)

• readOnly: whether or not the elements retrievent from the query are in read only mode.

Note, that the EJB3 public final draft has introduced the notion of @QueryHint, which is probably a better wayto define those hints.

Entity Beans

Hibernate 3.1 beta 9 36

Page 240: Tutorial Hibernate

Chapter 3. Hibernate ValidatorAnnotations are a very convenient and elegant way to specify invariant constraints for a domain model. Youcan, for example, express that a property should never be null, that the account balance should be strictly posit-ive, etc. These domain model constraints are declared in the bean itself by annotating its properties. A validatorcan then read them and check for constraint violations. The validation mechanism can be executed in differentlayers in your application without having to duplicate any of these rules (presentation layer, data access layer).Hibernate Validator has been designed for that purpose.

Hibernate Validator works at two levels. First, it is able to check in-memory instances of a class for constraintviolations. Second, it can apply the constraints to the Hibernate metamodel and incorporate them into the gener-ated database schema.

Each constraint annotation is associated to a validator implementation responsible for checking the constrainton the entity instance. A validator can also (optionally) apply the constraint to the Hibernate metamodel, allow-ing Hibernate to generate DDL that expresses the constraint. With the appropriate event listener, you can ex-ecute the checking operation on inserts and updates done by Hibernate. Hibernate Validator is not limited to usewith Hibernate. You can easily use it anywhere in your application.

When checking instances at runtime, Hibernate Validator returns information about constraint violations in anarray of InvalidValues. Among other information, the InvalidValue contains an error description messagethat can embed the parameter values bundle with the annotation (eg. length limit), and message strings that maybe externalized to a ResourceBundle.

3.1. Constraints

3.1.1. What is a constraint?

A constraint is represented by an annotation. A constraint usually has some attributes used to parameterize theconstraints limits. The constraint apply to the annotated element.

3.1.2. Built in constraints

Hibernate Validator comes with some built-in constraints, which covers most basic data checks. As we'll seelater, you're not limited to them, you can in a minute write your own constraints.

Table 3.1. Built-in constraints

Annotation Apply on Runtime checking Hibernate Metadata im-pact

@Length(min=, max=) property (String) check if the string lengthmatch the range

Column length will be setto max

@Max(value=) property (numeric orstring representation of anumeric)

check if the value is lessthan or equals to max

Add a check constraint onthe column

@Min(value=) property (numeric orstring representation of anumeric)

check if the value is morethan or equals to min

Add a check constraint onthe column

Hibernate 3.1 beta 9 37

Page 241: Tutorial Hibernate

Annotation Apply on Runtime checking Hibernate Metadata im-pact

@NotNull property check if the value is notnull

Column(s) are not null

@Past property (date or calen-dar)

check if the date is in thepast

Add a check constraint onthe column

@Future property (date or calen-dar)

check if the date is in thefuture

none

@Pattern(regex="regexp", flag=)

property (string) check if the propertymatch the regular expres-sion given a match flag(seejava.util.regex.Patte

rn )

none

@Range(min=, max=) property (numeric orstring representation of anumeric)

check if the value isbetween min and max(included)

Add a check constraint onthe column

@Size(min=, max=) property (array, collec-tion, map)

check if the element sizeis between min and max(included)

none

@AssertFalse property check that the methodevaluates to false (usefulfor constraints expressedin code rather than an-notations)

none

@AssertTrue property check that the methodevaluates to true (usefulfor constraints expressedin code rather than an-notations)

none

@Valid property (object) perform validation re-cursively on the associ-ated object. If the objectis a Collection or an ar-ray, the elements are val-idated recursively. If theobject is a Map, the valueelements are validated re-cursively.

none

@Email property (String) check whether the stringis conform to the emailaddress specification

none

3.1.3. Error messages

Hibernate Validator

Hibernate 3.1 beta 9 38

Page 242: Tutorial Hibernate

Hibernate Validator comes with a default set of error messages translated in a few languages (if yours is notpart of it, please sent us a patch). You can override those messages by creating a ValidatorMes-

sages.properties or (ValidatorMessages_loc.properties) out oforg.hibernate.validator.resources.DefaultValidatorMessages.properties and change the appropriatekeys. You can even add your own additional set of messages while writing your validator annotations.

Alternatively you can provide a ResourceBundle while checking programmatically the validation rules on abean.

3.1.4. Writing your own constraints

Extending the set of built-in constraints is extremely easy. Any constraint consists of two pieces: the constraintdescriptor (the annotation) and the constraint validator (the implementation class). Here is a simple user-defined descriptor:

@ValidatorClass(CapitalizedValidator.class)@Target(METHOD)@Retention(RUNTIME)@Documentedpublic @interface Capitalized {

CapitalizeType type() default Capitalize.FIRST;String message() default "has incorrect capitalization";

}

type is a parameter describing how the property should to be capitalized. This is a user parameter fully depend-ant on the annotation business.

message is the default string used to describe the constraint violation and is mandatory. You can hard code thestring or you can externalize part/all of it through the Java ResourceBundle mechanism. Parameters values aregoing to be injected inside the message when the {parameter} string is found (in our example Capitalization

is not {type} would generate Capitalization is not FIRST), externalizing the whole string in Valid-

atorMessages.properties is considered good practice. See Error messages.

@ValidatorClass(CapitalizedValidator.class)@Target(METHOD)@Retention(RUNTIME)@Documentedpublic @interface Capitalized {

CapitalizeType type() default Capitalize.FIRST;String message() default "{validator.capitalized}";

}

...#in ValidatorMessages.propertiesvalidator.capitalized=Capitalization is not {type}

As you can see the {} notation is recursive.

To link a descriptor to its validator implementation, we use the @ValidatorClass meta-annotation. The validat-or class parameter must name a class which implements Validator<ConstraintAnnotation>.

We now have to implement the validator (ie. the rule checking implementation). A validation implementationcan check the value of the a property (by implementing PropertyConstraint) and/or can modify the hibernatemapping metadata to express the constraint at the database level (by implementing PersistentClassCon-

straint).

public class CapitalizedValidatorimplements Validator<Capitalized>, PropertyConstraint {

Hibernate Validator

Hibernate 3.1 beta 9 39

Page 243: Tutorial Hibernate

private CapitalizeType type;

//part of the Validator<Annotation> contract,//allows to get and use the annotation valuespublic void initialize(Capitalized parameters) {

type = parameters.type();}

//part of the property constraint contractpublic boolean isValid(Object value) {

if (value==null) return true;if ( !(value instanceof String) ) return false;String string = (String) value;if (type == CapitalizeType.ALL) {

return string.equals( string.toUpperCase() );}else {

String first = string.substring(0,1);return first.equals( first.toUpperCase();

}}

}

The isValid() method should return false if the constraint has been violated. For more examples, refer to thebuilt-in validator implementations.

We only have seen property level validation, but you can write a Bean level validation annotation. Instead ofreceiving the return instance of a property, the bean itself will be passed to the validator. To activate the valida-tion checking, just annotated the bean itself instead. A small sample can be found in the unit test suite.

3.1.5. Annotating your domain model

Since you are already familiar with annotations now, the syntax should be very familiar.

public class Address {private String line1;private String line2;private String zip;private String state;private String country;private long id;

// a not null string of 20 characters maximum@Length(max=20)@NotNullpublic String getCountry() {

return country;}

// a non null string@NotNullpublic String getLine1() {

return line1;}

//no constraintpublic String getLine2() {

return line2;}

// a not null string of 3 characters maximum@Length(max=3) @NotNullpublic String getState() {

return state;}

Hibernate Validator

Hibernate 3.1 beta 9 40

Page 244: Tutorial Hibernate

// a not null numeric string of 5 characters maximum// if the string is longer, the message will//be searched in the resource bundle at key 'long'@Length(max=5, message="{long}")@Pattern(regex="[0-9]+")@NotNullpublic String getZip() {

return zip;}

// should always be true@AssertTruepublic boolean isValid() {

return true;}

// a numeric between 1 and 2000@Id @Min(1)@Range(max=2000)public long getId() {

return id;}

}

While the example only shows public property validation, you can also annotate fields of any kind of visibility.

@MyBeanConstraint(max=45)public class Dog {

@AssertTrue private boolean isMale;@NotNull protected String getName() { ... };...

}

You can also annotate interfaces. Hibernate Validator will check all superclasses and interfaces extended or im-plemented by a given bean to read the appropriate validator annotations.

public interface Named {@NotNull String getName();...

}

public class Dog implements Named {

@AssertTrue private boolean isMale;

public String getName() { ... };

}

The name property will be checked for nullity when the Dog bean is validated.

3.2. Using the Validator framework

Hibernate Validator is intended to be used to implement multi-layered data validation, where we express con-straints in one place (the annotated domain model) and apply them at various different layers of the application.

3.2.1. Database schema-level validation

Out of the box, Hibernate Annotations will translate the constraints you have defined for your entities into map-ping metadata. For example, if a property of your entity is annotated @NotNull, its columns will be declared asnot null in the DDL schema generated by Hibernate.

Hibernate Validator

Hibernate 3.1 beta 9 41

Page 245: Tutorial Hibernate

3.2.2. Hibernate event-based validation

Hibernate Validator has two built-in Hibernate event listeners. Whenever a PreInsertEvent or PreUp-

dateEvent occurs, the listeners will verify all constraints of the entity instance and throw an exception if anyconstraint is violated. Basically, objects will be checked before any inserts and before any updates made by Hi-bernate. This is the most convenient and the easiest way to activate the validation process. On constraint viola-tion, the event will raise a runtime InvalidStateException which contains an array of InvalidValues describ-ing each failure.

<hibernate-configuration>...<event type="pre-update">

<listenerclass="org.hibernate.validator.event.ValidatePreUpdateEventListener"/>

</event><event type="pre-insert">

<listenerclass="org.hibernate.validator.event.ValidatePreInsertEventListener"/>

</event></hibernate-configuration>

Note

When using Hibernate Entity Manager, the Validation framework is activated out of the box. If thebeans are not annotated with validation annotations, there is no performance cost.

3.2.3. Application-level validation

Hibernate Validator can be applied anywhere in your application code.

ClassValidator personValidator = new ClassValidator( Person.class );ClassValidator addressValidator = new ClassValidator( Address.class, ResourceBundle.getBundle("messages", Locale.ENGLISH) );

InvalidValue[] validationMessages = addressValidator.getInvalidValues(address);

The first two lines prepare the Hibernate Validator for class checking. The first one relies upon the error mes-sages embedded in Hibernate Validator (see Error messages), the second one uses a resource bundle for thesemessages. It is considered a good practice to execute these lines once and cache the validator instances.

The third line actually validates the Address instance and returns an array of InvalidValues. Your applicationlogic will then be able to react to the failure.

You can also check a particular property instead of the whole bean. This might be useful for property per prop-erty user interaction

ClassValidator addressValidator = new ClassValidator( Address.class, ResourceBundle.getBundle("messages", Locale.ENGLISH) );

//only get city property invalid valuesInvalidValue[] validationMessages = addressValidator.getInvalidValues(address, "city");

//only get potential city property invalid valuesInvalidValue[] validationMessages = addressValidator.getPotentialInvalidValues("city", "Paris")

3.2.4. Validation informations

As a validation information carrier, hibernate provide an array of InvalidValue. Each InvalidValue has a

Hibernate Validator

Hibernate 3.1 beta 9 42

Page 246: Tutorial Hibernate

buch of methods describing the individual issues.

getBeanClass() retrieves the failing bean type

getBean()retrieves the failing instance (if any ie not when using getPotentianInvalidValues())

getValue() retrieves the failing value

getMessage() retrieves the proper internationalized error message

getRootBean() retrieves the root bean instance generating the issue (useful in conjunction with @Valid), is nullif getPotentianInvalidValues() is used.

getPropertyPath() retrieves the dotted path of the failing property starting from the root bean

Hibernate Validator

Hibernate 3.1 beta 9 43

Page 247: Tutorial Hibernate

Chapter 4. Hibernate Lucene IntegrationLucene is a high-performance Java search engine library available from the Apache Software Foundation. Hi-bernate Annotations includes a package of annotations that allows you to mark any domain model object as in-dexable and have Hibernate maintain a Lucene index of any instances persisted via Hibernate.

4.1. Using Lucene to index your entities

4.1.1. Annotating your domain model

First, we must declare a persistent class as @Indexed:

@Entity@Indexed(index="indexes/essays")public class Essay {

...}

The index attribute tells Hibernate where the Lucene index is located (a directory on your file system). If youwish to define a base directory for all lucene indexes, you can use the hibernate.lucene.index_dir propertyin your configuration file.

Lucene indexes contain four kinds of fields: keyword fields, text fields, unstored fields and unindexed fields.Hibernate Annotations provides annotations to mark a property of an entity as one of the first three kinds of in-dexed fields.

@Entity@Indexed(index="indexes/essays")public class Essay {

...

@Id@Keyword(id=true)public Long getId() { return id; }

@Text(name="Abstract")public String getSummary() { return summary; }

@Lob@Unstoredpublic String getText() { return text; }

}

These annotations define an index with three fields: Id, Abstract and Text.

Note: you must specify @Keyword(id=true) on the identifier property of your entity class.

The analyzer class used to index the elements is configurable through the hibernate.lucene.analyzer prop-erty. If none defined, org.apache.lucene.analysis.standard.StandardAnalyzer is used as the default.

4.1.2. Enabling automatic indexing

Finally, we enable the LuceneEventListener for the three Hibernate events that occur after changes are com-mitted to the database.

Hibernate 3.1 beta 9 44

Page 248: Tutorial Hibernate

<hibernate-configuration>...<event type="post-commit-update"

<listenerclass="org.hibernate.lucene.event.LuceneEventListener"/>

</event><event type="post-commit-insert"

<listenerclass="org.hibernate.lucene.event.LuceneEventListener"/>

</event><event type="post-commit-delete"

<listenerclass="org.hibernate.lucene.event.LuceneEventListener"/>

</event></hibernate-configuration>

Hibernate Lucene Integration

Hibernate 3.1 beta 9 45