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
[2]
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 Modelo de Objetos
TABLA Persona
Persona
Nombre Apellido Teléfono
Juan López 666111122 -String nombre
-String apellido
Ana Hurtado 959121212
-String teléfono
Francisco Soria 889121212
Belén Jiménez 789456123
instance: Persona
nombre = Juan
apellido = López
teléfono = 666111122
[3]
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.
[4]
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
[5]
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.
[6]
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.
[7]
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
[8]
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.
[9]
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.
[10]
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 Packagesdefault package de la aplicación.
[11]
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
[12]
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
[13]
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.
[14]
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>
[15]
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.
[16]
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>
[17]
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>
[18]
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>
[19]
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.
[20]
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;
}
}
[21]
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;
}
}
[22]
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.
[23]
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>
[24]
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.
[25]
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);
}
}
[26]
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.
[27]
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:
[28]
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.
[29]
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>
[30]
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.
[31]
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();
}
}
[32]
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.
[33]
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.
[34]
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
[35]
36. HIBERNATE
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;
}
}
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
[36]
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>
[37]
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/
[38]
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. librequired*.jar
c. liboptional*.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:
[39]
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>
[40]
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 dynamic-map
representación de entidades dom4j
pojo
hibernate.order_updates Ordenar las actualizaciones true
por clave primaria false
hibernate.generate_statistics Generar estadísticas de true
rendimiento false
hibernate.use_identifier_rollback Resetea el identificador true
asociado en caso de false
eliminación
hibernate.use_sql_comments Genera comentarios en las true
sentencias SQL false
[41]
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
Utilizar el rowcount que devuelve true
hibernate.jdbc.batch_versioned_data
executeBatch() false
Seleccionar un
hibernate.jdbc.factory_class
org.hibernate.jdbc.Batcher propio
Uso de resultsets scrollables de true
hibernate.jdbc.use_scrollable_resultset
JDBC2 false
Uso de streams para la escritura true
hibernate.jdbc.use_streams_for_binary
de tipos de datos serializables false
Uso de getGeneratedKeys() de true
hibernate.jdbc.use_get_generated_keys
JDBC3 false
Uso de un ConnectionProvider
hibernate.connection.provider_class
propio
true
hibernate.connection.autocommit Habilita el Autocommit
false
auto
Modo de Liberación de conexiones on_close
hibernate.connection.release_mode
JDBC after_transaction
after_statement
Propiedad específica de JDBC para
hibernate.connection.<propertyName>
la conexión
Propiedad específica de JNDI para
hibernate.jndi.<propertyName>
la conexión
Nivel de aislamiento de
hibernate.connection.isolation
transacciones
[42]
43. HIBERNATE
Propiedades de caché: gestión de la caché de segundo nivel.
Nombre de CacheProvider
hibernate.cache.provider_class
propio.
Optimizar la operación de la
true
hibernate.cache.use_minimal_puts caché al mínimo número de
false
escrituras.
true
hibernate.cache.use_query_cache Habilitar la cache de consultas
false
Deshabilitar completamente la true
hibernate.cache.use_second_level_cache
cache false
Nombre de un interfaz
hibernate.cache.query_cache_factory
QueryCache propio
Prefijo de los nombres
hibernate.cache.region_prefix
regionales de la caché
true
hibernate.cache.use_structured_entries Usar entradas de cache legibles
false
Propiedades de transacciones: gestión de transacciones.
Nombre de la factoría de
hibernate.transaction.factory_class
transacciones
Nombre para obtener la
jta.UserTransaction
transacción JTA
Nombre del
hibernate.transaction.manager_lookup_class
TransactionManagerLookup
La sesión se lanza antes de true
hibernate.transaction.flush_before_completion
concluir la transacción false
Cierra la sesión tras concluir la true
hibernate.transaction.auto_close_session
transacción false
[43]
44. HIBERNATE
Otras propiedades.
Estrategia para jta
obtener la sesión thread
hibernate.current_session_context_class
actual managed
custom Class
org.hibernate.hql.ast.
ASTQueryTranslatorFactory
Implementación del
hibernate.query.factory_class
HQL Parser
org.hibernate.hql.classic.
ClassicQueryTranslatorFactory
Mapeo a consultas
hibernate.query.substitutions
SQL
validate
Genera el esquema update
hibernate.hbm2ddl.auto automáticamente al
create
crear la Sesión
create-drop
Utilización de CGLIB true
hibernate.cglib.use_reflection_optimizer
en lugar de reflexión 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.
[44]
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.
[45]
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"
/>
[46]
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.
[47]
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.
[48]
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.
[49]
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.
[50]
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"
/>
[51]
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
[52]
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.
[53]
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.
[54]
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.
[55]
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.
[56]
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.
[57]
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.
[58]
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):
[59]
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/
[60]