Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Hibernate-Documentacion Español
Hibernate-Documentacion Español
1 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
Traduccion a Espaol
por David Marco Palao (programacion@davidmarco.es)
3.3.2.GA
Parte 1 - La primera Aplicacion Hibernate
1.1 Configuracion
1.2 La primera clase
1.3 El archivo de mapeo
1.4 Configuracion de Hibernate
1.5 Construyendo con Maven
1.6 Puesta en marcha y clases de ayuda
1.7 Leyendo y almacenando objetos
Parte 2 - Mapeo de asociaciones
2.1 Mapeando la clase Persona
2.2 Una asociacion unidireccional basada en Set
2.3 Trabajando con la asociacion
2.4 Colecciones de valores
2.5 Asociaciones bidireccionales
2.6 Trabajando con enlaces bidireccionales
Part 3 - La aplicacion web EventManager
3.1 Escribiendo un servlet basico
3.2 Procesando la solicitud y formando la pagina de respuesta
3.3 Desplegando y probando
Resumen
Este capitulo, pensado para nuevos usuarios, ofrece una introduccion paso a paso a Hibernate, comenzando
con una sencilla aplicacion que usa una base de datos en memoria. El tutorial esta basado en un tutorial
anterior desarrollado por Michal Gloegl. Todo el codigo fuente puedes encontrarlo en el directorio
project/tutorials/web de tu distribucion de Hibernate.
Importante
Este tutorial asume que el usuario tiene conocimientos tanto de Java como de SQL. Si dispones
de un conocimiento limitado de Java y SQL es aconsejable que comiences con una buena
introduccion a estas tecnologias como paso previo a intentar aprender Hibernate.
28/02/2010 22:34
2 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
Nota
La distribucion de Hibernate contiene otra aplicacion de ejemplo bajo el directorio de proyectos
tutorial/eg.
Nota
Aunque puedes usar cualquier base de datos con la que te sientas comodo, nosotros vamos a
usar HSQLDB (una base de datos en memoria escrita en Java) para evitar describir la
instralacion y puesta en marcha de un servidor de bases de datos en concreto.
1.1 Configuracion
Lo primero que necesitamos hacer es poner en marcha el entorno de desarrollo. Usaremos el "esquema
estandar" defendido por un monton de herramientas de construccion como Maven. Maven, en particular,
dispone de un buen recurso web describiendo este esquema. Como este tutorial es una aplicacion web,
crearemos y haremos uso de los directorios src/main/java, src/main/resources y src/main/webapp.
Usaremos Maven en este tutorial, tomando ventaja de sus capacidades de gestion de dependencias asi como
la habilidad de muchos IDE's de configurar automaticamente un proyecto basandose en el descriptor Maven.
28/02/2010 22:34
3 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.hibernate.tutorials</groupId>
<artifactId>hibernate-tutorial</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>First Hibernate Tutorial</name>
<build>
<!-- we dont want the version to be part of the generated war file name -->
<finalName>${artifactId}</finalName>
</build>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
</dependency>
<!-- Because this is a web app, we also have a dependency on the servlet api. -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</dependency>
<!-- Hibernate uses slf4j for logging, for our purposes here use the simple backend -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</dependency>
<!-- Hibernate gives you a choice of bytecode providers between cglib and javassist -->
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
</dependency>
</dependencies>
</project>
Consejo
Usar Maven no es obligatorio. Si deseas usar cualquier otra herramienta para construir este
tutorial (como Ant) el esquema seguira siendo el mismo. El unico cambio es que necesitaras
manipular manualmente todas las dependencias requeridas. Si utilizas una herramienta como
Ivy
ofreciendo
mantenimiento
transitivo
de
dependencias,
seguiras
necesitando
las
dependencias mencionadas debajo. De cualquier otra manera necesitaras tener todas las
dependencias, tanto explicitas como transitivas, y aadirlas al classpath del proyecto. Si estas
trabajando con el paquete de distribucion de Hibernate, estas dependencias son hibernate3.jar,
28/02/2010 22:34
4 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
todos los archivos del directorio lib/required asi como todos los archivos de los directorios
lib/bytecode/cglib o lib/bytecode/javassist; de manera adicional, necesitaras tanto el archivo
jar del servlet API como una de las implementaciones del API para logging slf4j.
La clase usa nombres estandar JavaBean para los metodos getter y setter de cada propiedad, asi como
visibilidad privada para las variables. Aunque este es el diseo recomendado no es obligatorio, ya que
Hibernate puede acceder a las variables directamente. Sin embargo, el uso de metodos de acceso es
beneficioso por su robustez a la hora de refactorizar.
28/02/2010 22:34
5 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
La propiedad id almacena un valor que identifica de manera unica cada evento. Todas las clases que
representan entidades persistentes (asi como otras clases dependientes menos importantes) necesitaran
dicha propiedad identificativa si queremos hacer uso de todo el conjunto de caracteristicas de Hibernate. De
hecho, muchas aplicaciones, especialmente aplicaciones web, necesitan distinguir ciertos objetos por su
identidad, por lo que debes considerarlo como una caracteristica en lugar de como una limitacion. Sin
embargo, normalmente no manipularemos la identidad de un objeto , por lo que su metodo setter debe ser
privado. Solo Hibernate asignara la identidad cuando un objeto sea almacenado en la base de datos.
Hibernate puede acceder tanto metodos de acceso de tipo publico, privado y protegido, como directamente
las variables tambien de tipo publico, privado y protegido. Esta eleccion es tuya y puedes elegir la que mejor
se ajuste al diseo de tu aplicacion.
El constructor sin argumentos es necesario para todas las clases persistentes; Hibernate tiene que crear
objetos por ti, usando reflexion. El constructor puede ser privado, aunque se requiere que tenga visibilidad
publica o de paquete para la generacion de proxys en tiempo de ejecucion, y para recuper datos de forma
eficiente sin manipular del codigo de bytes.
Guarda este archivo en el directorio src/main/java/org/hibernate/tutorial/domain.
El DTD de Hibernate es sofisticado. Puedes usarlo para tareas de auto-completado de elementos y atributos
XML en tu editor o IDE. Abrir el archivo DTD en tu editor de texto es la manera mas facil de tener una vision
general de todos los elementos y atributos, de ver los valores por defecto, asi como ver algunos
comentarios. Hibernate no carga el archivo DTD desde la web, si no que primero lo busca en el classpath de
la aplicacion. El archivo DTD esta incluido en el archivo hibernate-core.jar (tambien esta incluido en el
archivo hibernate3.jar si estas usando el paquete de distribucion).
Importante
Para acortar el codigo de los futuros ejemplos, omitiremos la declaracion del DTD. Por supuesto
que en tu codigo debe aparecer siempre, ya que su inclusion no es opcional.
Entre las dos etiquetas hibernate-mapping incluye un elemento class . Todas las clases de entidades
persistentes (de nuevo, podrian haber clases dependientes, las cuales veremos despues, que no son
entidades de primera clase) necesitan un mapeo a una tabla en la base de datos SQL:
28/02/2010 22:34
6 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
<hibernate-mapping package="org.hibernate.tutorial.domain">
<class name="Event" table="EVENTS">
</class>
</hibernate-mapping>
Hasta ahora hemos indicado a Hibernate como persistir/leer objetos de la clase Event en/desde la tabla
EVENTS. Cada instancia esta ahora representada por una fila en dicha tabla. Ahora podemos continuar
mapeando la propiedad que representa la identidad de cada instancia. Como no queremos preocuparnos del
manejo de este identificador, configuramos una estrategia de generacion de identidad en una columna de
claves primarias:
<hibernate-mapping package="org.hibernate.tutorial.domain">
<class name="Event" table="EVENTS">
<id name="id" column="EVENT_ID">
<generator class="native"/>
</id>
</class>
</hibernate-mapping>
Consejo
native no es considerada como la mejor estrategia en terminos de portabilidad.
Finalmente, necesitamos indicar a Hibernate como mapear las propiedades restantes de la entidad. Por
defecto, ninguna propiedad de la entidad es considerada persistente:
28/02/2010 22:34
7 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
<hibernate-mapping package="org.hibernate.tutorial.domain">
<class name="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>
De manera similar al elemento id, el atributo name del elemento property informa a Hibernate que metodo
setter usar. En este caso, Hibernate buscara los metodos getDate(), setDate(), getTitle() y setTitle().
Nota
Por que el mapeo de la propiedad date incluye el atributo column pero no lo incluye el mapeo
de title ? Sin el atributo column, Hibernate usa por defecto el nombre de la propiedad como
nombre para la columna. Esto funciona para title , sin embargo date es una palabra reservada
en muchas bases de datos por lo que necesitas mapear la propiedad date con un nombre
diferente.
El mapeo de title tambien carece del atributo type . Los tipos de datos declarados y usados en los archivos de
mapeo no son tipos de datos Java; tampoco son tipos de base de datos SQL. Son tipos llamados tipos de
datos Hibernate , conversores que pueden trasladar desde tipos de datos Java a SQL y viceversa. De nuevo,
Hibernate intentara determinar la conversion correcta y el tipo de datos por el mismo si el atributo type no
se encuentra presente. En algunos casos esta deteccion automatica, que es determinada mediante reflexion,
puede no resultar en el tipo que esperabas o necesitabas. Este es el caso con la propiedad date . Hibernate no
puede conocer si la propiedad, que es del tipo java.util.Date , debe ser mapeada a una columna SQL del tipo
date , timestamp, o time . La informacion completa de fecha y hora es preservada mapeando la propiedad con
un convertidor timestamp.
Consejo
Hibernate determina el tipo de datos a usar mediante reflexion en el momento en que los
archivos de mapeo son procesados. Esto puede tomar tiempo y recursos, por lo que si el
rendimiento en el arranque es importante deberias considerar definir explicitamente todos los
tipos a usar.
28/02/2010 22:34
8 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
Nota
Vamos a hacer esto para que los datos permanezcan entre ejecuciones.
Utilizaremos
el
mvn exec:java
plugin
'exec'
de
Maven
para
arrancar
el
servidor
HSQLDB
ejecutando:
Veras como se inicia y asocia a un socket TCP/IP; alli es donde nuestra aplicacion conectara mas tarde. Si
quieres comenzar el tutorial con una base de datos limpia, deten HSQLDB, elimina todos los archivos en el
directorio target/data y arranca de nuevo HSQLDB.
Hibernate conectara con la base de datos en nombre de tu aplicacion, por lo que necesita conocer como
obtener conexiones. Para este tutorial usaremos un pool de conexiones (de manera opuesta a usar
javax.sql.DataSource ). HIbernate viene con soporte para dos pool de conexiones JDBC de terceros: c3p0 y
Atencion
El pool de conexiones integrado en Hibernate no esta pensado de ninguna manera para su uso
en produccion, ya que carece de diversas caracteristicas disponibles en cualquier pool de
conexiones decente.
Para la configuracion de Hibernate, podemos usar un sencillo archivo hibernate.properties , un mas sofisticado
archivo hibernate.cfg.xml, o incluso una configuracion programatica completa. Muchos usuarios prefieren el
archivo de configuracion XML:
28/02/2010 22:34
9 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
Nota
Observa que este archivo de configuracion define un DTD diferente
Has configurado un SessionFactory. SessionFactory es una factoria global reponsable de una base de datos en
particular. Si dispones de varias bases de datos, deberias usar varias configuraciones <session-factory> en
varios archivos de configuracion para simplificar el arranque.
Los cuatro primeros elementos property contienen la configuracion necesaria para la conexion JDBC. El
elemento contiene la configuracion necesaria para la conexion JDBC. El elemento property que hace
referencia a 'dialect' especifica que variante de SQL tiene que generar Hibernate.
28/02/2010 22:34
10 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
Consejo
En muchos casos, Hibernate es capaz de determinar correctamente el dialecto que debe usar.
task-segment: [compile]
aplicacion.
org.hibernate.SessionFactory
es
usado
para
obtener
instancias
de
org.hibernate.Session.
o thread.
al mismo tiempo son ejecutados en serie, nunca en paralelo) que es instanciado una sola vez.
A continuacion crearemos una clase de ayuda HibernateUtil que se encargara de iniciar y hacer accesible
org.hibernate.SessionFactory de manera conveniente.
28/02/2010 22:34
11 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
package org.hibernate.tutorial.util;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
return new Configuration().configure().buildSessionFactory();
}
catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
diferentes
de
el)
estatico.
Tambien
podriamos
haber
buscado
la
referencia
28/02/2010 22:34
12 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
package org.hibernate.tutorial;
import org.hibernate.Session;
import java.util.*;
import org.hibernate.tutorial.domain.Event;
import org.hibernate.tutorial.util.HibernateUtil;
public class EventManager {
public static void main(String[] args) {
EventManager mgr = new EventManager();
if (args[0].equals("store")) {
mgr.createAndStoreEvent("My Event", new Date());
}
HibernateUtil.getSessionFactory().close();
}
private void createAndStoreEvent(String title, Date theDate) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Event theEvent = new Event();
theEvent.setTitle(title);
theEvent.setDate(theDate);
session.save(theEvent);
session.getTransaction().commit();
}
}
En el metodo createAndStoreEvent() hemos creado un objeto Event y lo hemos manejado con Hibernate. En
este punto, Hibernate se preocupa de las operaciones SQL por nosotros y ejecuta una operacion INSERT en la
base de datos.
La clase org.hibernate.Session esta diseada para representar una unica unidad de trabajo a ser realizada.
Por ahora mantendremos las cosas simples y asumiremos una granularidad uno-a-uno entre el objeto
org.hibernate.Session de Hibernate y una transaccion de base de datos. Para proteger a nuestro codigo del
sistema subyacente de transacciones usaremos la API org.hibernate.Transaction. En este caso en particular
estamos usando semantica transaccional basada en JDBC, pero tambien podria funcionar mediante JTA.
Que es lo que hace sessionFactory.getCurrentSession()? Para empezar, puedes llamarlo cuantas veces quieras
y
desde
donde
quieras
una
vez
que
consigas
un
objeto
org.hibernate.SessionFactory.
El
metodo
getCurrentSession() siempre devuelve la unidad de trabajo "actual". Recuerda que cambiamos la opcion de
28/02/2010 22:34
13 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
Importante
Hibernate ofrece tres metodos para el seguimiento de la sesion actual. El metodo basado en
"thread" no es el deseado para uso en produccion; es unicamente util para prototipos y
tutoriales como este. El seguimiento de la sesion actual es discutido en mas detalle mas tarde.
Una sesion org.hibernate.Session comienza cuando la primera llamada a getCurrentSession() es hecha por el
thread actual. Entonces, es asociada por Hibernate al thread actual. Cuando la transaccion termina, ya sea
aceptada
(commit)
cancelada
(rollback),
Hibernate
desasocia
automaticamente
la
sesion
org.hibernate.Session del thread y la cierra por ti. Si llamas de nuevo a getCurrentSession() , obtienes una
nueva sesion org.hibernate.Session y puedes comenzar una nueva unidad de trabajo.
Hablando del alcance de la unidad de trabajo, deberia org.hibernate.Session usarse para ejecutar una o
varias operaciones de base de datos? El ejemplo anterior usa un objeto org.hibernate.Session para una unica
operacion. Sin embargo, esto es pura coincidencia; el ejemplo no es lo suficientemente complejo para
mostrar otra opcion. El alcance de org.hibernate.Session puede es flexible, pero nunca debes disear tu
aplicacion para usar un nuevo objeto org.hibernate.Session por cada operacion de base de datos. Aunque
esto es lo que ocurre en los siguientes ejemplos, considera que sesion-por-operacion es un anti-pattern (un
diseo contrario a las buenas practicas de programacion). Una aplicacion web real es mostrada mas tarde en
el tutorial, la cual te ayudara a ilustrar mejor este concepto.
El ejemplo anterior tambien ha evitado cualquier manejo de errores asi como cancelaciones de la transaccion
en caso de que los primeros hubiera ocurrido.
Para ejecutar el codigo anterior, tenemos que hacer uso de la opcion 'exec' de Maven para llamar a nuestra
clase
con
los
ajustes
de
classpath
necesarios:
Nota
Tal vez necesites realizar primero una llamada a mvn compile.
Deberias ver a Hibernate arrancando y, dependiendo de tu configuracion, un monton de salida de log. Cerca
del final, se mostrara la siguiente linea:
[java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)
28/02/2010 22:34
14 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
if (args[0].equals("store")) {
mgr.createAndStoreEvent("My Event", new Date());
}
else if (args[0].equals("list")) {
List events = mgr.listEvents();
for (int i = 0; i < events.size(); i++) {
Event theEvent = (Event) events.get(i);
System.out.println(
"Event: " + theEvent.getTitle() + " Time: " + theEvent.getDate()
);
}
}
Aqui estamos usando una solicitud Hibernate Query Language (HQL) para leer todos los objetos Event desde
la base de datos. Hibernate generara las sentencias SQL adecuadas, las enviara a la base de datos y creara
objetos Event con los datos obtenidos. HQL te permite crear consultas mucho mas complejas.
Ahora podemos probar nuestra nueva funcionalidad, de nuevo usando la opcion 'exec' de Maven:
mvn exec:java -Dexec.mainClass="org.hibernate.tutorial.EventManager" -Dexec.args="list"
28/02/2010 22:34
15 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
package org.hibernate.tutorial.domain;
public class Person {
private Long id;
private int age;
private String firstname;
private String lastname;
public Person() {}
// Accessor methods for all properties, private setter for 'id'
}
continuacion,
crea
un
nuevo
archivo
de
mapeo
con
el
nombre
src/main/resources/org/hibernate/tutorial/domain/Person.hbm.xml
<hibernate-mapping package="org.hibernate.tutorial.domain">
<class name="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>
Crea una asociacion entre estas dos entidades. Las personas pueden participar en enventos, y los eventos
tienen participantes. Las cuestiones de diseo con las que tienes que tratar son: direccionalidad,
multiplicidad, y comportamiento de la coleccion.
28/02/2010 22:34
16 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
Antes de mapear esta asociacion, consideremos como sera desde el otro lado. Podemos mantener esta
asociacion unidireccional o crear otra coleccion en la clase Event, si es que queremos ser capaces de navegar
en ambas direcciones. Esto no es necesario desde una perspectiva funcional. Siempre puedes ejecutar una
consulta explicita para obtener los participantes para un evento en particular. Esta es una decision de diseo
que te dejamos a ti, pero lo que queda claro de esta discursion es la multiplicidad de la asociacion: "muchos"
valores en ambos lados forman una asociacion de tipo many-to-many (muchos-a-muchos). Por lo tanto,
usamos un mapeo many-to-many de Hibernate:
<class name="Person" table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="native"/>
</id>
<property name="age"/>
<property name="firstname"/>
<property name="lastname"/>
<set name="events" table="PERSON_EVENT">
<key column="PERSON_ID"/>
<many-to-many column="EVENT_ID" class="Event"/>
</set>
</class>
Hibernate soporta un amplio rango de mapeo de colecciones, siendo set el mas comun. Para una asociacion
de tipo many-to-many, o relacion entre entidades n:m, se requiere una tabla de asociacion. Cada fila en esta
tabla representa un enlace entre una persona y un evento. El nombre de la tabla es declarado usando el
atributo table del elemento set . El nombre de la columna identificadora de la asociacion para el lado las
personas, es definido con el elemento key; el nombre de la columna identificadora de la asociacion para el
lado de los eventos con el atributo column del elemento many-to-many. Tambien tienes que informar a
Hibernate de la clase de objetos que almacena tu coleccion.
El esquema de base de datos para este mapeo es el siguiente:
28/02/2010 22:34
17 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
_____________
|
|
EVENTS
__________________
|
|
PERSON_EVENT
_____________
|_____________|
|__________________|
|_____________|
| *EVENT_ID
|<---->| *EVENT_ID
EVENT_DATE |
| *PERSON_ID
TITLE
|__________________|
|_____________|
|
PERSON
|<---->| *PERSON_ID
AGE
FIRSTNAME
LASTNAME
|_____________|
Despues de obtener un objeto Person y un objeto Event , modificamos la colecion utilizando sus metodos
tipicos. No hay una llamada explicita a update() or save(); Hibernate detecta automaticamente que la
coleccion ha sido modificada y necesita ser actualizada. Esto se conoce como comprobacion automatica de
datos obsoletos (en el original ingles se utiliza el termino 'dirty', que significa suciedad, en lugar de
'obsoleto'. En espaol la traduccion literal de la frase original no seria aclaratoria). Tambien puedes intentar
modificar las propiedades correspondientes al nombre o a la fecha de cualquiera de estos objetos. Mientras
esten en estado persistente, osea, ligados a una instancia activa de org.hibernate.Session, Hibernate
monitorea cualquier cambio y ejecuta instancias SQL de manera totalmente transparente para el usuario. El
proceso de sincronizacion entre es estado de la memoria y la base de datos, realizado normalmente al final
de la unidad de trabajo, es llamado flushing (descargar). En nuestro codigo, la unidad de trabajo termina
aceptando (commit) o cancelando (rollback) la transaccion en la base de datos.
Puedes obtener personas y eventos en diferentes unidades de trabajo. O puedes modificar un objeto fuera
del alcance de org.hibernate.Session, cuando no esta en estado persistente (si ya fue persistido anteriormente,
este estado es llamado separado, o 'detached'). Incluso puede modificar una coleccion cuando esta
separada:
28/02/2010 22:34
18 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
La llamada a update convierte de nuevo la entidad separado en persistida, asociandola con una nueva unidad
de trabajo, por lo que cualquier modificacion que hagas en ella mientras estaba separada puede ser
guardada en la base de datos. Esto incluye cualquier modificacion (adiciones/eliminaciones) que hagas en
una coleccion de dicha entidad.
No se ha usado mucho en nuestro ejemplo, pero es un concepto importante que puedes incorporar en tu
propia aplicacion. Completa este ejercicio aadiendo una nueva accion al metodo main de EventManager e
invocalo desde la linea de comandos. Si necesitas los identificadores de una persona y un evento, el metodo
save() los devuelve (tal vez tengas que modificar algunos de los metodos anteriores para devolver dicho
identificador):
else if (args[0].equals("addpersontoevent")) {
Long eventId = mgr.createAndStoreEvent("My Event", new Date());
Long personId = mgr.createAndStorePerson("Foo", "Bar");
mgr.addPersonToEvent(personId, eventId);
System.out.println("Added person " + personId + " to event " + eventId);
}
El codigo anterior es un ejemplo de una asociacion entre dos clases igualmente importantes: dos entidades.
Como se ha mencionado anteriormente, hay otras clases y tipos en un modelo tipico, que son considerados
"menos importantes". Algunos ya los has visto, como int o java.lang.String. Llamamos a esas clases tipos con
valor, y sus instancias dependen de una entidad en concreto. Las instancias de tipos con valor no tienen
identidad, ni tampoco son compartidos entre entidades. Dos personas no pueden hacer referencia al mismo
objeto firstname , incluso aunque tengan el mismo nombre. Tipos con valor no solo pueden ser encontrados en
el JDK , si no que tambien puedes escribir tus propias clases dependientes como Address o MonetaryAmount .
De hecho, en una aplicacion Hibernate todas las clases del JDK son consideradas tipos con valor.
Tambien puedes disear una coleccion de tipos con valor. Esto es conceptualmente diferente de una coleccion
28/02/2010 22:34
19 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
de referencias a otras entidades, pero parecen casi iguales cuando las vemos en Java.
La diferencia en comparacion con el mapeo anterior es el uso de element , el cual indica a Hibernate que la
coleccion no contiene referencias a otras entidades, si no una coleccion cuyos elementos son tipos con valor,
especificamente del tipo string. El nombre con la primera letra en minusculas indica el mapeo de un
tipo/conversor de Hibernate. De nuevo, el atributo table dentro del elemento set determina el nombre de la
tabla donde almacenar la coleccion. El elemento key define el nombre de la columna de claves foraneas en la
tabla que contiene la coleccion. El atributo column en el elemento element define el nombre de la columna
donde los valores de las direcciones de correo electronico seran almacenadas.
Aqui esta el esquema actualizado:
_____________
|
|
EVENTS
__________________
|
|
PERSON_EVENT
_____________
___________________
|_____________|
|__________________|
| *EVENT_ID
|<--->| *EVENT_ID
EVENT_DATE |
| *PERSON_ID
TITLE
|__________________|
|_____________|
| PERSON_EMAIL_ADDR |
|_____________|
|___________________|
PERSON
|<--->| *PERSON_ID
|<--->|
*PERSON_ID
AGE
*EMAIL_ADDR
FIRSTNAME
|___________________|
LASTNAME
|_____________|
Puedes ver que la clave primaria de la tabla que almacena la coleccion es de hecho una clave compuesta que
utiliza ambas columnas. Esto tambien implica que no pueden existir direcciones de correo electronico
duplicadas por persona, que es exactamente la semantica que necesitamos para una coleccion de tipo set en
Java.
Ahora puedes intentar aadir elecmentos a esta coleccion, exactamente como lo hicimos antes asociando
28/02/2010 22:34
20 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
personas y eventos. Es el mismo tipo de codigo en Java, utilizando metodos getter y setter:
private void addEmailToPerson(Long personId, String emailAddress) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Person aPerson = (Person) session.load(Person.class, personId);
// adding to the emailAddress collection might trigger a lazy load of the collection
aPerson.getEmailAddresses().add(emailAddress);
session.getTransaction().commit();
}
Esta vez no utilizamos una consulta de tipo fetch para inicializar la coleccion. Monitorea el log de SQL e
intenta optimizarla con una lectura temprana de datos (eager fetch).
Nota
Una base de datos relacional es mas flexible que un lenguaje de programacion en red, ya que
no necesita una direccion de navegacion entre asociaciones; los datos pueden ser vistos y
recuperados de cualquier manera posible.
Estos son mapeos normales de tipo set en ambos archivos. Fijate que los nombres de las columnas en key y
many-to-many se intercambian en ambos archivos de mapeo. El aadido mas importante es el atributo
inverse="true" en el elemento set del mapeo de la coleccion Event .
El significado de esto es que Hibernate deberia utilizar el otro lado, en este caso la clase Person , cuando
28/02/2010 22:34
21 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
necesita buscar informacion acerca de un enlace entre las dos clases. Esto es muy sencillo de comprender
una vez que ves como es creado el enlace bidireccional entre nuestras dos entidades.
bidireccionales.
Muchos desarrolladores programan defensivamente y crean metodos de mantenimiento de enlaces para
ajustar correctamente ambos lados (por ejemplo, en Person):
protected Set getEvents() {
return events;
}
protected void setEvents(Set events) {
this.events = events;
}
public void addToEvent(Event event) {
this.getEvents().add(event);
event.getParticipants().add(this);
}
public void removeFromEvent(Event event) {
this.getEvents().remove(event);
event.getParticipants().remove(this);
}
Los metodos get y set de la coleccion son ahora de tipo 'protected'. Esto permite a las clases en el mismo
paquete y las subclases seguir accediendo a dichos metodos, pero previene que todo el mundo pueda alterar
la coleccion directamente. Repite este paso en la coleccion del otro lado.
Y que hay del atributo de mapeo inverse? Para ti, y para Java, un enlace bidireccional es simplemente una
manera de ajustar las referencias en ambos lados de forma correcta. Hibernate, sin embargo, no tiene la
informacion suficiente para organizar correctamente las sentencias SQL INSERT y UPDATE (para evitar
violaciones de uso). Haciendo uno de los lados de la asociacion inverse informa a Hibernate que considere ese
lado como un espejo del otro lado. Esto es todo lo necesario para que Hibernate pueda resolver cualquier
problema que surja en el momento de transformar un modelo de navegacion direccional en un esquema SQL
para la base de datos. Las reglas son sencillas: todas las asociaciones bidireccionales necesitan que un lado
sea declarado inverse . En una asociacion uno-a-muchos este tiene que ser el lado 'muchos', y en una
asociacion muchos-a-muchos puedes elegir cualquiera de los lados.
28/02/2010 22:34
22 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
dentro de dicha transaccion independientemente de si los datos son leidos o escritos. no use el modo 'autocommit' en tus aplicaciones.
No uses un objeto Session para cada operacion de base de datos. Usa solamente una que alcance a la
solicutud HTTP completa. Usa getCurrentSession(), de manera que sea automaticamente asociada al hilo de
ejecucion actual.
A continuacion, las posibles acciones de la solicitud son procesadas y la respuesta HTML es formada. En
breve iremos a esa parte.
28/02/2010 22:34
23 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
puedes escribir un filtro servlet. Mira en el sitio web de Hibernate y en su Wiki si quieres mas informacion
sobre este patro, llamado Abrir Sesion en la Vista. Lo necesitaras tan pronto como consideres formar la vista
mediante JSP, en lugar de mediante servlets.
Este estilo de escribir codigo, mezclando Java y HTML, no es nada escalable en una aplicacion mas compleja
(ten en cuenta que solo se estan ilustrando conceptos basicos de Hibernate en este tutorial). El codigo
imprime una tanto una cabecera como un pie de pagina HTML. Dentro de esta pagina, se muestran un
formulario HTML para introducir eventos y una lista de todos los eventos almacenados en la base de datos. El
primer metodo es muy trivial, y solo produce salida HTML:
28/02/2010 22:34
24 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
El metodo listEvents() usa un objeto Session asociado al hilo de ejecucion actual para ejecutar una consulta a
la base de datos:
private void listEvents(PrintWriter out, SimpleDateFormat dateFormatter) {
List result = HibernateUtil.getSessionFactory()
.getCurrentSession().createCriteria(Event.class).list();
if (result.size() > 0) {
out.println("<h2>Events in database:</h2>");
out.println("<table border='1'>");
out.println("<tr>");
out.println("<th>Event title</th>");
out.println("<th>Event date</th>");
out.println("</tr>");
Iterator it = result.iterator();
while (it.hasNext()) {
Event event = (Event) it.next();
out.println("<tr>");
out.println("<td>" + event.getTitle() + "</td>");
out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>");
out.println("</tr>");
}
out.println("</table>");
}
}
Por ultimo, la accion store es redirigida al metodo createAndStoreEvent(), el cual tambien usa el objeto Session
del hilo de ejecucion actual:
protected void createAndStoreEvent(String title, Date theDate) {
Event theEvent = new Event();
theEvent.setTitle(title);
theEvent.setDate(theDate);
HibernateUtil.getSessionFactory()
.getCurrentSession().save(theEvent);
}
The servlet is now complete. A request to the servlet will be processed in a single Session and Transaction. As
earlier in the standalone application, Hibernate can automatically bind these objects to the current thread of
execution. This gives you the freedom to layer your code and access the SessionFactory in any way you like.
Usually you would use a more sophisticated design and move the data access code into data access objects
(the DAO pattern). See the Hibernate Wiki for more examples.
28/02/2010 22:34
25 de 25
http://www.davidmarco.es/tutoriales/hibernate-reference/
de separar en capas tu codigo y acceder a SessionFactory de la manera que quieras. Normalmente usarias un
diseo mas sofisticado y moverias el codigo de acceso al interior de un objeto de acceso a datos (patron de
diseo DAO). En el Wiki de Hibernate puedes encontrar mas ejemplos.
Para construir y desplegar la aplicacion ejecuta el comando mvn package en el directorio del proyecto, y copia
el archivo resultante hibernate-tutorial.war en el directorio webapps de tu instalacion de Tomcat.
Nota
Si no tienes Tomcat instalado, descargalo desde http://tomcat.apache.org/ y sigue las
instrucciones de instalacion que encontraras en la misma web. Nuestra aplicacion no requiere
cambios en la configuracion estandar de Tomcat.
Hibernate se inicia cuando la primera solicitud alcanza tu servlet (el inicializador estatico en HibernateUtil es
llamado) y de obtener una salida detallada si cualquier excepcion ocurriera.
Resumen
Este tutorial ha cubierto los aspectos basicos sobre como escribir una sencilla aplicacion Hibernate
independiente, asi como una pequea aplicacion web. Tanto en el sitio web de Hibernate como en el sitio
web del traductor hay disponibles mas tutoriales.
28/02/2010 22:34