Manual hibernate v2

90
HIBERNATE

description

 

Transcript of Manual hibernate v2

Page 1: Manual hibernate v2

HIBERNATE

Page 2: Manual hibernate v2

[2]

HIBERNATE

ÍNDICE

1 INTRODUCCIÓN .................................................................................................................... 3

1.1 Object-Relational Mapping.......................................................................................... 3

1.2 Hibernate y MVC en entornos web ............................................................................. 5

2 MI PRIMERA APLICACIÓN CON HIBERNATE ......................................................................... 7

2.1 Pasos para crear una aplicación Hibernate ................................................................. 7

2.2 Desarrollo paso a paso................................................................................................. 8

3 FRAMEWORK HIBERNATE .................................................................................................. 38

3.1 Introducción............................................................................................................... 38

3.2 Instalación.................................................................................................................. 39

3.3 Configuración............................................................................................................. 39

3.4 El interfaz Session ...................................................................................................... 58

4 ASOCIACIONES.................................................................................................................... 62

4.1 Relaciones en Bases de datos .................................................................................... 63

4.2 Many-to-one .............................................................................................................. 65

4.3 Many-to-many ........................................................................................................... 67

4.4 One-to-one................................................................................................................. 69

5 RECUPERACIÓN DE DATOS ................................................................................................. 71

5.1 HQL ............................................................................................................................ 73

5.2 Criteria ....................................................................................................................... 83

5.3 SQL ............................................................................................................................. 89

Page 3: Manual hibernate v2

[3]

HIBERNATE

1. INTRODUCCIÓN

1.1. OBJECT-RELATIONAL MAPPING

Hibernate es una implementación de mapeo Objeto-Relacional (ORM – Object-Relational

Mapping). El objetivo de un ORM es hacer corresponder el modelo de datos de la aplicación

con el modelo de clases de la misma, los cuales no son tan distintos como se pueda pensar.

Para ello, Hibernate realiza el mapeo de elementos (tablas, columnas) entre una Base de datos

relacional y objetos de la aplicación en cuestión:

Base de Datos Relacional

Juan

Ana

Francisco

Belén

López

Hurtado

Soria

Jiménez

666111122

959121212

889121212

789456123

Nombre Apellido Teléfono

TABLA Persona

Persona

-String nombre

-String apellido

-String teléfono

Modelo de Objetos

instance: Persona

nombre = Juan

apellido = López

teléfono = 666111122

Page 4: Manual hibernate v2

[4]

HIBERNATE

Este tipo de sistema facilita todas las operaciones de persistencia y acceso a los datos desde la

aplicación al presentar las siguientes características principales:

� Independiente de SQL: Hibernate incorpora funcionalidades para las operaciones

simples de recuperación y actualización de datos para las cuales no será necesario

utilizar las sentencias SELECT, INSERT, UPDATE o DELETE habituales, siendo sustituidas

por llamadas a métodos (list, save, update, delete, …). Hibernate se encarga de

generar la sentencia SQL que se encargará de realizar tales operaciones. De cualquier

modo, se pueden especificar consultas SQL en caso necesario o incluso consultas en el

dialecto de Hibernate, el HQL, con sintaxis similar a SQL pero que establece una serie

de elementos propios del sistema ORM que gestiona.

� Independiente del SGBD: al aislar las funciones de manipulación de datos del lenguaje

SQL, se consigue que la aplicación pueda comunicarse con cualquier SGBD ya que no

existirán dependencias o particularidades en las sentencias de consulta o actualización

de datos que harían a la aplicación dependiente de un SGBD en cuestión. El encargado

de realizar la traducción final entre las operaciones Objeto-relacionales y las

sentencias SQL es Hibernate, con lo cual el problema de la compatibilidad queda

solventado. Incorpora soporte para la mayoría de los sistemas de bases de datos

existentes.

� Independiente de JDBC: Hibernate contiene una API completa que aísla a la aplicación,

no solamente de las operaciones con lenguaje SQL, sino también la utilización de

objetos propios de la gestión de Bases de datos JDBC (Statement, ResultSet, …). Este

tipo de objetos habituales en la programación Java de acceso a Bases de datos, se

sustituye por el uso de objetos mucho más sencillos como colecciones de clases tipo

JavaBeans que contendrán los datos de los almacenes de datos.

Page 5: Manual hibernate v2

[5]

HIBERNATE

1.2. HIBERNATE Y MVC EN ENTORNOS WEB

En una arquitectura MVC genérica como la siguiente, Hibernate juega un papel primordial

como capa de persistencia de la aplicación. Como aparece en el esquema, los JavaBeans

encargados de ejecutar la lógica de la aplicación interacturán con datos habitualmente

almacenados en una Base de datos. Hibernate se introduce en este contexto para dar soporte

de acceso y gestión de los datos a la capa de Modelo.

El patrón MVC se utiliza de forma amplia en el desarrollo de aplicaciones web.

Fuente: http://java.sun.com/blueprints/patterns/MVC-detailed.html

Page 6: Manual hibernate v2

[6]

HIBERNATE

Controller (Controlador):

� Servlet central que recibe las peticiones, procesa la URL recibida y delega el

procesamiento a los JavaBeans.

� Servlet que almacena el resultado del procesamiento realizado por los JavaBeans en el

contexto de la petición, la sesión o la aplicación.

� Servlet que transfiere el control a un JSP que lleva a cabo la presentación de

resultados.

Model (Modelo):

� JavaBeans (o EJBs para aplicaciones más escalables) que desempeñan el rol de modelo:

� Algunos beans ejecutan lógica.

� Otros guardan datos.

� Normalmente:

� El Servlet Controller invoca un método en un bean de lógica y éste devuelve un

bean de datos.

� El programador del JSP tiene acceso a beans de datos.

View (Vista):

� Rol ejecutado por JSPs.

� El Servlet Controller transfiere el control al JSP después de guardar en un contexto el

resultado en forma de un bean de datos.

� JSP usa jsp:useBean y jsp:getProperty para recuperar datos y formatear respuesta en

HTML o XML.

En resumen:

� Los JavaBeans o EJBs ejecutan la lógica de negocio y guardan los resultados.

� Los JSPs proporcionan la información formateada.

� Los servlets coordinan/controlan la ejecución de los beans y los JSPs.

Page 7: Manual hibernate v2

[7]

HIBERNATE

2. MI PRIMERA APLICACIÓN CON HIBERNATE

Esta parte del manual es una guía paso a paso para realizar una aplicación sencilla, es tal vez la

mejor manera de introducirse con Hibernate y es recomendable realizarla antes de leer el

resto del manual ya que mejorará la comprensión de éste.

Esta aplicación será desarrollada haciendo uso del IDE Netbeans 6.7. Se puede descargar de

http://netbeans.org/.

2.2. PASOS PARA CREAR UNA APLICACIÓN HIBERNATE

Estos son los pasos mínimos que se deberán seguir para desarrollar la aplicación

satisfactoriamente.

1. Determinar el modelo de datos de la aplicación.

2. Añadir las librerías Java de Hibernate.

3. Definir las clases de persistencia.

4. Crear los ficheros de mapeo.

5. Configurar Hibernate.

6. Crear clases de ayuda.

7. Cargar y guardar objetos.

8. Ejecutar la primera versión.

9. Asociar clases.

10. Ejecutar la aplicación.

Page 8: Manual hibernate v2

[8]

HIBERNATE

2.3. DESARROLLO PASO A PASO

1. Determinar el modelo de datos de la aplicación.

Este ejemplo se trata de una sencilla introducción donde únicamente se dispone de

dos clases Event y Person tanto en la base de datos del sistema como en el modelo de

objetos de la aplicación. También se usarán clases auxiliares para la gestión del acceso

y operaciones con los datos.

En primer lugar, es necesario crear la Base de datos en MySQL. Su nombre será

FirstApp pero no se van a crear tablas en la misma ya que se configurará una opción en

el fichero de configuración de Hibernate (hibernate.cfg.xml) para que genere el

esquema de la Base de datos al desplegar la aplicación.

Como se ha comentado, la aplicación se creará a través de Netbeans. Este entorno de

desarrollo dispone de herramientas para la configuración del acceso a la Base de

datos.

En primer lugar es posible que sea necesario configurar el driver de la base de datos

que se va a utilizar en la aplicación, en este caso MySQL. Para ello es necesario

seleccionar la opción New Connection… del menú del botón derecho del ratón sobre la

opción Drivers.

A continuación se selecciona el fichero .jar que contiene el driver de MySQL y se dejan

el resto de campos con los valores que aparecen al seleccionarlo. Todo lo relacionado

Page 9: Manual hibernate v2

[9]

HIBERNATE

con la gestión de Drivers NO es propio de Hibernate, se está configurando el entorno

de Netbeans para la utilización de los Asistentes existentes.

Ahora hay que crear la conexión desde la pestaña Services seleccionando la opción

New Connection… del menú del botón derecho del ratón sobre la opción Databases.

Page 10: Manual hibernate v2

[10]

HIBERNATE

Los datos que se completan son los correspondientes a la conexión con la base de

datos que se encuentra en el servidor MySQL.

2. Añadir las librerías Java de Hibernate.

Netbeans dispone de un asistente para la creación de aplicaciones Web donde se

puede indicar que la aplicación empleará Hibernate. De esta forma, Netbeans realiza

una serie de acciones de forma automática que ayudarán al desarrollo mediante este

Framework.

Se deberá crear un nuevo proyecto web con Netbeans, para ello dirigirse al menú: File

� New Project � Web � Web Application. No se debe olvidar añadir el Framework,

como muestran las imágenes siguientes.

Page 11: Manual hibernate v2

[11]

HIBERNATE

Al crear el proyecto, Netbeans crea un fichero hibernate.cfg con los valores para conectar

con la Base de datos que se ha establecido al crear el proyecto. Dicho archivo se encuentra

en la carpeta Source Packages\default package de la aplicación.

Page 12: Manual hibernate v2

[12]

HIBERNATE

3. Definir las clases de persistencia.

El paso siguiente es añadir clases para representar a los elementos de la Base de datos.

La clase Event (paquete events) es una clase tipo JavaBean sencilla, cuyas propiedades

coinciden con los campos de la tabla Events que existirá en la Base de datos.

package events;

import java.util.Date;

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

public Event() { }

public Long getId() {

return id; }

private void setId(Long id) {

this.id = id; }

public Date getDate() {

return date; }

public void setDate(Date date) {

this.date = date; }

public String getTitle() {

return title; }

public void setTitle(String title) {

this.title = title; }

}

La propiedad id representa el identificador único para un evento. Todas las clases de

persistencia deben tener un identificador si se quiere utilizar el conjunto completo de

Page 13: Manual hibernate v2

[13]

HIBERNATE

funcionalidades de Hibernate. El constructor sin argumentos también es un requisito

para todas las clases persistentes.

Se recomienda en la mayoría de las ocasiones utilizar clases para representar los tipos

de los atributos en lugar de utilizar tipos básicos, debido a que para aquellos campos

que se permitan valores nulos en la Base de datos, la representación del valor del

campo será natural en la representación Java (null), mientras que con tipos básicos

podría resultar en un error.

En el paquete events también se introducirá la clase Person, la cual se representará de

forma similar por el momento:

package events; public class Person {

private Long id; private int age; private String firstname; private String lastname;

public Person() { }

// getters y setters para todas las propiedades

}

4. Crear los ficheros de mapeo.

Para cada una de las clases de persistencia tiene que existir un fichero de mapeo que se

encarga de definir la relación entre las tablas y columnas de éstas en la Base de datos, y las

clases y sus propiedades en el modelo de objetos.

En Netbeans existe una utilidad para mapear las clases con las tablas de la Base de datos

de forma automática. Para ello, seleccionar la opción New � Other � Hibernate �

Hibernate Mapping File

Page 14: Manual hibernate v2

[14]

HIBERNATE

El nombre del fichero (Event.hbm.xml) coincidirá con el nombre de la clase que mapea

y se guardará habitualmente en el mismo paquete donde se encuentra dicha clase.

Page 15: Manual hibernate v2

[15]

HIBERNATE

Solamente queda seleccionar la clase y la tabla a mapear. En este caso no se puede

especificar ninguna tabla ya que la Base de datos se encuentra vacía.

Al pulsar Finish se genera un archivo de mapeo, al cual es necesario añadir el nombre

de la tabla que está mapeando, y completar con los datos del mapeo de las columnas.

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

<class name="events.Event" table="EVENTS">

<id name="id" column="EVENT_ID">

<generator class="native"/>

</id>

<property name="date" type="timestamp" column="EVENT_DATE"/>

<property name="title"/>

</class>

</hibernate-mapping>

Page 16: Manual hibernate v2

[16]

HIBERNATE

Algunos detalles sobre este archivo:

<class name="events.Event" table="EVENTS">

Aunque inicialmente el archive se crea con una serie de propiedades para el

mapeo (dynamic-insert="false" dynamic-update="false" …) se han eliminado

para simplificar el ejemplo. Realmente las únicas propiedades imprescindibles

son name (nombre de la clase) y table (nombre de la tabla).

<id name="id" column="EVENT_ID">

En primer lugar se declara el identificador. Esta definición se destaca porque se

identifica con la etiqueta id. La propiedad name representa el nombre del

atributo en la clase, y la propiedad column el nombre del campo en la tabla.

<generator class="native"/>

Dentro del identificador se declara cómo se gestiona la asignación de ésta. En

este caso se ha elegido el valor native, que delega la gestión de la clave en el

SGBD utilizado, es decir, que no es necesario preocuparse de la

gestión/asignación de claves de forma manual. Existen otras posibilidades de

generadores de clave que se estudiarán más adelante en este documento.

Page 17: Manual hibernate v2

[17]

HIBERNATE

<property name="date" type="timestamp" column="EVENT_DATE"/>

La siguiente propiedad en declararse es date. En este caso también name

representa el nombre del atributo en la clase, y column el nombre del campo

en la tabla. La propiedad type representa el tipo de datos que se va a utilizar,

ya que aunque Hibernate es capaz de detectar que este campo se refiere a un

valor de tipo Date, es necesario especificar si esa fecha se gestionará como

time, date o timestamp. Esta propiedad type sirve para realizar conversiones

de tipos si es necesario en la aplicación, de forma que un campo que en la base

de datos es numérico se trate como String, o Boolean, …

<property name="title"/>

Por último se declara el campo title. En este caso solamente se especifica el

nombre del atributo de la clase porque el nombre coincide con el nombre de la

columna de la tabla de Base de datos, si no sería necesario especificar el

nombre de dicha columna.

El fichero de mapeo para la clase Person quedará de la siguiente forma:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

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

<hibernate-mapping>

<class name="events.Person" table="PERSON">

<id name="id" column="PERSON_ID">

<generator class="native"/>

</id>

<property name="age"/>

<property name="firstname"/>

<property name="lastname"/>

</class>

</hibernate-mapping>

Page 18: Manual hibernate v2

[18]

HIBERNATE

5. Configurar Hibernate.

Hasta el momento se han creado las clases para representar los objetos de la Base de

datos y los ficheros de mapeo para las mismas. Para que la aplicación funcione de

forma correcta, es necesario configurar Hibernate a través de su fichero de

configuración hibernate.cfg.xml.

En este fichero se establecerá al menos la conexión con la base de datos, y se

especificarán cuáles son los ficheros de mapeo que se están empleando en la

aplicación, es decir, que si un fichero de mapeo no aparece en hibernate.cfg el mapeo

no funcionará.

El fichero hibernate.cfg se encuentra en el paquete por defecto de la aplicación. Si se

abre este fichero con Netbeans, en primer lugar aparece la vista Diseño. Al seleccionar

la vista XML (esquina superior izquierda de la ventana) aparecerá el código que

Netbeans introdujo por defecto al crear la aplicación y especificar la conexión a la Base

de datos que se iba a emplear en la misma:

<?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 name="session1">

<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/firstapp

</property>

<property name="hibernate.connection.username">root</property>

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

<mapping resource="events/Event.hbm.xml"/>

<mapping resource="events/Person.hbm.xml"/>

</session-factory>

</hibernate-configuration>

Page 19: Manual hibernate v2

[19]

HIBERNATE

<session-factory name="session1">

Indica el session factory utilizado en la aplicación. Si la aplicación accede a más de una

Base de datos es necesario declarar más de un session-factory, cada uno de ellos

identificado por un nombre distinto.

<property name="hibernate.dialect">

La propiedad dialect indica la variante de SQL que Hibernate genera a la hora de

acceder a la Base de datos.

Además de los datos de la conexión a la Base de datos y los ficheros de mapeo de la

aplicación (que Netbeans ha introducido de forma automática al crear los propios ficheros

hbm), en hibernate.cfg deben aparecer propiedades importantes que definen los

siguientes aspectos:

� current_session_context_class: Hibernate funciona con sesiones para acceder a los

datos. Es necesario definir una clase de gestión de sesiones para poder realizar

operaciones con la Base de datos.

� cache.provider_class: especifica el comportamiento de la Caché de 2º nivel.

� show_sql: mostrar en los logs las sentencias SQL que Hibernate lanza cada vez que

realiza una operación con la Base de datos.

� hbm2ddl.auto: crear el esquema de la Base de datos de forma automática al desplegar

la aplicación.

Los valores a añadir a hibernate.cfg para el ejemplo son los siguientes:

<!-- Enable Hibernate's automatic session context management-->

<property name="current_session_context_class">thread</property>

<!-- Disable the second-level cache -->

<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>

<!-- Echo all executed SQL to stdout -->

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

<!-- Drop and re-create the database schema on startup -->

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

Page 20: Manual hibernate v2

[20]

HIBERNATE

6. Crear clases de ayuda.

Una vez que se ha configurado Hibernate, ya se puede escribir el código Java para el

acceso a los datos.

Como se ha comentado anteriormente, Hibernate utiliza el concepto de sesión para

acceder a los datos, de forma que cualquier operación de consulta o manipulación de

los datos debe estar enmarcada en una sesión.

Para que la gestión de sesiones quede aislada del resto de operaciones de acceso a

datos, y para evitar la duplicidad de código, es habitual generar una clase de ayuda que

se encarga de la inicialización de sesiones.

Netbeans permite la generación de esta clase a través del menú New� Other �

Hibernate � HibernateUtil. En este caso la clase HibernateUtil se incorporará al

paquete util.

Page 21: Manual hibernate v2

[21]

HIBERNATE

package util;

import org.hibernate.cfg.Configuration;

import org.hibernate.SessionFactory;

public class HibernateUtil {

private static final SessionFactory sessionFactory;

static {

try {

// Create the SessionFactory from hibernate.cfg.xml

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

} catch (Throwable ex) {

// Log the exception.

System.err.println("Initial SessionFactory creation failed." + ex);

throw new ExceptionInInitializerError(ex);

}

}

public static SessionFactory getSessionFactory() {

return sessionFactory;

}

}

Page 22: Manual hibernate v2

[22]

HIBERNATE

Cuando una clase de la aplicación quiera acceder a la sesión actual, simplemente

invocará al método estático getSessionFactory() (HibernateUtil.getSessionFactory()).

7. Cargar y guardar objetos.

Para manipular los datos de la Base de datos se va a crear una clase EventManager en

el paquete event para el almacenamiento y recuperación de eventos:

public class EventManager {

public void createAndStoreEvent(String title, Date theDate) {

//Obtener la sesión actual

Session session = HibernateUtil.getSessionFactory().getCurrentSession();

//Comenzar la transacción

session.beginTransaction();

//Crear el evento

Event theEvent = new Event();

theEvent.setTitle(title);

theEvent.setDate(theDate);

//Almacenarlo

session.save(theEvent);

//Confirmar transacción

session.getTransaction().commit();

}

public List listEvents() {

//Obtener la sesión actual

Session session = HibernateUtil.getSessionFactory().getCurrentSession();

//Comenzar la transacción

session.beginTransaction();

//Obtener la lista de eventos

List result = session.createQuery("from Event").list();

//Confirmar transacción

session.getTransaction().commit();

return result;

}

}

Page 23: Manual hibernate v2

[23]

HIBERNATE

Los métodos que se han definido son los siguientes, los cuales se describen para

comprobar la sencillez de las acciones que realizan:

� createAndStoreEvent: crea y almacena un evento realizando los siguientes pasos:

1. Acceder a la sesión actual: a través del método estático getSessionFactory() de la

clase HibernateUtil creada anteriormente en el proyecto.

2. Abrir una transacción: utilizando la sesión actual.

3. Crear el objeto: de la forma habitual en Java.

4. Almacenar el objeto: simplemente invocando al método save del interfaz Session

(sesión actual).

5. Confirmar la transacción: utilizando también la sesión actual.

En la siguiente imagen se muestra un esquema del funcionamiento de Hibernate

basado en sesiones:

� listEvents: obtiene una lista de todos los eventos de la Base de datos realizando los

siguientes pasos:

1. Acceder a la sesión actual: a través del método estático getSessionFactory() de la

clase HibernateUtil creada anteriormente en el proyecto.

2. Abrir una transacción: utilizando la sesión actual. Las consultas a bases de datos

también requieren de una transacción en Hibernate.

Page 24: Manual hibernate v2

[24]

HIBERNATE

3. Obtener el conjunto de resultados: se obtiene una colección (List) de instancias de la

clase Event cuyos datos coincidirán con los datos de los registros existentes en la

Base de datos. Existen varias formas de obtener datos de una tabla en Hibernate, en

este caso se ha utilizado un Query HQL. Posteriormente se tratarán en profundidad

los aspectos relacionados con los distintos tipos de consultas en Hibernate.

4. Confirmar la transacción: utilizando también la sesión actual.

8. Ejecutar la primera versión.

Para probar lo que se ha implementado hasta el momento se va a crear una sencilla

aplicación Web.

Para ello en primer lugar se modificará la página index.jsp del proyecto para que

funcione como formulario para introducir los datos:

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">

<title>JSP Page</title>

</head>

<body>

<form action="Servlet">

<table>

<tr>

<td>title</td><td><input type="text" name="title"></input></td>

</tr>

<tr>

<td>date</td><td><input type="text" name="date"></input></td>

</tr>

<tr>

<td><input type="submit"/></td>

</tr>

</table>

</form>

</body>

</html>

Page 25: Manual hibernate v2

[25]

HIBERNATE

A continuación se crea el Servlet que se encargará de la lógica de la aplicación. Para

crear un Servlet Netbeans también dispone de un Asistente a través de la opción New

� Other � Web � Servlet

Su nombre será Servlet y se almacenará en el paquete servlet. En la siguiente pantalla

se dejan por defecto los parámetros que se añadirán al fichero web.xml.

Page 26: Manual hibernate v2

[26]

HIBERNATE

Para ejecutar la aplicación simplemente usar la opción Run � Run Main Project (F6).

public class Servlet extends HttpServlet { protected void processRequest(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); try {

EventManager mng = new EventManager(); //Parsear la fecha Date date = new SimpleDateFormat("dd/MM/yyyy").

parse(request.getParameter("date")); mng.createAndStoreEvent(request.getParameter("title"), date);

//Mostrar los datos almacenados this.mostrarTabla(out, mng.listEvents());

} catch (Exception ex) { out.println(ex);

} finally { out.close();

} }

private void mostrarTabla(PrintWriter out, List lista) {

out.println("<table>"); Iterator it = lista.iterator(); //Iterar sobre todos los eventos while (it.hasNext()) {

Event event = (Event) it.next(); out.println("<tr>"); out.println("<td>" + event.getTitle() + "</td>"); out.println("<td>" + event.getDate() + "</td>"); out.println("</tr>");

} out.println("</table>");

}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

processRequest(request, response); }

}

Page 27: Manual hibernate v2

[27]

HIBERNATE

9. Asociar clases.

La asociación entre clases es uno de los aspectos más importantes de Hibernate, así

como la asociación entre tablas es la base del modelo relacional de Bases de datos.

Este aspecto puede resultar complejo en algunas ocasiones. Posteriormente se

tratarán en profundidad los aspectos relacionados con los distintos tipos de

asociaciones en Hibernate.

En los pasos 3 y 4 se definieron las clases Event y Person a través de código Java y

ficheros de mapeo. Ahora es necesario modelar la relación que existe entre ambas a

través de una asociación Hibernate.

La relación que existe entre ambas tablas es una relación M a M, de forma que a un

Evento acudirán varias Personas, y una Persona puede acudir a diversos Eventos. A

nivel de Bases de datos, esta relación dará lugar a una tabla intermedia

PERSON_EVENT. Esta relación en Hibernate se puede modelar de diversas maneras, y

además se puede reflejar de distintas formas en las clases implicadas.

En primer lugar, la relación más sencilla que se puede establecer es la relación

Unidireccional entre 2 tablas, en este caso entre las tablas Person y Person_Event. Para

el ejemplo, el tipo de relación escogido es algo complejo, denominado en Hibernate

como relación con tabla intermedia (join table). En el capítulo 4 se explicarán los tipos

de relaciones más generales, y la forma de eludir las relaciones complejas, pero es

bueno presentar en el ejemplo otro tipos de relaciones que pueden encontrarse en

aplicaciones Hibernate.

Las relaciones se modelan con colecciones, normalmente conjuntos (Set) de forma que

se agregan los fragmentos de código que aparecen a continuación. En la clase

simplemente se crea el atributo de la clase Set y se generan los métodos get/set para

el mismo.

Page 28: Manual hibernate v2

[28]

HIBERNATE

private Set events = new HashSet();

public Set getEvents() {

return events;

}

public void setEvents(Set events) {

this.events = events;

}

<set name="events" table="PERSON_EVENT">

<key column="PERSON_ID"/>

<many-to-many column="EVENT_ID" class="events.Event"/>

</set>

En Person.hbm.xml se genera una nueva propiedad para la tabla de tipo conjunto. Los

atributos de esa propiedad son los siguientes:

� name: nombre de la propiedad de la clase a la que hace referencia.

� table: tabla intermedia de la base de datos.

� key: clave de la tabla intermedia (PERSON_EVENT) que aporta la clase actual.

La propiedad columna es el nombre de la columna de la tabla de Base de

datos. Es decir, esta etiqueta significa que el nombre del campo que relaciona

a la tabla que modela la clase Person (PERSON) y a la tabla intermedia

(PERSON_EVENT) es PERSON_ID, el cual estará relacionado con el campo id de

la clase Person (en este caso también se denomina PERSON_ID).

� many-to-many: indica para la tabla intermedia (PERSON_EVENT) cuál es la otra

clase que está implicada en la relación (la clase Event) y cuál es la clave

foránea que esta clase aporta a la relación.

Con todo esto únicamente se ha posibilitado que a través de una persona concreta, es

decir, una instancia de la clase Person, se pueda conocer de forma directa los eventos

a los cuales está asociada. En Hibernate esto se traduce en acceder simplemente a los

atributos de dicha clase. Por ejemplo, el método getEvents devuelve un Set con los

eventos asociados a una persona:

Page 29: Manual hibernate v2

[29]

HIBERNATE

Person aPerson = (Person) session.load(Person.class, personId);

aPerson.getEvents();

Esta utilidad es suficiente, pero se pueden realizar cosas más avanzadas, como

manipular objetos persistentes y luego transformarlos en acciones contra la base de

datos. Por ejemplo, el siguiente método asigna un evento a una persona de forma

sencilla, sin gestionar claves ni relaciones entre tablas, simplemente a través de la

relación normal entre conjuntos:

public void addPersonToEvent(Long personId, Long eventId) {

//Obtener la sesión actual

Session session = HibernateUtil.getSessionFactory().getCurrentSession();

//Comenzar la transacción

session.beginTransaction();

//Cargar la persona por clave

Person aPerson = (Person) session.load(Person.class, personId);

//Cargar el evento por clave

Event anEvent = (Event) session.load(Event.class, eventId);

//Agregar el evento a la persona

aPerson.getEvents().add(anEvent);

//Confirmar transacción

session.getTransaction().commit();

}

La línea aPerson.getEvents().add(anEvent) es donde se crea la relación entre ambos

elementos, y al realizar la confirmación en la Base de datos se crean los registros

necesarios para satisfacer dicha relación.

A continuación se va a modelar la relación inversa. Una relación en Hibernate se puede

modelar de forma unidireccional o bidireccional. Para el segundo de los casos, se

establecen en los ficheros de configuración 2 relaciones, por una parte la relación

directa (que corresponde con la que ya se ha generado en el ejemplo), y por la otra

parte se modela la relación inversa. Es decir, si en el ejemplo la relación directa servía

para conocer cuáles eran los eventos a los que estaba asociada una persona, la

relación inversa proporciona información sobre cuáles son las personas que participan

en un determinado evento.

Page 30: Manual hibernate v2

[30]

HIBERNATE

Para ello, en la clase Event se establece también una propiedad de tipo Set que

almacenará los datos de las personas asociadas a un evento concreto:

private Set participants = new HashSet();

public Set getParticipants() {

return participants;

}

public void setParticipants(Set participants) {

this.participants = participants;

}

El mapeo es muy similar al que se estableció para la clase Person, teniendo en cuenta que

hay que incluir la propiedad inverse (con valor true):

� name: nombre de la propiedad de la clase a la que hace referencia.

� table: tabla intermedia de la base de datos.

� inverse: en toda relación bidireccional uno de los 2 extremos debe ser

inverse=true, lo cual es utilizado por Hibernate a la hora de construir las

sentencias SQL para interactuar con la Base de datos.

� key: el nombre del campo que relaciona a la tabla que modela la clase Event

(EVENT) y a la tabla intermedia (PERSON_EVENT) es EVENT_ID, el cual estará

relacionado con el campo id de la clase Event (EVENT_ID).

� many-to-many: indica para la tabla intermedia (PERSON_EVENT) cuál es la otra

clase que está implicada en la relación (la clase Person) y cuál es la clave

foránea que esta clase aporta a la relación (PERSON_ID).

<set name="participants" table="PERSON_EVENT" inverse="true">

<key column="EVENT_ID"/>

<many-to-many column="PERSON_ID" class="events.Person"/>

</set>

Page 31: Manual hibernate v2

[31]

HIBERNATE

Gracias a esta nueva relación se pueden conocer las personas que pertenecen a un

determinado evento. La pregunta es, ¿funcionará correctamente un método como el

siguiente?

public void addPersonToEvent(Long personId, Long eventId) {

//Obtener la sesión actual

Session session = HibernateUtil.getSessionFactory().getCurrentSession();

//Comenzar la transacción

session.beginTransaction();

//Cargar la persona por clave

Person aPerson = (Person) session.load(Person.class, personId);

//Cargar el evento por clave

Event anEvent = (Event) session.load(Event.class, eventId);

//Agregar la persona al evento

anEvent.getParticipants().add(aPerson);

//Confirmar transacción

session.getTransaction().commit();

}

10. Ejecutar la aplicación.

Para probar todos los conceptos vistos hasta el momento, se crea un nuevo Servlet de

la misma forma especificada en el paso 8, donde se realizarán las siguientes pruebas:

� Cargar eventos por clave (emng.loadEvent(new Long(1))), para comprobar si

existe un evento con identificador igual a 1.

� Almacenar personas (mng.createAndStorePerson(…): almacena una persona

de prueba.

� Agregar personas a eventos (mng.addPersonToEvent(new Long(1), e.getId())):

agrega la persona creada anteriormente al evento.

� Listar participantes de un evento (e.getParticipants()): obtiene la lista de

participantes al evento para mostrar sus nombres y la lista de eventos.

Page 32: Manual hibernate v2

[32]

HIBERNATE

public class ServletP extends HttpServlet {

protected void processRequest(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException {

response.setContentType("text/html;charset=UTF-8");

PrintWriter out = response.getWriter();

try {

PersonManager mng = new PersonManager();

//Comprobar que existe el evento con id=1

EventManager emng = new EventManager();

Event e = emng.loadEvent(new Long(1));

if (e == null) { //No existe el evento

out.println("Debe crear un evento");

} else {

//Almacenar

mng.createAndStorePerson(new Integer(30), "José", "Guardiola");

//Agregar un evento como prueba (id=1)

mng.addPersonToEvent(new Long(1), e.getId());

//Refrescar los datos

e = emng.loadEvent(new Long(1));

//Mostrar los datos almacenados en el evento

this.mostrarTabla(out, e.getParticipants());

}

} catch (Exception ex) {

out.println(ex);

} finally {

out.close();

}

}

Page 33: Manual hibernate v2

[33]

HIBERNATE

private void mostrarTabla(PrintWriter out, Set lista) {

out.println("<table>");

Iterator it = lista.iterator();

//Iterar sobre todos los eventos

while (it.hasNext()) {

Person p = (Person) it.next();

out.println("<tr>");

out.println("<td>" + p.getAge() + "</td>");

out.println("<td>" + p.getFirstname() + "</td>");

out.println("<td>" + p.getLastname() + "</td>");

out.println("<td>" + p.getEvents() + "</td>");

out.println("</tr>");

}

out.println("</table>");

}

}

Ejemplo:

1. En primer lugar se crea un evento.

2. A continuación se pulsa el enlace Prueba relaciones.

3. Tras la ejecución del código del Servlet se muestra la lista de participantes en el evento

inicial. Para el ejemplo, la persona que se almacena como prueba estará al menos en el

evento creado inicialmente.

Page 34: Manual hibernate v2

[34]

HIBERNATE

El código del Servlet resulta sencillo ya que toda la lógica de interacción con Hibernate se

encuentra en las clases PersonManager y EventManager, destacando la forma en que se

han realizado las operaciones basadas en relaciones:

� Agregar personas a eventos (mng.addPersonToEvent(new Long(1), e.getId())): la

persona se agrega al evento a través del conjunto de eventos en los que participa

una persona (ver PersonManager).

� Listar participantes de un evento (e.getParticipants()): los participantes a un

evento se obtienen a través del conjunto de personas que participa en un evento.

Para ello se carga el evento, se obtienen sus participantes, y para cada uno de ellos

se muestra su nombre y la lista de eventos en los cuales participa.

El método loadPerson carga una persona a través del valor de su campo clave (id). Para ello

se utiliza el método load del interfaz Session. El procedimiento de recuperación de una

instancia también sigue el esquema de sesiones y transacciones explicado con anterioridad.

El método listPerson devuelve un listado de todas las personas a través de un Criteria. Esta

forma de recuperar datos se explica en el capítulo 5.2 con detenimiento, pero por el

momento destacar que el método setFetchMode posibilita la recuperación de datos

relacionados, es decir, Hibernate por defecto no cargará los datos de los conjuntos que

estén relacionados con una instancia (por ejemplo, los eventos a los que está asociada una

persona) para no sobrecargar innecesariamente al sistema, así que en caso de querer

recuperarlos hay que notificarlo de esta forma, entre otras.

Page 35: Manual hibernate v2

[35]

HIBERNATE

public class EventManager {

/**

* Almacenar un evento

* @param title título

* @param theDate fecha

*/

public void createAndStoreEvent(String title, Date theDate) {

//Obtener la sesión actual

Session session = HibernateUtil.getSessionFactory().getCurrentSession();

//Comenzar la transacción

session.beginTransaction();

//Crear el evento

Event theEvent = new Event();

theEvent.setTitle(title);

theEvent.setDate(theDate);

//Almacenarlo

session.save(theEvent);

//Confirmar transacción

session.getTransaction().commit();

}

/**

* Cargar un evento

* @return evento

*/

public Event loadEvent(Long eventId) {

//Obtener la sesión actual

Session session = HibernateUtil.getSessionFactory().getCurrentSession();

//Comenzar la transacción

session.beginTransaction();

//Cargar el evento por clave

Page 36: Manual hibernate v2

[36]

HIBERNATE

El método loadEvent carga un evento a través del valor de su campo clave (id). Para

ello se utiliza un Criteria al cual se le añade una restricción de igualdad

(Restrictions.eq) para filtrar por el campo clave (id). Además se utilizan los métodos

necesarios para que se carguen los participantes de un evento

Event anEvent = (Event) session.createCriteria(Event.class).

setFetchMode("participants", FetchMode.JOIN).

setFetchMode("participants.events", FetchMode.JOIN).

add(Restrictions.eq("id", eventId)).

uniqueResult();

//Confirmar transacción

session.getTransaction().commit();

return anEvent;

}

/**

* Listar los eventos

* @return lista de eventos

*/

public List listEvents() {

//Obtener la sesión actual

Session session = HibernateUtil.getSessionFactory().getCurrentSession();

//Comenzar la transacción

session.beginTransaction();

//Obtener la lista de personas

List result = session.createCriteria(Event.class).

setFetchMode("participants", FetchMode.JOIN).

list();

//Confirmar transacción

session.getTransaction().commit();

return result;

}

}

Page 37: Manual hibernate v2

[37]

HIBERNATE

(setFetchMode("participants", FetchMode.JOIN)) y para cada uno de dichos

participantes, que en su propiedad events (ver clase Person) se carguen los datos de

sus eventos (setFetchMode("participants", FetchMode.JOIN)). De esta forma, para

cada evento se podrían conocer los datos completos de las personas asociadas (edad,

nombre, apellidos y eventos a los que se encuentra asociada). El método uniqueResult

indica que la consulta deberá devolver un único valor, el cual es una instancia de la

clase solicitada.

Para estas 2 clases, los ficheros de mapeo quedan configurados según los cambios que

se han comentado al introducir las relaciones entre ambas. Para ejecutar el proyecto

simplemente queda modificar la página JSP de inicio, para que incluya la opción

Prueba relaciones:

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">

<title>JSP Page</title>

</head>

<body>

<form action="Servlet">

<table>

<tr>

<td>title</td><td><input type="text" name="title"></input></td>

</tr>

<tr>

<td>date</td><td><input type="text" name="date"></input></td>

</tr>

<tr>

<td><input type="submit"/></td>

</tr>

</table>

</form>

<a href="ServletP">Prueba relaciones</a>

</body>

</html>

Page 38: Manual hibernate v2

[38]

HIBERNATE

3. FRAMEWORK HIBERNATE

3.1. INTRODUCCIÓN

En el desarrollo de una aplicación J2EE se trabaja continuamente con objetos. Sin embargo, las

bases de datos relacionales no trabajan con ellos, sino con conjuntos y relaciones entre

conjuntos. Entonces ¿Cómo se almacena un objeto en una base de datos? ¿Y cómo se

mantienen las relaciones cuando se convierte un elemento de una base de datos en un objeto

Java? La causa de esta disparidad es la llamada diferencia Objeto-Relacional.

El problema se agrava cuanto más complejo sea el material a trasladar de un paradigma a otro.

En estos casos se hace necesario un puente entre ambos conceptos. Es lo que se llama un

mapeador objeto-relacional, también conocido por sus siglas en inglés ORM (Object-Relational

Mapping).

Un ORM permite al programador aislarse de los detalles básicos. Basta definir cómo se deben

trasladar los datos entre ambos modelos y el mapeador se encargará de aplicarlo en cada

ocasión.

Hibernate es un potente servicio de consulta y persistencia objeto/relacional con el cual es

posible desarrollar clases persistentes mediante lenguajes orientados a objetos manteniendo

sus características básicas (incluyendo herencia, polimorfismo, colecciones, etc...). Permite

expresar consultas tanto en SQL como en su propia extensión de este lenguaje, denominada

HSQL.

Hibernate es un proyecto de software libre con código abierto, por lo que su desarrollo se

encuentra en continua evolución. La página del proyecto:

http://www.hibernate.org/

Page 39: Manual hibernate v2

[39]

HIBERNATE

3.2. INSTALACIÓN

IMPORTANTE:

Este apartado sirve como guía de ayuda de instalación manual de Hibernate para su uso dentro de una aplicación.

No es recomendable realizar la instalación de Hibernate “a mano”. Normalmente, se utilizan entornos de desarrollo integrados como Netbeans, con los que se puede elegir durante la creación del proyecto que añada los frameworks deseados, entre ellos Hibernate, de forma que el entorno los configurará adecuadamente de forma automática.

En caso de que se descargue la última versión, se recomienda seguir la ayuda oficial ya que estas instrucciones pueden estar obsoletas.

1. Descargar la distribución de Hibernate Core de:

https://www.hibernate.org/344.html

2. Descomprimir el fichero

3. Copiar los archivos jar necesarios a la carpeta lib de nuestra aplicación:

a. hibernate3.jar

b. \lib\required\*.jar

c. \lib\optional\*.jar

4. Crear el fichero hibernate.cfg.xml en el paquete por defecto de la aplicación.

3.3. CONFIGURACIÓN

Hibernate se configura a través hibernate.cfg.xml. En el paso 5 del ejemplo anterior se

explicaron algunos de los elementos de configuración que se pueden encontrar en este

fichero:

Page 40: Manual hibernate v2

[40]

HIBERNATE

<?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 name="session1">

<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/firstapp

</property>

<property name="hibernate.connection.username">root</property>

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

<!-- Enable Hibernate's automatic session context management-->

<property name="current_session_context_class">thread</property>

<!-- Disable the second-level cache -->

<property name="cache.provider_class">

org.hibernate.cache.NoCacheProvider

</property>

<!-- Echo all executed SQL to stdout -->

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

<!-- Drop and re-create the database schema on startup -->

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

<mapping resource="events/Event.hbm.xml"/>

<mapping resource="events/Person.hbm.xml"/>

</session-factory>

</hibernate-configuration>

Page 41: Manual hibernate v2

[41]

HIBERNATE

Este archivo es leído por el framework cuando arranca y contiene los siguientes elementos:

� Sesiones: session factory utilizados en la aplicación. Si la aplicación accede a más de

una Base de datos es necesario declarar más de un session-factory, cada uno de ellos

identificado por un nombre distinto.

� Propiedades: dentro de cada sesión se declaran una serie propiedades para la

conexión y configuración del acceso a la Base de datos:

� Datos de la conexión: datos para conectar con la Base de datos.

hibernate.connection.driver_class Driver JDBC de la Base de datos

hibernate.connection.url URL de conexión a la Base de datos

hibernate.connection.username Usuario

hibernate.connection.password Clave

hibernate.connection.pool_size Máximo número de conexiones del pool

� Propiedades de configuración: parámetros de configuración generales.

hibernate.max_fetch_depth Profundidad máxima de joins para relaciones simples

hibernate.default_batch_fetch_size Tamaño por defecto para el fetching de asociaciones

hibernate.default_entity_mode Modo por defecto para la representación de entidades

dynamic-map

dom4j

pojo

hibernate.order_updates Ordenar las actualizaciones por clave primaria

true

false

hibernate.generate_statistics Generar estadísticas de rendimiento

true

false

hibernate.use_identifier_rollback Resetea el identificador asociado en caso de eliminación

true

false

hibernate.use_sql_comments Genera comentarios en las sentencias SQL

true

false

Page 42: Manual hibernate v2

[42]

HIBERNATE

� Propiedades de conexión: otras propiedades de la conexión.

hibernate.jdbc.fetch_size Tamaño de búsqueda

hibernate.jdbc.batch_size Actualización batch de JDBC2

hibernate.jdbc.batch_versioned_data Utilizar el rowcount que devuelve executeBatch()

true

false

hibernate.jdbc.factory_class Seleccionar un org.hibernate.jdbc.Batcher propio

hibernate.jdbc.use_scrollable_resultset Uso de resultsets scrollables de JDBC2

true

false

hibernate.jdbc.use_streams_for_binary Uso de streams para la escritura de tipos de datos serializables

true

false

hibernate.jdbc.use_get_generated_keys Uso de getGeneratedKeys() de JDBC3

true

false

hibernate.connection.provider_class Uso de un ConnectionProvider propio

hibernate.connection.autocommit Habilita el Autocommit true

false

hibernate.connection.release_mode Modo de Liberación de conexiones JDBC

auto

on_close

after_transaction

after_statement

hibernate.connection.<propertyName> Propiedad específica de JDBC para la conexión

hibernate.jndi.<propertyName> Propiedad específica de JNDI para la conexión

hibernate.connection.isolation Nivel de aislamiento de transacciones

Page 43: Manual hibernate v2

[43]

HIBERNATE

� Propiedades de caché: gestión de la caché de segundo nivel.

hibernate.cache.provider_class Nombre de CacheProvider propio.

hibernate.cache.use_minimal_puts Optimizar la operación de la caché al mínimo número de escrituras.

true false

hibernate.cache.use_query_cache Habilitar la cache de consultas true false

hibernate.cache.use_second_level_cache Deshabilitar completamente la cache

true false

hibernate.cache.query_cache_factory Nombre de un interfaz QueryCache propio

hibernate.cache.region_prefix Prefijo de los nombres regionales de la caché

hibernate.cache.use_structured_entries Usar entradas de cache legibles true false

� Propiedades de transacciones: gestión de transacciones.

hibernate.transaction.factory_class Nombre de la factoría de transacciones

jta.UserTransaction Nombre para obtener la transacción JTA

hibernate.transaction.manager_lookup_class Nombre del TransactionManagerLookup

hibernate.transaction.flush_before_completion La sesión se lanza antes de concluir la transacción

true

false

hibernate.transaction.auto_close_session Cierra la sesión tras concluir la

transacción

true

false

Page 44: Manual hibernate v2

[44]

HIBERNATE

� Otras propiedades.

hibernate.current_session_context_class

Estrategia para

obtener la sesión

actual

jta

thread

managed

custom Class

hibernate.query.factory_class Implementación del

HQL Parser

org.hibernate.hql.ast.

ASTQueryTranslatorFactory

org.hibernate.hql.classic.

ClassicQueryTranslatorFactory

hibernate.query.substitutions Mapeo a consultas SQL

hibernate.hbm2ddl.auto Genera el esquema automáticamente al crear la Sesión

validate

update

create

create-drop

hibernate.cglib.use_reflection_optimizer Utilización de CGLIB

en lugar de reflexión

true

false

� Mapeos: ficheros de mapeo de clases (hbm.xml) que se utilizan en la aplicación.

3.3.1. MAPEOS

Los ficheros de mapeo que se utilizan en la aplicación se definen en hibernate.cfg, indicando la

ruta donde se encuentran los mismos. Cada uno de estos ficheros con extensión hbm.xml

mapean una clase de la aplicación con una de las tablas de la Base de datos especificada en la

conexión, y contienen los detalles de dicho mapeo.

Page 45: Manual hibernate v2

[45]

HIBERNATE

Los ficheros de mapeo normalmente incluyen el nombre de la tabla y clase relacionadas, y el

detalle del mapeo entre columnas de la tabla y propiedades de la clase, como puede

observarse en la ilustración anterior. Un fichero de mapeo puede contener varias clases

mapeadas, aunque habitualmente se realiza con una sola clase (cuyo nombre debería coincidir

con el nombre del fichero de mapeo).

Los elementos básicos de un fichero de mapeo aparecen a continuación. Se marcarán en

negrita aquellos elementos importantes para su utilización:

1. hibernate-mapping: datos generales del mapeo.

<hibernate-mapping

schema="schemaName" (a)

catalog="catalogName" (b)

default-cascade="cascade_style" (c)

default-access="field|property|ClassName" (d)

default-lazy="true|false" (e)

auto-import="true|false" (f)

package="package.name" (g)

/>

a. schema (opcional): nombre del esquema de bases de datos.

b. catalog (opcional): nombre del catálogo de la base de datos.

c. default-cascade (opcional – none por defecto): estilo de cascada por defecto.

Page 46: Manual hibernate v2

[46]

HIBERNATE

d. default-access (opcional – property por defecto): estrategia de acceso a las

propiedades.

e. default-lazy (opcional – true por defecto): valor por defecto del atributo lazy

de los mapeos de clases y colecciones.

f. auto-import (opcional – true por defecto): permite usar nombres de clases sin

calificar en el lenguaje SQL. Si hay dos clases con el mismo nombre sin calificar,

esta propiedad tiene que tener el valor false.

g. package (opcional): prefijo de paquete para los nombres de clases sin cualificar.

2. class: datos de la clase persistente y su relación con la tabla de la base de datos.

<class

name="ClassName" (a)

table="tableName" (b)

discriminator-value="discriminator_value" (c)

mutable="true|false" (d)

schema="owner" (e)

catalog="catalog" (f)

proxy="ProxyInterface" (g)

dynamic-update="true|false" (h)

dynamic-insert="true|false" (i)

select-before-update="true|false" (j)

polymorphism="implicit|explicit" (k)

where="arbitrary sql where condition" (l)

persister="PersisterClass" (m)

batch-size="N" (n)

optimistic-lock="none|version|dirty|all" (o)

lazy="true|false" (p)

entity-name="EntityName" (q)

check="arbitrary sql check condition" (r)

rowid="rowid" (s)

subselect="SQL expression" (t)

abstract="true|false" (u)

node="element-name"

/>

Page 47: Manual hibernate v2

[47]

HIBERNATE

a. name (opcional): nombre completo de la clase o interfaz Java. También se

pueden hacer persistentes clases estáticas internas.

b. table (opcional – por defecto el nombre de la clase sin cualificar): nombre de

la tabla de la base de datos.

c. discriminator-value (opcional – por defecto el nombre de la clase): distingue

subclases individuales, se usa para comportamiento polimórfico.

d. mutable (opcional – true por defecto): especifica si las instancias son mutables

o no. Las clases inmutables no se pueden modificar ni eliminar.

e. schema (opcional): sobreescribe el nombre de esquema especificado por

<hibernate-mapping>.

f. catalog (opcional): sobreescribe el nombre de catálogo especificado por

<hibernate-mapping>.

g. proxy (opcional): interfaz de proxy para la incialización perezosa.

h. dynamic-update (opcional – false por defecto): la sentencia SQL UPDATE se

genera en tiempo de ejecución y solamente contiene las columnas cuyos

valores hayan cambiado.

i. dynamic-insert (opcional – false por defecto): la sentencia SQL INSERT se

genera en tiempo de ejecución y solamente contiene las columnas cuyos

valores son distintos a null.

j. select-before-update (opcional – false por defecto): se produce una sentencia

SELECT antes de actualizar para comprobar si un objeto ha sido modificado. Si

no no se produce dicha actualización.

k. polymorphism (opcional – implicit por defecto): determina si se utiliza

polimorfismo implícito o explícito a la hora de recuperar objetos en las

consultas.

l. where (opcional): condición WHERE a utilizar cuando se recuperan objetos de

esta clase.

m. persister (opcional): especifica un ClassPersister propio, permitiendo la

redefinición de la estrategia de almacenamiento persistente.

n. batch-size (opcional – 1 por defecto): tamaño batch para la búsqueda de

instancias de esta clase por campo clave.

Page 48: Manual hibernate v2

[48]

HIBERNATE

o. optimistic-lock (opcional – version por defecto): determina la estrategia de

bloqueo optimista.

p. lazy (opcional): búsqueda perezosa.

q. entity-name (opcional – por defecto el nombre de la clase): Hibernate3

permite mapear una misma clase distintas veces (con distintas tablas), y este

nombre será el utilizado.

r. check (opcional): expression SQL para generar una comprobación en la

generación automática de esquemas.

s. rowid (opcional): utilización de ROWID.

t. subselect (opcional): mapea una entidad immutable de solo lectura con una

subconsulta de la base de datos, por ejemplo para usar vistas en lugar de

tablas.

u. abstract (opcional): marca superclases abstractas en jerarquías con <union-

subclass>.

3. id: dentro de la etiqueta class, se debe mapear la clave primaria de la tabla.

<id

name="propertyName" (a)

type="typename" (b)

column="column_name" (c)

unsaved-value="null|any|none|undefined|id_value" (d)

access="field|property|ClassName"> (e)

node="element-name|@attribute-name|element/@attribute|."

<generator class="generatorClass"/>

</id>

a. name (opcional): nombre de la propiedad.

b. type (opcional): indica el tipo.

c. column (opcional –nombre de la propiedad por defecto): nombre de la

columna.

Page 49: Manual hibernate v2

[49]

HIBERNATE

d. unsaved-value (opcional): valor del identificador que indica que la instancia es

nueva, para distinguir de instancias guardadas o cargadas en sesiones

anteriores. Casi nunca se utiliza en Hibernate 3.

e. access (opcional – property por defecto): estrategia de acceso al valor de la

propiedad.

4. generator: dentro del id, es necesario definir un generador de identificadores únicos

para la clase.

<generator class="org.hibernate.id.TableHiLoGenerator">

<param name="table">uid_table</param>

<param name="column">next_hi_value_column</param>

</generator>

a. class: nombre de la clase que implementa el interfaz

org.hibernate.id.IdentifierGenerator, a la cual se pueden pasar parámetros a

través de los elementos <param>. Asimismo, existe una serie de clases

predefinidas:

i. increment: identificadores únicos siempre que no existan otros

procesos insertando en la tabla.

ii. identity: columna de tipo identity (autonumérica) para aquellos

sistemas que la soportan.

iii. sequence: asignación basada en secuencia para aquellos sistemas que

la soportan.

iv. hilo: genera identificadores con un algoritmo hi/lo dado un nombre de

tabla y de columna.

v. seqhilo: genera identificadores con un algoritmo hi/lo dado un nombre

de secuencia.

vi. uuid: genera claves de tipo uuid de tamaño 32.

vii. guid: usa una cadena guid generada por la base de datos para aquellos

sistemas que la soportan.

Page 50: Manual hibernate v2

[50]

HIBERNATE

viii. native: selecciona identity, sequence o hilo dependiendo de la base de

datos en uso.

ix. assigned: asignado por la aplicación. Es la opción por defecto si no se

incluye generator.

x. select: asignación por trigger.

xi. foreign: usa el identificador de otro objeto asociado, normalmente a

través de un <one-to-one>.

xii. sequence-identity: usa una secuencia y el método getGeneratedKeys

de JDBC3 para la generación.

5. composite-id: utilizado en el caso que la clave primaria esté compuesta por más de

una columna.

<composite-id

name="propertyName" (a)

class="ClassName" (b)

mapped="true|false" (c)

access="field|property|ClassName"> (d)

node="element-name|."

<key-property name="propertyName" type="typename" column="column_name"/> (e)

<key-many-to-one name="propertyName class="ClassName" column="column_name"/> (f)

......

</composite-id>

a. name (opcional): nombre de la propiedad.

b. class (opcional): la forma de implementar una clave compuesta en Hibernate

es a través de una clase. Esta propiedad indica el nombre de dicha clase, que

contendrá atributos para cada una de las columnas que componen la clave.

c. mapped (opcional – false por defecto): utilización de un identificador

compuesto mapeado.

d. access (opcional – property por defecto): estrategia de acceso al valor de la

propiedad.

Page 51: Manual hibernate v2

[51]

HIBERNATE

e. key-property: dentro de la etiqueta composite-id, indica cada uno de los campos

que forman la clave y que no corresponden a claves foráneas de otras tablas:

i. name: nombre de la propiedad (de la clase que implementa el

identificador).

ii. type (opcional): tipo Hibernate de la propiedad.

iii. column (opcional – nombre de la propiedad por defecto): nombre de

la columna.

f. key-many-to-one: dentro de la etiqueta composite-id, indica cada uno de los

campos que forman la clave y que corresponden a claves foráneas de otras tablas:

i. name: nombre de la propiedad (de la clase que implementa el identificador).

ii. class: nombre de la clase relacionada a través de la clave foránea.

iii. column (opcional – nombre de la propiedad por defecto): nombre de

la columna.

6. property: declaración de propiedades de la clase.

<property

name="propertyName" (a)

column="column_name" (b)

type="typename" (c)

update="true|false" (d)

insert="true|false" (e)

formula="arbitrary SQL expression" (f)

access="field|property|ClassName" (g)

lazy="true|false" (h)

unique="true|false" (i)

not-null="true|false" (j)

optimistic-lock="true|false" (k)

generated="never|insert|always" (l)

node="element-name|@attribute-name|element/@attribute|."

index="index_name"

unique_key="unique_key_id"

length="L"

precision="P"

scale="S"

/>

Page 52: Manual hibernate v2

[52]

HIBERNATE

a. name: nombre de la propiedad (primera letra siempre en minuscule).

b. column (opcional – nombre de la propiedad por defecto): nombre de la

columna de base de datos mapeada. También puede ser especificado a través

de etiquetas <column>.

c. type (opcional): indica el tipo Hibernate, que puede tratarse de uno de los

siguientes, aunque en caso de no especificar Hibernate lo asignará

automáticamente en función del tipo de la columna de base de datos:

i. Tipo básico Hibernate (integer, string, character, date, …).

ii. Clase Java o tipo básico (int, float, java.util.Date, …).

iii. Clase Java serializable.

iv. Clase propia.

d. update (opcional – true por defecto): las columnas mapeadas deben incluirse

en las sentencias UPDATE.

e. insert (opcional – true por defecto): las columnas mapeadas deben incluirse en

las sentencias INSERT.

f. formula (opcional): expresión SQL que define el valor de una propiedad

calculada. Las propiedades calculadas no se mapean a ninguna columna.

g. access (opcional – property por defecto): estrategia utilizada para acceder a la

propiedad.

h. lazy (opcional – false por defecto): indica si esta propiedad debe ser buscada

de forma perezosa cuando la instancia se accede por vez primera.

i. unique (opcional): restricción unique para esta columna.

j. not-null (opcional): restricción not-null para esta columna.

k. optimistic-lock (opcional – true por defecto): las actualizaciones de esta

propiedad requieren la adquisición del bloqueo optimista.

l. generated (opcional – never por defecto): esta propiedad es generada por la

base de datos.

7. many-to-one

Page 53: Manual hibernate v2

[53]

HIBERNATE

<many-to-one

name="propertyName" (a)

column="column_name" (b)

class="ClassName" (c)

cascade="cascade_style" (d)

fetch="join|select" (e)

update="true|false" (f)

insert="true|false" (g)

property-ref="propertyNameFromAssociatedClass" (h)

access="field|property|ClassName" (i)

unique="true|false" (j)

not-null="true|false" (k)

optimistic-lock="true|false" (l)

lazy="proxy|no-proxy|false" (m)

not-found="ignore|exception" (n)

entity-name="EntityName" (o)

formula="arbitrary SQL expression" (p)

node="element-name|@attribute-name|element/@attribute|."

embed-xml="true|false"

index="index_name"

unique_key="unique_key_id"

foreign-key="foreign_key_name"

/>

a. name: nombre de la propiedad.

b. column (opcional): nombre de la columna de clave foránea. También puede

ser especificado a través de etiquetas <column>.

c. class (opcional): nombre de la clase asociada.

d. cascade (opcional): operaciones que deben ejecutarse en cascada desde el

padre al objeto asociado.

e. fetch (opcional – select por defecto): elige entre búsqueda mediante outer-

join o select secuencial.

f. update (opcional – true por defecto): las columnas mapeadas deben incluirse

en las sentencias UPDATE.

g. insert (opcional – true por defecto): las columnas mapeadas deben incluirse en

las sentencias INSERT.

Page 54: Manual hibernate v2

[54]

HIBERNATE

h. property-ref (opcional): nombre de la propiedad de la clase asociada relacionada

con esta foreign key. Si no se especifica se utiliza la clave de la clase asociada.

i. access (opcional – property por defecto): estrategia utilizada para acceder a la

propiedad.

j. unique (opcional): restricción unique para la clave foránea.

k. not-null (opcional): restricción not-null para la clave foránea.

l. optimistic-lock (opcional – true por defecto): las actualizaciones de esta

propiedad requieren la adquisición del bloqueo optimista.

m. lazy (optional – proxy por defecto): indica si esta propiedad debe ser buscada

de forma perezosa cuando la instancia se accede por vez primera.

n. not-found (opcional – exception por defecto): cómo se tratan las claves

foráneas que referencian a filas inexistentes.

o. entity-name (opcional): nombre de entidad de la clase asociada.

p. formula (opcional): expresión SQL que define el valor de una propiedad calculada.

8. one-to-one

<one-to-one

name="propertyName" (a)

class="ClassName" (b)

cascade="cascade_style" (c)

constrained="true|false" (d)

fetch="join|select" (e)

property-ref="propertyNameFromAssociatedClass" (f)

access="field|property|ClassName" (g)

formula="any SQL expression" (h)

lazy="proxy|no-proxy|false" (i)

entity-name="EntityName" (j)

node="element-name|@attribute-name|element/@attribute|."

embed-xml="true|false"

foreign-key="foreign_key_name"

/>

a. name: nombre de la propiedad.

b. class (opcional): nombre de la clase asociada.

Page 55: Manual hibernate v2

[55]

HIBERNATE

c. cascade (opcional): operaciones que deben ejecutarse en cascada desde el

padre al objeto asociado.

d. constrained (opcional): una clave foránea de la clave primaria de la tabla

mapeada referencia a la tabla de la clase asociada.

e. fetch (opcional – select por defecto): elige entre búsqueda mediante outer-

join o select secuencial.

f. property-ref (opcional): nombre de la propiedad de la clase asociada relacionada

con esta foreign key. Si no se especifica se utiliza la clave de la clase asociada.

g. access (opcional – property por defecto): estrategia utilizada para acceder a la

propiedad.

h. formula (opcional): expresión SQL que define el valor de una propiedad calculada.

i. lazy (optional – proxy por defecto): indica si esta propiedad debe ser buscada

de forma perezosa cuando la instancia se accede por vez primera.

j. entity-name (opcional): nombre de entidad de la clase asociada.

9. set: las relaciones inversas normalmente se modelan a través de un conjunto (set), pero

existen otros elementos que se pueden usar como list, map, bag, array o primitive-array.

Page 56: Manual hibernate v2

[56]

HIBERNATE

<set

name="propertyName" (a)

table="table_name" (b)

schema="schema_name" (c)

lazy="true|extra|false" (d)

inverse="true|false" (e)

cascade="all|none|save-update|delete|all-delete-orphan|delete-orphan" (f)

sort="unsorted|natural|comparatorClass" (g)

order-by="column_name asc|desc" (h)

where="arbitrary sql where condition" (i)

fetch="join|select|subselect" (j)

batch-size="N" (k)

access="field|property|ClassName" (l)

optimistic-lock="true|false" (m)

mutable="true|false" (n)

node="element-name|."

embed-xml="true|false" >

<key .... /> (o)

<map-key .... />

<element .... />

</map>

a. name: nombre de la propiedad.

b. table (opcional – nombre de la propiedad por defecto): nombre de la tabla de

la colección (no se usa para one-to-many).

c. schema (opcional): nombre del esquema para sobreescribir el esquema

definido en el elemento raíz.

d. lazy (opcional – true por defecto).

e. inverse (opcional – false por defecto): indica el lado inverso en una asociación

bidireccional.

f. cascade (opcional - none por defecto): permite operaciones en cascada sobre

los elementos hijos.

g. sort (opcional): especifica una colección ordenada mediante orden natural o

especificando una clase comparadora.

Page 57: Manual hibernate v2

[57]

HIBERNATE

h. order-by (opcional): solo en JDK 1.4, especifica un nombre de columna que

define el orden de iteración.

i. where (opcional): condición WHERE a utilizar cuando se recuperan o eliminan

objetos de la colección.

j. fetch (opcional – select por defecto): elige entre búsqueda mediante outer-

join o select secuencial.

k. batch-size (opcional – 1 por defecto): tamaño batch para la búsqueda de

instancias de esta clase por campo clave.

l. access (opcional – property por defecto): estrategia utilizada para acceder a la

propiedad.

m. optimistic-lock (opcional – true por defecto): las actualizaciones del estado de

la colección requieren la adquisición del bloqueo optimista.

n. mutable (opcional – true por defecto): los elementos de la colección nunca

cambian.

o. key: dentro de la etiqueta set indica la clave foránea de la colección:

<key

column="columnname" (i)

on-delete="noaction|cascade" (ii)

property-ref="propertyName" (iii)

not-null="true|false" (iv)

update="true|false" (v)

unique="true|false" (vi)

/>

i. column (opcional): nombre de la columna que actúa como clave foránea.

También puede especificarse a través de elementos <column>.

ii. on-delete (opcional – noaction por defecto): la clave foránea tiene

activado el borrado en cascada en la base de datos.

iii. property-ref (opcional): la clave foránea se refiere a columnas que no

son la clave primaria de la tabla original.

iv. not-null (opcional): las claves foráneas no se pueden establecer como

null.

Page 58: Manual hibernate v2

[58]

HIBERNATE

v. update (opcional): las claves foráneas no se pueden actualizar.

vi. unique (opcional): la clave foránea debería tener una restricción

unique.

p. one-to-many: indica

<one-to-many

class="ClassName" (i)

not-found="ignore|exception" (ii)

entity-name="EntityName" (iii)

node="element-name"

embed-xml="true|false"

/>

i. class: nombre de la clase asociada.

ii. not-found (opcional – exception por defecto): cómo se tratan las

claves foráneas que referencian a filas inexistentes.

iii. entity-name (opcional): nombre de entidad de la clase asociada.

3.4. EL INTERFAZ SESSION

El interfaz Session es la principal vía de interacción entre la aplicación Java e Hibernate. Representa

a la sesión actual, donde se producen transacciones para la interacción con la Base de datos,

efectuando operaciones de lectura, creación, modificación y eliminación de instancias de clases

mapeadas. Las instancias de tales clases se pueden encontrar en tres estados posibles:

� transient: no es persistente, no está asociada a ninguna sesión. Se pueden convertir en

persistent invocando a los métodos save, persist o saveOrUpdate. Se encuentra en la

aplicación pero no en la Base de datos.

� persistent: asociado a una única sesión. Se encuentra en la aplicación y en la Base de

datos.

Page 59: Manual hibernate v2

[59]

HIBERNATE

� detached: persistente previamente, no asociado con ninguna sesión. Se encuentra en

la Base de datos, pero no se ha recuperado desde la aplicación.

En la siguiente ilustración aparecen estos estados, y cuáles son los métodos que provocan la

transición de uno a otro.

Para poder operar con una sesión es necesario obtener una instancia de la misma y comenzar

una transacción. Cualquier operación sobre dicha instancia debe contenerse dentro de una

transacción.

Si se produce una excepción dentro de la transacción, ésta debe ser cancelada (rollback), si no

hay que confirmarla para hacer persistentes los cambios (commit):

Page 60: Manual hibernate v2

[60]

HIBERNATE

//Obtener la sesión actual

Session session = HibernateUtil.getSessionFactory().getCurrentSession();

try{

//Comenzar la transacción

session.beginTransaction();

//Operaciones con session

}catch (Exception ex){

//Deshacer transacción

session.getTransaction().rollback();

}finally{

//Confirmar transacción

session.getTransaction().commit();

}

Los métodos que se invocan sobre la instancia de Session producen operaciones sobre la Base

de datos a través de sentencias SQL, generadas en el dialecto concreto configurado en

hibernate.cfg. De esta forma, las siguientes sentencias se producen para los métodos

enumerados anteriormente:

� INSERT: save, persist, saveOrUpdate, replicate.

� UPDATE: update, merge, saveOrUpdate, replicate.

� DELETE: delete.

A continuación se exponen algunos métodos de la clase Session utilizados habitualmente. Para

mayor información se puede acceder a la API de Hibernate:

http://www.hibernate.org/hib_docs/v3/api/

Page 61: Manual hibernate v2

[61]

HIBERNATE

public Transaction beginTransaction()

Comienza una unidad de trabajo y devuelve la instancia de la transacción asociada. El objeto Transaction es quien tiene los métodos commit y rollback.

public Transaction getTransaction() Obtiene la instancia de transacción asociada con esta sesión. El objeto Transaction es quien tiene los métodos commit y rollback.

public Criteria createCriteria(Class pstClass) Crea una nueva instancia de Criteria.

public Query createQuery(String qryString) Crea una nueva instancia de Query para una cadena HQL.

public SQLQuery createSQLQuery(String qryString)

Crea una nueva instancia de SQLQuery para una cadena SQL.

public Object get(Class clazz, Serializable id)

Devuelve la instancia persistente de una clase, correspondiente al identificador id, o null si no existe un elemento con dicho identificador.

public Object load(Class theClass, Serializable id)

Devuelve la instancia persistente de la clase dada, correspondiente al identificador id, asumiendo que la instancia existe. Este método no debe utilizarse para determinar si existe una instancia (utilizar get en su lugar).

public Serializable save(Object object)

Convierte en persistente la instancia actual, asignando en primer lugar un identificador generado (o utilizando el id actual si se ha utilizado un generador asigned).

public void saveOrUpdate(Object object) Guarda (save) o actualiza (update) una instancia, dependiendo de si ésta era persistente anteriormente o no.

public void update(Object object) Actualiza una instancia persistente con el identificador de la instancia actual.

public Object merge(Object object)

Copia el estado de una instancia en el objeto persistente con el mismo identificador. Si no existe una instancia persistente en la sesión actual la carga. En otro caso, si la instancia no se había guardado, guarda una copia y la devuelve como una nueva instancia persistente.

public void persist(Object object) Convierte una instancia transient en persistente.

Page 62: Manual hibernate v2

[62]

HIBERNATE

public void delete(Object object) Elimina una instancia persistente del almacén de datos.

public void replicate(Object o,ReplicationMode rm)

Hace persistente el estado de una instancia, reutilizando su identificador.

public void refresh(Object object)

Vuelve a leer el estado de una instancia de la base de datos. Útil cuando se ejecutan triggers, se inserta un campo binario, o las sesiones son muy largas.

public Serializable getIdentifier(Object object) Devuelve el identificador asociado a una instancia.

public boolean contains(Object object) Comprueba si una instancia está asociada con esta sesión.

public void evict(Object object) Elimina una instancia de la sesión, con lo cual las actualizaciones no tendrán reflejo en la Base de datos.

4. ASOCIACIONES

Las asociaciones en Hibernate representan las relaciones entre tablas. En un modelo ORM, las

claves foráneas habitualmente no se modelan con el campo identificador únicamente, sino con

instancias completas de objetos para poder aprovechar la potencia del Framework.

Page 63: Manual hibernate v2

[63]

HIBERNATE

En la imagen anterior se puede observar la relación que existe entre la tabla Person (Persona) y

Address (Dirección). Se trata de una relación uno a muchos donde el campo AddressId actúa como

una clave foránea de la tabla Address (maestro) en la tabla Person (detalle) lo cual modela que en

una Dirección pueden habitar muchas Personas. Este tipo de relación es muy habitual en Bases de

datos y como se ha comentado, se suele denominar uno a muchos (leyendo en la dirección

Address � Person). En Hibernate suele leerse de forma inversa, muchos a uno (many-to-one), pero

sigue tratándose de la misma relación. Esta relación a nivel de clases persistentes se modela de

forma sencilla a través de un atributo address en la clase Person, de forma que si se dispone de una

instancia de Person se pueden conocer todos los datos de la instancia Address donde reside esa

persona, sin necesidad de realizar ningún tipo de consulta adicional a la Base de datos. En una Base

de datos irremediablemente habría que realizar una consulta cruzada para recuperar tales datos.

El otro atributo fruto de esta relación que aparece a nivel de clases es el conjunto people. Esto

Hibernate se denomina la relación inversa, ya que las relaciones se pueden modelar en ambos

sentidos. Este sentido de la relación es el que habitualmente se utiliza al realizar una consulta

cruzada, es decir, cuando interesa conocer quiénes son los habitantes de una determinada

dirección. De nuevo Hibernate proporciona esta información de forma sencilla sin necesidad

de implementar consultas, ya que el atributo de tipo Set de la clase Address contendrá todas

las instancias de la clase Person para aquellas personas que cumplan con la relación.

Para poder aprovechar estas funcionalidades es necesario tener en cuenta otros factores como

la configuración de los ficheros de mapeo, o los métodos a utilizar para recuperar los valores

de la Base de datos. Ambos conceptos se explican ampliamente en este capítulo y el siguiente.

4.1. RELACIONES EN BASES DE DATOS

En general, en una base de datos existen tres tipos de relación:

Page 64: Manual hibernate v2

[64]

HIBERNATE

Las relaciones que existen entre las tablas dan lugar a modificaciones en las mismas a la hora

de normalizar el modelo, de forma que no existan campos duplicados ni pérdidas de integridad

por un mal diseño.

De las imágenes anteriores cabe destacar un aspecto importante, todas los tipos de relaciones

que aparecían en primera instancia, se pueden reducir a un único tipo, la relación Muchos a

uno, ya que:

� Las relaciones Uno a Uno se eliminan

� Las relaciones Muchos a Muchos se transforman en relaciones Muchos a Uno sobre la

tabla intermedia.

Este hecho va a simplificar considerablemente la gestión de asociaciones en Hibernate, ya que

solamente hará falta controlar un tipo de relación para poder construir modelos ORM que

representen modelos Entidad-Relación de cierta complejidad.

Los tipos de asociaciones en Hibernate que servirán para modelar las relaciones citadas es la

siguiente:

� Unidireccionales: solamente se representa un extremo de la relación.

� Unidireccionales con tabla de unión.

� Bidireccionales: se representan ambos extremos.

� Bidireccionales con tabla de unión.

Page 65: Manual hibernate v2

[65]

HIBERNATE

Teniendo en cuenta esos tipos de relaciones y los dos sentidos de la relación aparecen en

Hibernate 12 tipos distintos de relación que se pueden modelar de forma directa. Todas estas

relaciones se pueden reducir básicamente a tres tipos que serán explicados a continuación.

Como se ha comentado, lo más sencillo para el desarrollo puede resultar en el diseño

adecuado del modelo Entidad-Relación de la Base de datos, y simplemente en utilizar mapeos

many-to-one para las relaciones entre las tablas.

Para mayor información se puede consultar la documentación de Hibernate relativa a Asociaciones:

http://www.hibernate.org/hib_docs/v3/reference/en/html/associations.html

4.2. MANY-TO-ONE

La primera de las relaciones que se explicarán es la relación Many-to-one. Para poder modelar

un sistema de Bases de datos puede resultar la única relación imprescindible ya que como se

ha explicado, el resto de ellas se pueden transformar.

Esta relación normalmente se denomina Maestro-Detalle, donde existe un extremo Maestro

que dispone de las claves principales, y un Detalle donde esta clave aparece como foránea.

A nivel de Hibernate, la relación bidireccional se modela de la siguiente forma:

� Many-to-one en el Detalle.

� One-to-Many en el Maestro, incorporando el atributo inverse. No es obligatorio

modelar este sentido de la relación.

En el siguiente ejemplo aparecen los mapeos que se producirán en las clases para modelar

esta relación. En el apartado 3.3.1 se puede consultar el significado completo de los atributos

que aquí aparecen.

Page 66: Manual hibernate v2

[66]

HIBERNATE

<class name="Person">

<id name="id" column="personId">

<generator class="native"/>

</id>

<many-to-one name="address" column="addressId"/>

</class>

En la clase Person se mapea el sentido many-to-one que es el más sencillo:

� A la clase se agrega un atributo Address address.

� En el fichero de mapeo se agrega una etiqueta many-to-one:

� name: nombre del atributo.

� column: nombre de la columna de la tabla de base de datos que se

corresponde con el atributo (la clave foránea).

<class name="Address">

<id name="id" column="addressId">

<generator class="native"/>

</id>

<set name="people" inverse="true">

<key column="addressId"/>

<one-to-many class="Person"/>

</set>

</class>

En la clase Address se mapea el sentido one-to-many, el sentido inverso:

� A la clase se agrega un atributo Set people.

� En el fichero de mapeo se agrega una etiqueta set:

� name: nombre del atributo.

� inverse (true): la relación es inversa.

� key:

� column: nombre de la columna de la tabla de base de datos que actúa

como clave foránea en la otra tabla.

� one-to-many

� class: nombre de la clase que modela la otra tabla en la relación.

Page 67: Manual hibernate v2

[67]

HIBERNATE

4.3. MANY-TO-MANY

Como se ha comentado, esta relación se puede reducir a dos relaciones Many-to-one sobre la

tabla intermedia de la Base de datos.

En Hibernate se pueden modelar relaciones Many-to-Many con tablas intermedias de forma

directa en lugar de recurrir a dos relaciones Many-to-One de la siguiente forma:

� Many-to-many en cada tabla.

� No es necesario crear la clase persistente Person_Address.

<class name="Person">

<id name="id" column="personId">

<generator class="native"/>

</id>

<set name="addresses" table="PersonAddress">

<key column="personId"/>

<many-to-many column="addressId" class="Address"/>

</set>

</class>

Page 68: Manual hibernate v2

[68]

HIBERNATE

En la clase Person se mapea el sentido directo:

� A la clase se agrega un atributo Set addresses.

� En el fichero de mapeo se agrega una etiqueta set:

� name: nombre del atributo.

� table: nombre de la tabla intermedia de la base de datos.

� key:

� column: nombre de la columna de la tabla que modela esta clase que

actúa como clave foránea en la otra tabla.

� many-to-many

� class: nombre de la clase que modela la otra tabla en la relación.

� column: nombre de la columna de la otra tabla en la relación que

actúa como la otra clave foránea en la tabla intermedia.

<class name="Address">

<id name="id" column="addressId">

<generator class="native"/>

</id>

<set name="people" inverse="true" table="PersonAddress">

<key column="addressId"/>

<many-to-many column="personId" class="Person"/>

</set>

</class>

En la clase Address se mapea el sentido inverso:

� A la clase se agrega un atributo Set people.

� En el fichero de mapeo se agrega una etiqueta set:

� name: nombre del atributo.

� inverse (true): la relación es inversa.

� table: nombre de la tabla intermedia de la base de datos.

� key:

� column: nombre de la columna de la tabla que modela esta clase que

actúa como clave foránea en la otra tabla.

Page 69: Manual hibernate v2

[69]

HIBERNATE

� many-to-many

� class: nombre de la clase que modela la otra tabla en la relación.

� column: nombre de la columna de la otra tabla en la relación que

actúa como la otra clave foránea en la tabla intermedia.

4.4. ONE-TO-ONE

Como se ha comentado, esta relación se puede eliminar de la Base de datos pasando los datos

de una de las tablas a la otra.

En Hibernate se pueden modelar relaciones One-to-One de forma directa de la siguiente

forma:

� Many-to-one en un sentido, incluyendo el atribute unique.

� One-to-one en el sentido inverso.

� Se mantienen ambos mapeos.

Page 70: Manual hibernate v2

[70]

HIBERNATE

<class name="Person">

<id name="id" column="personId">

<generator class="native"/>

</id>

<many-to-one name="address" column="addressId" unique="true" not-null="true"/>

</class>

En la clase Person se mapea el sentido directo:

� A la clase se agrega un atributo Address address.

� En el fichero de mapeo se agrega una etiqueta many-to-one:

� name: nombre del atributo.

� column: nombre de la columna de la tabla de base de datos que se

corresponde con el atributo (la clave foránea).

� unique (true): restricción de unicidad para cumplir la relación.

<class name="Address">

<id name="id" column="addressId">

<generator class="native"/>

</id>

<one-to-one name="person" property-ref="address"/>

</class>

En la clase Address se mapea el sentido inverso:

� A la clase se agrega un atributo Person person.

� En el fichero de mapeo se agrega una etiqueta one-to-one:

� name: nombre del atributo.

� property-ref: nombre de la propiedad que se corresponde con esta clase en el

otro extremo.

Page 71: Manual hibernate v2

[71]

HIBERNATE

5. RECUPERACIÓN DE DATOS

En las bases de datos relacionales, las operaciones más frecuentes suelen ser las

recuperaciones de datos de una o más tablas o vistas. Para poder realizar esta operación, en

dichas bases de datos se recurre al lenguaje SQL, y más concretamente a la sentencia SELECT.

De esta forma, especificando una sentencia más o menos compleja es posible obtener la

información solicitada

Como se vio en el apartado anterior, la relación más habitual entre dos tablas es la relación

Maestro-Detalle o many-to-one. Sobre esta relación se pueden efectuar 2 tipos de consultas

cruzadas bastante habituales:

Obtener todos los datos de las personas y de la de la dirección donde viven:

SELECT PERSON.NAME, PERSON.AGE, ADDRESS.STREET, ADDRESS.NUMBER

FROM PERSON, ADDRESS

WHERE PERSON.ADDRESSID = ADDRESS.ADDRESSID

Obtener los nombres de todas las personas que viven en una dirección determinada:

SELECT PERSON.NAME

FROM PERSON, ADDRESS

WHERE PERSON.ADDRESSID = ADDRESS.ADDRESSID

AND ADDRESSID = 1

Page 72: Manual hibernate v2

[72]

HIBERNATE

En Hibernate se podría realizar de la misma forma (SQLQuery) pero lo más habitual no es esto,

sino aprovechar las características del modelo ORM para acceder a esta información de forma

más eficiente. Para ello existen dos alternativas:

� Usar una consulta HQL para ejecutar la consulta, de forma que usando un lenguaje

orientado a objetos pero muy similar a SQL.

� Usar un Criteria, de forma que a través de programación se puedan utilizar diversos

métodos para recuperar los datos deseados de forma similar a como lo hace HQL.

Como se ha venido comentando, la relación entre las dos tablas anteriores se modela a nivel

de clases con una asociación many-to-one, y los siguientes atributos relacionados:

� Address address en la clase Person, que permitirá acceder a todos los datos de la

dirección de una Persona.

� Set people en la clase Address, que permitirá conocer todas las personas que habitan

una misma dirección.

Lo que se modela en las clases es exactamente lo que se estaba accediendo a través de

sentencias SELECT anteriores. Por tanto casi todo el trabajo ya está hecho, solamente hace

falta acceder a los datos:

List list = (List) session.createQuery("from Person").list();

List list = (List) session.createCriteria(Person.class).list();

En ambos casos se obtiene una lista con una instancia de la clase Person por cada una de las

personas de la tabla Person, en cuyo atributo address se puede consultar toda la información

necesaria sobre la dirección. Sobre esta capacidad es necesario destacar que no siempre

Hibernate recupera todos los datos relacionados ya que suele comportarse en modo “lazy”, es

decir, solamente recupera los datos principales de la clase, mientras que los atributos de

asociaciones (como address) quedan sin inicializar por motivos de eficiencia. Para forzar que

estos atributos siempre se carguen se utilizan los fetch o alias a la hora de recuperar los datos,

los cuales se explican con mayor detalle en los apartados posteriores.

Page 73: Manual hibernate v2

[73]

HIBERNATE

Set set = ((Address)session.createQuery("from Address where id=1").list()).getPeople();

Set set = ((Address)session.createCriteria(Address.class).

add(Restrictions.eq("id", new Integer(1)))

.list()).getPeople();

5.1. HQL

Hibernate usa un potente lenguaje de consulta (HQL), que es similar en apariencia a SQL. En

comparación con SQL, sin embargo, HQL es completamente orientado a objetos y es capaz de

gestionar conceptos como herencia, polimorfismo y asociaciones.

La información completa se puede encontrar en:

http://www.hibernate.org/hib_docs/v3/reference/en/html/queryhql.html

5.1.1. SENSIBILIDAD A MAYÚSCULAS

Exceptuando los nombres de clases y atributos, el lenguaje HQL es insensible a mayúsculas

(por ejemplo se admite SELECT, select o SEleCT).

5.1.2. DATOS DE LA CONSULTA

La consulta HQL más simple es la siguiente:

from Cat

Devuelve todas las instancias de la clase Cat. Normalmente NO es necesario utilizar el nombre

de paquete (from eg.Cat) ya que la opción auto-import es true por defecto.

A la hora de referirse a Cat desde otras partes de la consulta, normalmente se utiliza un alias a

continuación de la clase. La especificación del alias puede usar la palabra clave as (as cat) o no:

from Cat cat

Page 74: Manual hibernate v2

[74]

HIBERNATE

5.1.3. JOINS

Los joins son la forma de realizar cruces entre tablas en Bases de datos. Habitualmente no se

utilizan los joins porque la forma de escribir las consultas resulta más amigable utilizando

condiciones where, de forma que es el gestor de la Base de datos quien se encarga de realizar

la transformación necesaria para utilizar este tipo de unión. Las consultas siguientes son

equivalentes en cuanto al resultado obtenido:

SELECT e.nombre, d.nombre FROM empleados e, departamentos d

WHERE d.numero = e.dnumero

SELECT e.nombre, d.nombre FROM empleados e

INNER JOIN departamentos d ON d.numero = e.dnumero

Existen varios tipos de JOIN posibles entre 2 tablas de la Base de datos:

� CROSS JOIN: unión de todos los datos de dos tablas sin especificar ningún criterio

(producto cartesiano). Este tipo de JOIN es muy ineficiente.

10 Javier 2B

Manuel 1B

12 Antonio 1A

GARAGE NOMBRE PISO

PROPIETARIOS

22 12

22 11

20 10

TAMAÑO NUMERO

GARAJES

20 10 Manuel 1B

22 11 Manuel 1B

20 10 12 Antonio 1A

22 11 12 Antonio 1A

22 12 12 Antonio 1A

… … … …

Page 75: Manual hibernate v2

[75]

HIBERNATE

En Hibernate se especifica de forma similar a SQL:

from Formula, Parameter

� INNER JOIN o JOIN: unión de los datos de dos tablas solamente en aquellos casos

donde existan datos en el campo de combinación. Este tipo de JOIN es el que resulta

equivalente a realizar la combinación a través de un WHERE.

En Hibernate también se especifica de forma similar a SQL, solamente teniendo en cuenta que

no se hacen uniones entre tablas propiamente dichas, sino con los atributos sobre los que se

han montado asociaciones. Se puede usar con las palabras clave inner join o inner outer join, o

incluso simplemente join:

from Cat as cat

inner join cat.mate as mate

10 Javier 2B

Manuel 1B

12 Antonio 1A

GARAGE NOMBRE PISO

PROPIETARIOS

22 12

22 11

20 10

TAMAÑO NUMERO

GARAJES

20 10 Javier 2B

22 12 Antonio 1A

TAMAÑO NUMERO NOMBRE PISO

Page 76: Manual hibernate v2

[76]

HIBERNATE

� LEFT OUTER JOIN o LEFT JOIN: se seleccionan todos los datos de la primera tabla de la

combinación, uniendo los datos de la segunda tabla solamente en los casos que sea

posible, y poniendo null en los casos que no sea posible.

De nuevo se pueden especificar con HQL sobre los atributos sobre los que se han

montado asociaciones uniones de este tipo a través de left join o left outer join:

from Cat as cat

inner join cat.mate as mate

left outer join cat.kittens as kitten

� RIGHT OUTER JOIN o RIGHT JOIN: se seleccionan todos los datos de la segunda tabla

de la combinación, uniendo los datos de la primera tabla solamente en los casos que

sea posible, y poniendo null en los casos que no sea posible.

10 Javier 2B

Manuel 1B

12 Antonio 1A

GARAGE NOMBRE PISO

PROPIETARIOS

22 12

22 11

20 10

TAMAÑO NUMERO

GARAJES

null null Manuel 1B

20 10 Javier 2B

22 12 Antonio 1A

TAMAÑO NUMERO NOMBRE PISO

Page 77: Manual hibernate v2

[77]

HIBERNATE

Se puede especificar con HQL con right join o right outer join.

� FULL OUTER JOIN o FULL JOIN: es una combinación de los dos anteriores.

10 Javier 2B

Manuel 1B

12 Antonio 1A

GARAGE NOMBRE PISO

PROPIETARIOS

22 12

22 11

20 10

TAMAÑO NUMERO

GARAJES

22 11 null null

null null Manuel 1B

20 10 Javier 2B

22 12 Antonio 1A

TAMAÑO NUMERO NOMBRE PISO

10 Javier 2B

Manuel 1B

12 Antonio 1A

GARAGE NOMBRE PISO

PROPIETARIOS

22 12

22 11

20 10

TAMAÑO NUMERO

GARAJES

22 11 null null

20 10 Javier 2B

22 12 Antonio 1A

TAMAÑO NUMERO NOMBRE PISO

Page 78: Manual hibernate v2

[78]

HIBERNATE

from Formula form

full join form.parameter param

En general, sobre cualquier tipo de join se pueden establecer condiciones:

from Cat as cat

left join cat.kittens as kitten

with kitten.bodyWeight > 10.0

Por último, es muy importante la utilización de la palabra clave fetch. Como se ha comentado,

normalmente las colecciones o atributos que se refieren a asociaciones mapeadas no se

inicializan debido al comportamiento “lazy” por defecto. Para asegurar la recuperación de los

datos en dichas propiedades, es necesario utilizar fetch en conjunción con los joins:

from Cat as cat

inner join fetch cat.mate

left join fetch cat.kittens child

left join fetch child.kittens

Para realizar un fetch sobre todas las propiedades susceptibles de búsqueda, se puede utilizar

una sentencia de este tipo:

from Document

fetch all properties

order by name

También es posible la utilización de un join implícito, es decir, no aparece la cláusula join en la

consulta pero para Hibernate es necesario realizarla para resolver las condiciones impuestas.

En este ejemplo no sería necesario realizar un join cat.mate si directamente se incluye como

condición en la condición where, aunque se recomienda su inclusión por claridad a la hora de

leer la consulta:

from Cat as cat

where cat.mate.name like '%s%'

Page 79: Manual hibernate v2

[79]

HIBERNATE

5.1.4. SELECT

La cláusula Select permite obtener propiedades individuales en lugar de recuperar instancias

completas. Estas propiedades pueden referirse a atributos simples o a atributos mapeados. En

el siguiente ejemplo se accede a la propiedad name (String) y a la propiedad mate (Cat),

destacando que en este último caso se hace un join implícito (si no sería necesario incluir un

join cat.mate).

select cat.name, cat.mate as cmate

from Cat cat

Dentro de las cláusulas Select se puede hacer referencia al identificador de la clase de dos

formas distintas:

� Utilizando la propiedad id ya que este atributo se encuentra mapeado siempre con ese

identificador (cat.id).

� Utilizando el nombre de la propiedad si ésta tiene un nombre distinto (cat.catId).

Cuando se hace una consulta HQL sin Select, a nivel de programación se recuperan las

instancias concretas de la clase implicada en dicha consulta. En el siguiente fragmento de

código se recuperan una colección de tipo List, donde cada elemento es una instancia de la

clase Cat:

List list = session.createQuery("from Cat").list();

Cat cat = (Cat) list.get(0);

Cuando se utiliza la cláusula Select, o si se realiza un cross join de clases (from Formula,

Parameter), resulta imposible para Hibernate determinar qué clase ha de emplear en la

recuperación de los datos, por lo que éstos aparecen en forma de Object[]. Por tanto el

conjunto de datos resultado sería una colección List de Object[]:

List list = session.createQuery("select cat.name, cat.mate from Cat cat").list();

Object[] res = (Object[]) list.get(0);

Page 80: Manual hibernate v2

[80]

HIBERNATE

En el Object[] obtenido como resultado, cada una de las posiciones es un objeto de la clase

concreta a la que pertenece el atributo, ya sea porque se ha mapeado de forma explícita en el

fichero hbm.xml (atributo type de la etiqueta property), o porque Hibernate lo ha resuelto de

forma automática:

String name = (String) res[0];

Cat mate = (Cat) res[1];

En lugar de obtener el resultado en un Object[], se puede forzar en la consulta a obtener una

colección de tipo List:

select new list(mother, offspr, mate.name)

from DomesticCat as mother

inner join mother.mate as mate

left outer join mother.kittens as offspr

También se puede obtener una colección Map, donde los alias actúan como claves y los

valores son los recuperados en la consulta:

select new map( max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n )

from Cat cat

Por último, se puede forzar a devolver el resultado en una clase concreta, cuyo constructor

debe responder a la estructura utilizada en la consulta:

select new Family(mother, mate, offspr)

from DomesticCat as mother

join mother.mate as mate

left join mother.kittens as offspr

Page 81: Manual hibernate v2

[81]

HIBERNATE

5.1.5. OPERACIONES Y FUNCIONES AGREGADAS

En HQL se pueden utilizar las operaciones y expresiones habituales en SQL:

� Operadores matemáticos: +, -, *, /

� Operadores de comparación: =, >=, <=, <>, !=, like

� Operadores lógicos: and, or, not, in, is null, …

� Funciones: concat() (o ||), current_timestamp(), day(), month(), …

� …

Asimismo, es posible utilizar funciones agregadas habituales:

� avg(), sum(), min(), max(), count()

5.1.6. CONDICIONES

La cláusula Where se utiliza para la especificación de condiciones en las consultas. Puede

incorporar expresiones para determinar la condición, de forma similar a como se realiza en

SQL, teniendo en cuenta las características orientadas a objetos de HQL:

from Foo foo

where foo.bar.baz.customer.address.city is not null

Es posible comparar instancias completas, así como acceder a la propiedad class:

from Cat cat, Cat rival

where cat.mate = rival.mate

and cat.class = DomesticCat

Page 82: Manual hibernate v2

[82]

HIBERNATE

5.1.7. ORDEN

La cláusula utilizada para ordenar el conjunto de resultados es Order By:

from DomesticCat cat

order by cat.name asc, cat.weight desc, cat.birthdate

5.1.8. AGRUPACIÓN

La agrupación del conjunto de resultados normalmente es utilizada para la aplicación de

funciones de agregado sobre los grupos. La utilización en HQL no difiere de la habitual en SQL:

select cat.color, sum(cat.weight), count(cat)

from Cat cat

group by cat.color

having cat.color in (eg.Color.TABBY, eg.Color.BLACK)

5.1.9. CONSULTAS ANIDADAS

HQL también permite la utilización de consultas anidadas tanto en la cláusula Select como en

las condiciones Where:

select cat.id, (select max(kit.weight) from cat.kitten kit) as kitweight

from Cat as fatcat

where fatcat.weight > (

select avg(cat.weight) from DomesticCat cat

)

Page 83: Manual hibernate v2

[83]

HIBERNATE

5.2. CRITERIA

Hibernate proporciona una API intuitiva y extensible para la construcción de consultas, la cual

evita al desarrollador la necesidad de conocer HQL ni SQL para la recuperación de datos.

La información completa se puede encontrar en la ayuda de la API de la clase Criteria, o en el

siguiente enlace:

http://www.hibernate.org/hib_docs/v3/reference/en/html/querycriteria.html

5.2.1. DATOS DE LA CONSULTA

La forma más simple para construir un Criteria es la siguiente:

Criteria crit = sess.createCriteria(Cat.class);

El método createCriteria devuelve un objeto perteneciente al interfaz Criteria. La mayoría de

los métodos que se invocan sobre un Criteria devuelven otro objeto de tipo Criteria, de forma

que las llamadas se van enlazando para construir una consulta completa. A lo largo de este

capítulo se explican los distintos métodos disponibles.

List cats = session.createCriteria(Cat.class)

.setProjection( Projections.projectionList()

.add( Projections.rowCount() )

.add( Projections.avg("weight") )

.add( Projections.max("weight") )

.add( Projections.min("weight") )

.add( Projections.groupProperty("color") )

)

.addOrder( Order.asc("color") )

.list();

Page 84: Manual hibernate v2

[84]

HIBERNATE

Hay dos formas básicas de recuperar los datos de un Criteria:

� Invocando al método list, que devolverá una lista con las instancias concretas

obtenidas por la consulta.

List cats = session.createCriteria(Cat.class)

.list();

Cat cat = (Cat) cats.get(0);

� Invocando al método uniqueResult, que devolverá un único resultado como

consecuencia de la ejecución de la consulta. Este método resulta en excepción si el

resultado no es un único registro.

Cat cat = (Cat) session.createCriteria(Cat.class)

.add(Restrictions.eq("id", new Integer(1)))

.uniqueResult();

5.2.2. JOINS

En un Criteria, la única posibilidad de join que existe es utilizar un fetch:

List cats = sess.createCriteria(Cat.class)

.add( Restrictions.like("name", "Fritz%") )

.setFetchMode("mate", FetchMode.JOIN)

.setFetchMode("kittens", FetchMode.JOIN)

.list();

Page 85: Manual hibernate v2

[85]

HIBERNATE

El valor FetchMode.JOIN indica que debe realizarse un inner join a la hora de realizar el fetch.

En el caso que se quiera realizar otro tipo de join se utilizará el método createAlias indicando

que se trata de un LEFT_JOIN, RIGHT_JOIN o FULL_JOIN:

List cats = sess.createCriteria(Cat.class)

.createAlias("kittens", "kt", CriteriaSpecification.LEFT_JOIN)

.add( Restrictions.eqProperty("kt.name", "miao") )

.list();

En el siguiente ejemplo aparece la creación de un alias normal, es simplemente equivalente a

la creación de un alias en HQL (as kt):

List cats = sess.createCriteria(Cat.class)

.createAlias("kittens", "kt")

.add( Restrictions.eqProperty("kt.name", "miao") )

.list();

5.2.3. SELECT

La cláusula Select de SQL o HQL permite realizar la operación denominada Proyección en

álgebra relacional. El método setProjection se encarga de indicar que se van a seleccionar

propiedades concretas del conjunto de resultados:

List results = session.createCriteria(Domestic.class, "cat")

.createAlias("kittens", "kit")

.setProjection( Projections.projectionList()

.add( Property.forName("cat.name") )

.add( Projections.property("kit.name"), "kitName" )

)

.list();

Page 86: Manual hibernate v2

[86]

HIBERNATE

El método setProjection recibe como parámetro un ProjectionList, que se irá construyendo a

través de métodos add. El método add recibe la propiedad concreta que se va a proyectar, y el

segundo parámetro en caso de existir se trata del alias de dicha propiedad.

Para recuperar una propiedad a incluir en la lista de proyecciones se puede utilizar

indistintamente Property.forName() o Projections.property().

Al igual que ocurre con HQL cuando se utiliza setProjection resulta imposible para Hibernate

determinar qué clase ha de emplear en la recuperación de los datos, por lo que éstos aparecen

en forma de Object[].

5.2.4. CONDICIONES

Para establecer condiciones en un Criteria se usa la clase Restrictions. A través del método add

del objeto Criteria se van añadiendo las distintas restricciones, de forma que se van uniendo a

través de una conjunción, es decir, a través de and. En la siguiente consulta, la condición

resultante será name like "Fritz%" and weight >= 10.

List cats = sess.createCriteria(Cat.class)

.add( Restrictions.like("name", "Fritz%") )

.add( Restrictions.ge("weight", new Integer(10)) )

.list();

Existen restricciones para la mayoría de los operadores habituales en consultas SQL. Para unir

restricciones a través de un or se puede usar el método or() para 2 restricciones, o

disjunction() para un número mayor de restricciones:

List cats = sess.createCriteria(Cat.class)

.add( Restrictions.like("name", "Fritz%") )

.add( Restrictions.or(

Restrictions.eq( "age", new Integer(0) ),

Restrictions.isNull("age")

) )

.list();

Page 87: Manual hibernate v2

[87]

HIBERNATE

List cats = sess.createCriteria(Cat.class)

.add( Restrictions.in( "name", new String[] { "Fritz", "Izi", "Pk" } ) )

.add( Restrictions.disjunction()

.add( Restrictions.isNull("age") )

.add( Restrictions.eq("age", new Integer(0) ) )

.add( Restrictions.eq("age", new Integer(1) ) )

.add( Restrictions.eq("age", new Integer(2) ) )

) )

.list();

También se pueden obtener restricciones a través de las propiedades directamente:

Property age = Property.forName("age");

List cats = sess.createCriteria(Cat.class)

.add( Restrictions.disjunction()

.add( age.isNull() )

.add( age.eq( new Integer(0) ) )

.add( age.eq( new Integer(1) ) )

.add( age.eq( new Integer(2) ) )

) )

.add( Property.forName("name").in( new String[] { "Fritz", "Izi", "Pk" } ) )

.list();

Por último, se pueden establecer restricciones SQL directamente, indicando la condición SQL,

los valores a aplicar y los tipos de dichos valores:

List cats = sess.createCriteria(Cat.class)

.add( Restrictions.sqlRestriction("lower({alias}.name) like lower(?)",

"Fritz%",

Hibernate.STRING) )

.list();

Page 88: Manual hibernate v2

[88]

HIBERNATE

5.2.5. ORDEN

El método utilizado para ordenar el conjunto de resultados es addOrder. Este método recibe

una instancia de la clase Order que se puede obtener invocando al método asc() (orden

ascendente) o desc (orden descendente) sobre la propia clase Order, o sobre una propiedad

directamente:

List cats = sess.createCriteria(Cat.class)

.add( Restrictions.like("name", "F%")

.addOrder( Order.asc("name") )

.addOrder( Property.forName("age").desc() )

.list();

5.2.6. AGRUPACIÓN

La agrupación del conjunto de resultados y las funciones de agregado también están

permitidas sobre un Criteria. Las funciones agregadas se añaden a la lista de proyecciones del

criteria de la misma forma que las propiedades:

List results = session.createCriteria(Cat.class)

.setProjection( Projections.projectionList()

.add( Projections.rowCount() )

.add( Projections.avg("weight") )

.add( Projections.max("weight") )

.add( Projections.groupProperty("color") )

)

.list();

Page 89: Manual hibernate v2

[89]

HIBERNATE

5.2.7. LIMITAR EL CONJUNTO DE SALIDA

Para limitar el número de elementos devueltos por un criteria se utiliza el método limit(),

conjugado con el método setFirstResult() si se quiere empezar en un registro distinto del

primero (0). El siguiente Criteria obtiene una lista con 5 instancias de la clase Cat, comenzando

en el registro con índice 2 del conjunto de resultados.

List list = sess.createCriteria(Cat.class).

setMaxResults(5).

setFirstResult(2).

list();

5.3. SQL

También se permite la utilización de consultas en SQL nativo desde Hibernate, lo cual resulta

bastante útil en algunos casos como la utilización de sentencias DDL o ejecución de

procedimientos.

La principal desventaja de este método es que al utilizar el dialecto SQL de una base de datos

concreta, la portabilidad de la aplicación entre distintos tipos de Bases de datos se ve

comprometida.

La información completa se puede encontrar en la página correspondiente del portal de

Hibernate:

http://www.hibernate.org/hib_docs/v3/reference/en/html/querysql.html

Page 90: Manual hibernate v2

[90]

HIBERNATE

5.3.1. CONSULTAS SQL

Las consultas SQL se especifican a través del método createSQLQuery:

List list = sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS")

.list();

Object[] res = (Object[]) list.get(0);

La lista obtenida como resultado estará formada por un Object[] con las instancias que

representan a los campos especificados en la consulta. Se pueden especificar los tipos de las

instancias devueltas de forma explícita a través del método addScalar:

List list = sess.createSQLQuery("SELECT * FROM CATS")

.addScalar("ID", Hibernate.LONG)

.addScalar("NAME", Hibernate.STRING)

.addScalar("BIRTHDATE", Hibernate.DATE);

Object[] res = (Object[]) list.get(0);

Se puede añadir una entidad a una consulta de forma sencilla, para que la lista de resultados

sea un conjunto de instancias de la clase concreta:

List list = sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS")

.addEntity(Cat.class);

Cat cat = (Cat) list.get(0);