hibernate-orm/reference/es/modules/tutorial.xml

1270 lines
60 KiB
XML
Raw Normal View History

<chapter id="tutorial">
<title>Introducci&#x00f3;n a Hibernate</title>
<sect1 id="tutorial-intro">
<title>Prefacio</title>
<para>
Este cap&#x00ed;tulo es un tutorial introductorio de Hibernate. Comenzamos con
una aplicaci&#x00f3;n simple de l&#x00ed;nea de comandos usando un base de datos
en-memoria y desarroll&#x00e1;ndola en f&#x00e1;cil para entender los pasos.
</para>
<para>
Este tutorial est&#x00e1; concebido para usuarios nuevos de Hibernate pero
requiere conocimiento en Java y SQL. Est&#x00e1; basado en un tutorial de
Michael Gloegl. Las bibliotecas de terceros que mencionamos son para JDK 1.4
y 5.0. Podr&#x00ed;as necesitar otras para JDK 1.3.
</para>
</sect1>
<sect1 id="tutorial-firstapp">
<title>Parte 1 - La primera Aplicaci&#x00f3;n Hibernate</title>
<para>
Primero, crearemos una aplicaci&#x00f3;n simple de Hibenate basada en consola.
Usamos usamos una base de datos en-memoria (HSQL DB), de modo que no necesitamos
instalar ning&#x00fa;n servidor de base de datos.
</para>
<para>
Asumamos que necesitamos una aplicaci&#x00f3;n peque&#x00f1;a de base de datos que
pueda almacenar eventos que queremos atender, e informaci&#x00f3;n acerca de los
hostales de estos eventos.
</para>
<para>
La primera cosa que hacemos, es armar nuestro directorio de desarrollo y poner
en &#x00e9;l todas las bibliotecas Java que necesitamos. Descarga la distribuci&#x00f3;n
de Hibernate del sitio web de Hibernate. Extrae el paquete y coloca todas las
bibliotecas requeridas encontradas en <literal>/lib</literal> dentro del directorio
<literal>/lib</literal> de nuestro nuevo directorio de desarrollo de trabajo.
Debe asemejarse a esto:
</para>
<programlisting><![CDATA[.
+lib
antlr.jar
cglib-full.jar
asm.jar
asm-attrs.jars
commons-collections.jar
commons-logging.jar
ehcache.jar
hibernate3.jar
jta.jar
dom4j.jar
log4j.jar ]]></programlisting>
<para>
Este es el conjunto m&#x00ed;nimo de bibliotecas requeridas para Hibernate (observa que
tambi&#x00e9;n hemos copiado hibernate3.jar, el fichero principal). Ver el fichero
<literal>README.txt</literal> en el directorio <literal>lib/</literal> de la distribuci&#x00f3;n
de Hibernate para m&#x00e1;s informaci&#x00f3;n sobre bibliotecas de terceros requeridas y
opcionales. (Realmente, Log4J no es requerida aunque preferida por muchos desarrolladores).
</para>
<para>
Por siguiente, creamos una clase que represente el evento que queremos
almacenar en base de datos.
</para>
<sect2 id="tutorial-firstapp-firstclass">
<title>La primera clase</title>
<para>
Nuestra primera clase persistente es un JavaBean simple con algunas propiedades:
</para>
<programlisting><![CDATA[import java.util.Date;
public class Event {
private Long id;
private String title;
private Date date;
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;
}
}]]></programlisting>
<para>
Puedes ver que esta clase usa las convenciones de nombrado est&#x00e1;ndar de JavaBean
para m&#x00e9;todos getter y setter de propiedad, as&#x00ed; como visibilidad privada
para los campos. Esto es un dise&#x00f1;o recomendado, aunque no requerido. Hibernate
tambi&#x00e9;n puede acceder a los campos directamente; el beneficio de los m&#x00e9;todos
de acceso es la robustez para la refactorizaci&#x00f3;n.
</para>
<para>
La propiedad <literal>id</literal> tiene un valor &#x00fa;nico de identificador para
un evento en particular. Todas las clase de entidad persistentes ( tambi&#x00e9;n hay
clases dependientes menos importantes) necesitar&#x00e1;n una propiedad identificadora
similar si queremos usar el conjunto completo de funcionalidades de Hibernate. De hecho,
la mayor&#x00ed;a de las aplicaciones (esp. aplicaciones web) necesitan distinguir
objetos por identificador, de modo que debes considerar esto como un aspecto en vez de una
limitaci&#x00f3;n. Sin embargo, usualmente no manipulamos la identidad de un objeto, por
lo tanto el m&#x00e9;todo setter debe ser privado. S&#x00f3;lo Hibernate asignar&#x00e1;
identificadores cuando un objeto sea salvado. Puedes ver que Hibernate puede acceder a
m&#x00e9;todos de acceso p&#x00fa;blicos, privados y protegidos, tanto como directamente a
campos (p&#x00fa;blicos, privados y protegidos). La elecci&#x00f3;n est&#x00e1; en ti,
y puedes ajustarla a tu dise&#x00f1;o de aplicaci&#x00f3;n.
</para>
<para>
El constructor sin argumentos es un requerimiento para todas las clases persistentes.
Hibernate tiene que crear objetos para ti, usando reflecci&#x00f3;n Java. El constructor
puede ser privado, sin embargo, la visibilidad de paquete es requerida para la generaci&#x00f3;n
de proxies en tiempo de ejecuci&#x00f3;n y la recuperaci&#x00f3;n de datos sin
instrumentaci&#x00f3;n del bytecode.
</para>
<para>
Coloca este fichero de c&#x00f3;digo Java en un directorio llamado <literal>src</literal>
en la carpeta de desarrollo. El directorio ahora debe verse como esto:
</para>
<programlisting><![CDATA[.
+lib
<Hibernate and third-party libraries>
+src
Event.java]]></programlisting>
<para>
En el pr&#x00f3;ximo paso, le decimos a Hibernate sobre esta clase persistente.
</para>
</sect2>
<sect2 id="tutorial-firstapp-mapping">
<title>El fichero de mapeo</title>
<para>
Hibernate necesita saber c&#x00f3;mo cargar y almacenar objetos de la
clase persistente. Aqu&#x00ed; es donde el fichero de mapeo de Hibernate
entra en juego. El fichero de mapeo le dice a Hibernate a qu&#x00e9; tabla en
la base de datos tiene que acceder, y qu&#x00e9; columnas en esta tabla debe usar.
</para>
<para>
La estructura b&#x00e1;sica de un fichero de mapeo se parece a esto:
</para>
<programlisting><![CDATA[<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
[...]
</hibernate-mapping>]]></programlisting>
<para>
Observa que el DTD de Hibernate es muy sofisticado. Puedes usarlo para
autocompleci&#x00f3;n de los elementos y atributos XML de mapeo en tu
editor o IDE. Debes tambi&#x00e9;n abrir el fichero DTD en tu editor de
texto. Es la forma m&#x00e1;s f&#x00e1;cil para tener un panorama de todos
los elementos y atributos y ver los valores por defectos, as&#x00ed; como
algunos comentarios. Nota que Hibernate no cargar&#x00e1; el fichero DTD de
la web, sino que primero buscar&#x00e1; en el classpath de la aplicaci&#x00f3;n.
El fichero DTD est&#x00e1; inclu&#x00ed;do en <literal>hibernate3.jar</literal>
as&#x00ed; como tambi&#x00e9;n en el directorio <literal>src/</literal> de la
distribuci&#x00f3;n de Hibernate.
</para>
<para>
Omitiremos la declaraci&#x00f3;n de DTD en futuros ejemplos para acortar
el c&#x00f3;digo. Por supuesto, no es opcional.
</para>
<para>
Entre las dos etiquetas <literal>hibernate-mapping</literal>, incluye
un elemento <literal>class</literal>. Todas las clases de entidad
persistentes (de nuevo, podr&#x00ed;a haber m&#x00e1;s adelante clases
dependientes, que no sean entidades de-primera-clase) necesitan dicho mapeo
a una tabla en la base de datos SQL:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Event" table="EVENTS">
</class>
</hibernate-mapping>]]></programlisting>
<para>
Hasta ahora dijimos a Hibernate c&#x00f3;mo persistir y cargar el objeto
de clase <literal>Event</literal> a la tabla <literal>EVENTS</literal>,
cada instancia representada por una fila en esta tabla. Ahora continuamos con
un mapeo de la propiedad de identificado &#x00fa;nico a la clave primaria
de la tabla. Adem&#x00e1;s, como no queremos cuidar del manejo de este identificador,
configuramos la estrategia de generaci&#x00f3;n de identificadores para una columna
clave primaria delegada:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Event" table="EVENTS">
<id name="id" column="EVENT_ID">
<generator class="increment"/>
</id>
</class>
</hibernate-mapping>]]></programlisting>
<para>
El elemento <literal>id</literal> el la declaraci&#x00f3;n de la propiedad
identificadora, <literal>name="id"</literal> declara el nombre de la
propiedad Java. Hibernate usar&#x00e1; los m&#x00e9;todos getter y setter
para acceder a la propiedad. El attributo de columna dice a Hibernate cu&#x00e1;l
columna de la tabla <literal>EVENTS</literal> usamos para esta clave primaria.
El elemento anidado <literal>generator</literal> especifica la estrategia de
generaci&#x00f3;n de identificadores, en este caso usamos <literal>increment</literal>,
que es un m&#x00e9;todo muy simple de incremento de n&#x00fa;mero en-memoria
&#x00fa;til mayormente para testeo (y tutoriales). Hibernate tambi&#x0e9;n
soporta identificadores generados por base de datos, globalmente &#x00fa;nicos,
as&#x00ed; como tambi&#x00e9;n asignados por aplicaci&#x00f3;n (o cualquier
estrategia para la que hayas escrito una extensi&#x00f3;n).
</para>
<para>
Finalmente inclu&#x00ed;mos declaraciones para las propiedades persistentes
de la clases en el fichero de mapeo. Por defecto, ninguna propiedad de la clase
se considera persistente:
</para>
<programlisting><![CDATA[
<hibernate-mapping>
<class name="Event" table="EVENTS">
<id name="id" column="EVENT_ID">
<generator class="increment"/>
</id>
<property name="date" type="timestamp" column="EVENT_DATE"/>
<property name="title"/>
</class>
</hibernate-mapping>]]></programlisting>
<para>
Al igual que con el elemento <literal>id</literal>, el atributo <literal>name</literal>
del elemento <literal>property</literal> dice a Hibernate c&#x00e1;les m&#x00e9;todos
getter y setter usar.
</para>
<para>
¿Por qu&#x00e9; el mapeo de la propiedad <literal>date</literal>
incluye el atributo <literal>column</literal>, pero el de la de
<literal>title</literal> no? Sin el atributo <literal>column</literal>
Hibernate usa por defecto el nombre de propiedad como nombre de columna.
Esto funciona bien para <literal>title</literal>. Sin embargo,
However, <literal>date</literal> es una palabra reservada en la
mayor&#x00ed;a de las bases de datos, as&#x00ed; que mejor la mapeamos
a un nombre diferente.
</para>
<para>
La pr&#x00f3;xima cosa interesante es que el mapeo de <literal>title</literal>
carece de un atributo <literal>type</literal>. Los tipos que declaramos y usamos
en el fichero de mapeo no son, como podr&#x00ed;as esperar, tipos de datos Java.
Tampoco son tipos de base de datos SQL. Estos tipos son los llamados as&#x00ed;
<emphasis>Tipos de mapeo de Hibernate</emphasis>, convertidores que pueden
traducir de tipos Java a SQL y vice versa. De nuevo, Hibernate intentar&#x00e1;
determinar la conversi&#x00f3;n y el mapeo mismo de tipo correctos si el atributo
<literal>type</literal> no estuviese presente en el mapeo. En algunos casos esta
detecci&#x00f3;n autom&#x00e1;tica (usando reflecci&#x00f3;n en la clase Java)
puede no tener lo que esperas o necesitas. Este es el caso de la propiedad
<literal>date</literal>. Hibernate no puede saber is la propiedad mapear&#x00e1;
a una columna <literal>date</literal>, <literal>timestamp</literal> o
<literal>time</literal>. Declaramos que queremos preservar la informaci&#x00f3;n
completa de fecha y hora mapeando la propiedad con un <literal>timestamp</literal>.
</para>
<para>
Este fichero de mapeo debe ser salvado como <literal>Event.hbm.xml</literal>,
justo en el directorio pr&#x00f3;ximo al fichero de c&#x00f3;digo fuente de
la clase Java <literal>Event</literal>. El nombrado de los ficheros de mapeo
puede ser arbitrario, sin embargo, el sufijo <literal>hbm.xml</literal> se ha
vuelto una convenci&#x00f3;n el la comunidad de desarrolladores de Hibernate.
La estructura de directorio debe ahora verse como esto:
</para>
<programlisting><![CDATA[.
+lib
<Hibernate and third-party libraries>
+src
Event.java
Event.hbm.xml]]></programlisting>
<para>
Continuamos con la configuraci&#x00f3;n principal de Hibernate.
</para>
</sect2>
<sect2 id="tutorial-firstapp-configuration">
<title>Configuraci&#x00f3;n de Hibernate</title>
<para>
Tenemos ahora una clase persistente y su fichero de mapeo en su sitio. Es momento de
configurar Hibernate. Antes que hagamos esto, necesitaremos una base de datos.
HSQL DB, un DBMS SQL en-memoria basado en Java, puede ser descargado del sitio web
de HSQL DB. Realmente, de esta descarga s&#x00f3;lo necesitas el <literal>hsqldb.jar</literal>.
Coloca este fichero en el directorio <literal>lib/</literal> de la carpeta de desarrollo.
</para>
<para>
Crea un directorio llamado <literal>data</literal> en la ra&#x00ed;z del directorio de
desarrollo. All&#x00ed; es donde HSQL DB almacenar&#x00e1; sus ficheros de datos.
</para>
<para>
Hibernate es la capa en tu aplicaci&#x00f3;n que se conecta a esta base de datos,
de modo que necesita informaci&#x00f3;n de conexi&#x00f3;n. Las conexiones se hacen
a trav&#x00e9;s de un pool de conexiones JDBC, que tamb&#x00e9;n tenemos que configurar.
La distribuci&#x00f3;n de Hibernate contiene muchas herramientas de pooling de conexiones
JDBC de c&#x00f3;digo abierto, pero para este tutorial usaremos el pool de conexiones
prefabricado dentro de Hibernate. Observa que tienes que copiar la biblioteca requerida
en tu classpath y usar diferentes configuraciones de pooling de conexiones si quieres
usar un software de pooling JDBC de terceros de calidad de producci&#x00f3;n.
</para>
<para>
Para la configuraci&#x00f3;n de Hibernate, podemos usar un fichero
<literal>hibernate.properties</literal> simple, un fichero <literal>hibernate.cfg.xml</literal>
ligeramente m&#x00e1;s sofisticado, o incluso una configuraci&#x00f3;n completamente
program&#x00e1;tica. La mayor&#x00ed;a de los usuarios prefieren el fichero de
configuraci&#x00f3;n XML:
</para>
<programlisting><![CDATA[<?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>
<!-- Database connection settings -->
<property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="connection.url">jdbc:hsqldb:data/tutorial</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.HSQLDialect</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="Event.hbm.xml"/>
</session-factory>
</hibernate-configuration>]]></programlisting>
<para>
Observa que esta configuraci&#x00f3;n XML usa un DTD diferente.
Configuramos la <literal>SessionFactory</literal> de Hibernate, una
f&#x00e1;brica global responsable de una base de datos en particular.
Si tienes varias bases de datos, usa varias configuraciones
<literal>&lt;session-factory&gt;</literal> , usualmente en varios
ficheros de configuraci&#x00f3;n (para un arranque m&#x00e1;s f&#x00e1;cil).
</para>
<para>
Los primeros cuatro elementos <literal>property</literal> contienen la configuraci&#x00f3;n
necesaria para la conexi&#x00f3;n JDBC. El elemento de dialecto <literal>property</literal>
especifica la variante de SQL en particular que genera Hibernate. La opci&#x00f3;n
<literal>hbm2ddl.auto</literal> activa la generaci&#x00f3;n autom&#x00e1;tica de esquemas
de base de datos, directamente en la base de datos. Esto, por supuesto, puede desactivarse
(quitando la opci&#x00f3;n config) o redirigido a un fichero con la ayuda de la tarea
de Ant <literal>SchemaExport</literal>. Finalmente, agregamos el(los) fichero(s) de mapeo
para clases persistentes.
</para>
<para>
Copia este fichero dentro del directorio de c&#x00f3;digo fuente, de modo que
termine ubicado en la rai&#x00ed;z del classpath. Hibernate busca autom&#x00e1;ticamente
un fichero llamado <literal>hibernate.cfg.xml</literal> en la ra&#x00ed;z del classpath
al arrancar.
</para>
</sect2>
<sect2 id="tutorial-firstapp-ant">
<title>Construyendo con Ant</title>
<para>
Construiremos ahora el tutorial con Ant. Necesitar&#x00e1;s tener Ant instalado.
Obt&#x00e9;nlo de <ulink url="http://ant.apache.org/bindownload.cgi">P&#x00e1;gina
de descarga de Ant</ulink>. No se cubrir&#x00e1; aqu&#x00ed; c&#x00f3;mo instalar Ant.
Por favor refi&#x00e9;rete al <ulink url="http://ant.apache.org/manual/index.html">
Manual de Ant</ulink>. Despu&#x00e9;s que hayas instalado Ant, podemos comenzar a
crear el buildfile. Ser&#x00e1; llamado <literal>build.xml</literal> y colocado
directamente en el directorio de desarrollo.
</para>
<note>
<title>Reparar Ant</title>
<para>
Observa que la distribuci&#x00f3;n de Ant est&#x00e1; por defecto rota
(como se describe en el FAQ de Ant) y tiene que ser reparado por ti,
por ejemplo, si quisieras usar JUnit desde dentro de tu fichero de construcci&#x00f3;n.
Para hacer que funcione la tarea de JUnit (no lo necesitaremos en este tutorial),
copia junit.jar a <literal>ANT_HOME/lib</literal> o quita el trozo de plugin
<literal>ANT_HOME/lib/ant-junit.jar</literal>.
</para>
</note>
<para>
Un fichero de construcci&#x00f3;n b&#x00e1;sico se ve como esto:
</para>
<programlisting><![CDATA[<project name="hibernate-tutorial" default="compile">
<property name="sourcedir" value="${basedir}/src"/>
<property name="targetdir" value="${basedir}/bin"/>
<property name="librarydir" value="${basedir}/lib"/>
<path id="libraries">
<fileset dir="${librarydir}">
<include name="*.jar"/>
</fileset>
</path>
<target name="clean">
<delete dir="${targetdir}"/>
<mkdir dir="${targetdir}"/>
</target>
<target name="compile" depends="clean, copy-resources">
<javac srcdir="${sourcedir}"
destdir="${targetdir}"
classpathref="libraries"/>
</target>
<target name="copy-resources">
<copy todir="${targetdir}">
<fileset dir="${sourcedir}">
<exclude name="**/*.java"/>
</fileset>
</copy>
</target>
</project>]]></programlisting>
<para>
Esto dir&#x00e1; a Ant que agregue todos los ficheros en el directorio lib que terminen con
<literal>.jar</literal> al classpath usado para la compilaci&#x00f3;n. Tambi&#x00e9;n copiar&#x00e1;
todos los ficheros que no sean c&#x00f3;digo Java al directorio objetivo, por ejemplo,
ficheros de configuraci&#x00f3;n y mapeos de Hibernate. Si ahora corres Ant, debes obtener
esta salida:
</para>
<programlisting><![CDATA[C:\hibernateTutorial\>ant
Buildfile: build.xml
copy-resources:
[copy] Copying 2 files to C:\hibernateTutorial\bin
compile:
[javac] Compiling 1 source file to C:\hibernateTutorial\bin
BUILD SUCCESSFUL
Total time: 1 second ]]></programlisting>
</sect2>
<sect2 id="tutorial-firstapp-helpers">
<title>Arranque y ayudantes</title>
<para>
Es momento de cargar y almacenar algunos objetos <literal>Event</literal>,
pero primero tenemos que completar la configuraci&#x00f3;n de alg&#x00fa;n
c&#x00f3;digo de infraestructura. Tenemos que arrancar Hibernate. Este
arranque incluye construir un objeto <literal>SessionFactory</literal> global
y almacenarlo en alg&#x00fa;n sitio de f&#x00e1;cil acceso en el c&#x00f3;digo
de aplicaci&#x00f3;n. Una <literal>SessionFactory</literal> puede abrir nuevas
<literal>Session</literal>'s. Una <literal>Session</literal> representa un unidad
de trabajo mono-hebra. La <literal>SessionFactory</literal> es un objeto global
seguro entre hebras, instanciado una sola vez.
</para>
<para>
Crearemos una clase de ayuda <literal>HibernateUtil</literal> que cuide del
arranque y haga conveniente el manejo de <literal>Session</literal>.
El as&#x00ed; llamado patr&#x00f3;n <emphasis>Sesi&#x00f3;n de Hebra Local
(ThreadLocal Session)</emphasis> es &#x00fa;til aqu&#x00ed;; mantenemos la unidad
de trabajo actual asociada a la hebra actual. Echemos una mirada a la implementaci&#x00f3;n:
</para>
<programlisting><![CDATA[import org.hibernate.*;
import org.hibernate.cfg.*;
public class HibernateUtil {
public static final SessionFactory sessionFactory;
static {
try {
// Create the SessionFactory from hibernate.cfg.xml
sessionFactory = 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 final ThreadLocal session = new ThreadLocal();
public static Session currentSession() throws HibernateException {
Session s = (Session) session.get();
// Open a new Session, if this thread has none yet
if (s == null) {
s = sessionFactory.openSession();
// Store it in the ThreadLocal variable
session.set(s);
}
return s;
}
public static void closeSession() throws HibernateException {
Session s = (Session) session.get();
if (s != null)
s.close();
session.set(null);
}
}]]></programlisting>
<para>
Esta clase no &#x00f3;lo produce la <literal>SessionFactory</literal> global en
su inicializador static (llamado s&#x00f3;lo una vez por la JVM al cargar la clase),
sino que tambi&#x00e9;n tiene una variable <literal>ThreadLocal</literal> para
tener la <literal>Session</literal> para la hebra actual. No importa cu&#x00e1;ndo
llames a <literal>HibernateUtil.currentSession()</literal>, siempre devolver&#x00e1;
la misma unidad de trabajo de Hibernate en la misma hebra. Una llamada a
<literal>HibernateUtil.closeSession()</literal> termina la unidad de trabajo actualmente
asociada a la hebra.
</para>
<para>
Aseg&#x00fa;rate de entender el concepto Java de una variable local a una hebra antes
de usar esta ayuda. Una clase <literal>HibernateUtil</literal> m&#x00e1;s potente puede
encontrarse en <literal>CaveatEmptor</literal>, http://caveatemptor.hibernate.org/,
as&#x00ed; como en el libro "Hibernate in Action". Observa que esta clase no es necesaria
si despliegas Hibernate en un servidor de aplicaciones J2EE: una <literal>Session</literal>
ser&#x00e1; autom&#x00e1;ticamente ligada a la transacci&#x00f3;n JTA actual, y puedes
buscar la <literal>SessionFactory</literal> a trav&#x00e9;s de JNDI. Si usas JBoss AS,
Hibernate puede ser desplegado como un servicio de sistema manejado y autom&#x00e1;ticamente
ligar&#x00e1; la <literal>SessionFactory</literal> a un nombre JNDI.
</para>
<para>
Coloca <literal>HibernateUtil.java</literal> en el directorio de fuentes de desarrollo,
junto a <literal>Event.java</literal>:
</para>
<programlisting><![CDATA[.
+lib
<Hibernate and third-party libraries>
+src
Event.java
Event.hbm.xml
HibernateUtil.java
hibernate.cfg.xml
+data
build.xml]]></programlisting>
<para>
Esto tambi&#x00e9;n debe compilar sin problemas. Finalmente necesitamos configurar
un sistema de logging (registro). Hibernate usa commons logging y te deja la elecci&#x00f3;n
entre Log4J y logging de JDK 1.4. La mayor&#x00ed;a de los desarrolladores prefieren
Log4J: copia <literal>log4j.properties</literal> de la distribuci&#x00f3;n de Hibernate
(est&#x00e1; en el directorio <literal>etc/</literal>) a tu directorio <literal>src</literal>,
junto a <literal>hibernate.cfg.xml</literal>. Echa una mirada a la configuraci&#x00f3;n de
ejemplo y cambia los ajustes si te gusta tener una salida m&#x00e1;s verborr&#x00e1;gica.
Por defecto, s&#x00f3;lo se muestra el mensaje de arranque de Hibernate en la salida.
</para>
<para>
La infraestructura del tutorial est&#x00e1; completa, y estamos listos para hacer
alg&#x00fa;n trabajo real con Hibernate.
</para>
</sect2>
<sect2 id="tutorial-firstapp-workingpersistence">
<title>Cargando y almacenando objetos</title>
<para>
Finalmente, podemos usar Hibernate para cargar y almacenar objetos.
Escribimos una clase <literal>EventManager</literal> con un m&#x00e9;todo
<literal>main()</literal>:
</para>
<programlisting><![CDATA[import org.hibernate.Transaction;
import org.hibernate.Session;
import java.util.Date;
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.sessionFactory.close();
}
}]]></programlisting>
<para>
Leemos algunos argumentos de la l&#x00ed;nea de comandos, y si el primer
argumento es "store", creamos y almacenamos un nuevo Event:
</para>
<programlisting><![CDATA[private void createAndStoreEvent(String title, Date theDate) {
Session session = HibernateUtil.currentSession();
Transaction tx = session.beginTransaction();
Event theEvent = new Event();
theEvent.setTitle(title);
theEvent.setDate(theDate);
session.save(theEvent);
tx.commit();
HibernateUtil.closeSession();
}]]></programlisting>
<para>
Creamos un nuevo objeto <literal>Event</literal>, y se lo damos a Hibernate.
Hibernate cuida ahora del SQL y ejecuta <literal>INSERT</literal>s en la base
de datos. Echemos una mirada al c&#x00f3;digo de manejo de <literal>Session</literal>
y <literal>Transaction</literal> antes de ejecutar esto.
</para>
<para>
Una <literal>Session</literal> es una sola unidad de trabajo. Podr&#x00ed;a sorprenderte
que tengamos una API adicional, <literal>Transaction</literal>. Esto implica que una unidad
de trabajo puede ser "m&#x00e1;s larga" que una sola transacci&#x00f3;n de base de datos;
imagina una unidad de trabajo que se abarca varios ciclos petici&#x00f3;n/respuesta HTTP
(por ejemplo, un di&#x00e1;logo asistente) en una aplicaci&#x00f3;n web. Separar las
transacciones de base de datos de "las unidades de trabajo de la aplicaci&#x00f3;n desde
el punto de vista del usuario" es uno de los conceptos b&#x00e1;sicos de dise&#x00f1;o de
Hibernate. Llamamos una unidad de trabajo larga <emphasis>Transacci&#x00f3;n de
Aplicaci&#x00f3;n</emphasis>, usualmente encapsulando varias transacciones de base de
datos m&#x00e1;s cortas. Por ahora mantendremos las cosas simples y asumiremos una
granularidad uno-a-uno entre una <literal>Session</literal> y una <literal>Transaction</literal>.
</para>
<para>
¿Qu&#x00e9; es lo que hacen <literal>Transaction.begin()</literal> y <literal>commit()</literal>?
¿D&#x00f3;nde est&#x00e1; el rollback en caso que algo vaya mal? La API de <literal>Transaction</literal>
de Hibernate es opcional realmente, pero la usamos por conveniencia y portabilidad. Si manejases
la transacci&#x00f3;n de base de datos por ti mismo (por ejemplo, llamando a
<literal>session.connection.commit()</literal>), ligar&#x00ed;as el c&#x00f3;digo a un entorno
de despliegue particular, en este JDBC directo no manejado. Estableciendo la f&#x00e1;brica
de <literal>Transaction</literal> en tu configuraci&#x00f3;n de Hibernate puedes desplegar
tu capa de persistencia en cualquier sitio. Echa una mirada al <xref linkend="transactions"/>
para m&#x00e1;s informaci&#x00f3;n sobre manejo y demarcaci&#x00f3;n de transacciones.
Hemos saltado tambi&#x00e9;n cualquier manejo de excepciones y rollback en este ejemplo.
</para>
<para>
Para ejecutar la primera rutina tenemos que agregar un objetivo llamable al fichero
de construcci&#x00f3;n de Ant:
</para>
<programlisting><![CDATA[<target name="run" depends="compile">
<java fork="true" classname="EventManager" classpathref="libraries">
<classpath path="${targetdir}"/>
<arg value="${action}"/>
</java>
</target>]]></programlisting>
<para>
El valor del argumento <literal>action</literal> es establecido por l&#x00ed;nea de
comandos al llamar al objetivo:
</para>
<programlisting><![CDATA[C:\hibernateTutorial\>ant run -Daction=store]]></programlisting>
<para>
Debes ver, despu&#x00e9;s de la compilaci&#x00f3;n, a Hibernate arrancando y, dependiendo
de tu configuraci&#x00f3;n mucha salida de registro (log). Al final encontrar&#x00e1;s
la siguiente l&#x00ed;nea:
</para>
<programlisting><![CDATA[[java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)]]></programlisting>
<para>
Esta es la <literal>INSERT</literal> ejecutada por Hibernate, los signos de preguntas
representan par&#x00e1;metros de ligado JDBC. Para ver los valores ligados como
argumentos, o para reducir la verborragia del registro, chequea tu
<literal>log4j.properties</literal>.
</para>
<para>
Ahora quisi&#x00e9;ramos listar acontecimientos almacenados tambi&#x00e9;n,
as&#x00ed; que agregamos una opci&#x00f3;n al m&#x00e9;todo principal:
</para>
<programlisting><![CDATA[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());
}
}]]></programlisting>
<para>
Agregamos tambi&#x00e9;n un nuevo m&#x00e9;todo <literal>listEvents()</literal>:
</para>
<programlisting><![CDATA[private List listEvents() {
Session session = HibernateUtil.currentSession();
Transaction tx = session.beginTransaction();
List result = session.createQuery("from Event").list();
tx.commit();
session.close();
return result;
}]]></programlisting>
<para>
Lo que hacemos aqu&#x00ed; es usar una consulta HQL (Lenguaje de Consulta de Hibernate
o Hibernate Query Language) para cargar todos los objetos <literal>Event</literal>
existentes de la base de datos. Hibernate generar&#x00e1; el SQL apropiado, lo enviar&#x00e1;
a la base de datosy poblar&#x00e1; los objetos <literal>Event</literal> con datos.
Puedes, por supuesto, crear consultas m&#x00e1;s complejas con HQL.
</para>
<para>
Si ahora llamas a Ant con <literal>-Daction=list</literal>, debes ver los eventos
que has almacenado hasta ahora. Puede sorprenderte que esto no funcione, al menos
si has seguido este tutorial paso por paso; el resultado siempre estar&#x00e1;
vac&#x00ed;o. La razon de esto es la opci&#x00f3;n <literal>hbm2ddl.auto</literal>
en la configuraci&#x00f3;n de Hibernate: Hibernate recrear&#x00e1; la base de datos
en cada ejecuci&#x00f3;n. Deshabil&#x00ed;tala quitando la opci&#x00f3;n, y ver&#x00e1;s
resultados en tu listado despu&#x00e9;s que llames a la acci&#x00f3;n <literal>store</literal>
unas cuantas veces. La generaci&#x00f3;n y exportaci&#x00f3;n de esquema es &#x00fa;til
mayormente en testeo unitario.
</para>
</sect2>
</sect1>
<sect1 id="tutorial-associations">
<title>Part 2 - Mapeando asociaciones</title>
<para>
Hemos mapeado un clase de entidad persistente a una tabla. Construyamos sobre esto y agreguemos
algunas asociaciones de clase. Primero agregaremos personas a nuestra aplicaci&#x00f3;n,
y almacenaremos una lista de eventos en las que participan.
</para>
<sect2 id="tutorial-associations-mappinguser">
<title>Mapeando la clase Person</title>
<para>
El primer corte de la clase <literal>Person</literal> es simple:
</para>
<programlisting><![CDATA[public class Person {
private Long id;
private int age;
private String firstname;
private String lastname;
Person() {}
// Accessor methods for all properties, private setter for 'id'
}]]></programlisting>
<para>
Crea un fichero de mapeo llamado <literal>Person.hbm.xml</literal>:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Person" table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="increment"/>
</id>
<property name="age"/>
<property name="firstname"/>
<property name="lastname"/>
</class>
</hibernate-mapping>]]></programlisting>
<para>
Finalmente, agrega el nuevo mapeo a la configuraci&#x00f3;n de Hibernate:
</para>
<programlisting><![CDATA[ <mapping resource="Event.hbm.xml"/>
<mapping resource="Person.hbm.xml"/>
]]></programlisting>
<para>
Crearemos ahora una asociaci&#x00f3;n entre estas dos entidades. Obviamente,
las personas pueden participar en eventos, y los eventos tienen participantes.
Las cuestiones de dise&#x00f1;o con que tenemos que tratar son: direccionalidad,
multiplicidad y comportamiento de colecci&#x00f3;n.
</para>
</sect2>
<sect2 id="tutorial-associations-unidirset">
<title>Una asociaci&#x00f3;n unidireccional basada en Set</title>
<para>
Agregaremos una colecci&#x00f3;n de eventos a la clase <literal>Person</literal>.
De esta forma podemos navegar facilmente a los eventos de una persona en particular,
sin ejecutar una consulta expl&#x00ed;cita, llamando a <literal>aPerson.getEvents()</literal>.
Usamos una colecci&#x00f3;n Java, un <literal>Set</literal>, porque la colecci&#x00f3;n no
contendr&#x00e1; elementos duplicados y el ordenamiento no nos es relevante.
</para>
<para>
Hasta ahora hemos dise&#x00f1;ado asociaciones unidireccionales multivaluadas, implementadas con un
<literal>Set</literal>. Escribamos el c&#x00f3;digo para esto en las clases Java y luego lo
mapeemos:
</para>
<programlisting><![CDATA[public class Person {
private Set events = new HashSet();
public Set getEvents() {
return events;
}
public void setEvents(Set events) {
this.events = events;
}
}]]></programlisting>
<para>
Antes que mapeemos esta asociaci&#x00f3;n, piensa sobre el otro lado. Claramente, podemos
mantener esto solamente unidireccional. O podemos crear otra colecci&#x00f3;n en el
<literal>Event</literal>, si queremos ser capaces de navegarlos bidireccionalmente;
por ejemplo, <literal>anEvent.getParticipants()</literal>. Esta es una elecci&#x00f3;n
de dise&#x00f1;o que recae en ti, pero lo que est&#x00e1; claro de esta discusi&#x00f3;n
es la multiplicidad de la asociaci&#x00f3;n: "multi" valuada a ambos lados, llamamos a esto
una asociaci&#x00f3;n <emphasis>muchos-a-muchos</emphasis>. Por lo tanto, usamos un mapeo
many-to-many de Hibernate:
</para>
<programlisting><![CDATA[<class name="Person" table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="increment"/>
</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>]]></programlisting>
<para>
Hibernate soporta todo tipo de mapeos de colecci&#x00f3;n, siendo el m&#x00e1;s com&#x00fa;n
un <literal>&lt;set&gt;</literal>. Para una asociaci&#x00f3;n muchos-a-muchos (o relaci&#x00f3;n
de entidad <emphasis>n:m</emphasis>), se necesita una tabla de asociaci&#x00f3;n. Cada fila en esta
tabla representa un enlace entre una persona y un evento. Esta tabla se configura con el atributo
<literal>table</literal> del elemento <literal>set</literal>. El nombre de la columna identificadora
en la asociaci&#x00f3;n, para el lado de la persona, se define con el elemento
<literal>&lt;key&gt;</literal>. El nombre de columna para el lado del evento se define con el atributo
<literal>column</literal> del <literal>&lt;many-to-many&gt;</literal>. Tambi&#x00e9;n tienes que decirle
a Hibernate la clase de los objetos en tu colecci&#x00f3;n (correcto: la clase del otro lado de la
colecci&#x00f3;n de referencias).
</para>
<para>
El esquema de base de datos para este mapeo es, por lo tanto:
</para>
<programlisting><![CDATA[
_____________ __________________
| | | | _____________
| EVENTS | | PERSON_EVENT | | |
|_____________| |__________________| | PERSON |
| | | | |_____________|
| *EVENT_ID | <--> | *EVENT_ID | | |
| EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID |
| TITLE | |__________________| | AGE |
|_____________| | FIRSTNAME |
| LASTNAME |
|_____________|
]]></programlisting>
</sect2>
<sect2 id="tutorial-associations-working">
<title>Trabajando la asociaci&#x00f3;n</title>
<para>
Traigamos alguna gente y eventos juntos en un nuevo m&#x00e9;todo en
<literal>EventManager</literal>:
</para>
<programlisting><![CDATA[private void addPersonToEvent(Long personId, Long eventId) {
Session session = HibernateUtil.currentSession();
Transaction tx = session.beginTransaction();
Person aPerson = (Person) session.load(Person.class, personId);
Event anEvent = (Event) session.load(Event.class, eventId);
aPerson.getEvents().add(anEvent);
tx.commit();
HibernateUtil.closeSession();
}]]></programlisting>
<para>
Despu&#x00e9;s de cargar una <literal>Person</literal> y un <literal>Event</literal>, simplemente
modifica la colecci&#x00f3;n usando sus m&#x00e9;todos normales. Como puedes ver, no hay una llamada
expl&#x00ed;cita a <literal>update()</literal> o <literal>save()</literal>. Hibernate detecta
autom&#x00e1;ticamente que la colecci&#x00f3;n ha sido modificada y necesita ser salvada. Esto
es llamado <emphasis>chequeo sucio autom&#x00f3;tico (automatic dirty checking)</emphasis>, y
tambi&#x00e9;n puedes intentarlo modificando el nombre de la propiedad de fecha de cualquiera de tus
objetos. Mientras est&#x00e9;n en estado <emphasis>persistente</emphasis>, esto es, ligados a una
<literal>Session</literal> de Hibernate particular (es decir, justo han sido cargados o almacenados
en una unidad de trabajo), Hibernate monitoriza cualquier cambio y ejecuta SQL en estilo
escribe-por-detr&#x00e1;s. El proceso de sincronizaci&#x00f3;n del estado de memoria con la base
de datos, usualmente s&#x00f3;lo al final de una unidad de trabajo,
es llamado <emphasis>limpieza (flushing)</emphasis>.
</para>
<para>
Podr&#x00ed;as, por supuesto, cargar persona y evento en unidades de trabajo diferentes. O
modificas un objeto fuera de una <literal>Session</literal>, cuando no est&#x00e1; en estado
persistente (si antes era persistente llamamos a este estado <emphasis>separado (detached)
</emphasis>). En c&#x00f3;digo (no muy realista), esto se ver&#x00ed;a como sigue:
</para>
<programlisting><![CDATA[ private void addPersonToEvent(Long personId, Long eventId) {
Session session = HibernateUtil.currentSession();
Transaction tx = session.beginTransaction();
Person aPerson = (Person) session.load(Person.class, personId);
Event anEvent = (Event) session.load(Event.class, eventId);
tx.commit();
HibernateUtil.closeSession();
aPerson.getEvents().add(anEvent); // aPerson is detached
Session session2 = HibernateUtil.currentSession();
Transaction tx2 = session.beginTransaction();
session2.update(aPerson); // Reattachment of aPerson
tx2.commit();
HibernateUtil.closeSession();
}
]]></programlisting>
<para>
La llamada a <literal>update</literal> hace a un objeto persistente de nuevo, podr&#x00ed;as
decir que la liga a una nueva unidad de trabajo, de modo que cualquier modificaci&#x00f3;n que
le hagas mientras est&#x00e9; separado puede ser salvada a base de datos.
</para>
<para>
Bueno, esto no es muy usado en nuestra situaci&#x00f3;n actual, pero es un concepto
importante que puedes dise&#x00f1;ar en tu propia aplicaci&#x00f3;n. Por ahora, completa
este ejercicio agregando una nueva acci&#x00f3;n al m&#x00e9;todo main de
<literal>EventManager</literal> y ll&#x00e1;mala desde la l&#x00ed;nea de comandos.
Si necesitas los identificadores de una persona o evento, el m&#x00e9;todo
<literal>save()</literal> los devuelve.
</para>
<para>
Esto fue un ejemplo de una asociaci&#x00f3;n entre dos clases igualmente importantes, dos entidades.
Como se ha mencionado anteriormente, hay otras clases y tipos en un modelo t&#x00ed;pico,
usualmente "menos importantes". Algunos ya los habr&#x00e1;s visto, como un <literal>int</literal>
o un <literal>String</literal>. Llamamos a estas clases <emphasis>tipos de valor (value types)</emphasis>,
y sus instancias <emphasis>dependen</emphasis> de una entidad en particular. Las instancias de estos
tipos no tienen su propia identidad, ni son compartidas entre entidades (dos personas no referencian
el mismo objeto <literal>firstname</literal>, incluso si tuvieran el mismo primer nombre). Por supuesto,
los tipos de valor no s&#x00f3;lo pueden encontrarse en el JDK (de hecho, en una aplicaci&#x00f3;n
Hibernate todas las clases del JDK son consideradas tipos de valor), sino que adem&#x00e1;s puedes
escribir por ti mismo clases dependientes, por ejemplo, <literal>Address</literal> o
<literal>MonetaryAmount</literal>.
</para>
<para>
Tambi&#x00e9;n puedes dise&#x00f1;ar una colecci&#x00f3;n de tipos de valor. Esto es conceptualmente
muy diferente de una colecci&#x00f3;n de referencias a otras entidades, pero se ve casi lo mismo en
Java.
</para>
</sect2>
<sect2 id="tutorial-associations-valuecollections">
<title>Colecci&#x00f3;n de valores</title>
<para>
Agregamos una colecci&#x00f3;n de objetos tipificados en valor a la entidad <literal>Person</literal>.
Queremos almacenar direcciones de email, de modo que el tipo que usamos es <literal>String</literal>,
y la colecci&#x00f3;n es nuevamente un <literal>Set</literal>:
</para>
<programlisting><![CDATA[private Set emailAddresses = new HashSet();
public Set getEmailAddresses() {
return emailAddresses;
}
public void setEmailAddresses(Set emailAddresses) {
this.emailAddresses = emailAddresses;
}]]></programlisting>
<para>
El mapeo de este <literal>Set</literal>:
</para>
<programlisting><![CDATA[<set name="emailAddresses" table="PERSON_EMAIL_ADDR">
<key column="PERSON_ID"/>
<element type="string" column="EMAIL_ADDR"/>
</set>]]></programlisting>
<para>
La diferencia comparada con el mapeo anterior es la parte <literal>element</literal>, que le dice
a Hibernate que la colecci&#x00f3;n no contiene referencias a otra entidad, sino una colecci&#x00f3;n
de elementos de tipo <literal>String</literal> (el nombre en min&#x00fa;sculas te dice que es un
tipo/conversor de mapeo de Hibernate). Una vez m&#x00e1;s, el atributo <literal>table</literal> del
elemento <literal>set</literal> determina el nombre de la tabla para la colecci&#x00f3;n. El elemento
<literal>key</literal> define el nombre de la columna clave for&#x00e1;nea en la tabla de colecci&#x00f3;n.
El atributo <literal>column</literal> en el elemento <literal>element</literal> define el nombre de
columna donde realmente ser&#x00e1;n almacenados los valores <literal>String</literal>.
</para>
<para>
Echa una mirada al esquema actualizado:
</para>
<programlisting><![CDATA[
_____________ __________________
| | | | _____________
| EVENTS | | PERSON_EVENT | | | ___________________
|_____________| |__________________| | PERSON | | |
| | | | |_____________| | PERSON_EMAIL_ADDR |
| *EVENT_ID | <--> | *EVENT_ID | | | |___________________|
| EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID | <--> | *PERSON_ID |
| TITLE | |__________________| | AGE | | *EMAIL_ADDR |
|_____________| | FIRSTNAME | |___________________|
| LASTNAME |
|_____________|
]]></programlisting>
<para>
Puedes ver que la clave primaria de la tabla de colecci&#x00f3;n es de hecho una clave
compuesta, usando ambas columnas. Esto implica tambi&#x00e9;n que no pueden haber
direcciones de email duplicadas por persona, que es exactamente la sem&#x00e1;ntica
que necesitamos para un conjunto en Java.
</para>
<para>
Puedes ahora intentar y agregar elementos a esta colecci&#x00f3;n, al igual que
hicimos antes enlazando personas y eventos. Es el mismo c&#x00f3;digo en Java.
</para>
</sect2>
<sect2 id="tutorial-associations-bidirectional">
<title>Asociaciones bidireccionales</title>
<para>
A continuacion vamos a mapear una asociaci&#x00f3;n bidireccional, haciendo que la
asociaci&#x00f3;n entre persona y evento funcione desde ambos lados en Java. Por supuesto,
el esquema de base de datos no cambia; todav&#x00ed;a necesitamos multiplicidad muchos-a-muchos.
Una base de datos relacional es m&#x00e1;s flexible que un lenguaje de programaci&#x00f3;n
de red, as&#x00ed; que no necesita nada parecido a una direcci&#x00f3;n de navegaci&#x00f3;n;
los datos pueden ser vistos y recuperados en cualquier forma posible.
</para>
<para>
Primero agrega una colecci&#x00f3;n de participantes a la clase de eventos
<literal>Event</literal>:
</para>
<programlisting><![CDATA[private Set participants = new HashSet();
public Set getParticipants() {
return participants;
}
public void setParticipants(Set participants) {
this.participants = participants;
}]]></programlisting>
<para>
Ahora mapea este lado de la asociaci&#x00f3;n tambi&#x00e9;n, en
<literal>Event.hbm.xml</literal>.
</para>
<programlisting><![CDATA[<set name="participants" table="PERSON_EVENT" inverse="true">
<key column="EVENT_ID"/>
<many-to-many column="PERSON_ID" class="Person"/>
</set>]]></programlisting>
<para>
Como ves, estos son mapeos normales de <literal>set</literal> en ambos documentos de
mapeo. Nota que los nombres de columnas en <literal>key</literal> y
<literal>many-to-many</literal> fueron permutados en ambos documentos de mapeo. Aqu&#x00ed; la
adici&#x00f3;n m&#x00e1;s importante es el atributo <literal>inverse="true"</literal> en el
elemento <literal>set</literal> del mapeo de colecci&#x00f3;n de <literal>Event</literal>.
</para>
<para>
Lo que esto significa es que Hibernate debe tomar el otro lado - la clase <literal>Person</literal> -
cuando necesite descubrir informaci&#x00f3;n sobre el enlace entre las dos. Esto ser&#x00e1; mucho
m&#x00e1;s f&#x00e1;cil de entender una vez que veas c&#x00f3;mo se crea el enlace bidireccional
entre nuestras dos entidades.
</para>
</sect2>
<sect2 id="tutorial-associations-usingbidir">
<title>Trabajando enlaces bidireccionales</title>
<para>
Primero, ten en mente que Hhibernate no afecta la sem&#x00e1;ntica normal de Java. ¿C&#x00f3;mo
hemos creado un enlace entre una <literal>Person</literal> y un <literal>Event</literal> en el
ejemplo unidireccional? Hemos agregado una instancia de <literal>Event</literal> a la colecci&#x00f3;n
de referencias de eventos de una instancia de <literal>Person</literal>. De modo que, obviamente,
si queremos que este enlace funcione bidireccionalmente, tenemos que hacer lo mismo del otro lado,
agregando una referencia a <literal>Person</literal> a la colecci&#x00f3;n en un <literal>Event</literal>.
Este "establecer el enlace a ambos lados" es absolutamente necesario y nunca debes olvidar hacerlo.
</para>
<para>
Muchos desarrolladores programan a la defensiva y crean m&#x00e9;todos de
manejo de un enlace para establecer correctamente ambos lados, por ejemplo
en <literal>Person</literal>:
</para>
<programlisting><![CDATA[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);
}]]></programlisting>
<para>
Nota que los m&#x00e9;todos get y set para esta colecci&#x00f3;n son ahora protegidos. Esto le
permite a clases en el mismo paquete y a subclases acceder a&#x00fa;n a los m&#x00e9;todos, pero
previene a cualquier otro de ensuciarse con la colecci&#x00f3;n directamente (bueno, casi).
Probablemente debas hacer lo mismo con la colecci&#x00f3;n al otro lado.
</para>
<para>
Y ¿qu&#x00e9; del atributo de mapeo <literal>inverse</literal>? Para ti, y para Java, un enlace
bidireccional es simplemente cuesti&#x00f3;n de establecer correctamente las referencias a ambos
lados. Hibernate, sin embargo, no tiene suficiente informaci&#x00f3;n para arreglar correctamente
sentencias <literal>INSERT</literal> y <literal>UPDATE</literal> de SQL (para evitar violaci&#x00f3;n
de restricciones), y necesita alguna ayuda para manejar asociaciones bidireccionales apropiadamente.
El hacer un lado de la asociaci&#x00f3;n <literal>inverse</literal> le dice a Hibernate que basicamente
lo ignore, que lo considere un <emphasis>espejo</emphasis> del otro lado. Esto es todo lo necesario para
que Hibernate resuelva todas las incidencias al transformar un modelo de navegaci&#x00f3;n direccional a
un esquema SQL de base de datos. Las reglas que tienes que recordar son directas: Todas las asociaciones
bidireccionales necesitan uno de los lados como <literal>inverse</literal>. En una asociaci&#x00f3;n
uno-a-muchos debe ser el lado-de-muchos. En una asociaci&#x00f3;n muchos-a-muchos, puedes tomar cualquier
lado, no hay diferencia.
</para>
<!--
<para>
In the next section we integrate Hibernate with Tomcat and WebWork - the <literal>EventManager</literal>
doesn't scale anymore with our growing application.
</para>
-->
</sect2>
</sect1>
<sect1 id="tutorial-summary">
<title>Summary</title>
<para>
Este tutorial cubri&#x00f3; los fundamentos de escribir una simple aplicaci&#x00f3;n independiente
de Hibernate.
</para>
<para>
Si ya te sientes confidente con Hibernate, contin&#x00fa;a navegando a trav&#x00e9;s de la
tabla de contenidos de la documentaci&#x00f3;n de referencia para los temas que encuentres
interesantes. Los m&#x00e1;s consultados son procesamiento transaccional (<xref linkend="transactions"/>),
rendimiento de recuperaci&#x00f3;n (<xref linkend="performance"/>), o el uso de la API
(<xref linkend="objectstate"/>) y las funcionalidades de consulta (<xref linkend="objectstate-querying"/>).
</para>
<para>
No olvides chequear el sitio web de Hibernate por m&#x00e1;s (especializados) tutoriales.
</para>
</sect1>
</chapter>