Author: steve.ebersole(a)jboss.com
Date: 2007-10-09 14:45:36 -0400 (Tue, 09 Oct 2007)
New Revision: 14075
Added:
core/trunk/documentation/manual/es-ES/src/main/docbook/Hibernate_Reference.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/
core/trunk/documentation/manual/es-ES/src/main/docbook/content/architecture.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/association_mapping.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/basic_mapping.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/batch.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/best_practices.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/collection_mapping.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/component_mapping.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/configuration.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/events.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/example_mappings.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/example_parentchild.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/example_weblog.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/filters.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/inheritance_mapping.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/performance.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/persistent_classes.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/preface.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/query_criteria.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/query_hql.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/query_sql.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/quickstart.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/session_api.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/toolset_guide.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/transactions.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/tutorial.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/content/xml.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/images/
core/trunk/documentation/manual/es-ES/src/main/docbook/images/AuthorWork.png
core/trunk/documentation/manual/es-ES/src/main/docbook/images/AuthorWork.zargo
core/trunk/documentation/manual/es-ES/src/main/docbook/images/CustomerOrderProduct.png
core/trunk/documentation/manual/es-ES/src/main/docbook/images/CustomerOrderProduct.zargo
core/trunk/documentation/manual/es-ES/src/main/docbook/images/EmployerEmployee.png
core/trunk/documentation/manual/es-ES/src/main/docbook/images/EmployerEmployee.zargo
core/trunk/documentation/manual/es-ES/src/main/docbook/images/full_cream.png
core/trunk/documentation/manual/es-ES/src/main/docbook/images/full_cream.svg
core/trunk/documentation/manual/es-ES/src/main/docbook/images/hibernate_logo_a.png
core/trunk/documentation/manual/es-ES/src/main/docbook/images/lite.png
core/trunk/documentation/manual/es-ES/src/main/docbook/images/lite.svg
core/trunk/documentation/manual/es-ES/src/main/docbook/images/overview.png
core/trunk/documentation/manual/es-ES/src/main/docbook/images/overview.svg
core/trunk/documentation/manual/es-ES/src/main/docbook/legal_notice.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/legal_notice2.xml
core/trunk/documentation/manual/es-ES/src/main/docbook/translators.xml
Removed:
core/trunk/documentation/manual/es-ES/src/main/docbook/master.xml
core/trunk/documentation/manual/es-ES/src/main/resources/
Log:
new docbook layout (prep for translations migration to PO)
Added: core/trunk/documentation/manual/es-ES/src/main/docbook/Hibernate_Reference.xml
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/Hibernate_Reference.xml
(rev 0)
+++
core/trunk/documentation/manual/es-ES/src/main/docbook/Hibernate_Reference.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,88 @@
+<?xml version='1.0' encoding="iso-8859-1"?>
+<!--
+ ~ Copyright (c) 2007, Red Hat Middleware, LLC. All rights reserved.
+ ~
+ ~ This copyrighted material is made available to anyone wishing to use, modify,
+ ~ copy, or redistribute it subject to the terms and conditions of the GNU
+ ~ Lesser General Public License, v. 2.1. This program is distributed in the
+ ~ hope that it will be useful, but WITHOUT A WARRANTY; without even the implied
+ ~ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details. You should have received a
+ ~ copy of the GNU Lesser General Public License, v.2.1 along with this
+ ~ distribution; if not, write to the Free Software Foundation, Inc.,
+ ~ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ ~
+ ~ Red Hat Author(s): Steve Ebersole
+ -->
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
+ <!ENTITY versionNumber "3.3.0.alpha1">
+ <!ENTITY copyrightYear "2004">
+ <!ENTITY copyrightHolder "Red Hat Middleware, LLC.">
+]>
+
+<book>
+
+ <bookinfo>
+ <title>HIBERNATE - Persistencia Relacional para Java
Idiomático</title>
+ <subtitle>Documentación de Referencia de
Hibernate</subtitle>
+ <releaseinfo>&versionNumber;</releaseinfo>
+ <productnumber>&versionNumber;</productnumber>
+ <issuenum>1</issuenum>
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/hibernate_logo_a.png"
align="center" />
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="images/hibernate_logo_a.png"
depth="3cm" />
+ </imageobject>
+ </mediaobject>
+ <copyright>
+ <year>©rightYear;</year>
+ <holder>©rightHolder;</holder>
+ </copyright>
+ <xi:include href="translators.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+ <xi:include href="legal_notice.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+ <xi:include href="legal_notice2.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+ </bookinfo>
+
+ <toc/>
+
+ <xi:include href="content/preface.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+
+ <xi:include href="content/tutorial.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+
+ <xi:include href="content/architecture.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+
+ <xi:include href="content/configuration.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+
+ <xi:include href="content/persistent_classes.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+
+ <xi:include href="content/basic_mapping.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+ <xi:include href="content/collection_mapping.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+ <xi:include href="content/association_mapping.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+ <xi:include href="content/component_mapping.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+ <xi:include href="content/inheritance_mapping.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+
+ <xi:include href="content/session_api.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+ <xi:include href="content/transactions.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+ <xi:include href="content/events.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+ <xi:include href="content/batch.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+
+ <xi:include href="content/query_hql.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+ <xi:include href="content/query_criteria.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+ <xi:include href="content/query_sql.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+ <xi:include href="content/filters.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+ <xi:include href="content/xml.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+
+ <xi:include href="content/performance.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+
+ <xi:include href="content/toolset_guide.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+
+ <xi:include href="content/example_parentchild.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+ <xi:include href="content/example_weblog.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+ <xi:include href="content/example_mappings.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+
+ <xi:include href="content/best_practices.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+
+</book>
+
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/content/architecture.xml
(from rev 12794,
core/trunk/documentation/manual/es-ES/src/main/docbook/modules/architecture.xml)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/content/architecture.xml
(rev 0)
+++
core/trunk/documentation/manual/es-ES/src/main/docbook/content/architecture.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,279 @@
+<chapter id="architecture">
+
+ <title>Arquitectura</title>
+
+ <sect1 id="architecture-overview" revision="1">
+ <title>Visión General</title>
+
+ <para>
+ Una visión a (muy) alto nivel de la arquitectura de Hibernate:
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="../images/overview.svg"
format="SVG" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../images/overview.gif"
format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ Este diagrama muestra a Hibernate usando la base de datos y los datos de
+ configuración para proveer servicios de persistencia (y objetos
+ persistentes) a la aplicación.
+ </para>
+
+ <para>
+ Nos gustaría mostrar una vista más detallada de la
arquitectura de tiempo
+ de ejecución. Desafortunadamente, Hibernate es flexible y soporta
diferentes
+ enfoques. Mostraremos los dos extremos. En la arquitectura
"sencilla", es la
+ aplicación la que provee su propias conexiones JDBC y gestiona sus
propias
+ transacciones. Este enfoque usa un mínimo subconjunto de la API de
Hibernate:
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="../images/lite.svg"
format="SVG" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../images/lite.gif"
format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ La arquitectura "full cream" abstrae a la aplicación de
las APIs
+ de JDBC/JTA y deja que Hibernate se encargue de los detalles.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="../images/full_cream.svg"
format="SVG" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../images/full_cream.gif"
format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ He aquí algunas definiciones de los objetos en los diagramas:
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>SessionFactory
(<literal>org.hibernate.SessionFactory</literal>)</term>
+ <listitem>
+ <para>
+ Caché threadsafe (inmutable) de mapeos compilados
para
+ una sola base de datos. Es una fábrica de
<literal>Session</literal>
+ y un cliente de
<literal>ConnectionProvider</literal>. Opcionalmente,
+ puede mantener una caché (de segundo nivel) de
datos reusables
+ entre transacciones, a un nivel de proceso o de cluster.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Session
(<literal>org.hibernate.Session</literal>)</term>
+ <listitem>
+ <para>
+ Objeto mono-hebra, de corta vida que representa una
conversación
+ entre la aplicación y el almacenamiento
persistente. Envuelve una
+ conexión JDBC. Es una fábrica de
<literal>Transaction</literal>.
+ Mantiene una caché requerida (de primer nivel) de
objetos persistentes,
+ usada mientras se navega el grafo de objetos o se recuperen
objetos por
+ identificador.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Objetos y colecciones persistentes</term>
+ <listitem>
+ <para>
+ Objetos de corta vida, mono-hebra conteniendo estado
persistente y
+ funciónalidad de negocio. Estos pueden ser
JavaBeans/POJOs
+ (Plain Old Java Objects, o sea, cualquier objeto Java), la
única
+ cosa especial en ellos es que estan asociados actualmente con
una
+ (y sólo una)
<literal>Session</literal>. Tan pronto como la
+ <literal>Session</literal> sea cerrada,
serán separados y
+ estarán libres para ser usados en cualquier capa
de aplicación.
+ (por ejemplo, directamente como objetos de transferencia de
datos hacia
+ y desde la capa de presentación).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Objetos y colecciones transitorios y
separados</term>
+ <listitem>
+ <para>
+ Instancias de clases persistentes que no estan acutualmente
asociadas
+ con una <literal>Session</literal>. Pueden haber
sido instanciadas por
+ la aplicación y (aún) no haber sido
hechas persistentes,
+ o pueden haber sido instanciadas por una
<literal>Session</literal> cerrada.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Transaction
(<literal>org.hibernate.Transaction</literal>)</term>
+ <listitem>
+ <para>
+ (Opcional) Un objeto de corta vida, mono-hebra, usado por la
aplicación
+ para especificar unidades atómicas de trabajo.
Abstrae a la aplicación
+ de las subyacentes transacciones JDBC, JTA o CORBA. En
algunos casos, una
+ <literal>Session</literal> puede extenderse sobre
varias <literal>Transaction</literal>s.
+ Sin embargo, la demarcación de la
transacción, ya sea usando la API
+ subyacente o <literal>Transaction</literal>,
nunca es opcional!
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ConnectionProvider
(<literal>org.hibernate.connection.ConnectionProvider</literal>)</term>
+ <listitem>
+ <para>
+ (Opcional) Una fábrica (y pool) de conexiones
JDBC. Abstrae a la aplicación
+ del <literal>Datasource</literal> o
<literal>DriverManager</literal> subyacente.
+ No se expone a la aplicación, pero puede ser
extendido/implementado por
+ el desarrollador.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>TransactionFactory
(<literal>org.hibernate.TransactionFactory</literal>)</term>
+ <listitem>
+ <para>
+ (Opcional) Una fábrica de instancias de
<literal>Transaction</literal>.
+ No se expone a la aplicación, pero puede ser
extendido/implementado por
+ el desarrollador.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Interfaces de
Extensión</emphasis></term>
+ <listitem>
+ <para>
+ Hibernate ofrece muchas interfaces de extensión
opcional que puedes
+ implementar para modificar a medida el comportamiento de tu
capa de persistencia.
+ Para más detalles, mira la
documentación de la API.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ Dada una arquitectura "sencilla", la aplicación pasa por
alto las APIs
+ de
<literal>Transaction</literal>/<literal>TransactionFactory</literal>
y/o
+ <literal>ConnectionProvider</literal>, para hablar directamente a
JTA o JDBC.
+ </para>
+ </sect1>
+
+ <sect1 id="architecture-states" revision="1">
+ <title>Estados de instancia</title>
+ <para>
+ Una instancia de una clase persistente puede estar en uno de tres estados
+ diferentes, definidos respecto de su <emphasis>contexto de
persistencia</emphasis>.
+ El objeto <literal>Session</literal> de Hibernate es el contexto
de persistencia:
+ </para>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>transitorio</term>
+ <listitem>
+ <para>
+ La instancia no está y nunca estuvo asociada con
+ un contexto de persistencia. No tiene identidad persistente
+ (valor de clave primaria).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>persistente</term>
+ <listitem>
+ <para>
+ La instancia está actualmente asociada con un
+ contexto de persistencia. Tiene una identidad persistente
+ (valor de clave primaria) y, quizás, una fila
+ correspondiente en la base de datos. Para un contexto de
+ persistencia en particular, Hibernate
<emphasis>garantiza</emphasis>
+ que la identidad persistente es equivalente a la identidad
+ Java (localización en memoria del objeto).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>separado</term>
+ <listitem>
+ <para>
+ La instancia estuvo una vez asociada con un contexto
+ de persistencia, pero ese contexto fue cerrado, o la
+ instancia fue serializada a otro proceso. Tiene una
+ identidad persistente y, quizás, una fila
correspondiente
+ en la base de datos. Para las instancias separadas,
+ Hibernate no establece ninguna garantía sobre
+ la relación entre identidad persistente e identidad
Java.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect1>
+
+ <sect1 id="architecture-jmx" revision="1">
+ <title>Integración JMX</title>
+
+ <para>
+ JMX es el estándar J2EE para la gestión de componentes
Java. Hibernate
+ puede ser gestionado por medio de un servicio estándar JMX.
+ Proveemos una implementación de MBean en la
distribución,
+ <literal>org.hibernate.jmx.HibernateService</literal>.
+ </para>
+
+ <para>
+ Para ejemplo de cómo desplegar Hibernate como un servicio JMX en
un Servidor
+ de Aplicaciones JBoss, por favor, mira la Guía del Usuario de
JBoss.
+ En JBoss AS, tienes además estos beneficios si despliegas usando
JMX:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Gestión de
Sesión:</emphasis> El ciclo de vida de la
<literal>Session</literal>
+ de Hibernate puede estar automáticamente ligado al
ámbito de una transacción
+ JTA. Esto significa que ya no tienes que abrir ni cerrar la
<literal>Session</literal> manualmente,
+ esto pasa a ser trabajo de un interceptor EJB de JBoss.
Además tampoco tienes
+ que preocuparte más de la demarcación de la
transacción (a menos que
+ que quieras escribir una capa de persitencia portable, por supuesto,
usa la API de
+ <literal>Transaction</literal> de Hibernate para esto).
Para acceder a una
+ <literal>Session</literal> llama al
<literal>HibernateContext</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Despliegue de HAR:</emphasis> Usualmente
despliegas el servicio JMX de Hibernate
+ usando un descriptor de despliegue de servicio de JBoss (en un
fichero EAR y/o SAR), que soporta
+ todas las opciones de configuración usuales de una
<literal>SessionFactory</literal> de
+ Hibernate. Sin embargo, todavía tienes que nombrar todos
tus ficheros de mapeo en el
+ descriptor de despliegue. Si decides usar el depliegue de HAR
opcional, JBoss detectará
+ automáticamente todos los ficheros de mapeo en tu fichero
HAR.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Para más información sobre estas opciones, consulta la
+ Guía de Usuario del JBoss AS.
+ </para>
+
+ <para>
+ Otra funcionalidad disponible como un servicio JMX son las
estadísticas en
+ tiempo de ejecución de Hibernate. Mira <xref
linkend="configuration-optional-statistics"/>.
+ </para>
+ </sect1>
+
+ <sect1 id="architecture-jca" revision="1">
+ <title>Soporte JCA:</title>
+ <para>
+ Hiberate puede además ser configurado como un conector JCA. Por
favor mira el
+ sitio web para más detalles. Por favor ten en cuenta que el
soporte de JCA
+ de Hibernate está aún considerado experimental.
+ </para>
+ </sect1>
+
+</chapter>
+
Copied:
core/trunk/documentation/manual/es-ES/src/main/docbook/content/association_mapping.xml
(from rev 12794,
core/trunk/documentation/manual/es-ES/src/main/docbook/modules/association_mapping.xml)
===================================================================
---
core/trunk/documentation/manual/es-ES/src/main/docbook/content/association_mapping.xml
(rev 0)
+++
core/trunk/documentation/manual/es-ES/src/main/docbook/content/association_mapping.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,527 @@
+<chapter id="associations">
+
+ <title>Mapeos de Asociación</title>
+
+ <sect1 id="assoc-intro" revision="1">
+ <title>Introducción</title>
+
+ <para>
+ Los mapeos de asociación son frecuentemente las cosas mas
difíciles
+ de hacer correctamente. En esta sección iremos a través
de los casos
+ canónicos uno a uno, comenzando con los mapeos unidireccionales, y
considerando
+ luego los casos bidireccionales. Usaremos
<literal>Person</literal> y <literal>Address</literal>
+ en todos los ejemplos.
+ </para>
+
+ <para>
+ Clasificaremos las asociaciones por cuanto mapeen o no a una tabla
+ de unión interviniente, y por su multiplicidad.
+ </para>
+
+ <para>
+ Las claves foráneas que aceptan valores nulos (en adelante,
nullables)
+ no son consideradas una buena práctica en el modelado
tradicional de datos,
+ así que todos nuestros ejemplos usan claves
foráneas no nullables.
+ Esto no es un requerimiento de Hibernate, y todos los mapeos
funcionarán
+ si quitas las restricciones de nulabilidad.
+ </para>
+
+ </sect1>
+
+ <sect1 id="assoc-unidirectional" revision="1">
+ <title>Asociaciones Unidireccionales</title>
+
+ <sect2 id="assoc-unidirectional-m21">
+ <title>muchos a uno</title>
+
+ <para>
+ Una <emphasis>asociación unidireccional
muchos-a-uno</emphasis> es el tipo
+ más común de asociaciones unidireccionales.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-121">
+ <title>uno a uno</title>
+
+ <para>
+ Una <emphasis>asociación unidireccional uno-a-uno en una
clave primaria</emphasis>
+ es casi idéntica. La única diferencia es la
restricción de unicidad
+ de la columna.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ unique="true"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null
unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ <para>
+ Usualmente, una <emphasis>asociación unidireccional
uno-a-uno en una
+ clave primaria</emphasis> usa un generador de id especial. (Observa que
hemos
+ invertido el sentido de la asociación en este ejemplo).
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+</class>
+
+<class name="Address">
+ <id name="id" column="personId">
+ <generator class="foreign">
+ <param name="property">person</param>
+ </generator>
+ </id>
+ <one-to-one name="person" constrained="true"/>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table Address ( personId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-12m">
+ <title>uno a muchos</title>
+
+ <para>
+ Una <emphasis>asociación unidireccional uno-a-muchos en una
clave foránea</emphasis>
+ es un caso muy inusual, y realmente no está recomendada.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses">
+ <key column="personId"
+ not-null="true"/>
+ <one-to-many class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table Address ( addressId bigint not null primary key, personId bigint not null )
+ ]]></programlisting>
+
+ <para>
+ Creemos que es mejor usar una tabla de unión para este tipo de
asociación.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-unidirectional-join" revision="1">
+ <title>Asociaciones unidireccionales con tablas de
unión</title>
+
+ <sect2 id="assoc-unidirectional-join-12m">
+ <title>uno a muchos</title>
+
+ <para>
+ Una <emphasis>asociación unidireccional uno-a-muchos en una
tabla de unión</emphasis>
+ es más preferible. Observa que especificando
<literal>unique="true"</literal>, hemos
+ cambiado la multiplicidad de muchos-a-muchos a uno-a-muchos.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses" table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ unique="true"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId not null, addressId bigint not null primary key )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-join-m21">
+ <title>muchos a uno</title>
+
+ <para>
+ Una <emphasis>asociación unidireccional muchos-a-uno en una
tabla de unión</emphasis>
+ es bastante común cuando la asociación es opcional.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true">
+ <key column="personId" unique="true"/>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"/>
+ </join>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null primary key, addressId bigint not
null )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-join-121">
+ <title>uno a uno</title>
+
+ <para>
+ Una <emphasis>asociación unidireccional uno-a-uno en una
tabla de unión</emphasis>
+ es inusual en extremo, pero posible.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true">
+ <key column="personId"
+ unique="true"/>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"
+ unique="true"/>
+ </join>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null primary key, addressId bigint not
null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-join-m2m">
+ <title>muchos a muchos</title>
+
+ <para>
+ Finalmente, tenemos una <emphasis>asociación unidireccional
muchos-a-muchos</emphasis>
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses" table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null, addressId bigint not null, primary
key (personId, addressId) )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-bidirectional" revision="1">
+ <title>Asociaciones Bidireccionales</title>
+
+ <sect2 id="assoc-bidirectional-m21">
+ <title>uno a muchos / muchos a uno</title>
+
+ <para>
+ Una <emphasis>asociación bidireccional
muchos-a-uno</emphasis> es
+ el tipo más común de asociación. (Esta es la
relación
+ estándar padre/hijo.)
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <set name="people" inverse="true">
+ <key column="addressId"/>
+ <one-to-many class="Person"/>
+ </set>
+</class>]]></programlisting>
+
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-bidirectional-121">
+ <title>uno a uno</title>
+
+ <para>
+ Una <emphasis>asociación bidireccional uno-a-uno en una
clave foránea</emphasis>
+ es bastante común.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ unique="true"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <one-to-one name="person"
+ property-ref="address"/>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null
unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ <para>
+ Una <emphasis>asociación bidireccional uno-a-uno en una
clave primaria</emphasis>
+ usa el generador de id especial.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <one-to-one name="address"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="personId">
+ <generator class="foreign">
+ <param name="property">person</param>
+ </generator>
+ </id>
+ <one-to-one name="person"
+ constrained="true"/>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table Address ( personId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-bidirectional-join" revision="1">
+ <title>Asociaciones bidireccionales con tablas de
unión</title>
+
+ <sect2 id="assoc-bidirectional-join-12m">
+ <title>uno a muchos / muchos a uno</title>
+
+ <para>
+ Una <emphasis>asociación bidireccional uno-a-muchos en una
tabla de unión</emphasis>.
+ Observa que el <literal>inverse="true"</literal> puede
ir a cualquier lado de la asociación,
+ en la colección, o en la unión.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses"
+ table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ unique="true"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ inverse="true"
+ optional="true">
+ <key column="addressId"/>
+ <many-to-one name="person"
+ column="personId"
+ not-null="true"/>
+ </join>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null, addressId bigint not null primary
key )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-bidirectional-join-121">
+ <title>uno a uno</title>
+
+ <para>
+ Una <emphasis>asociación bidireccional uno-a-uno en una
tabla de unión</emphasis>
+ es inusual en extremo, pero posible.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true">
+ <key column="personId"
+ unique="true"/>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"
+ unique="true"/>
+ </join>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true"
+ inverse="true">
+ <key column="addressId"
+ unique="true"/>
+ <many-to-one name="address"
+ column="personId"
+ not-null="true"
+ unique="true"/>
+ </join>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null primary key, addressId bigint not
null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-bidirectional-join-m2m">
+ <title>muchos a muchos</title>
+
+ <para>
+ Finalmente, tenemos una <emphasis>asociación bidireccional
muchos-a-muchos</emphasis>.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <set name="people" inverse="true">
+ <key column="addressId"/>
+ <many-to-many column="personId"
+ class="Person"/>
+ </set>
+</class>]]></programlisting>
+
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null, addressId bigint not null, primary
key (personId, addressId) )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+
+</chapter>
+
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/content/basic_mapping.xml
(from rev 12794,
core/trunk/documentation/manual/es-ES/src/main/docbook/modules/basic_mapping.xml)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/content/basic_mapping.xml
(rev 0)
+++
core/trunk/documentation/manual/es-ES/src/main/docbook/content/basic_mapping.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,3222 @@
+<chapter id="mapping">
+ <title>Mapeo O/R Básico</title>
+
+ <sect1 id="mapping-declaration" revision="1">
+ <title>Declaración de mapeo</title>
+
+ <para>
+ Los mapeos objeto/relacional se definen usualmente en un documento XML.
+ El documento de mapeo está diseñado para ser
leíble y
+ editable a mano. El lenguaje de mapeo es Java-céntrico, o sea que
los
+ mapeos se construyen alrededor de declaraciones de clases persistentes,
+ no declaraciones de tablas.
+ </para>
+
+ <para>
+ Observa que, incluso aunque muchos usuarios de Hibernate eligen escribir el
+ XML a mano, existe una cantidad de herramientas para generar el documento de
+ mapeo, incluyendo XDoclet, Middlegen y AndroMDA.
+ </para>
+
+ <para>
+ Comencemos por un mapeo de ejemplo:
+ </para>
+
+ <programlisting id="mapping-declaration-ex1"
revision="1"><![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 package="eg">
+
+ <class name="Cat"
+ table="cats"
+ discriminator-value="C">
+
+ <id name="id">
+ <generator class="native"/>
+ </id>
+
+ <discriminator column="subclass"
+ type="character"/>
+
+ <property name="weight"/>
+
+ <property name="birthdate"
+ type="date"
+ not-null="true"
+ update="false"/>
+
+ <property name="color"
+ type="eg.types.ColorUserType"
+ not-null="true"
+ update="false"/>
+
+ <property name="sex"
+ not-null="true"
+ update="false"/>
+
+ <property name="litterId"
+ column="litterId"
+ update="false"/>
+
+ <many-to-one name="mother"
+ column="mother_id"
+ update="false"/>
+
+ <set name="kittens"
+ inverse="true"
+ order-by="litter_id">
+ <key column="mother_id"/>
+ <one-to-many class="Cat"/>
+ </set>
+
+ <subclass name="DomesticCat"
+ discriminator-value="D">
+
+ <property name="name"
+ type="string"/>
+
+ </subclass>
+
+ </class>
+
+ <class name="Dog">
+ <!-- mapping for Dog could go here -->
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Discutiremos ahora el contenido del documento de mapeo. Describiremos
sólo los
+ elementos y atributos que son usados por Hibernate en tiempo de
ejecución. El
+ documento de mapeo contiene además algunos atributos y elementos
extra opcionales
+ que afectan los esquemas de base de datos exportados por la herramienta de
exportación
+ de esquemas. (Por ejemplo, el atributo
<literal>not-null</literal>.)
+ </para>
+
+
+
+ <sect2 id="mapping-declaration-doctype" revision="2">
+ <title>Doctype</title>
+
+ <para>
+ Todos los mapeos XML deben declarar el doctype mostrado. El DTD actual
puede
+ ser encontrado en el URL mencionado arriba, en el directorio
+ <literal>hibernate-x.x.x/src/org/hibernate</literal>, o en
<literal>hibernate3.jar</literal>.
+ Hibernate siempre buscará el DTD primero en el classpath. Si
experimentas
+ búsquedas del DTD usando una conexión de Internet,
chequea tu declaración
+ de DTD contra la contenida en el classpath.
+ </para>
+ </sect2>
+
+ <sect2 id="mapping-declaration-mapping" revision="3">
+ <title>hibernate-mapping</title>
+
+ <para>
+ Este elemento tiene muchos atributos opcionales. Los atributos
<literal>schema</literal>
+ y <literal>catalog</literal> especifican que las tablas a las
que se refiere en el mapeo
+ pertenecen al esquema y/o catálogo mencionado(s). De
especificarse, los nombres de
+ tablas serán cualificados por el nombre de esquema y
catálogo dados.
+ De omitirse, los nombres de tablas no serán cualificados. El
atributo
+ <literal>default-cascade</literal> especifica qué
estilo de cascada debe asumirse
+ para las propiedades y colecciones que no especifican un atributo
<literal>cascade</literal>.
+ El atributo <literal>auto-import</literal> nos permite usar
nombres de clase sin cualificar
+ en el lenguaje de consulta, por defecto.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="hm1" coords="2 55"/>
+ <area id="hm2" coords="3 55"/>
+ <area id="hm3" coords="4 55"/>
+ <area id="hm4" coords="5 55"/>
+ <area id="hm5" coords="6 55"/>
+ <area id="hm6" coords="7 55"/>
+ <area id="hm7" coords="8 55"/>
+ </areaspec>
+ <programlisting><![CDATA[<hibernate-mapping
+ schema="schemaName"
+ catalog="catalogName"
+ default-cascade="cascade_style"
+ default-access="field|property|ClassName"
+ default-lazy="true|false"
+ auto-import="true|false"
+ package="package.name"
+ />]]></programlisting>
+ <calloutlist>
+ <callout arearefs="hm1">
+ <para>
+ <literal>schema</literal> (opcional): El nombre
de un esquema de la base de datos.
+ </para>
+ </callout>
+ <callout arearefs="hm2">
+ <para>
+ <literal>catalog</literal> (opcional): El nombre
de un catálogo de la base de datos.
+ </para>
+ </callout>
+ <callout arearefs="hm3">
+ <para>
+ <literal>default-cascade</literal> (opcional -
por defecto a <literal>none</literal>):
+ Un estilo de cascada por defecto.
+ </para>
+ </callout>
+ <callout arearefs="hm4">
+ <para>
+ <literal>default-access</literal> (opcional -
por defecto a <literal>property</literal>):
+ La estrategia que Hibernate debe usar para acceder a todas
las propiedades.
+ Puede ser una implementación personalizada de
<literal>PropertyAccessor</literal>.
+ </para>
+ </callout>
+ <callout arearefs="hm5">
+ <para>
+ <literal>default-lazy</literal> (opcional - por
defecto a <literal>true</literal>):
+ El valor por defecto para los atributos
<literal>lazy</literal> de mapeos de clase
+ y colleción no especificados.
+ </para>
+ </callout>
+ <callout arearefs="hm6">
+ <para>
+ <literal>auto-import</literal> (opcional - por
defecto a <literal>true</literal>):
+ Especifica si podemos usar nombres de clases no cualificados
(de clases en este mapeo)
+ en el lenguaje de consulta.
+ </para>
+ </callout>
+ <callout arearefs="hm7">
+ <para>
+ <literal>package</literal> (opcional):
Especifica un prefijo de paquete a asumir
+ para los nombres no cualificados de clase en el documento de
mapeo.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Si tienes dos clases persistentes con el mismo nombre (sin cualificar),
debes establecer
+ <literal>auto-import="false"</literal>. Hibernate
lanzará una excepción si
+ intentas asignar dos clases al mismo nombre "importado".
+ </para>
+
+ <para>
+ Observa que el elemento <literal>hibernate-mapping</literal>
te permite anidar
+ muchos mapeos <literal><class></literal>
persistentes, como se muestra arriba.
+ Sin embargo, es una buena práctica (y se espera de algunas
herramientas) mapear
+ sólo a una sola clase persistente (o a una sola
jerarquía de clases) en
+ un fichero de mapeo y nombrarlo después de la superclase
persistente;
+ por ejemplo, <literal>Cat.hbm.xml</literal>,
<literal>Dog.hbm.xml</literal>,
+ o, si se usa herencia, <literal>Animal.hbm.xml</literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-class" revision="3">
+ <title>class</title>
+
+ <para>
+ Puedes declarar una clase persistente usando el elemento
<literal>class</literal>:
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="class1" coords="2 55"/>
+ <area id="class2" coords="3 55" />
+ <area id="class3" coords="4 55"/>
+ <area id="class4" coords="5 55" />
+ <area id="class5" coords="6 55"/>
+ <area id="class6" coords="7 55" />
+ <area id="class7" coords="8 55"/>
+ <area id="class8" coords="9 55" />
+ <area id="class9" coords="10 55" />
+ <area id="class10" coords="11 55"/>
+ <area id="class11" coords="12 55"/>
+ <area id="class12" coords="13 55"/>
+ <area id="class13" coords="14 55"/>
+ <area id="class14" coords="15 55"/>
+ <area id="class15" coords="16 55"/>
+ <area id="class16" coords="17 55"/>
+ <area id="class17" coords="18 55"/>
+ <area id="class18" coords="19 55"/>
+ <area id="class19" coords="20 55"/>
+ <area id="class20" coords="21 55"/>
+ <area id="class21" coords="22 55"/>
+ </areaspec>
+ <programlisting><![CDATA[<class
+ name="ClassName"
+ table="tableName"
+ discriminator-value="discriminator_value"
+ mutable="true|false"
+ schema="owner"
+ catalog="catalog"
+ proxy="ProxyInterface"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ select-before-update="true|false"
+ polymorphism="implicit|explicit"
+ where="arbitrary sql where condition"
+ persister="PersisterClass"
+ batch-size="N"
+ optimistic-lock="none|version|dirty|all"
+ lazy="true|false"
+ entity-name="EntityName"
+ check="arbitrary sql check condition"
+ rowid="rowid"
+ subselect="SQL expression"
+ abstract="true|false"
+ node="element-name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="class1">
+ <para>
+ <literal>name</literal> (opcional): El nombre
completamente cualificado de la clase
+ Java persistente (o interface). Si este atributo es omitido,
se asume que el mapeo
+ es para una entidad non-POJO.
+ </para>
+ </callout>
+ <callout arearefs="class2">
+ <para>
+ <literal>table</literal> (opcional - por defecto
al nombre no cualificado de la clase):
+ El nombre de su tabla en base de datos.
+ </para>
+ </callout>
+ <callout arearefs="class3">
+ <para>
+ <literal>discriminator-value</literal> (opcional
- por defecto al nombre de la clase):
+ Un valor que distingue subclases individuales, usado para el
comportamiento
+ polimórfico. Los valores aceptables incluyen
<literal>null</literal>
+ y <literal>not null</literal>.
+ </para>
+ </callout>
+ <callout arearefs="class4">
+ <para>
+ <literal>mutable</literal> (opcional, por defecto
a <literal>true</literal>):
+ Especifica que las instancias de la clase (no) son mutables.
+ </para>
+ </callout>
+ <callout arearefs="class5">
+ <para>
+ <literal>schema</literal> (opcional):
Sobreescribe el nombre de esquema especificado
+ por el elemento raíz
<literal><hibernate-mapping></literal>.
+ </para>
+ </callout>
+ <callout arearefs="class6">
+ <para>
+ <literal>catalog</literal> (opcional):
Sobreescribe el nombre de catálogo
+ especificado por el elemento raíz
<literal><hibernate-mapping></literal>.
+ </para>
+ </callout>
+ <callout arearefs="class7">
+ <para>
+ <literal>proxy</literal> (opcional): Especifica
una interface a usar para proxies
+ de inicialización perezosa. Puedes especificar el
nombre mismo de la clase.
+ </para>
+ </callout>
+ <callout arearefs="class8">
+ <para>
+ <literal>dynamic-update</literal> (opcional, por
defecto a <literal>false</literal>):
+ Especifica que el SQL <literal>UPDATE</literal>
debe ser generado en tiempo de
+ ejecución y contener solamente aquellas columnas
cuyo valor haya cambiado.
+ </para>
+ </callout>
+ <callout arearefs="class9">
+ <para>
+ <literal>dynamic-insert</literal> (opcional, por
defecto a <literal>false</literal>):
+ Especifica que el SQL <literal>INSERT</literal>
debe ser generado en tiempo de
+ ejecución y contener solamente aquellas columnas
cuyo valores no son nulos.
+ </para>
+ </callout>
+ <callout arearefs="class10">
+ <para>
+ <literal>select-before-update</literal>
(opcional, por defecto a <literal>false</literal>):
+ Especifica que Hibernate
<emphasis>nunca</emphasis> debe realizar un SQL
<literal>UPDATE</literal>
+ a menos que se tenga certeza que un objeto haya sido
modificado realmente.
+ En ciertos casos, (realmente, sólo cuando un
objeto transitorio ha sido asociado
+ con una sesión nueva usando
<literal>update()</literal>), esto significa que Hibernate
+ realizará una SQL
<literal>SELECT</literal> extra para determinar si un
+ <literal>UPDATE</literal> es realmente
requerido.
+ </para>
+ </callout>
+ <callout arearefs="class11">
+ <para>
+ <literal>polymorphism</literal> (opcional, por
defecto a <literal>implicit</literal>):
+ Determina si se usa polimorfismo de consulta
implícito o explícito.
+ </para>
+ </callout>
+ <callout arearefs="class12">
+ <para>
+ <literal>where</literal> (opcional) especifica
una condición SQL <literal>WHERE</literal>
+ arbitraria paraa ser usada al recuperar objetos de esta
clase.
+ </para>
+ </callout>
+ <callout arearefs="class13">
+ <para>
+ <literal>persister</literal> (opcional):
Especifica un <literal>ClassPersister</literal>
+ personalizado.
+ </para>
+ </callout>
+ <callout arearefs="class14">
+ <para>
+ <literal>batch-size</literal> (opcional, por
defecto a <literal>1</literal>)
+ especifica un "tamaño de lote" para
traer instancias de esta clase por
+ identificador.
+ </para>
+ </callout>
+ <callout arearefs="class15">
+ <para>
+ <literal>optimistic-lock</literal> (opcional, por
defecto a <literal>version</literal>):
+ Determina la estrategia optimista de bloqueo.
+ </para>
+ </callout>
+ <callout arearefs="class16">
+ <para>
+ <literal>lazy</literal> (opcional):
+ La recuperación perezosa puede ser deshabilitada
por completo estableciendo
+ <literal>lazy="false"</literal>.
+ </para>
+ </callout>
+ <callout arearefs="class17">
+ <para>
+ <literal>entity-name</literal> (opcional):
Hibernate3 permite que una clase sea
+ mapeada varias veces (potencialmente a tablas diferentes), y
permite que los mapeos
+ de entidad sean representados por Maps o XML al nivel de
Java. En estos casos,
+ debes proveer un nombre explícito arbitrario para
la entidad.
+ Para más información, mira
+ <xref
linkend="persistent-classes-dynamicmodels"/> y <xref
linkend="xml"/>.
+ </para>
+ </callout>
+ <callout arearefs="class18">
+ <para>
+ <literal>check</literal> (opcional): Una
expresión SQL usada para generar
+ una restricción
<emphasis>check</emphasis> multi-fila para la generación
+ automática de esquema.
+ </para>
+ </callout>
+ <callout arearefs="class19">
+ <para>
+ <literal>rowid</literal> (opcional): Hibernate
puede usar los llamados ROWIDs en las
+ bases de datos que los soporten. Por ejemplo, en Oracle,
Hibernate puede usar la columna
+ extra <literal>rowid</literal> para
actualizaciones rápidas si estableces esta
+ opción a <literal>rowid</literal>. Un
ROWID es un detalle de implementación
+ y representa la posición física de la
tupla almacenada.
+ </para>
+ </callout>
+ <callout arearefs="class20">
+ <para>
+ <literal>subselect</literal> (opcional): Mapea
una entidad inmutable y de sólo
+ lectura a una subselect de base de datos. Es útil
si quieres tener una vista
+ en vez de una tabla base, pero no tienes vistas. Mira debajo
para más información.
+ </para>
+ </callout>
+ <callout arearefs="class21">
+ <para>
+ <literal>abstract</literal> (opcional): Usado
para marcar superclases abstractas en
+ jerarquías
<literal><union-subclass></literal>.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Es perfectamente aceptable que la clase persistente mencionada sea una
interface.
+ Entonces declararías clases que implementan esa interface
usando el elemento
+ <literal><subclass></literal>. Puedes persistir
cualquier clase interna
+ <emphasis>estática</emphasis>. Debes especificar
el nombre de la clase usando la forma
+ estándar. Por ejemplo,
<literal>eg.Foo$Bar</literal>.
+ </para>
+
+ <para>
+ Las clases inmutables,
<literal>mutable="false"</literal>, no pueden ser actualizadas o
+ borradas por la aplicación. Esto permite a Hibernate hacer
ciertas optimizaciones
+ menores de rendimiento.
+ </para>
+
+ <para>
+ El atributo opcional <literal>proxy</literal> habilita la
inicialización postergada
+ de instancias persistentes de la clase. Hibernate inicialmente
retornará proxies
+ CGLIB que implementan la interface mencionada. El objeto persistente real
será
+ cargado cuando se invoque un método del proxy. Mira
"Proxies para Inicialización
+ Postergada" debajo.
+ </para>
+
+ <para>
+ Por polimorfismo <emphasis>implícito</emphasis> se
entiende que las instancias de la clase
+ serán devueltas por una consulta que mencione cualquier
superclase, o interface implementada,
+ o la clase misma; y que las instancias de cualquier subclase de la clase
serán devueltas
+ por una clase que mencione a la clase en sí.
+ Por polimorfismo <emphasis>explícito</emphasis> se
entiende que instancias de la clase
+ serán devueltas sólo por consultas que mencionen
explícitamente la clase;
+ y que las consultas que mencionen la clase devolverán
sólo instancias de subclases
+ mapeadas dentro de esta declaración
<literal><class></literal> como una
+ <literal><subclass></literal> o
<literal><joined-subclass></literal>.
+ Para la mayoría de los propósitos el defecto,
+ <literal>polymorphism="implicit"</literal>, resulta
apropiado.
+ El polimorfismo explícito es útil cuando dos clases
diferentes están
+ mapeadas a la misma tabla (esto permite tener una clase
"liviana" que contenga un subconjunto
+ de columnas de la tabla).
+ </para>
+
+ <para>
+ El atributo <literal>persister</literal> te permite
personalizar la estrategia de persistencia
+ para la clase. Puedes, por ejemplo, especificar tu propia subclase de
+ <literal>org.hibernate.persister.EntityPersister</literal> o
incluso puedes proveer una implementación
+ completamente nueva de la interface
<literal>org.hibernate.persister.ClassPersister</literal> que implemente
+ la persistencia por medio, por ejemplo, de llamadas a procedimientos
almacenados, serialización a
+ ficheros planos o LDAP. Para un ejemplo simple (de persistencia a una
<literal>Hashtable</literal>) mira
+ <literal>org.hibernate.test.CustomPersister</literal>.
+ </para>
+
+ <para>
+ Observa que los valores de <literal>dynamic-update</literal>
y <literal>dynamic-insert</literal>
+ no son heredados por las subclases y por lo tanto deben especificarse en
los elementos
+ <literal><subclass></literal> o
<literal><joined-subclass></literal>.
+ Estos ajustes pueden incrementar el rendimiento en algunos casos, pero
podrían mermarlo en otros.
+ Ten juicio en su uso.
+ </para>
+
+ <para>
+ Generalmente el uso de
<literal>select-before-update</literal> disminuirá el rendimiento.
+ Es muy útil prevenir que se llame innecesariamente a un
disparador de actualización de
+ base de datos al volver a unir un grafo de instancias separadas a una
<literal>Session</literal>.
+ </para>
+
+ <para>
+ Si habilitas <literal>dynamic-update</literal>,
tendrás opción de estrategias
+ de bloqueo optimistas:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>version</literal> chequea las columnas de
versión/timestamp
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>all</literal> chequea todas las columnas
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>dirty</literal> chequea las columnas
modificadas, permitiendo algunas
+ actualizaciones concurrentes
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>none</literal> no usa bloqueo optimista
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ Recomendamos <emphasis>muy</emphasis> fuertemente que uses
columnas de
+ versión/timestamp para bloqueo optimista con Hibernate. Esta
es la estrategia
+ óptima con respecto al rendimiento y es la única
estrategia que maneja
+ correctamente las modificaciones hechas a las instancias separadas.
+ (por ejemplo, cuando se usa
<literal>Session.merge()</literal>).
+ </para>
+
+ <para>
+ Para un mapeo de Hibernate, no hay diferencia entre una vista y una tabla
base.
+ Como se supone esto es transparente a nivel de base de datos (observa que
algunos
+ DBMS no soportan correctamente las vistas, especialmente con las
actualizaciones).
+ A veces quieres usar una vista, pero no puedes crear una en la base de
datos
+ (por ejemplo, con un esquema heredado). En este caso, puedes mapear una
entidad inmutable
+ de sólo lectura a una expresión de subconsulta SQL
dada.
+ </para>
+
+ <programlisting><![CDATA[<class name="Summary">
+ <subselect>
+ select item.name, max(bid.amount), count(*)
+ from item
+ join bid on bid.item_id = item.id
+ group by item.name
+ </subselect>
+ <synchronize table="item"/>
+ <synchronize table="bid"/>
+ <id name="name"/>
+ ...
+</class>]]></programlisting>
+
+ <para>
+ Declara las tablas con las que sincronizar esta entidad, asegurando que
el auto-flush
+ ocurre correctamente, y que las consultas contra la entidad derivada no
devuelven datos
+ desactualizados. El
<literal><subselect></literal> está disponible tanto
+ como un atributo o como un elemento anidado de mapeo.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-id" revision="3">
+ <title>id</title>
+
+ <para>
+ Las clases mapeadas <emphasis>deben</emphasis> declarar la
columna de clave primaria de la tabla
+ de la base de datos. En la mayoría de los casos
tendrá también una propiedad
+ estilo Javabeans que tenga el identificador único de una
instancia. El elemento
+ <literal><id></literal> define el mapeo de esa
propiedad a la columna de clave primaria.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="id1" coords="2 70"/>
+ <area id="id2" coords="3 70" />
+ <area id="id3" coords="4 70"/>
+ <area id="id4" coords="5 70" />
+ <area id="id5" coords="6 70" />
+ </areaspec>
+ <programlisting><![CDATA[<id
+ name="propertyName"
+ type="typename"
+ column="column_name"
+ unsaved-value="null|any|none|undefined|id_value"
+ access="field|property|ClassName"
+ node="element-name|@attribute-name|element/(a)attribute|.">
+
+ <generator class="generatorClass"/>
+</id>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="id1">
+ <para>
+ <literal>name</literal> (opcional): El nombre de
la propiedad del indentificador.
+ </para>
+ </callout>
+ <callout arearefs="id2">
+ <para>
+ <literal>type</literal> (opcional): Un nombre que
indica el tipo Hibernate.
+ </para>
+ </callout>
+ <callout arearefs="id3">
+ <para>
+ <literal>column</literal> (opcional - por defecto
al nombre de la propiedad):
+ El nombre de la columna de clave primaria.
+ </para>
+ </callout>
+ <callout arearefs="id4">
+ <para>
+ <literal>unsaved-value</literal> (opcional - por
defecto al valor "sensible"):
+ Una valor de la propiedad identificadora que indica que una
instancia está
+ recién instanciada (sin salvar),
distinguiéndola de instancias separadas
+ que fueran salvadas o cargadas en una sesión
previa.
+ </para>
+ </callout>
+ <callout arearefs="id5">
+ <para>
+ <literal>access</literal> (opcional - por defecto
a <literal>property</literal>):
+ La estrategia que Hibernate debe usar para acceder al valor
de la propiedad.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Si se omite el atributo <literal>name</literal>, se asume que
la clase no tiene propiedad
+ identificadora.
+ </para>
+
+ <para>
+ El atributo <literal>unsaved-value</literal> es importante!
Si la propiedad identificadora de tu
+ clase no tiene por defecto el valor por defecto normal de Java (null o
cero), entonces debes especificar
+ el valor por defecto real.
+ </para>
+
+ <para>
+ Hay una declaración
<literal><composite-id></literal> alternativa para permitir
acceso
+ a datos heredados con claves compuestas. Desalentamos fuertemente su uso
para cualquier otra cosa.
+ </para>
+
+ <sect3 id="mapping-declaration-id-generator"
revision="2">
+ <title>Generator</title>
+
+ <para>
+ El elemento hijo opcional
<literal><generator></literal> nombra una clase Java
+ usada en generar identificadores únicos para instancias de
la clase persistente.
+ De requerirse algún parámetro para configurar o
inicializar la instancia del generador,
+ se pasa usando el elemento
<literal><param></literal>.
+ </para>
+
+ <programlisting><![CDATA[<id name="id"
type="long" column="cat_id">
+ <generator class="org.hibernate.id.TableHiLoGenerator">
+ <param name="table">uid_table</param>
+ <param name="column">next_hi_value_column</param>
+ </generator>
+</id>]]></programlisting>
+
+ <para>
+ Todos los generadores implementan la interface
<literal>org.hibernate.id.IdentifierGenerator</literal>.
+ Esta es una interface muy simple; algunas aplicaciones pueden escoger
proveer sus propias
+ implementaciones especializadas. Sin embargo, Hibernate provee un
rango de implementaciones
+ prefabricadas. Hay nombres alias de atajo para los generadores
prefabricados:
+ <variablelist>
+ <varlistentry>
+
<term><literal>increment</literal></term>
+ <listitem>
+ <para>
+ genera indentificadores de tipo
<literal>long</literal>, <literal>short</literal> o
+ <literal>int</literal> que sólo
son únicos cuando ningún otro proceso
+ está insertando datos en la misma tabla.
<emphasis>No usar en un cluster.</emphasis>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>identity</literal></term>
+ <listitem>
+ <para>
+ soporta columnas de identidad en DB2, MySQL, MS SQL
Server, Sybase y
+ HypersonicSQL. El identificador devuelto es de tipo
<literal>long</literal>,
+ <literal>short</literal> o
<literal>int</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>sequence</literal></term>
+ <listitem>
+ <para>
+ usa una secuencia en DB2, PostgreSQL, Oracle, SAP DB,
McKoi o un generador
+ en Interbase. El identificador devuelto es de tipo
<literal>long</literal>,
+ <literal>short</literal> o
<literal>int</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>hilo</literal></term>
+ <listitem>
+ <para
id="mapping-declaration-id-hilodescription" revision="1">
+ usa un algoritmo alto/bajo para generar eficientemente
identificadores de tipo
+ <literal>long</literal>,
<literal>short</literal> o <literal>int</literal>,
+ dada una tabla y columna como fuente de valores altos
(por defecto
+ <literal>hibernate_unique_key</literal> y
<literal>next_hi</literal> respectivamente).
+ El algoritmo alto/bajo genera identificadores que son
únicos sólo para una
+ base de datos particular.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>seqhilo</literal></term>
+ <listitem>
+ <para>
+ usa un algoritmo alto/bajo para generar eficientemente
identificadores de tipo
+ <literal>long</literal>,
<literal>short</literal> o <literal>int</literal>,
+ dada una secuencia de base de datos.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>uuid</literal></term>
+ <listitem>
+ <para>
+ usa un algoritmo UUID de 128 bits para generar
identificadore de tipo
+ cadena, únicos en una ref (se usa la
direccón IP). El UUID
+ se codifica como una cadena hexadecimal de 32
dígitos de largo.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>guid</literal></term>
+ <listitem>
+ <para>
+ usa una cadena GUID generada por base de datos en MS SQL
Server y MySQL.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>native</literal></term>
+ <listitem>
+ <para>
+ selecciona <literal>identity</literal>,
<literal>sequence</literal> o
+ <literal>hilo</literal> dependiendo de las
capacidades de la base de datos
+ subyacente.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>assigned</literal></term>
+ <listitem>
+ <para>
+ deja a la aplicación asignar un identificador
al objeto antes
+ de que se llame a <literal>save()</literal>.
Esta es la estrategia
+ por defecto si no se especifica un elemento
<literal><generator></literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>select</literal></term>
+ <listitem>
+ <para>
+ recupera una clave primaria asignada por un disparador de
base de datos
+ seleccionando la fila por alguna clave única y
recuperando el valor de
+ la clave primaria.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>foreign</literal></term>
+ <listitem>
+ <para>
+ usa el identificador de otro objeto asociado.
Generalmente usado en conjuncón
+ a una asociacón de clave primaria
<literal><uno-a-uno></literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-hilo"
revision="1">
+ <title>Algoritmo alto/bajo</title>
+ <para>
+ Los generadores <literal>hilo</literal> y
<literal>seqhilo</literal> proveen dos implementaciones
+ alternativas del algoritmo alto/bajo, un enfoque favorito en
generación de identificadores.
+ La primera implementación requiere de una tabla
"especial" de base de datos para tener el siguiente
+ valor "alto" disponible.
+ La segunda usa una secuencia del estilo de Oracle (donde se
soporte).
+ </para>
+
+ <programlisting><![CDATA[<id name="id"
type="long" column="cat_id">
+ <generator class="hilo">
+ <param name="table">hi_value</param>
+ <param name="column">next_value</param>
+ <param name="max_lo">100</param>
+ </generator>
+</id>]]></programlisting>
+
+ <programlisting><![CDATA[<id name="id"
type="long" column="cat_id">
+ <generator class="seqhilo">
+ <param name="sequence">hi_value</param>
+ <param name="max_lo">100</param>
+ </generator>
+</id>]]></programlisting>
+
+ <para>
+ Desafortunadamente, no puedes usar
<literal>hilo</literal> cuando le proveas tu propia
+ <literal>Connection</literal> a Hibernate. Cuando
Hibernate está usando un datasource
+ del servidor de aplicaciones para obtener conexiones alistadas con
JTA, debes configurar
+ correctamente el
<literal>hibernate.transaction.manager_lookup_class</literal>.
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-uuid">
+ <title>Algoritmo UUID</title>
+ <para>
+ El UUID contiene: la dirección IP, el instante de arranque
de la JVM
+ (con una precisión de un cuarto de segundo), el tiempo de
sistema y un valor
+ de contador (único en la JVM). No es posible obtener una
dirección MAC o
+ una dirección de memoria desde código Java,
así que esto es lo mejor
+ que podemos hacer sin usar JNI.
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-sequences">
+ <title>Columnas de identidad y secuencias</title>
+ <para>
+ Para las bases de datos que soportan columnas de identidad (DB2,
MySQL, Sybase, MS SQL),
+ puedes usar generación de claves
<literal>identity</literal>. Para las bases de datos
+ que soportan secuencias (DB2, Oracle, PostgreSQL, Interbase, McKoi,
SAP DB) puedes usar la generación
+ de claves del estilo <literal>sequence</literal>. Ambas
estrategias requieren dos consultas SQL
+ para insertar un nuevo objeto.
+ </para>
+
+ <programlisting><![CDATA[<id name="id"
type="long" column="person_id">
+ <generator class="sequence">
+ <param name="sequence">person_id_sequence</param>
+ </generator>
+</id>]]></programlisting>
+
+ <programlisting><![CDATA[<id name="id"
type="long" column="person_id" unsaved-value="0">
+ <generator class="identity"/>
+</id>]]></programlisting>
+
+ <para>
+ Para desarrollos multiplataforma, la estrategia
<literal>native</literal>
+ eiligirá de entre las estrategias
<literal>identity</literal>,
+ <literal>sequence</literal> y
<literal>hilo</literal>, dependiendo de las capacidades
+ de la base de datos subyacentes.
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-assigned">
+ <title>Identificadores asignados</title>
+ <para>
+ Si quieres que la aplicación asigne los identificadores
(en contraposición
+ a que los genere Hibernate), puedes usar el generador
<literal>assigned</literal>.
+ Este generador especial usará el valor identificador ya
asignado a la
+ propiedad identificadora del objeto. Este generador se usa cuandola
clave primaria es
+ una clave natural en vez de una clave sustituta. Este es el
comportamiento por defecto si
+ no especificas un elemento
<literal><generator></literal>.
+ </para>
+
+ <para>
+ Elegir el generador <literal>assigned</literal> hace que
Hibernate use
+ <literal>unsaved-value="undefined"</literal>,
forzando a Hibernate a ir
+ a la base de datos para determinar si una instancia es transitoria o
separada,
+ a menos que haya una propiedad de versión o timestamp, o
que tu definas
+ <literal>Interceptor.isUnsaved()</literal>.
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-select">
+ <title>Claves primarias asignadas por disparadores</title>
+ <para>
+ Para esquemas heredados solamente (Hibernate no genera DDL con
disparadores).
+ </para>
+
+ <programlisting><![CDATA[<id name="id"
type="long" column="person_id">
+ <generator class="select">
+ <param name="key">socialSecurityNumber</param>
+ </generator>
+</id>]]></programlisting>
+
+ <para>
+ En el ejemplo de arriba, hay una propiedad ánica llamada
+ <literal>socialSecurityNumber</literal> definida por la
clase, como
+ una clave natural, y una clave sustituta llamada
<literal>person_id</literal>
+ cuyo valor es generado por un disparador.
+ </para>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-compositeid"
revision="2">
+ <title>composite-id</title>
+
+ <programlisting><![CDATA[<composite-id
+ name="propertyName"
+ class="ClassName"
+ unsaved-value="undefined|any|none"
+ access="field|property|ClassName"
+ node="element-name|.">
+
+ <key-property name="propertyName" type="typename"
column="column_name"/>
+ <key-many-to-one name="propertyName class="ClassName"
column="column_name"/>
+ ......
+</composite-id>]]></programlisting>
+
+ <para>
+ Para una tabla con clave compuesta, puedes mapear múltiples
propiedades
+ de la clase como propiedades identificadoras. El elemento
<literal><composite-id></literal>
+ acepta los mapeos de propiedad
<literal><key-property></literal> y
+ los mapeos <literal><key-many-to-one></literal>
como elementos hijo.
+ </para>
+
+ <programlisting><![CDATA[<composite-id>
+ <key-property name="medicareNumber"/>
+ <key-property name="dependent"/>
+</composite-id>]]></programlisting>
+
+ <para>
+ Tu clase persistente <emphasis>debe</emphasis> sobreescribir
<literal>equals()</literal>
+ y <literal>hashCode()</literal> para implementar igualdad de
identificador compuesto.
+ Debe también implementar
<literal>Serializable</literal>.
+ </para>
+
+ <para>
+ Desafortunadamente, este enfoque de identificadores compuestos significa
que un objeto
+ persistente es su propio identificador. No existe otra "asa"
conveniente más que el
+ objeto mismo. Debes instanciar una instancia de la clase misma y poblar
sus propiedades
+ identificadoras antes que puedas <literal>load()</literal> el
estado persistente asociado
+ a una clave compuesta. Describiremos un enfoque mucho más
conveniente donde el identificador
+ compuesto está implementado como una clase separada en
<xref linkend="components-compositeid"/>.
+ Los atributos descriptos debajo solamente se aplican a este enfoque
alternativo:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>name</literal> (opcional): Una propiedad de
tipo componente que tiene el identificador
+ compuesto (ver siguiente sección).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>class</literal> (opcional - por defecto al
tipo de la propiedad determinado
+ por reflección): La clase del componente usado como
identificador compuesto (ver siguiente sección).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>unsaved-value</literal> (opcional - por
defecto a <literal>undefined</literal>):
+ Indica que las instancias transitorias deben ser consideradas
como recién instanciadas,
+ si se establece a <literal>any</literal>, o
separadas, si se establece a <literal>none</literal>.
+ Lo mejor
+
+ Indicates that transient instances should be considered newly
instantiated, if set
+ to <literal>any</literal>, or detached, if set to
<literal>none</literal>.
+ Lo mejor en todos los casos es dejar el valor por defecto.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-discriminator"
revision="3">
+ <title>discriminator</title>
+
+ <para>
+ El elemento <literal><discriminator></literal>
es requerido para la persistencia
+ polimórfica usando la estrategia de mapeo de
tabla-por-jerarquía-de-clases y
+ declara una columna discriminadora de la tabla. La columna discriminidora
contiene valores
+ de marca que le dicen a la capa de persistencia qué subclase
instanciar para una fila
+ en particular. Un conjunto restringido de tipos puede ser usado:
+ <literal>string</literal>,
<literal>character</literal>, <literal>integer</literal>,
+ <literal>byte</literal>,
<literal>short</literal>, <literal>boolean</literal>,
+ <literal>yes_no</literal>,
<literal>true_false</literal>.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="discriminator1" coords="2 60"/>
+ <area id="discriminator2" coords="3 60" />
+ <area id="discriminator3" coords="4 60" />
+ <area id="discriminator4" coords="5 60" />
+ <area id="discriminator5" coords="6 60" />
+ </areaspec>
+ <programlisting><![CDATA[<discriminator
+ column="discriminator_column"
+ type="discriminator_type"
+ force="true|false"
+ insert="true|false"
+ formula="arbitrary sql expression"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="discriminator1">
+ <para>
+ <literal>column</literal> (opcional - por defecto
a <literal>class</literal>) el
+ nombre de la columna discriminadora.
+ </para>
+ </callout>
+ <callout arearefs="discriminator2">
+ <para>
+ <literal>type</literal> (opcional - por defecto a
<literal>string</literal>) un
+ nombre que indique el tipo Hibernate
+ </para>
+ </callout>
+ <callout arearefs="discriminator3">
+ <para>
+ <literal>force</literal> (optconal - por defecto
a <literal>false</literal>)
+ "fuerza" a Hibernate a especificar valores
discriminadores permitidos incluso
+ cuando se recuperan todas las instancias de la clase
raíz.
+ </para>
+ </callout>
+ <callout arearefs="discriminator4">
+ <para>
+ <literal>insert</literal> (opcional - por defecto
a <literal>true</literal>)
+ establezca este a <literal>false</literal> si tu
columna discriminadora es
+ también parte de un identificador mapeado
compuesto. (Le dice a Hibernate
+ que no incluya la columna en los SQL
<literal>INSERT</literal>s.)
+ </para>
+ </callout>
+ <callout arearefs="discriminator5">
+ <para>
+ <literal>formula</literal> (opcional) una
expresión SQL arbitraria que
+ es ejecutada cuando un tipo tenga que ser evaluado. Permite
dicriminación
+ basada en el contenido.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Los valores reales de la columna discriminadora están
especificados por
+ el atributo <literal>discriminator-value</literal> de los
elementos
+ <literal><class></literal> y
<literal><subclass></literal>.
+ </para>
+
+ <para>
+ El atributo <literal>force</literal> es (sólo)
útil si la tabla contiene
+ filas con valores discriminadores "extra" que no
están mapeados a la clase
+ persistente. Generalmente este no es el caso.
+ </para>
+
+ <para>
+ Usando el atributo <literal>formula</literal> puedes declarar
una expresión SQL
+ arbitraria que será usada para evaluar el tipo de una fila:
+ </para>
+
+ <programlisting><![CDATA[<discriminator
+ formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0
else 1 end"
+ type="integer"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-version" revision="1">
+ <title>version (opcional)</title>
+
+ <para>
+ El elemento <literal><version></literal> es
opcional e indica que la
+ tabla contiene datos versionados. Esto es particularmente útil
si planeas
+ usar <emphasis>transacciones largas</emphasis> (ver debajo).
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="version1" coords="2 70"/>
+ <area id="version2" coords="3 70"/>
+ <area id="version3" coords="4 70"/>
+ <area id="version4" coords="5 70"/>
+ <area id="version5" coords="6 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<version
+ column="version_column"
+ name="propertyName"
+ type="typename"
+ access="field|property|ClassName"
+ unsaved-value="null|negative|undefined"
+ node="element-name|@attribute-name|element/(a)attribute|."
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="version1">
+ <para>
+ <literal>column</literal> (opcional - por defecto
al nombre de la propiedad): El nombre
+ de la columna que tiene el número de
versión.
+ </para>
+ </callout>
+ <callout arearefs="version2">
+ <para>
+ <literal>name</literal>: El nombre de una
propiedad de la clase persistente.
+ </para>
+ </callout>
+ <callout arearefs="version3">
+ <para>
+ <literal>type</literal> (opcional - por defecto a
<literal>integer</literal>):
+ El tipo del nú.mero de vesión.
+ </para>
+ </callout>
+ <callout arearefs="version4">
+ <para>
+ <literal>access</literal> (opcional - por defecto
a <literal>property</literal>): La
+ estrategia que Hibernate debe usar para acceder al valor de
la propiedad.
+ </para>
+ </callout>
+ <callout arearefs="version5">
+ <para>
+ <literal>unsaved-value</literal> (opcional - por
defecto a <literal>undefined</literal>):
+ Un valor de la propiedad de versión que indica que
una instancia está
+ recién instanciada (sin guardar),
distinguiéndola de instancias
+ separadas que fueran guardadas o cargadas en una
sesión previa.
+ (<literal>undefined</literal> especifica que debe
usarse el valor de la
+ propiedad identificadora.)
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Los números de versión deben ser de tipo
<literal>long</literal>,
+ <literal>integer</literal>,
<literal>short</literal>, <literal>timestamp</literal> o
+ <literal>calendar</literal> de Hibernate.
+ </para>
+
+ <para>
+ Una propiedad de versión o timestamp nunca debe ser nula para
una instancia
+ separada, de modo que Hibernate detectará cualquier instancia
con una versión
+ o timestamp nulo como transitoria, sin importar qué otras
estrategias
+ <literal>unsaved-value</literal> se hayan especificado.
+ <emphasis>Declarar una propiedad de versón o timestamp
nulable es una forma
+ fácil de evitar cualquier problema con la re-unión
transitiva en Hibernate,
+ especialmente útil para que usa identificadores asignados o
claves compuestas!</emphasis>
+ </para>
+ </sect2>
+
+ <sect2 id="mapping-declaration-timestamp">
+ <title>timestamp (opcional)</title>
+
+ <para>
+ El elemento opcional
<literal><timestamp></literal> indica que la tabla contiene
+ datos con sellos de tiempo. Esto esta concebido como una alternativa al
versionado.
+ Los timestamps (sellos de tiempo) son por su naturaleza una
implementación menos
+ segura de bloqueo optimista. Sin embrago, a veces la
aplicación puede usar los
+ timestamps en otras formas.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="timestamp1" coords="2 70"/>
+ <area id="timestamp2" coords="3 70" />
+ <area id="timestamp3" coords="4 70" />
+ <area id="timestamp4" coords="5 70" />
+ </areaspec>
+ <programlisting><![CDATA[<timestamp
+ column="timestamp_column"
+ name="propertyName"
+ access="field|property|ClassName"
+ unsaved-value="null|undefined"
+ node="element-name|@attribute-name|element/(a)attribute|."
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="timestamp1">
+ <para>
+ <literal>column</literal> (opcional - por defecto
al nombre de la propiedad): El nombre
+ de una columna que tiene el timestamp.
+ </para>
+ </callout>
+ <callout arearefs="timestamp2">
+ <para>
+ <literal>name</literal>: El nombre de una
propiedad del estilo JavaBeans de tipo
+ Java <literal>Date</literal> o
<literal>Timestamp</literal> de la clase persistente.
+ </para>
+ </callout>
+ <callout arearefs="timestamp3">
+ <para>
+ <literal>access</literal> (opcional - por defecto
a <literal>property</literal>): La
+ estrategia que Hibernate debe usar para acceder al valor de
la propiedad.
+ </para>
+ </callout>
+ <callout arearefs="timestamp4">
+ <para>
+ <literal>unsaved-value</literal> (opcional - por
defecto a <literal>null</literal>):
+ Un valor de propiedad de versión que indica que
una instancia está
+ recién instanciada (sin guardar),
distinguiéndola de instancias separadas
+ que hayan sido guardadas o cargadas en una sesión
previa.
+ (<literal>undefined</literal> especifica que debe
usarse el valor de la propiedad
+ identificadora.)
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Note that <literal><timestamp></literal> is
equivalent to
+ <literal><version
type="timestamp"></literal>.
+ </para>
+ </sect2>
+
+
+ <sect2 id="mapping-declaration-property" revision="2">
+ <title>property</title>
+
+ <para>
+ El elemento <literal><property></literal>
declara una propiedad persistente estilo
+ JavaBean de la clase.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="property1" coords="2 70"/>
+ <area id="property2" coords="3 70"/>
+ <area id="property3" coords="4 70"/>
+ <areaset id="property4-5" coords="">
+ <area id="property4" coords='5 70'/>
+ <area id="property5" coords='6 70'/>
+ </areaset>
+ <area id="property6" coords="7 70"/>
+ <area id="property7" coords="8 70"/>
+ <area id="property8" coords="9 70"/>
+ <area id="property9" coords="10 70"/>
+ <area id="property10" coords="11 70"/>
+ <area id="property11" coords="12 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<property
+ name="propertyName"
+ column="column_name"
+ type="typename"
+ update="true|false"
+ insert="true|false"
+ formula="arbitrary SQL expression"
+ access="field|property|ClassName"
+ lazy="true|false"
+ unique="true|false"
+ not-null="true|false"
+ optimistic-lock="true|false"
+ node="element-name|@attribute-name|element/(a)attribute|."
+ index="index_name"
+ unique_key="unique_key_id"
+ length="L"
+ precision="P"
+ scale="S"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="property1">
+ <para>
+ <literal>name</literal>: el nombre de la
propiedad, con la letra inicial
+ en minúsculas.
+ </para>
+ </callout>
+ <callout arearefs="property2">
+ <para>
+ <literal>column</literal> (opcional - por defecto
al nombre de la propiedad): el nombre
+ de la columna de tabla de base de datos mapeada. Esto puede
también ser especificado
+ con elemento(s)
<literal><column></literal> anidado(s).
+ </para>
+ </callout>
+ <callout arearefs="property3">
+ <para>
+ <literal>type</literal> (opcional): un nombre que
indica el nobre Hibernate.
+ </para>
+ </callout>
+ <callout arearefs="property4-5">
+ <para>
+ <literal>update, insert</literal> (opcional - por
defecto a <literal>true</literal>) :
+ especifica que las columnas mapeadas deben ser
incluídas en las sentencias SQL
+ <literal>UPDATE</literal> y/o
<literal>INSERT</literal> . Especificando ambas a
+ <literal>false</literal> permite una propiedad
"derivada" cuyo valor es inicializado desde
+ alguna otra propiedad que mapee a la misma columna (o
columnas) o por un disparador u otra
+ aplicación.
+ </para>
+ </callout>
+ <callout arearefs="property6">
+ <para>
+ <literal>formula</literal> (opcional): una
expresión SQL que define el valor
+ para una propiedad
<emphasis>computada</emphasis>. Las propiedades computadas no tienen
+ una columna mapeada propia.
+ </para>
+ </callout>
+ <callout arearefs="property7">
+ <para>
+ <literal>access</literal> (opcional - por defecto
a <literal>property</literal>): La
+ estrategia que Hibernate debe usar para acceder al valor de
la propiedad.
+ </para>
+ </callout>
+ <callout arearefs="property8">
+ <para>
+ <literal>lazy</literal> (opcional - por defecto a
<literal>false</literal>): Especifica
+ que esta propiedad debe ser traída perezosamente
cuando la variable de instancia
+ sea accedida por primera vez (requiere
instrumentación en tiempo de compilación).
+ </para>
+ </callout>
+ <callout arearefs="property9">
+ <para>
+ <literal>unique</literal> (opcional): Habilita la
generació DDL de una restricción
+ de unicidad para las columnas. Además, permite que
ésta sea un blanco objetivo de
+ una <literal>property-ref</literal>.
+ </para>
+ </callout>
+ <callout arearefs="property10">
+ <para>
+ <literal>not-null</literal> (opcional): Habilita
la generació DDL de una restricción
+ de nulabilidad para las columnas.
+ </para>
+ </callout>
+ <callout arearefs="property11">
+ <para>
+ <literal>optimistic-lock</literal> (opcional -
por defecto a <literal>true</literal>):
+ Especifica que las actualizaciones a esta propiedad requieran
o no de la obtención
+ de un bloqueo optimista. En otras palabras, determina si debe
ocurrir un incremento de versión
+ cuando la propiedad este sucia (desactualizada).
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ <emphasis>typename</emphasis> puede ser:
+ </para>
+
+ <orderedlist spacing="compact">
+ <listitem>
+ <para>
+ El nombre de un tipo básico Hibernate (por ejemplo,
<literal>integer, string, character,
+ date, timestamp, float, binary, serializable, object,
blob</literal>).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ El nombre de una clase Java de tipo básico (por
ejemplo, <literal>int, float,
+ char, java.lang.String, java.util.Date, java.lang.Integer,
java.sql.Clob</literal>).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ El nombre de una clase Java serializable.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ El nombre de un tipo personalizado (por ejemplo,
<literal>com.illflow.type.MyCustomType</literal>).
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ Si no especificas un tipo, Hibernate usará
reflección sobre la
+ propiedad mencionada para deducir el tipo Hibernate correcto. Hibernate
intentará
+ interpretar el nombre de la clase de retorno del getter de la propiedad
usando las reglas
+ 2, 3 y 4 en ese orden. Sin embargo, esto no siempre suficiente. En
ciertos casos, necesitarás
+ aún el atributo <literal>type</literal>. (Por
ejemplo, para distinguir entre
+ <literal>Hibernate.DATE</literal> y
<literal>Hibernate.TIMESTAMP</literal>,
+ o especificar un tipo personalizado.)
+ </para>
+
+ <para>
+ El atributo <literal>access</literal> te deja controlar
cómo Hibernate
+ accederá a la propiedad en tiempo de ejecución. Por
defecto, Hibernate
+ llamará al par de getter/setter de la propiedad. Si
especificas
+ <literal>access="field"</literal>, Hibernate se
saltará el par get/set y
+ accederá al campo directamente usando reflección.
Puedes especificar tu
+ propia estrategia de acceso a la propiedad mencionando una clase que
implemente
+ la interface
<literal>org.hibernate.property.PropertyAccessor</literal>.
+ </para>
+
+ <para>
+ Una aspecto especialmente poderoso son las propiedades derivadas. Estas
propiedades
+ son por definición de sólo lectura, el valor de la
propiedad es computado
+ en tiempo de carga. Tu declaras la computación como una
expresión SQL,
+ y ésta se traduce a cláusula de subconsulta
<literal>SELECT</literal>
+ en la consulta SQL que cargue una instancia:
+ </para>
+
+ <programlisting><![CDATA[
+<property name="totalPrice"
+ formula="( SELECT SUM (li.quantity*p.price) FROM LineItem li, Product p
+ WHERE li.productId = p.productId
+ AND li.customerId = customerId
+ AND li.orderNumber = orderNumber
)"/>]]></programlisting>
+
+ <para>
+ Observa que puedes referenciar la propia tabla de las entidades sin
declarar un alias
+ o una columna particular (<literal>customerId</literal> en el
ejemplo dado). Observa
+ además que puedes usar el elemento anidado de mapeo
<literal><formula></literal>
+ si no te gusta usar el atributo.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-manytoone"
revision="3">
+ <title>many-to-one</title>
+
+ <para>
+ Una asociación ordinaria a otra clase persistente se declara
usando
+ el elemento <literal>many-to-one</literal>. El modelo
relacional es una
+ asociación muchos-a-uno: una clave foránea en una
tabla está
+ referenciando la columna (o columnas) de la clave primaria de la tabla
objetivo.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="manytoone1" coords="2 70"/>
+ <area id="manytoone2" coords="3 70"/>
+ <area id="manytoone3" coords="4 70"/>
+ <area id="manytoone4" coords="5 70"/>
+ <area id="manytoone5" coords="6 70"/>
+ <areaset id="manytoone6-7" coords="">
+ <area id="manytoone6" coords='7 70'/>
+ <area id="manytoone7" coords='8 70'/>
+ </areaset>
+ <area id="manytoone8" coords="9 70"/>
+ <area id="manytoone9" coords="10 70"/>
+ <area id="manytoone10" coords="11 70"/>
+ <area id="manytoone11" coords="12 70"/>
+ <area id="manytoone12" coords="13 70"/>
+ <area id="manytoone13" coords="14 70"/>
+ <area id="manytoone14" coords="15 70"/>
+ <area id="manytoone15" coords="16 70"/>
+ <area id="manytoone16" coords="17 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<many-to-one
+ name="propertyName"
+ column="column_name"
+ class="ClassName"
+ cascade="cascade_style"
+ fetch="join|select"
+ update="true|false"
+ insert="true|false"
+ property-ref="propertyNameFromAssociatedClass"
+ access="field|property|ClassName"
+ unique="true|false"
+ not-null="true|false"
+ optimistic-lock="true|false"
+ lazy="true|proxy|false"
+ not-found="ignore|exception"
+ entity-name="EntityName"
+ formula="cualquier expresión SQL"
+ node="element-name|@attribute-name|element/(a)attribute|."
+ embed-xml="true|false"
+ index="index_name"
+ unique_key="unique_key_id"
+ foreign-key="foreign_key_name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="manytoone1">
+ <para>
+ <literal>name</literal>: El nombre de la
propiedad.
+ </para>
+ </callout>
+ <callout arearefs="manytoone2">
+ <para>
+ <literal>column</literal> (opcional): El nombre
de la columna clave foránea.
+ También puede ser especificado por uno o varios
elementos anidados
+ <literal><column></literal>.
+ </para>
+ </callout>
+ <callout arearefs="manytoone3">
+ <para>
+ <literal>class</literal> (opcional - por defecto
al tipo de la propiedad
+ determinado por reflección): El nombre de la clase
asociada.
+ </para>
+ </callout>
+ <callout arearefs="manytoone4">
+ <para>
+ <literal>cascade</literal> (opcional): Especifica
qué operaciones deben
+ ir en cascada desde el objeto padre al objeto asociado
+ </para>
+ </callout>
+ <callout arearefs="manytoone5">
+ <para>
+ <literal>fetch</literal> (opcional - por defecto
a 1<literal>select</literal>):
+ Escoge entre recuperación outer-join o por
selección secuencial.
+ </para>
+ </callout>
+ <callout arearefs="manytoone6-7">
+ <para>
+ <literal>update, insert</literal> (opcional - por
defecto a <literal>true</literal>)
+ especifica que las columnas mapeadas deben ser
incluídas en las sentencias SQL
+ <literal>UPDATE</literal> y/o
<literal>INSERT</literal>. Establecer ambas a
+ <literal>false</literal> permite una
asociación puramente "derivada" cuyo
+ valor es inicializado desde alguna otra propiedad que mapea a
las misma columna (o columnas),
+ o por un disparador, o por otra aplicación.
+ </para>
+ </callout>
+ <callout arearefs="manytoone8">
+ <para>
+ <literal>property-ref</literal>: (opcional) El
nombre de la propiedad de la clase
+ asociada que está unida a la clave
foránea. Si no se especifica, se usa
+ la clave primaria de la clase asociada.
+ </para>
+ </callout>
+ <callout arearefs="manytoone9">
+ <para>
+ <literal>access</literal> (opcional - por defecto
a <literal>property</literal>): La
+ estrategia que Hibernate debe usar para acceder al valor de
la propiedad.
+ </para>
+ </callout>
+ <callout arearefs="manytoone10">
+ <para>
+ <literal>unique</literal> (opcional): Habilita la
generación DDL de una
+ restricción de unicidad para la columna de clave
foránea. Además,
+ permite que ésta sea el objetivo de una
<literal>property-ref</literal>.
+ Esto hace efectivamente la multiplicidad de la
asociación uno a uno.
+ </para>
+ </callout>
+ <callout arearefs="manytoone11">
+ <para>
+ <literal>not-null</literal> (opcional): Habilita
la generación DDL de una
+ restricción de nulabilidad para las columnas de
clave foránea.
+ </para>
+ </callout>
+ <callout arearefs="manytoone12">
+ <para>
+ <literal>optimistic-lock</literal> (opcional -
por defecto a <literal>true</literal>):
+ Especifica que las actualizaciones a esta propiedad requieran
o no la obtención
+ del bloqueo optimista. En otras palabras, determina si debe
darse un incremento de versión
+ cuando esta propiedad esté desactualizada.
+ </para>
+ </callout>
+ <callout arearefs="manytoone13">
+ <para>
+ <literal>lazy</literal> (opcional - por defecto a
<literal>proxy</literal>):
+ Por defecto, las asociaciones de punto único van
con proxies.
+ <literal>lazy="true"</literal>
especifica que esta propiedad debe ser
+ traída perezosamente cuando la variable de
instancia sea accedida por primera
+ vez (requiere instrumentación del bytecode en
tiempo de compilación).
+ <literal>lazy="false"</literal>
especifica que la asociación siempre será
+ recuperada tempranamente.
+ </para>
+ </callout>
+ <callout arearefs="manytoone14">
+ <para>
+ <literal>not-found</literal> (opcional - por
defecto a <literal>exception</literal>):
+ Especifica cómo deben manejarse las claves
foráneas que referencien
+ filas inexistentes: <literal>ignore</literal>
tratará una fila perdida como
+ una asociación nula.
+ </para>
+ </callout>
+ <callout arearefs="manytoone15">
+ <para>
+ <literal>entity-name</literal> (opcional): El
nombre de entidad de la clase
+ asociada.
+ </para>
+ </callout>
+ <callout arearefs="manytoone16">
+ <para>
+ <literal>formula</literal> (opcional): una
expresión SQL que define el valor
+ para una clave foránea
<emphasis>computada</emphasis>.
+ </para>
+ </callout>
+
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Establecer el valor del atributo <literal>cascade</literal> a
cualquier valor
+ significativo distinto de <literal>none</literal>
propagará ciertas
+ operaciones al objeto asociado. Los valores significativos son los
nombres de las
+ operaciones básicas de Hibernate, <literal>persist,
merge, delete, save-update,
+ evict, replicate, lock, refresh</literal>, así como los
valores especiales
+ <literal>delete-orphan</literal> y
<literal>all</literal> y combinaciones de operaciones
+ separadas por coma, por ejemplo,
<literal>cascade="persist,merge,evict"</literal> o
+ <literal>cascade="all,delete-orphan"</literal>.
Para una explicación completa,
+ ver <xref linkend="objectstate-transitive"/>.
+ </para>
+
+ <para>
+ Una declaración típica
<literal>muchos-a-uno</literal> se parece a esto:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="product"
class="Product" column="PRODUCT_ID"/>]]></programlisting>
+
+ <para>
+ El atributo <literal>property-ref</literal> debe ser usado
solamente para el mapeo
+ de datos heredados donde una clave foránea referencia una
clave única de
+ la tabla asociada, distinta de la clave primaria. Este es un modelo
relacional feo.
+ Por ejemplo, supón que la clase
<literal>Product</literal> tuviera un número
+ único serial que no es la clave primaria. (El atributo
<literal>unique</literal>
+ controla la generación de DDL con la herramienta
SchemaExport.)
+ </para>
+
+ <programlisting><![CDATA[<property name="serialNumber"
unique="true" type="string"
column="SERIAL_NUMBER"/>]]></programlisting>
+
+ <para>
+ Entonces el mapeo para <literal>OrderItem</literal>
debería usar:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="product"
property-ref="serialNumber"
column="PRODUCT_SERIAL_NUMBER"/>]]></programlisting>
+
+ <para>
+ Sin embargo, esto no esta ciertamente alentado.
+ </para>
+
+ <para>
+ Si la clave única referenciada abarca múltiples
propiedades de la entidad asociada,
+ debes mapear las propiedades dentro de un elemento
<literal><properties></literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-onetoone" revision="2">
+ <title>one-to-one</title>
+
+ <para>
+ Una asociación uno-a-uno a otra clase persistente se declara
usando
+ un elemento <literal>one-to-one</literal>.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="onetoone1" coords="2 70"/>
+ <area id="onetoone2" coords="3 70"/>
+ <area id="onetoone3" coords="4 70"/>
+ <area id="onetoone4" coords="5 70"/>
+ <area id="onetoone5" coords="6 70"/>
+ <area id="onetoone6" coords="7 70"/>
+ <area id="onetoone7" coords="8 70"/>
+ <area id="onetoone8" coords="9 70"/>
+ <area id="onetoone9" coords="10 70"/>
+ <area id="onetoone10" coords="11 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<one-to-one
+ name="propertyName"
+ class="ClassName"
+ cascade="cascade_style"
+ constrained="true|false"
+ fetch="join|select"
+ property-ref="propertyNameFromAssociatedClass"
+ access="field|property|ClassName"
+ formula="cualquier expresión SQL"
+ lazy="true|proxy|false"
+ entity-name="EntityName"
+ node="element-name|@attribute-name|element/(a)attribute|."
+ embed-xml="true|false"
+ foreign-key="foreign_key_name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="onetoone1">
+ <para>
+ <literal>name</literal>: El nombre de la
propiedad.
+ </para>
+ </callout>
+ <callout arearefs="onetoone2">
+ <para>
+ <literal>class</literal> (opcional - por defecto
al tipo de la propiedad
+ determinado por reflección): El nombre de la clase
asociada.
+ </para>
+ </callout>
+ <callout arearefs="onetoone3">
+ <para>
+ <literal>cascade</literal> (opcional) especifica
qué operaciones deben
+ ir en cascada desde el objeto padre al objeto asociado.
+ </para>
+ </callout>
+ <callout arearefs="onetoone4">
+ <para>
+ <literal>constrained</literal> (opcional)
especifica que una restricción
+ de clave foránea de la tabla mapeada referencia a
la tabla de la clase
+ asociada. Esta opción afecta el orden en que van
en cascada
+ <literal>save()</literal> y
<literal>delete()</literal>, y determina cuándo
+ la asociación pueden ser virtualizados por proxies
(es también usado por
+ la herramienta de exportación de esquemas).
+ </para>
+ </callout>
+ <callout arearefs="onetoone5">
+ <para>
+ <literal>fetch</literal> (opcional - por defecto
<literal>select</literal>):
+ Elige entre recuperación outer-join o
recuperación por consulta secuencial.
+ </para>
+ </callout>
+ <callout arearefs="onetoone6">
+ <para>
+ <literal>property-ref</literal>: (opcional) El
nombre de una propiedad de la clase
+ asociada que esté unida a la clave primaria de
esta clase. Si no se especifica,
+ se usa la clave primaria de la clase asociada.
+ </para>
+ </callout>
+ <callout arearefs="onetoone7">
+ <para>
+ <literal>access</literal> (opcional - por defecto
a <literal>property</literal>): La
+ estrategia que Hibernate debe usar para acceder al valor de
la propiedad.
+ </para>
+ </callout>
+ <callout arearefs="onetoone8">
+ <para>
+ <literal>formula</literal> (opcional): Casi todas
las asociaciones uno-a-uno mapean
+ a la clave primaria de la entidad propietaria. En el raro
caso en que este no sea el caso,
+ puedes especificar alguna otra columna, o columnas, o
expresión para unir usando una
+ fórmula SQL. (Para un ejemplo ver
<literal>org.hibernate.test.onetooneformula</literal>).
+ </para>
+ </callout>
+ <callout arearefs="onetoone9">
+ <para>
+ <literal>lazy</literal> (opcional - por defecto a
<literal>proxy</literal>):
+ Por defecto, las asociaciones de punto único van
con proxies.
+ <literal>lazy="true"</literal>
especifica que esta propiedad debe ser
+ traída perezosamente cuando la variable de
instancia sea accedida por primera
+ vez (requiere instrumentación del bytecode en
tiempo de compilación).
+ <literal>lazy="false"</literal>
especifica que la asociación siempre será
+ recuperada tempranamente. <emphasis>Observa que si
<literal>constrained="false"</literal>,
+ la aplicación de proxies es imposible e Hibernate
traerá temprano la
+ asociación!</emphasis>
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Hay dos variedades de asociaciones uno-a-uno:
+ </para>
+ <itemizedlist>
+ <listitem><para>
+ asociaciones de clave primaria
+ </para></listitem>
+ <listitem><para>
+ asociaciones de clave foráneas única
+ </para></listitem>
+ </itemizedlist>
+
+ <para>
+ Las asociaciones de clave primaria no necesitan una columna de tabla
extra; si dos filas
+ están relacionadas por la asociación entonces las
dos filas de tablas comparten
+ el mismo valor de clave primaria. Por lo tanto, si quieres que dos
objetos estén relacionados
+ por una asociación de clave primaria, debes asegurarte que se
les asigne el mismo valor de
+ identificador!
+ </para>
+
+ <para>
+ Para una asociación de clave primaria, añade los
siguientes mapeos a
+ <literal>Employee</literal> y
<literal>Person</literal>, respectivamente.
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name="person"
class="Person"/>]]></programlisting>
+ <programlisting><![CDATA[<one-to-one name="employee"
class="Employee" constrained="true"/>]]></programlisting>
+
+ <para>
+ Ahora debemos asegurarnos que las claves primarias de las filas
relacionadas en las tablas
+ PERSON y EMPLOYEE sean iguales. Usamos una estrategia especial de
generación de identificador
+ de Hibernate llamada <literal>foreign</literal>:
+ </para>
+
+ <programlisting><![CDATA[<class name="person"
table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="foreign">
+ <param name="property">employee</param>
+ </generator>
+ </id>
+ ...
+ <one-to-one name="employee"
+ class="Employee"
+ constrained="true"/>
+</class>]]></programlisting>
+
+ <para>
+ A una instancia recién salvada de
<literal>Person</literal> se le asigna entonces
+ el mismo valor de clave primaria con que la instancia
<literal>Employee</literal>
+ referida por la propiedad <literal>employee</literal> de esta
<literal>Person</literal>.
+ </para>
+
+ <para>
+ Alternativamente, una clave foránea con una
restricción de unicidad, desde
+ <literal>Employee</literal> a
<literal>Person</literal>, puede ser expresada como:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="person"
class="Person" column="PERSON_ID"
unique="true"/>]]></programlisting>
+
+ <para>
+ Y esta asociación puede hacerse bidireccional agregando lo
siguiente al mapeo de
+ <literal>Person</literal> :
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name="employee"
class="Employee"
property-ref="person"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-naturalid">
+ <title>natural-id</title>
+
+ <programlisting><![CDATA[<natural-id
mutable="true|false"/>
+ <property ... />
+ <many-to-one ... />
+ ......
+</natural-id>]]></programlisting>
+
+ <para>
+ Aunque recomendamos el uso de claves delegadas como claves primarias,
todavía debes
+ intentar identificar claves naturales para todas las entidades. Una clave
natural es una
+ propiedad o combinación de propiedades que es única
y no nula. Si además
+ es inmutable, mejor aún. Mapea las propiedades de la clave
natural dentro del elemento
+ <literal><natural-id></literal>. Hibernate
generará las restricciones de clave
+ única y nulabilidad necesarias, y tu mapeo será
más auto-documentado.
+ </para>
+
+ <para>
+ Recomendamos fuertemente que implementes
<literal>equals()</literal> y
+ <literal>hashCode()</literal> para comparar las propiedades
de clave natural
+ de la entidad.
+ </para>
+
+ <para>
+ Este mapeo no está concebido para usar con entidades con
claves
+ primarias naturales.
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>mutable</literal> (opcional, por defecto a
<literal>false</literal>):
+ Por defecto, se asume que las propiedades de identificadores
naturales son
+ inmutables (constantes).
+ </para>
+ </listitem>
+ </itemizedlist>
+ </sect2>
+
+
+
+ <sect2 id="mapping-declaration-component"
revision="2">
+ <title>component, dynamic-component</title>
+
+ <para>
+ El elemento <literal><component></literal>
mapea propiedades de un objeto
+ hijo a columnas de la tabla de la clase padre. Los componentes pueden a
su vez
+ declarar sus propias propiedades, componentes o colecciones. Ver debajo
"Componentes".
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="component1" coords="2 45"/>
+ <area id="component2" coords="3 45"/>
+ <area id="component3" coords="4 45"/>
+ <area id="component4" coords="5 45"/>
+ <area id="component5" coords="6 45"/>
+ <area id="component6" coords="7 45"/>
+ <area id="component7" coords="8 45"/>
+ <area id="component8" coords="9 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<component
+ name="propertyName"
+ class="className"
+ insert="true|false"
+ update="true|false"
+ access="field|property|ClassName"
+ lazy="true|false"
+ optimistic-lock="true|false"
+ unique="true|false"
+ node="element-name|."
+>
+
+ <property ...../>
+ <many-to-one .... />
+ ........
+</component>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="component1">
+ <para>
+ <literal>name</literal>: El nombre de la
propiedad.
+ </para>
+ </callout>
+ <callout arearefs="component2">
+ <para>
+ <literal>class</literal> (opcional - por defecto
al tipo de la propiedad
+ determinado por reflección): El nombre de la clase
del componente (hijo).
+ </para>
+ </callout>
+ <callout arearefs="component3">
+ <para>
+ <literal>insert</literal>: Aparecen las columnas
mapeadas en
+ <literal>INSERT</literal>s SQL?
+ </para>
+ </callout>
+ <callout arearefs="component4">
+ <para>
+ <literal>update</literal>: Aparecen las columnas
mapeadas en
+ <literal>UPDATE</literal>s SQL?
+ </para>
+ </callout>
+ <callout arearefs="component5">
+ <para>
+ <literal>access</literal> (opcional - por defecto
a <literal>property</literal>): La
+ estrategia que Hibernate debe usar para acceder al valor de
la propiedad.
+ </para>
+ </callout>
+ <callout arearefs="component6">
+ <para>
+ <literal>lazy</literal> (opcional - por defecto a
<literal>false</literal>): Especifica
+ que este componente debe ser recuperado perezosamente cuando
la variable de instancia
+ sea accedida por primera vez (requiere
instrumentación de bytecode en tiempo de
+ compilación).
+ </para>
+ </callout>
+ <callout arearefs="component7">
+ <para>
+ <literal>optimistic-lock</literal> (opcional
- por defecto a <literal>true</literal>):
+ Especifica si las actualizaciones de este componente
requieren o no la
+ adquisición de un bloqueo optimista. En otras
palabras, determina si
+ debe ocurrir un incremento de versión cuando
esta propiedad está
+ desactualizada.
+ </para>
+ </callout>
+ <callout arearefs="component8">
+ <para>
+ <literal>unique</literal> (opcional - por
defecto a <literal>false</literal>):
+ Especifica que existe una restricción de
unicidad sobre todas las
+ columnas mapeadas del componente.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Las etiquetas hijas
<literal><property></literal> mapean propiedades de la
+ clase hija columnas de la tabla.
+ </para>
+
+ <para>
+ El elemento <literal><component></literal>
permite un subelemento
+ <literal><parent></literal> que mapea una
propiedad de la clase del componente
+ como una referencia de regreso a la entidad contenedora.
+ </para>
+
+ <para>
+ El elemento
<literal><dynamic-component></literal> permite que un
+ <literal>Map</literal> sea mapeado como un componente, donde
los nombres de
+ las propiedades se corresponden a las claves del mapa, ver <xref
linkend="components-dynamic"/>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-properties"
revision="2">
+ <title>properties</title>
+
+ <para>
+ El elemento <literal><properties></literal>
permite la definición de
+ un grupo de propiedades lógico con nombre de una clase. El uso
más
+ importante de la contrucción es que permite que una
combinación
+ de propiedades sea objetivo de una
<literal>property-ref</literal>. Es también
+ una forma conveniente de definir una restricción de unicidad
multicolumna.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="properties1" coords="2 45"/>
+ <area id="properties2" coords="3 45"/>
+ <area id="properties3" coords="4 45"/>
+ <area id="properties4" coords="5 45"/>
+ <area id="properties5" coords="6 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<properties
+ name="logicalName"
+ insert="true|false"
+ update="true|false"
+ optimistic-lock="true|false"
+ unique="true|false"
+>
+
+ <property ...../>
+ <many-to-one .... />
+ ........
+</properties>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="properties1">
+ <para>
+ <literal>name</literal>: El nombre
lógico del agrupamiento -
+ <emphasis>no</emphasis> un nombre de propiedad
real.
+ </para>
+ </callout>
+ <callout arearefs="properties2">
+ <para>
+ <literal>insert</literal>: Aparecen las columnas
mapeadas en
+ <literal>INSERT</literal>s SQL?
+ </para>
+ </callout>
+ <callout arearefs="properties3">
+ <para>
+ <literal>update</literal>: Aparecen las columnas
mapeadas en
+ <literal>UPDATE</literal>s SQL?
+ </para>
+ </callout>
+ <callout arearefs="properties4">
+ <para>
+ <literal>optimistic-lock</literal> (opcional
- por defecto a <literal>true</literal>):
+ Especifica si las actualizaciones de estas propiedades
requieren o no de la
+ adquisición de un bloqueo optimista. En otras
palabras, determina si
+ debe ocurrir un incremento de versión cuando
estas propiedades están
+ desactualizadas.
+ </para>
+ </callout>
+ <callout arearefs="properties5">
+ <para>
+ <literal>unique</literal> (opcional - por
defecto a <literal>false</literal>):
+ Especifica que existe una restricción de
unicidad sobre todas las
+ columnas mapeadas del componente.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Por ejemplo, si tenemos el siguiente mapeo de
<literal><properties></literal>:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="personNumber"/>
+ ...
+ <properties name="name"
+ unique="true" update="false">
+ <property name="firstName"/>
+ <property name="initial"/>
+ <property name="lastName"/>
+ </properties>
+</class>]]></programlisting>
+
+ <para>
+ Entonces puede que tengamos alguna asociación de datos
heredados que se refiera
+ a esta clave única de la tabla de
<literal>Person</literal>, en vez de la clave
+ primaria:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="person"
+ class="Person" property-ref="name">
+ <column name="firstName"/>
+ <column name="initial"/>
+ <column name="lastName"/>
+</many-to-one>]]></programlisting>
+
+ <para>
+ No recomendamos el uso de este tipo de cosas fuera del contexto del mapeo
de
+ datos heredados.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-subclass" revision="3">
+ <title>subclass</title>
+
+ <para>
+ Finalmente, la persistencia polimórfica requiere la
declaración
+ de la clase persistente raíz. Para la estrategia de mapeo
+ tabla-por-jerarquía-de-clases, se usa la
declaración de
+ <literal><subclass></literal>.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="subclass1" coords="2 55"/>
+ <area id="subclass2" coords="3 55"/>
+ <area id="subclass3" coords="4 55"/>
+ <area id="subclass4" coords="5 55"/>
+ </areaspec>
+ <programlisting><![CDATA[<subclass
+ name="ClassName"
+ discriminator-value="discriminator_value"
+ proxy="ProxyInterface"
+ lazy="true|false"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ entity-name="EntityName"
+ node="element-name">
+
+ <property .... />
+ .....
+</subclass>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="subclass1">
+ <para>
+ <literal>name</literal>: El nombre de clase
cualificado completamente
+ de la subclase.
+ </para>
+ </callout>
+ <callout arearefs="subclass2">
+ <para>
+ <literal>discriminator-value</literal> (opcional
- por defecto al nombre de la clase):
+ Un valor que distingue a subclases individuales.
+ </para>
+ </callout>
+ <callout arearefs="subclass3">
+ <para>
+ <literal>proxy</literal> (opcional): Especifica
una clase o interface a usar para
+ proxies de inicialización perezosa.
+ </para>
+ </callout>
+ <callout arearefs="subclass4">
+ <para>
+ <literal>lazy</literal> (opcional, por defecto a
<literal>true</literal>):
+ Establecer
<literal>lazy="false"</literal> deshabilita el uso de
recuperación
+ perezosa.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Cada subclase debe declarar sus propias propiedades persistentes y
subclases.
+ Se asume que las propiedades
<literal><version></literal> y
<literal><id></literal>
+ son heredadas de la clase raíz. Cada subclase en una
jerarquía debe
+ definir un <literal>discriminator-value</literal>
único. Si no se especifica ninguno,
+ se usa el nombre completamente cualificado de clase Java.
+ </para>
+
+ <para>
+ Es posible definir mapeos <literal>subclass</literal>,
<literal>union-subclass</literal>,
+ y <literal>joined-subclass</literal> en documentos de mapeo
separados, directamente debajo
+ de <literal>hibernate-mapping</literal>. Esto te permite extender
una jerarquía de clases
+ con sólo agregar un nuevo fichero de mapeo. Debes especificar un
atributo
+ <literal>extends</literal> en el mapeo de la subclase,
mencionando una superclase previamente mapeada.
+ Nota: Previamente esta funcionalidad hacía importante el orden de
los documentos de mapeo.
+ Desde Hibernate3, el orden de los ficheros de mapeo no importa cuando se usa
la palabra reservada
+ extends. El orden dentro de un mismo fichero de mapeo todavía
necesita ser definido como
+ superclases antes de subclases.
+ </para>
+
+ <programlisting><![CDATA[
+<hibernate-mapping>
+ <subclass name="DomesticCat" extends="Cat"
discriminator-value="D">
+ <property name="name" type="string"/>
+ </subclass>
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Para información acerca de mapeos de herencia, ver <xref
linkend="inheritance"/>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-joinedsubclass"
revision="3">
+ <title>joined-subclass</title>
+
+ <para>
+ Alternativamente, cada subclase puede ser mapeada a su propia tabla
+ (estrategia de mapeo tabla-por-subclase). El estado heredado se recupera
+ uniendo con la tabla de la superclase.
+ Usamos el elemento
<literal><joined-subclass></literal>.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="joinedsubclass1" coords="2 45"/>
+ <area id="joinedsubclass2" coords="3 45"/>
+ <area id="joinedsubclass3" coords="4 45"/>
+ <area id="joinedsubclass4" coords="5 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<joined-subclass
+ name="ClassName"
+ table="tablename"
+ proxy="ProxyInterface"
+ lazy="true|false"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ schema="schema"
+ catalog="catalog"
+ extends="SuperclassName"
+ persister="ClassName"
+ subselect="SQL expression"
+ entity-name="EntityName"
+ node="element-name">
+
+ <key .... >
+
+ <property .... />
+ .....
+</joined-subclass>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="joinedsubclass1">
+ <para>
+ <literal>name</literal>: El nombre de clase
completamente cualificado de
+ la subclase.
+ </para>
+ </callout>
+ <callout arearefs="joinedsubclass2">
+ <para>
+ <literal>table</literal>: El nombre de tabla de
la subclase.
+ </para>
+ </callout>
+ <callout arearefs="joinedsubclass3">
+ <para>
+ <literal>proxy</literal> (opcional): Especifica
una clase o interface a
+ usar para proxies de inicializacón perezosa.
+ </para>
+ </callout>
+ <callout arearefs="joinedsubclass4">
+ <para>
+ <literal>lazy</literal> (opcional, por defecto a
<literal>true</literal>):
+ Establecer
<literal>lazy="false"</literal> deshabilita el uso de
recuperación
+ perezosa.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ No se requiere de una columna discriminadora para esta estrategia de
mapeo. Cada subclase debe,
+ sin embargo, declarar una columna de tabla que tenga el identificador del
objeto usando el
+ elemento <literal><key></literal>. El mapeo del
comienzo del capítulo
+ debería ser reescrito como:
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class name="Cat" table="CATS">
+ <id name="id" column="uid"
type="long">
+ <generator class="hilo"/>
+ </id>
+ <property name="birthdate" type="date"/>
+ <property name="color" not-null="true"/>
+ <property name="sex" not-null="true"/>
+ <property name="weight"/>
+ <many-to-one name="mate"/>
+ <set name="kittens">
+ <key column="MOTHER"/>
+ <one-to-many class="Cat"/>
+ </set>
+ <joined-subclass name="DomesticCat"
table="DOMESTIC_CATS">
+ <key column="CAT"/>
+ <property name="name" type="string"/>
+ </joined-subclass>
+ </class>
+
+ <class name="eg.Dog">
+ <!-- mapping for Dog could go here -->
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Para información acerca de mapeos de herencia, ver <xref
linkend="inheritance"/>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-unionsubclass"
revision="2">
+ <title>union-subclass</title>
+
+ <para>
+ Una tercera opción es mapear sólo las clases
concretas de una
+ jerarquía de clases a tablas, (la estrategia
tabla-por-clase-concreta)
+ donde cada tabla define todo el estado persistente de la clase, incluyendo
el
+ estado heredado. En Hibernate, no es absolutamente necesario mapear
dichas
+ jerarquías de herencia. Puedes simplemente mapear cada clase
con una
+ declaración
<literal><class></literal> separada. Sin embargo,
+ si deseas usar asociaciones polimórficas (por ejemplo, una
asociación
+ a la superclase de tu jerarquía), debes usar el mapeo
+ <literal><union-subclass></literal>.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="unionsubclass1" coords="2 45"/>
+ <area id="unionsubclass2" coords="3 45"/>
+ <area id="unionsubclass3" coords="4 45"/>
+ <area id="unionsubclass4" coords="5 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<union-subclass
+ name="ClassName"
+ table="tablename"
+ proxy="ProxyInterface"
+ lazy="true|false"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ schema="schema"
+ catalog="catalog"
+ extends="SuperclassName"
+ abstract="true|false"
+ persister="ClassName"
+ subselect="SQL expression"
+ entity-name="EntityName"
+ node="element-name">
+
+ <property .... />
+ .....
+</union-subclass>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="unionsubclass1">
+ <para>
+ <literal>name</literal>: El nombre de clase
completamente cualificado de la subclase.
+ </para>
+ </callout>
+ <callout arearefs="unionsubclass2">
+ <para>
+ <literal>table</literal>: El nombre de tabla de
la subclase.
+ </para>
+ </callout>
+ <callout arearefs="unionsubclass3">
+ <para>
+ <literal>proxy</literal> (optional): Especifica
una clase o interface a
+ usar para proxies de inicializacón perezosa.
+ </para>
+ </callout>
+ <callout arearefs="unionsubclass4">
+ <para>
+ <literal>lazy</literal> (opcional, por defecto a
<literal>true</literal>):
+ Establecer
<literal>lazy="false"</literal> deshabilita el uso de
recuperación
+ perezosa.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ No se requiere columna o columna clave discriminadora para esta
estrategia de mapeo.
+ </para>
+
+ <para>
+ Para información acerca de mapeos de herencia, ver <xref
linkend="inheritance"/>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-join" revision="3">
+ <title>join</title>
+
+ <para>
+ Usando el elemento <literal><join></literal>,
es posible mapear
+ propiedades de una clase a varias tablas.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="join1" coords="2 50"/>
+ <area id="join2" coords="3 50"/>
+ <area id="join3" coords="4 50"/>
+ <area id="join4" coords="5 50"/>
+ <area id="join5" coords="6 50"/>
+ <area id="join6" coords="7 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<join
+ table="tablename"
+ schema="owner"
+ catalog="catalog"
+ fetch="join|select"
+ inverse="true|false"
+ optional="true|false">
+
+ <key ... />
+
+ <property ... />
+ ...
+</join>]]></programlisting>
+
+ <calloutlist>
+ <callout arearefs="join1">
+ <para>
+ <literal>table</literal>: El nombre de la clase
unida.
+ </para>
+ </callout>
+ <callout arearefs="join2">
+ <para>
+ <literal>schema</literal> (opcional): Sobrescribe
el nombre de esquema
+ especificado por el elemento raíz
<literal><hibernate-mapping></literal>.
+ </para>
+ </callout>
+ <callout arearefs="join3">
+ <para>
+ <literal>catalog</literal> (opcional):
Sobrescribe el nombre de catálogo
+ especificado por el elemento raíz
<literal><hibernate-mapping></literal>.
+ </para>
+ </callout>
+ <callout arearefs="join4">
+ <para>
+ <literal>fetch</literal> (opcional - por defecto
a <literal>join</literal>):
+ Si se establece a <literal>join</literal>, por
defecto, Hibernate usará
+ una unión interior (inner join) para recuperar un
<literal><join></literal>
+ definido por una clase o sus superclases y una
unión externa (outer join)
+ para un <literal><join></literal>
definido por una subclase.
+ Si se establece a <literal>select</literal>,
entonces Hibernate usará una
+ select secuencial para un
<literal><join></literal> definido en una subclase,
+ que será publicada sólo si una fila
resulta representar una instancia
+ de la subclase. Las uniones interiores todavía
serán usados para
+ recuperar un
<literal><join></literal> definido por la clase y sus
superclases.
+ </para>
+ </callout>
+ <callout arearefs="join5">
+ <para>
+ <literal>inverse</literal> (opcional - por
defecto a <literal>false</literal>):
+ De habilitarse, Hibernate no intentará insertar o
actualizar las propiedades
+ definidas por esta unión.
+ </para>
+ </callout>
+ <callout arearefs="join6">
+ <para>
+ <literal>optional</literal> (opcional - por
defecto a <literal>false</literal>):
+ De habilitarse, Hibernate insertará una fila
sólo si las propiedades
+ definidas por esta unión son no nulas y siempre
usará una unión
+ externa para recuperar las propiedades.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Por ejemplo, la información domiciliaria de una persona puede
ser mapeada
+ a una tabla separada (preservando a la vez la semántica de
tipo de valor para
+ todas las propiedades):
+ </para>
+
+ <programlisting><![CDATA[<class name="Person"
+ table="PERSON">
+
+ <id name="id" column="PERSON_ID">...</id>
+
+ <join table="ADDRESS">
+ <key column="ADDRESS_ID"/>
+ <property name="address"/>
+ <property name="zip"/>
+ <property name="country"/>
+ </join>
+ ...]]></programlisting>
+
+ <para>
+ Esta funcionalidad es a menudo solamente útil para modelos de
datos
+ heredados; recomendamos menos tablas que clases un modelo de dominio
más
+ granularizado. Sin embargo, es útil para cambiar entre
estrategias de mapeo
+ de herencias en una misma jerarquía, como se explica luego.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-key">
+ <title>key</title>
+
+ <para>
+ Hasta ahora hemos visto el elemento
<literal><key></literal> pocas veces.
+ Aparece en cualquier sitio en que el elemento padre de mapeo defina una
unión
+ a una nueva tabla, y define la clave foránea en la tabla
unida,
+ que referencia la clave primaria de la tabla original.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="key1" coords="2 50"/>
+ <area id="key2" coords="3 50"/>
+ <area id="key3" coords="4 50"/>
+ <area id="key4" coords="5 50"/>
+ <area id="key5" coords="6 50"/>
+ <area id="key6" coords="7 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<key
+ column="columnname"
+ on-delete="noaction|cascade"
+ property-ref="propertyName"
+ not-null="true|false"
+ update="true|false"
+ unique="true|false"
+/>]]></programlisting>
+
+ <calloutlist>
+ <callout arearefs="key1">
+ <para>
+ <literal>column</literal> (opcional): El nombre
de columna de la clave foránea.
+ Puede ser también especificado por elemento(s)
anidado(s)
+ <literal><column></literal>.
+ </para>
+ </callout>
+ <callout arearefs="key2">
+ <para>
+ <literal>on-delete</literal> (opcional, por
defecto a <literal>noaction</literal>):
+ Especifica si la restricción de clave
foránea tiene el borrado en cascada
+ habilitado a nivel de base de datos.
+ </para>
+ </callout>
+ <callout arearefs="key3">
+ <para>
+ <literal>property-ref</literal> (opcional):
Especifica que la clave foránea
+ referencia columnas que no son del la clave primaria de la
tabla original.
+ (Provisto para datos heredados.)
+ </para>
+ </callout>
+ <callout arearefs="key4">
+ <para>
+ <literal>not-null</literal> (opcional):
Especifica que las columnas de la clave
+ foránea son no nulables (esto está
implicado si la clave foránea
+ es también parte de la clave primaria).
+ </para>
+ </callout>
+ <callout arearefs="key5">
+ <para>
+ <literal>update</literal> (opcional): Especifica
que la clave foránea nunca
+ debe ser actualizada (esto está implicado si la
clave foránea
+ es también parte de la clave primaria).
+ </para>
+ </callout>
+ <callout arearefs="key6">
+ <para>
+ <literal>unique</literal> (opcional): Especifica
que la clave foránea
+ debe tener una restricción de unicidad (esto
está implicado si
+ la clave foránea es también la clave
primaria).
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Recomendamos que, para los sistemas en donde el rendimiento sea
importante, todas las
+ claves deben ser definidas
<literal>on-delete="cascade"</literal>, e Hibernate
usará
+ una restricción <literal>ON CASCADE
DELETE</literal> a nivel de base de datos,
+ en vez de muchas sentencias <literal>DELETE</literal>
individuales. Ten en cuenta que
+ esta funcionalidad se salta la habitual estrategia de bloqueo optimista
de Hibernate para
+ datos versionados.
+ </para>
+
+ <para>
+ Los atributos <literal>not-null</literal> y
<literal>update</literal> son útiles
+ al mapear una asociación uno a muchos unidireccional. Si
mapeas una uno a muchos
+ unidireccional a una clave foránea no nulable,
<emphasis>debes</emphasis> declarar
+ la columna clave usando <literal><key
not-null="true"></literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-column" revision="3">
+ <title>los elementos column y formula</title>
+ <para>
+ Cualquier elemento de mapeo que acepte un atributo
<literal>column</literal> aceptará
+ alternativamente un subelemento
<literal><column></literal>. De forma similar,
+ <literal><formula></literal> es una alternativa
al atributo <literal>formula</literal>.
+ </para>
+
+ <programlisting><![CDATA[<column
+ name="column_name"
+ length="N"
+ precision="N"
+ scale="N"
+ not-null="true|false"
+ unique="true|false"
+ unique-key="multicolumn_unique_key_name"
+ index="index_name"
+ sql-type="sql_type_name"
+ check="SQL expression"/>]]></programlisting>
+
+ <programlisting><![CDATA[<formula>expresión
SQL</formula>]]></programlisting>
+
+ <para>
+ Los atributos <literal>column</literal> y
<literal>formula</literal> pueden
+ incluso ser combinados dentro del mismo mapeo de propiedad o
asociación para
+ expresar, por ejemplo, condiciones de unión
exóticas.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one
name="homeAddress" class="Address"
+ insert="false" update="false">
+ <column name="person_id" not-null="true"
length="10"/>
+ <formula>'MAILING'</formula>
+</many-to-one>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-import">
+ <title>import</title>
+
+ <para>
+ Supón que tu aplicación tiene dos clases
persistentes con el mismo nombre,
+ y no quieres especificar el nombre completamenta cualificado (paquete) en
las consultas
+ Hibernate. Las clases pueden ser "importadas"
explícitamente, en vez de confiar en
+ <literal>auto-import="true"</literal>. Puedes
incluso importar clases e interfaces que
+ no estén mapeadas explícitamente.
+ </para>
+
+ <programlisting><![CDATA[<import
class="java.lang.Object"
rename="Universe"/>]]></programlisting>
+
+ <programlistingco>
+ <areaspec>
+ <area id="import1" coords="2 40"/>
+ <area id="import2" coords="3 40"/>
+ </areaspec>
+ <programlisting><![CDATA[<import
+ class="ClassName"
+ rename="ShortName"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="import1">
+ <para>
+ <literal>class</literal>: El nombre de clase
completamente cualificado de
+ cualquier clase Java.
+ </para>
+ </callout>
+ <callout arearefs="import2">
+ <para>
+ <literal>rename</literal> (opcional - por defecto
al nombre de clase sin cualificar):
+ Un nombre que será usado en el leguaje de
consulta.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ </sect2>
+
+ <sect2 id="mapping-types-anymapping" revision="2">
+ <title>any</title>
+
+ <para>
+ Hay un tipo más de mapeo de propiedad. El elemento de mapeo
<literal><any></literal>
+ define una asociacián polimórfica a clases desde
múltiples tablas. Este tipo
+ de mapeo siempre requiere más de una columna. La primera
columna contiene el tipo de la
+ entidad asociada. Las columnas restantes contienen el identificador. Es
imposible especificar una
+ restricción de clave foránea para este tipo de
asociación, por lo que
+ esto ciertamente no está concebido como la forma habitual de
mapear asociaciones
+ (polimórficas). Sólo debes usar esto en casos muy
especiales (por ejemplo,
+ trazas de auditoréa, datos de sesión de usuario,
etc).
+ </para>
+
+ <para>
+ El atributo <literal>meta-type</literal> permite a la
aplicación especificar un tipo
+ personalizado que mapee columnas de base de datos a clases persistentes
que tengan propiedades
+ identificadoras del tipo especificado por
<literal>id-type</literal>. Debes especificar el
+ mapeo de valores del meta-type a nombres de clase.
+ </para>
+
+ <programlisting><![CDATA[<any name="being"
id-type="long" meta-type="string">
+ <meta-value value="TBL_ANIMAL" class="Animal"/>
+ <meta-value value="TBL_HUMAN" class="Human"/>
+ <meta-value value="TBL_ALIEN" class="Alien"/>
+ <column name="table_name"/>
+ <column name="id"/>
+</any>]]></programlisting>
+
+ <programlistingco>
+ <areaspec>
+ <area id="any1" coords="2 50"/>
+ <area id="any2" coords="3 50"/>
+ <area id="any3" coords="4 50"/>
+ <area id="any4" coords="5 50"/>
+ <area id="any5" coords="6 50"/>
+ <area id="any6" coords="7 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<any
+ name="propertyName"
+ id-type="idtypename"
+ meta-type="metatypename"
+ cascade="cascade_style"
+ access="field|property|ClassName"
+ optimistic-lock="true|false"
+>
+ <meta-value ... />
+ <meta-value ... />
+ .....
+ <column .... />
+ <column .... />
+ .....
+</any>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="any1">
+ <para>
+ <literal>name</literal>: el nombre de la
propiedad.
+ </para>
+ </callout>
+ <callout arearefs="any2">
+ <para>
+ <literal>id-type</literal>: el tipo del
identificador.
+ </para>
+ </callout>
+ <callout arearefs="any3">
+ <para>
+ <literal>meta-type</literal> (opcional - por
defecto a <literal>string</literal>):
+ Cualquier tipo que sea permitido para un mapeo de
discriminador.
+ </para>
+ </callout>
+ <callout arearefs="any4">
+ <para>
+ <literal>cascade</literal> (opcional- por defecto
a <literal>none</literal>):
+ el estilo de cascada.
+ </para>
+ </callout>
+ <callout arearefs="any5">
+ <para>
+ <literal>access</literal> (opcional - por defecto
a <literal>property</literal>): La
+ estrategia que Hibernate debe usar para acceder al valor de
la propiedad.
+ </para>
+ </callout>
+ <callout arearefs="any6">
+ <para>
+ <literal>optimistic-lock</literal> (opcional -
por defecto a <literal>true</literal>):
+ Especifica si las actualizaciones de esta propiedad requieren
o no de la
+ adquisición del bloqueo optimista. En otras
palabras, determina si debe ocurrir
+ un incremento de versión cuando esta propiedad
está desactualizada.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="mapping-types">
+ <title>Tipos de Hibernate</title>
+
+ <sect2 id="mapping-types-entitiesvalues" revision="1">
+ <title>Entidades y Valores</title>
+
+ <para>
+ Para entender el comportamiento de varios objetos a nivel de lenguaje
Java
+ con respecto al servicio de persistencia, necesitamos clasificarlos en
dos grupos:
+ </para>
+
+ <para>
+ Una <emphasis>entidad</emphasis> existe independientemente de
cualquier otros
+ objetos que referencien a la entidad. Contrasta esto con el model
habitual de Java
+ donde un objeto desreferenciado es recolectado como basura. Las entidades
deben ser
+ salvadas y borradas explícitamente (excepto que las
grabaciones y borrados
+ puedan ser <emphasis>tratados en cascada</emphasis> desde una
entidad padre a sus hijos).
+ Esto es diferente al modelo de persistencia de objetos por alcance - y se
corresponde
+ más de cerca a cómo los objetos de
aplicación son usados
+ habitualmente en grandes sistemas. Las entidades soportan referencias
circulares y
+ compartidas, que tambié pueden ser versionadas.
+ </para>
+
+ <para>
+ El estado persistente de una entidad consiste en referencias a otras
entidades
+ e instancias de tipo <emphasis>valor</emphasis>. Los valores
son primitivos,
+ colecciones (no lo que está dentro de la
colección), componentes
+ y ciertos objetos inmutables. A diferencia de las entidades, los valores
+ (en particular las colecciones y los componentes)
<emphasis>son</emphasis>
+ hechos persitentes y borrados por alcance. Como los objetos valor (y
primitivos)
+ son persistidos y borrados junto a sus entidades contenedoras, no pueden
ser
+ versionados independientemente. Los valores no tienen identidad
independiente,
+ por los que no pueden ser compartidos por dos entidades o colleciones.
+ </para>
+
+ <para>
+ Hasta ahora, hemos estado usando el término "clase
persistente"
+ para referirnos a entidades. Continuaremos haciéndolo.
Hablando
+ estrictamente, sin embargo, no todas la clases con estado persistente
+ definidas por el usuario son entidades. Un
<emphasis>componente</emphasis>
+ es una clase definida por el usuario con semántica de valor.
+ Una propiedad Java de tipo
<literal>java.lang.String</literal> también
+ tiene semántica de valor. Dada esta definición,
podemos decir
+ que todos los tipo (clases) provistos por el JDK tienen una
semántica
+ de tipo valor en Java, mientras que los tipos definidos por el usuario
+ pueden ser mapeados con semántica de tipo valor o de entidad.
+ La desición corre por cuenta del desarrollador de la
aplicación.
+ Un buen consejo para una clase entidad en un modelo de dominio son las
referencias
+ compartidas a una sola instancia de esa clase, mientras que la
composición
+ o agregación usualmente se traducen a un tipo de valor.
+ </para>
+
+ <para>
+ Volveremos a visitar ambos conceptos a lo largo de la
documentación.
+ </para>
+
+ <para>
+ EL desafío es mapear el sistema de tipos de Java (y la
definición
+ de entidades y tipos de valor de los desarrolladores) al sistema de tipos
de
+ SQL/base de datos. EL puente entre ambos sistemas es provisto por
Hibernate:
+ para las entidades usamos
<literal><class></literal>,
+ <literal><subclass></literal>, etc. Para los
tipos de valor usamos
+ <literal><property></literal>,
<literal><component></literal>, etc,
+ usualmente con un atributo <literal>type</literal>. El valor
de este atributo
+ es el nombre de un <emphasis>tipo de mapeo</emphasis> de
Hibernate. Hibernate
+ provee de fábrica muchos mapeos (para tipos de valores del
JDK
+ estándar). Puedes escribir tus propios mapeos de tipo,
así como
+ implementar tus estrategias de conversión personalizadas, como
veremos luego.
+ </para>
+
+ <para>
+ Todos los tipos prefabricados de Hibernate soportan semántica
de nulos
+ excepto las colecciones.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-types-basictypes" revision="2">
+ <title>Tipos de valores básicos</title>
+
+ <para>
+ Los <emphasis>tipos de mapeo básicos</emphasis>
prefabricados pueden ser
+ categorizado a grandes rasgos en:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>integer, long, short, float, double,
character, byte,
+ boolean, yes_no, true_false</literal></term>
+ <listitem>
+ <para>
+ Mapeos de tipos primitivos de Java o clases de envoltura
a
+ la tipos de columna SQL (especícifica del
vendedor).
+ <literal>boolean, yes_no</literal> y
<literal>true_false</literal>
+ son codificaciones alternativas a
<literal>boolean</literal> de
+ Java o <literal>java.lang.Boolean</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>string</literal></term>
+ <listitem>
+ <para>
+ Un mapeo del tipo
<literal>java.lang.String</literal> a
+ <literal>VARCHAR</literal> (u Oracle
<literal>VAARCHAR2</literal>).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>date, time,
timestamp</literal></term>
+ <listitem>
+ <para>
+ Mapeos de tipo desde
<literal>java.util.Date</literal> y sus subclases
+ a tipos SQL <literal>DATE</literal>,
<literal>TIME</literal> y
+ <literal>TIMESTAMP</literal> (o
equivalente).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>calendar,
calendar_date</literal></term>
+ <listitem>
+ <para>
+ Mapeos de tipo desde
<literal>java.util.Date</literal> y sus subclases
+ a tipos SQL <literal>TIMESTAMP</literal> y
<literal>DATE</literal>
+ (o equivalente).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>big_decimal,
big_integer</literal></term>
+ <listitem>
+ <para>
+ Mapeos de tipo desde
<literal>java.math.BigDecimal</literal> y
+ <literal>java.math.BigInteger</literal> a
<literal>NUMERIC</literal>
+ (o <literal>NUMBER</literal> de Oracle).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>locale, timezone,
currency</literal></term>
+ <listitem>
+ <para>
+ Mapeos de tipo desde
<literal>java.util.Locale</literal>,
+ <literal>java.util.TimeZone</literal> y
+ <literal>java.util.Currency</literal> a
+ <literal>VARCHAR</literal> (o
<literal>VARCHAR2</literal> de Oracle).
+ Las instancias de <literal>Locale</literal> y
<literal>Currency</literal>
+ son mapeadas a sus códigos ISO. Las instancias
de
+ <literal>TimeZone</literal> son mapeadas a
sus <literal>ID</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>class</literal></term>
+ <listitem>
+ <para>
+ Un mapeo de tipo
<literal>java.lang.Class</literal> a
+ <literal>VARCHAR</literal> (o
<literal>VARCHAR2</literal> de Oracle).
+ Una <literal>Class</literal> es mapeara a su
nombre completamente
+ cualificado.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>binary</literal></term>
+ <listitem>
+ <para>
+ Mapea arreglos de bytes a un tipo binario SQL apropiado.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>text</literal></term>
+ <listitem>
+ <para>
+ Mapea cadenas largas Java al tipo SQL
<literal>CLOB</literal> o
+ <literal>TEXT</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+
<term><literal>serializable</literal></term>
+ <listitem>
+ <para>
+ Mapea tipos serializables Java a un tipo binario SQL
apropiado.
+ Puedes además indicar el tipo
<literal>serializable</literal>
+ de Hibernate con el nombre de una clase o interface
serializable Java
+ que no sea por defecto un tipo básico.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>clob,
blob</literal></term>
+ <listitem>
+ <para>
+ Mapeos de tipo para las clases JDBC
<literal>java.sql.Clob</literal> y
+ <literal>java.sql.Blob</literal>. Estos tipos
pueden ser inconvenientes
+ para algunas aplicaciones, pues el objeto blob o clob no
puede ser reusado
+ fuera de una transacción (Además,
el soporte del driver suele
+ ser malo e inconsistente).
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </para>
+
+ <para>
+ Los identificadores únicos de entidades y collecciones pueden
ser de cualquier
+ tipo básico excepto <literal>binary</literal>,
<literal>blob</literal>
+ y <literal>clob</literal>.
+ (Los identificadores compuestos están también
permitidos, ver debajo.)
+ </para>
+
+ <para>
+ Los tipos de valor básicos tienen sus constantes
<literal>Type</literal>
+ correspondientes definidas en
<literal>org.hibernate.Hibernate</literal>. Por ejemplo,
+ <literal>Hibernate.STRING</literal> representa el tipo
<literal>string</literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-types-custom" revision="2">
+ <title>Tipos de valor personalizados</title>
+
+ <para>
+ Es relativamente fácil para los desarrolladores crear sus
propios tipos de valor.
+ Por ejemplo, podrías querer persistir propiedades del tipo
<literal>java.lang.BigInteger</literal>
+ a columnas <literal>VARCHAR</literal>. Hibernate no provee un
tipo de fábrica para esto.
+ Pero los tipos personalizados no están limitados a mapear una
propiedad (o elemento de colección)
+ a una sola columna de tabla. Así, por ejemplo,
podrías tener una propiedad Java
+
<literal>getName()</literal>/<literal>setName()</literal> de tipo
<literal>java.lang.String</literal>
+ que fuera persistida a las columnas
<literal>FIRST_NAME</literal>, <literal>INITIAL</literal>,
+ <literal>SURNAME</literal>.
+ </para>
+
+ <para>
+ Para implementar un tipo personalizado, implementa bien
<literal>org.hibernate.UserType</literal>
+ o <literal>org.hibernate.CompositeUserType</literal> y
declara las propiedades usando el nombre
+ de clase completamente cualificado del tipo. Revisa
<literal>org.hibernate.test.DoubleStringType</literal>
+ para ver qué tipo de cosas son posibles.
+ </para>
+
+ <programlisting><![CDATA[<property name="twoStrings"
type="org.hibernate.test.DoubleStringType">
+ <column name="first_string"/>
+ <column name="second_string"/>
+</property>]]></programlisting>
+
+ <para>
+ Observa el uso de etiquetas
<literal><column></literal> para mapear una propiedad
+ a múltiples columnas.
+ </para>
+
+ <para>
+ Las interfaces <literal>CompositeUserType</literal>,
<literal>EnhancedUserType</literal>,
+ <literal>UserCollectionType</literal>, y
<literal>UserVersionType</literal> proveen
+ soporte a usos más especializados.
+ </para>
+
+ <para>
+ Puedes incluso proveer de parámetros a un
<literal>UserType</literal> en el
+ fichero de mapeo. Para hacer esto, tu
<literal>UserType</literal> debe implementar
+ la interface
<literal>org.hibernate.usertype.ParameterizedType</literal>. Para
+ proveer de parámetros a tu tipo personalizado, puedes usar el
elemento
+ <literal><type></literal> en tus ficheros de
mapeo.
+ </para>
+
+ <programlisting><![CDATA[<property name="priority">
+ <type name="com.mycompany.usertypes.DefaultValueIntegerType">
+ <param name="default">0</param>
+ </type>
+</property>]]></programlisting>
+
+ <para>
+ Ahora el <literal>UserType</literal> puede recuperar el valor
del parámetro
+ llamado <literal>default</literal> del objeto
<literal>Properties</literal>
+ que se le pasa.
+ </para>
+
+ <para>
+ Si usas cierto <literal>UserType</literal> muy
frecuentemente, puede ser útil
+ definir un nombre corto para é. Puedes hacer esto usando el
elemento
+ <literal><typedef></literal>. Los typedefs
asignan un nombre a un tipo
+ personalizado, y pueden también contener una lista de valores
por defecto
+ de parámetros si el tipo fuese parametrizado.
+ </para>
+
+ <programlisting><![CDATA[<typedef
class="com.mycompany.usertypes.DefaultValueIntegerType"
name="default_zero">
+ <param name="default">0</param>
+</typedef>]]></programlisting>
+
+ <programlisting><![CDATA[<property name="priority"
type="default_zero"/>]]></programlisting>
+
+ <para>
+ también es posible sobrescribir los parámetros
provistos en un typedef sobre
+ una base caso por caso usando parámetros de tipo en el mapeo
de la propiedad.
+ </para>
+
+ <para>
+ Aunque el rico espectro de tipos prefabricados y soporte de componentes
de Hibernate
+ significa que raramente <emphasis>necesites</emphasis> usar
un tipo personalizado;
+ sin embargo se considera una buena forma usar tipos personalizados para
clases (no-entidades)
+ que aparezcan frecuentemente en tu aplicación. Por ejemplo,
una clase
+ <literal>MonetaryAmount</literal> es una buena candidata para
un
+ <literal>CompositeUserType</literal>, incluso cuando puede
ser facilmente mapeada como un
+ componente. Un motivo para esto es la abstracción. Con un tipo
personalizado,
+ tus documentos de mapeo estará impermeabilizados contra
posibles cambios futuros en la
+ forma de representar valores monetarios.
+ </para>
+ </sect2>
+
+ </sect1>
+
+
+ <sect1 id="mapping-entityname">
+ <title>Mapeando una clase más de una vez</title>
+ <para>
+ Es posible proveer más de un mapeo para una clase persistente en
particular. En este caso debes
+ especificar un <emphasis>nombre de entidad</emphasis> para
desambiguar entr las instancias de las
+ dos entidades mapeadas. (Por defectom, el nombre de la entidad es el mismo
que el nombre de la clase.)
+ Hibernate te deja especificar el nombre de entidad al trabajar con objetos
persistentes, al escribir
+ consultas, o al mapear asociaciones a la entidad mencionada.
+ </para>
+
+ <programlisting><![CDATA[<class name="Contract"
table="Contracts"
+ entity-name="CurrentContract">
+ ...
+ <set name="history" inverse="true"
+ order-by="effectiveEndDate desc">
+ <key column="currentContractId"/>
+ <one-to-many entity-name="HistoricalContract"/>
+ </set>
+</class>
+
+<class name="Contract" table="ContractHistory"
+ entity-name="HistoricalContract">
+ ...
+ <many-to-one name="currentContract"
+ column="currentContractId"
+ entity-name="CurrentContract"/>
+</class>]]></programlisting>
+
+ <para>
+ Observa cómo las asociaciones ahora se especifican usando
<literal>entity-name</literal> en vez de
+ <literal>class</literal>.
+ </para>
+
+ </sect1>
+
+
+ <sect1 id="mapping-quotedidentifiers">
+ <title>identificadores SQL encomillados</title>
+ <para>
+ Puedes forzar a Hibernate a encomillar un identificador en el SQL
generado encerrando el nombre
+ de tabla o columna entre backticks en el documento de mapeo. Hibernate
usará el estilo de
+ encomillado para el <literal>Dialect</literal> SQL
(usualmente comillas dobles, excepto corchetes
+ para SQL Server y backsticks para MySQL).
+ </para>
+
+ <programlisting><![CDATA[<class name="LineItem"
table="`Line Item`">
+ <id name="id" column="`Item Id`"/><generator
class="assigned"/></id>
+ <property name="itemNumber" column="`Item #`"/>
+ ...
+</class>]]></programlisting>
+
+ </sect1>
+
+
+ <sect1 id="mapping-alternatives">
+ <title>Alternativas de metadatos</title>
+
+ <para>
+ XML no es para todos, asá que hay algunas formas alternativas de
definir metadatos de mapeo O/R
+ en Hibernate.
+ </para>
+
+ <sect2 id="mapping-xdoclet">
+ <title>Usando marcado de XDoclet</title>
+
+ <para>
+ Muchos usuarios de Hibernate prefieren embeber la información de
mapeo directamente
+ en el código fuente usando las
<literal>(a)hibernate.etiquetas</literal> XDoclet.
+ No cubriremos este enfoque en este documento, pues estrictamente es
considerado parte
+ de XDoclet. Sin embargo, incluímos el siguiente ejemplo de la
clase
+ <literal>Cat</literal> con mapeos XDoclet.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+import java.util.Set;
+import java.util.Date;
+
+/**
+ * @hibernate.class
+ * table="CATS"
+ */
+public class Cat {
+ private Long id; // identifier
+ private Date birthdate;
+ private Cat mother;
+ private Set kittens
+ private Color color;
+ private char sex;
+ private float weight;
+
+ /*
+ * @hibernate.id
+ * generator-class="native"
+ * column="CAT_ID"
+ */
+ public Long getId() {
+ return id;
+ }
+ private void setId(Long id) {
+ this.id=id;
+ }
+
+ /**
+ * @hibernate.many-to-one
+ * column="PARENT_ID"
+ */
+ public Cat getMother() {
+ return mother;
+ }
+ void setMother(Cat mother) {
+ this.mother = mother;
+ }
+
+ /**
+ * @hibernate.property
+ * column="BIRTH_DATE"
+ */
+ public Date getBirthdate() {
+ return birthdate;
+ }
+ void setBirthdate(Date date) {
+ birthdate = date;
+ }
+ /**
+ * @hibernate.property
+ * column="WEIGHT"
+ */
+ public float getWeight() {
+ return weight;
+ }
+ void setWeight(float weight) {
+ this.weight = weight;
+ }
+
+ /**
+ * @hibernate.property
+ * column="COLOR"
+ * not-null="true"
+ */
+ public Color getColor() {
+ return color;
+ }
+ void setColor(Color color) {
+ this.color = color;
+ }
+ /**
+ * @hibernate.set
+ * inverse="true"
+ * order-by="BIRTH_DATE"
+ * @hibernate.collection-key
+ * column="PARENT_ID"
+ * @hibernate.collection-one-to-many
+ */
+ public Set getKittens() {
+ return kittens;
+ }
+ void setKittens(Set kittens) {
+ this.kittens = kittens;
+ }
+ // addKitten not needed by Hibernate
+ public void addKitten(Cat kitten) {
+ kittens.add(kitten);
+ }
+
+ /**
+ * @hibernate.property
+ * column="SEX"
+ * not-null="true"
+ * update="false"
+ */
+ public char getSex() {
+ return sex;
+ }
+ void setSex(char sex) {
+ this.sex=sex;
+ }
+}]]></programlisting>
+
+ <para>
+ Para más ejemplos de XDoclet e Hibernate ver en el sitio web de
Hibernate.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-annotations" revision="2">
+ <title>Usando anotaciones JDK 5.0</title>
+ <para>
+ El JDK 5.0 introdujo anotaciones del estilo XDoclet a nivel del lenguaje,
+ con chequeo seguro de tipos en tiempo de compilación. Este
mecanismo es más
+ potente y que las anotaciones XDoclet, y mejor soportado por herramientas e
IDEs.
+ IntelliJ IDEA, por ejemplo, soporta auto-compleción y resaltado de
sintaxis de anotaciones
+ JDK 5.0. La nueva revisión de la especificación de EJB
(JSR-220) usa anotaciones
+ JDK 5.0 como el mecanismo primario de metadatos para beans de entidad.
Hibernate3 implementa
+ el <literal>EntityManager</literal> del JSR-220 (la API de
persistencia), y el soporte para
+ metadatos de mapeo está disponible vía el paquete
<emphasis>Hibernate Annotations</emphasis>,
+ como una descarga por separado. Tanto metadatos de EJB3 (JSR-220) como de
Hibernate3 están soportados.
+ </para>
+
+ <para>
+ Este es un ejemplo de una clase POJO anotada como un bean de entidad EJB:
+ </para>
+
+ <programlisting><![CDATA[@Entity(access = AccessType.FIELD)
+public class Customer implements Serializable {
+
+ @Id;
+ Long id;
+
+ String firstName;
+ String lastName;
+ Date birthday;
+
+ @Transient
+ Integer age;
+
+ @Embedded
+ private Address homeAddress;
+
+ @OneToMany(cascade=CascadeType.ALL)
+
+ @JoinColumn(name="CUSTOMER_ID")
+ Set<Order> orders;
+
+ // Getter/setter and business methods
+}]]></programlisting>
+
+ <para>
+ Ten en cuenta que el soporte a anotaciones JDK 5.0 (y JSR-220) es
todavía un
+ trabajo en progreso y no completado. Por favor, para más detalles
refiérete al modulo
+ de Anotaciones de Hibernate.
+ </para>
+
+ </sect2>
+ </sect1>
+
+</chapter>
+
+
+
+
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/content/batch.xml (from rev
12794, core/trunk/documentation/manual/es-ES/src/main/docbook/modules/batch.xml)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/content/batch.xml
(rev 0)
+++ core/trunk/documentation/manual/es-ES/src/main/docbook/content/batch.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,192 @@
+<chapter id="batch">
+ <title>Procesamiento por lotes</title>
+
+ <para>
+ Un enfoque ingenuo para insertar 100.000 filas en la base de datos usando
Hibernate podría verse así:
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+for ( int i=0; i<100000; i++ ) {
+ Customer customer = new Customer(.....);
+ session.save(customer);
+}
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ Esto podría caer sobre una <literal>OutOfMemoryException</literal> en
algún sitio
+ cerca de la fila 50.000. Esto es porque Hibernate tiene en caché todas las
instancias
+ de <literal>Customer</literal> recién instanciadas en el caché de
nivel de sesión.
+ </para>
+
+ <para>
+ En este capítulo te mostraremos cómo evitar este problema. Primero, sin embargo,
+ si estás haciendo procesamiento por lotes (batch processing), es absolutamente
crítico
+ que habilites el uso de loteo JDBC, si pretendes lograr un rendimiento
razonable.
+ Establece el tamaño de lote JDBC a un número razonable (digamos 10-50):
+ </para>
+
+<programlisting><![CDATA[hibernate.jdbc.batch_size
20]]></programlisting>
+
+ <para>
+ Podrías además querer hacer este tipo de trabajo en un proceso donde la
interacción con el caché de
+ segundo nivel esté completamente deshabilitado:
+ </para>
+
+<programlisting><![CDATA[hibernate.cache.use_second_level_cache
false]]></programlisting>
+
+ <sect1 id="batch-inserts">
+ <title>Inserciones en lote</title>
+
+ <para>
+ Al hacer persistentes objetos nuevos, debes limpiar con
<literal>flush()</literal> y
+ llamar a <literal>clear()</literal> en la sesión regularmente,
para controlar el tamaño
+ del caché de primer nivel.
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+for ( int i=0; i<100000; i++ ) {
+ Customer customer = new Customer(.....);
+ session.save(customer);
+ if ( i % 20 == 0 ) { //20, same as the JDBC batch size
+ //flush a batch of inserts and release memory:
+ session.flush();
+ session.clear();
+ }
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="batch-update" >
+ <title>Actualizaciones en lote</title>
+
+ <para>
+ Para recuperar y actualizar datos se aplican las mismas ideas.
Adicionalmente, necesitas usar
+ <literal>scroll()</literal> para sacar ventaja de los cursores
del lado del servidor en consultas
+ que devuelvan muchas filas de datos.
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+ScrollableResults customers = session.getNamedQuery("GetCustomers")
+ .setCacheMode(CacheMode.IGNORE)
+ .scroll(ScrollMode.FORWARD_ONLY);
+int count=0;
+while ( customers.next() ) {
+ Customer customer = (Customer) customers.get(0);
+ customer.updateStuff(...);
+ if ( ++count % 20 == 0 ) {
+ //flush a batch of updates and release memory:
+ session.flush();
+ session.clear();
+ }
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="batch-direct">
+ <title>update/delete en masa</title>
+
+ <para>
+ Como ya se ha discutido, el mapeo objeto/relacional automático y transparente
se refiere
+ al manejo de estado de objetos. Esto implica que el estado del objeto está
disponible
+ en memoria, por lo tanto actualizar o borrar (usando
<literal>UPDATE</literal> y
+ <literal>DELETE</literal> de SQL) datos directamente en la base
de datos no afectará el
+ estado en memoria. Sin embargo, Hibernate provee métodos para la ejecución de
sentencias
+ del estilo de <literal>UPDATE</literal> y
<literal>DELETE</literal> de SQL que se realizan
+ a través del Lenguaje de Consulta de Hibernate (Hibernate Query Language o
+ <xref linkend="queryhql">HQL</xref>).
+ </para>
+
+ <para>
+ La pseudo-sintáxis para sentencias <literal>UPDATE</literal> y
<literal>DELETE</literal> es:
+ <literal>( UPDATE | DELETE ) FROM? ClassName (WHERE
WHERE_CONDITIONS)?</literal>. Algunos puntos
+ a tener en cuenta:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ En la cláusula-from, la palabra clave FROM es opcional
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Puede haber sólo una clase mencionada en la cláusula-from, y
<emphasis>no puede</emphasis>
+ tener un alias.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ No puede especificarse ningún join (bien implícito o explícito) en
una consulta masiva de HQL.
+ Pueden usarse subconsultas en la cláusula-where.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ La cláusula-where es también opcional.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Como un ejemplo, para ejecutar un <literal>UPDATE</literal> HQL,
usa el
+ método <literal>Query.executeUpdate()</literal>:
+ </para>
+
+ <programlisting><![CDATA[Session session =
sessionFactory.openSession();
+ Transaction tx = session.beginTransaction();
+
+ String hqlUpdate = "update Customer set name = :newName where name =
:oldName";
+ int updatedEntities = s.createQuery( hqlUpdate )
+ .setString( "newName", newName )
+ .setString( "oldName", oldName )
+ .executeUpdate();
+ tx.commit();
+ session.close();]]></programlisting>
+
+ <para>
+ Para ejecutar un <literal>DELETE</literal> HQL, usa el mismo
método <literal>Query.executeUpdate()</literal>
+ (el método está nombrado para aquellos familiarizados con
+ <literal>PreparedStatement.executeUpdate()</literal> de JDBC):
+ </para>
+
+ <programlisting><![CDATA[Session session =
sessionFactory.openSession();
+ Transaction tx = session.beginTransaction();
+
+ String hqlDelete = "delete Customer where name = :oldName";
+ int deletedEntities = s.createQuery( hqlDelete )
+ .setString( "oldName", oldName )
+ .executeUpdate();
+ tx.commit();
+ session.close();]]></programlisting>
+
+ <para>
+ El valor <literal>int</literal> devuelto por el método
<literal>Query.executeUpdate()</literal>
+ indica el número de entidades afectadas por la operación. Considera que esto
puede o no
+ correlacionarse al número de filas afectadas en la base de datos. Una
operación masiva HQL podría
+ resultar en que se ejecuten múltiples sentencias de SQL reales, para
joined-subclass, por ejemplo.
+ El número devuelto indica el número de entidades reales afectadas por la
sentencia. Volviendo al
+ ejemplo de joined-subclass, un borrado contra una de las subclases puede
resultar realmente en
+ borrados contra no sólo la tabla a la que está mapeada esa subclase, sino
también la tabla "raíz"
+ y potencialmente tablas de joined-subclass más debajo en la jerarquía de
herencia.
+ </para>
+
+ <para>
+ Ten en cuenta que existen actualmente unas pocas limitaciones con las
operaciones HQL masivas,
+ que serán atendidas en lanzamientos futuros; consulta la hoja de ruta de JIRA
para más detalles.
+ </para>
+
+ </sect1>
+
+</chapter>
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/content/best_practices.xml
(from rev 12794,
core/trunk/documentation/manual/es-ES/src/main/docbook/modules/best_practices.xml)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/content/best_practices.xml
(rev 0)
+++
core/trunk/documentation/manual/es-ES/src/main/docbook/content/best_practices.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,229 @@
+<chapter id="best-practices" revision="3">
+ <title>Mejores Prácticas</title>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>Escribe clase finamente granularizadas y mapealas usando
<literal><component></literal>.</term>
+ <listitem>
+ <para>
+ Usa una clase <literal>Dirección</literal>
para encapsular <literal>calle</literal>,
+ <literal>distrito</literal>,
<literal>estado</literal>, <literal>código
postal</literal>.
+ Esto alienta la reutilización de código y
simplifica el refactoring.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Declara las propiedades identificadoras en clases
persistentes.</term>
+ <listitem>
+ <para>
+ Hibernate hace opcionales las propiedades identificadoras. Existen
todo tipo de razones
+ por las que debes usarlas. Recomendamos que los identificadores sean
'sintéticos'
+ (generados, sin ningún significado de negocio).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Identifica las claves naturales.</term>
+ <listitem>
+ <para>
+ Identifica las claves naturales de todas las entidades, y mapealas
usando
+ <literal><natural-id></literal>. Implementa
<literal>equals()</literal> y
+ <literal>hashCode()</literal> para comparar las
propiedades que componen la clave natural.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Coloca cada mapeo de clase en su propio fichero.</term>
+ <listitem>
+ <para>
+ No uses un solo documento monolítico de mapeo. Mapea
<literal>com.eg.Foo</literal> en
+ el fichero <literal>com/eg/Foo.hbm.xml</literal>. Esto
tiene sentido particularmente en un
+ ambiente de equipo.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Carga los mapeos como recursos.</term>
+ <listitem>
+ <para>
+ Despliega los mapeos junto a las clases que mapean.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Considera externalizar las cadenas de consulta.</term>
+ <listitem>
+ <para>
+ Esta es una buena práctica si tus consultas llaman a
funciones SQL que no son del
+ estándar ANSI. Externalizar las cadenas de consulta a
ficheros de mapeo hará la
+ aplicación más portable.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Usa variables de ligado.</term>
+ <listitem>
+ <para>
+ Igual que en JDBC, siempre remplaza valores no constantes con
"?". ¡Nunca uses manipulación
+ de cadenas para ligar un valor no constante en una consulta! Incluso
mejor, considera usar
+ parámetros con nombre en las consultas.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>No manejes tus propias conexiones JDBC.</term>
+ <listitem>
+ <para>
+ Hibernate deja a la aplicación administre las conexiones
JDBC. Este enfoque debe considerarse
+ como último recurso. Si no puedes usar los provedores de
conexión prefabricados, considera
+ prover tu propia implementación de
<literal>org.hibernate.connection.ConnectionProvider</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Considera usar un tipo personalizado.</term>
+ <listitem>
+ <para>
+ Supón que tienes un tipo Java, digamos de alguna
biblioteca, que necesita hacerse persistente
+ pero no provee los métodos de acceso necesarios para
mapearlo como un componente. Debes considerar
+ implementar <literal>org.hibernate.UserType</literal>.
Este enfoque libera al código de aplicación
+ de implementar transformaciones a / desde un tipo Hibernate.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Usa JDBC codificado a mano en cuellos de botella.</term>
+ <listitem>
+ <para>
+ En áreas del sistema de rendimiento crítico,
algunos tipos de operaciones podrían beneficiarse
+ del JDBC directo. Pero por favor, espero hasta que
<emphasis>sepas</emphasis> que algo es
+ un cuello de botella. Y no asumas que el JDBC directo es
necesariamente más rápido. Si necesitas
+ usar JDBC directo, podría ser valioso abrir una
<literal>Session</literal> de Hibernate y usar esa
+ conexión JDBC. De esta forma puedes usar aún la
misma estrategia de transacción y el mismo
+ proveedor de conexiones subyacente.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Comprende la limpieza (flushing) de
<literal>Session</literal>.</term>
+ <listitem>
+ <para>
+ De vez en cuando la sesión sincroniza su estado
persistente con la base de datos. El rendimiento
+ se verá afectado si este proceso ocurre demasiado
frecuentemente. A veces puedes minimizar
+ limpieza innecesaria deshabilitando la limpieza automática
o incluso cambiando el orden de las
+ consultas u otras operaciones en una transacción en
particular.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>En una aplicación en tres gradas, considera usar
objetos separados.</term>
+ <listitem>
+ <para>
+ Al usar una arquitectura de servlet / sesión, puedes pasar
objetos persistentes en el bean de
+ sesión hacia y desde la capa de servlet / JSP. Usa una
sesión nueva para atender el servicio de cada
+ petición. Usa
<literal>Session.merge()</literal> o
<literal>Session.saveOrUpdate()</literal> para
+ sincronizar los objetos con la base de datos.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>En una arquitectura en dos gradas, considera usar contexto de
persistencia largos.</term>
+ <listitem>
+ <para>
+ Las transacciones de base de datos tienen que ser tan cortas como sea
posible. Sin embargo,
+ frecuentemente es necesario implementar <emphasis>transacciones
de aplicación</emphasis>
+ ejecutándose en largo, una sola unidad de trabajo desde el
punto de vista de un usuario.
+ Una transacción de aplicación puede abarcar
muchos ciclos petición/respuesta del cliente.
+ Es común usar objetos separados para implementar
transacciones de aplicación. Una alternativa,
+ extremadamente apropiada en arquitecturas en dos gradas, es mantener
un solo contacto de persistencia
+ abierto (sesión) para todo el ciclo de vida de la
transacción de aplicación y simplemente
+ desconectar de la conexión JDBC al final de cada
petición, y reconectar al comienzo de la
+ petición subsecuente. Nunca compartas una única
sesión a través de más de una transacción
+ de aplicación, o estarás trabajando con datos
añejos.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>No trates la excepciones como recuperables.</term>
+ <listitem>
+ <para>
+ Esto es más una práctica necesaria que una
"mejor" práctica. Cuando ocurra una excepción,
+ deshaz (rollback) la <literal>Transaction</literal> y
cierra la <literal>Session</literal>.
+ Si no lo haces, Hibernate no puede garantizar que el estado en
memoria representa con exactitud
+ el estado persistente. Como un caso especial de esto, no uses
<literal>Session.load()</literal>
+ para determinar si una instancia con el identificador dado existe en
la base de datos. En cambio,
+ usa <literal>Session.get()</literal> o una consulta.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Prefiere la recuperación perezosa para las
asociaciones.</term>
+ <listitem>
+ <para>
+ Usa escasamente la recuperación temprana. Usa proxies y
colecciones perezosas para la mayoría
+ de asociaciones a clases probablemente no estén mantenidas
en el caché de segundo nivel. Para
+ las asociaciones a clases en caché, donde hay una
probabilidad de acceso a caché extremadamente
+ alta, deshabilita explícitamente la
recuperación temprana usando
<literal>lazy="false"</literal>.
+ Cuando sea apropiada la recuperación por unión
(join fetching) para un caso de uso en particular,
+ usa una consulta con un <literal>left join
fetch</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ Usa el patrón <emphasis>sesión abierta en
vista</emphasis>, o una <emphasis>fase de ensamblado</emphasis>
+ disciplinada para evitar problemas con datos no recuperados.
+ </term>
+ <listitem>
+ <para>
+ Hibernate liberal al desarrollador de escribir
<emphasis>Objetos de Transferencia de Datos
+ (Data Transfer Objects)</emphasis> (DTO). En una arquitectura
tradicional de EJB, los DTOs tienen
+ un propósito doble: primero, atacan el problema que los
beans de entidad no son serializables.
+ Segundo, definen implícitamente una fase de ensamblado
cuando se recuperan y se forman (marshalling)
+ todos los datos a usar por la vista en los DTOs antes de devolver el
control a la grada de
+ presentación. Hibernate elimina el primer
propósito. Sin embargo, aún necesitas una fase
+ de ensamblado (piensa en tus métodos de negocio como si
tuviesen un contrato estricto con la grada
+ de presentación sobre qué datos
están disponibles en los objetos separados) a menos que estés
+ preparado para tener el contexto de persistencia (la
sesión) abierto a través del proceso
+ de renderización de la vista. ¡Esta no es una
limitación de Hibernate! Es un requerimiento
+ fundamental de acceso seguro a datos transaccionales.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Considera abstraer tu lógica de negocio de
Hibernate</term>
+ <listitem>
+ <para>
+ Oculta el código de acceso a datos (Hibernate)
detrás de una interface. Combina los patrones
+ <emphasis>DAO</emphasis> y
<emphasis>Sesión de Hebra Local</emphasis>. Incluso puedes tener
+ algunas clases hechas persistentes por JDBC escrito a mano, asociadas
a Hibernate por medio
+ de un <literal>UserType</literal>. (Este consejo
está pensado para aplicaciones "suficientemente
+ grandes"; ¡no es apropiado para una
aplicación con cinco tablas!)
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>No uses mapeos de asociación
exóticos.</term>
+ <listitem>
+ <para>
+ Son raros los casos de uso de asociaciones reales muchos-a-muchos. La
mayor parte del tiempo
+ necesitas información adicional almacenada en una
"tabla de enlace". En este caso, es mucho
+ mejor usar dos asociaciones uno-a-muchos a una clase de enlace
intermedia. De hecho, pensamos
+ que la mayoría de asociaciones son uno-a-muchos y
muchos-a-uno, debes ser cuidadoso al usr
+ cualquier otro estilo de asociación y preguntarte si es
realmente necesario.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Prefiere las asociaciones bidireccionales.</term>
+ <listitem>
+ <para>
+ Las asociaciones unidireccionales son más
difíciles de consultar. En una aplicación grande,
+ casi todas las asociaciones deben ser navegables en ambas direcciones
en consultas.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+</chapter>
+
Copied:
core/trunk/documentation/manual/es-ES/src/main/docbook/content/collection_mapping.xml
(from rev 12794,
core/trunk/documentation/manual/es-ES/src/main/docbook/modules/collection_mapping.xml)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/content/collection_mapping.xml
(rev 0)
+++
core/trunk/documentation/manual/es-ES/src/main/docbook/content/collection_mapping.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,1241 @@
+<chapter id="collections">
+ <title>Mapeo de Colecciones</title>
+
+ <sect1 id="collections-persistent" revision="3">
+ <title>Colecciones persistentes</title>
+
+ <para>
+ Hibernate requiere que los campos valuados en colección
+ persistentes sean declarados como un tipo de interface, por ejemplo:
+ </para>
+
+ <programlisting><![CDATA[public class Product {
+ private String serialNumber;
+ private Set parts = new HashSet();
+
+ public Set getParts() { return parts; }
+ void setParts(Set parts) { this.parts = parts; }
+ public String getSerialNumber() { return serialNumber; }
+ void setSerialNumber(String sn) { serialNumber = sn; }
+}]]></programlisting>
+
+ <para>
+ La interface real podría ser
<literal>java.util.Set</literal>,
+ <literal>java.util.Collection</literal>,
<literal>java.util.List</literal>,
+ <literal>java.util.Map</literal>,
<literal>java.util.SortedSet</literal>,
+ <literal>java.util.SortedMap</literal> o ... lo que te guste!
+ (Donde "lo que te guste" significa que tendrás que
escribir una
+ implementación de
<literal>org.hibernate.usertype.UserCollectionType</literal>.)
+ </para>
+
+ <para>
+ Nota cómo hemos inicializado la variable de instancia de
+ <literal>HashSet</literal>. Esta es la mejor forma de
inicializar
+ propiedades valuadas en colección de instancias recién
+ instanciadas (no persistentes). Cuando haces persistente la instancia
+ - llamando a <literal>persist()</literal>, por ejemplo -
Hibernate realmente
+ remplazará el <literal>HashSet</literal> con una
instancia de una
+ implementación de <literal>Set</literal> propia de
Hibernate.
+ Observa errores como este:
+ </para>
+
+ <programlisting><![CDATA[Cat cat = new DomesticCat();
+Cat kitten = new DomesticCat();
+....
+Set kittens = new HashSet();
+kittens.add(kitten);
+cat.setKittens(kittens);
+session.persist(cat);
+kittens = cat.getKittens(); // Okay, kittens collection is a Set
+(HashSet) cat.getKittens(); // Error!]]></programlisting>
+
+ <para>
+ Las colecciones persistentes inyectadas por Hibernate se comportan
+ como <literal>HashMap</literal>,
<literal>HashSet</literal>,
+ <literal>TreeMap</literal>,
<literal>TreeSet</literal> o
+ <literal>ArrayList</literal>, dependiendo del tipo de interface.
+ </para>
+
+ <para>
+ Las instancias de colecciones tienen el comportamiento usual de tipos de
valor.
+ Son automáticamente persistidas al ser referenciadas por un
objeto
+ persistente y automáticamente borradas al desreferenciarse. Si
una
+ colección es pasada de un objeto persistente a otro, sus
elementos
+ serían movidos de una tabla a otra. Dos entidades pueden no
+ compartir una referencia a la misma instancia de colección.
+ Debido al modelo relacional subyacente, las propiedades valuadas en
+ colección no soportan la semántica de valor nulo.
Hibernate no
+ distingue entre una referencia de colección nula y una
colección
+ vacía.
+ </para>
+
+ <para>
+ No debes tener que preocuparte demasiado por esto. Usa las colecciones
+ persistentes de la misma forma en que usas colecciones de Java ordinarias.
+ Sólo asegúrate que entiendes la semántica de
las asociaciones
+ bidireccionales (discutida luego).
+ </para>
+
+ </sect1>
+
+ <sect1 id="collections-mapping" revision="2">
+ <title>Mapeos de colección</title>
+
+ <para>
+ El elemento de mapeo de Hibernate usado para mapear una colección
+ depende del tipo de la interface. Por ejemplom un elemento
+ <literal><set></literal> se usa para mapear
propiedades de tipo
+ <literal>Set</literal>.
+ </para>
+
+ <programlisting><![CDATA[<class name="Product">
+ <id name="serialNumber" column="productSerialNumber"/>
+ <set name="parts">
+ <key column="productSerialNumber" not-null="true"/>
+ <one-to-many class="Part"/>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ Aparte de <literal><set></literal>, existen
además
+ los elementos de mapeo <literal><list></literal>,
+ <literal><map></literal>,
<literal><bag></literal>,
+ <literal><array></literal> y
<literal><primitive-array></literal>.
+ El elemento <literal><map></literal> es
representativo:
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="mappingcollection1" coords="2 65"/>
+ <area id="mappingcollection2" coords="3 65"/>
+ <area id="mappingcollection3" coords="4 65"/>
+ <area id="mappingcollection4" coords="5 65"/>
+ <area id="mappingcollection5" coords="6 65"/>
+ <area id="mappingcollection6" coords="7 65"/>
+ <area id="mappingcollection7" coords="8 65"/>
+ <area id="mappingcollection8" coords="9 65"/>
+ <area id="mappingcollection9" coords="10 65"/>
+ <area id="mappingcollection10" coords="11
65"/>
+ <area id="mappingcollection11" coords="12
65"/>
+ <area id="mappingcollection12" coords="13
65"/>
+ <area id="mappingcollection13" coords="14
65"/>
+ </areaspec>
+ <programlisting><![CDATA[<map
+ name="propertyName"
+ table="table_name"
+ schema="schema_name"
+ lazy="true|false"
+ inverse="true|false"
+ cascade="all|none|save-update|delete|all-delete-orphan"
+ sort="unsorted|natural|comparatorClass"
+ order-by="column_name asc|desc"
+ where="arbitrary sql where condition"
+ fetch="join|select|subselect"
+ batch-size="N"
+ access="field|property|ClassName"
+ optimistic-lock="true|false"
+ node="element-name|."
+ embed-xml="true|false"
+>
+
+ <key .... />
+ <map-key .... />
+ <element .... />
+</map>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="mappingcollection1">
+ <para>
+ <literal>name</literal> el nombre de la propiedad de
colección
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection2">
+ <para>
+ <literal>table</literal> (opcional - por defecto al
nombre de la propiedad)
+ el nombre de la tabla de coleciión (no usado para
asociaciones
+ uno-a-muchos)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection3">
+ <para>
+ <literal>schema</literal> (opcional) el nombre de un
esquema de tablas
+ para sobrescribir el esquema declarado en el elemento
raíz
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection4">
+ <para>
+ <literal>lazy</literal> (opcional - por defecto a
<literal>true</literal>)
+ puede ser usado para deshabilitar la recuperación
perezosa y
+ especificar que la asociación es siempre recuperada
tempranamente
+ (no disponible para arrays)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection5">
+ <para>
+ <literal>inverse</literal> (opcional - por defecto a
<literal>false</literal>)
+ marca esta colección como el extremo
"inverso" de una asociación
+ bidireccional.
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection6">
+ <para>
+ <literal>cascade</literal> (opcional - por defecto a
<literal>none</literal>)
+ habilita operaciones en cascada a entidades hijas
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection7">
+ <para>
+ <literal>sort</literal> (opcional) especifica una
colección
+ con ordenamiento <literal>natural</literal>, o una
clase comparadora
+ dada
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection8">
+ <para>
+ <literal>order-by</literal> (opcional,
sólo JDK1.4) especifica una columna
+ de tabla (o columnas) que definen el orden de
iteración del
+ <literal>Map</literal>,
<literal>Set</literal> o bag, junto a un
+ <literal>asc</literal> o
<literal>desc</literal> opcional.
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection9">
+ <para>
+ <literal>where</literal> (opcional) especifica una
condición
+ <literal>WHERE</literal> de SQL arbitrario para ser
usada al recuperar o
+ quitar la colección (útil si la
colección debe
+ contener sólo un subconjunto de los datos
disponibles)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection10">
+ <para>
+ <literal>fetch</literal> (opcional, por defecto a
<literal>select</literal>)
+ Elige entre recuperación por unión externa
(outer-join),
+ recuperar por selección secuencial, y
recuperación por
+ subselección secuencial.
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection11">
+ <para>
+ <literal>batch-size</literal> (opcional, por defecto
a <literal>1</literal>)
+ especifica un "tamaño de lote" para la
recuperar
+ perezosamente instancias de esta colección.
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection12">
+ <para>
+ <literal>access</literal> (opcional - por defecto a
<literal>property</literal>):
+ La estrategia que debe usar Hibernate para acceder al valor de
la
+ propiedad.
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection12">
+ <para>
+ <literal>optimistic-lock</literal> (opcional - por
defecto a <literal>true</literal>):
+ Especifica que los cambios de estado de la colección
resultan en
+ incrementos de versión de la entidad dueña.
(Para asociaciones
+ uno a muchos, frecuentemente es razonable deshabilitar esta
opción.)
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <sect2 id="collections-foreignkeys" >
+ <title>Claves foráneas de
collección</title>
+
+ <para>
+ Las instancias de colección se distinguen en la base de datos
por la
+ clave foránea de la entidad que posee la colección.
Se hace
+ referencia a esta clave foránea como la
<emphasis>columna clave de
+ colección</emphasis> (o columnas) de la tabla de
colección.
+ La columna clave de la colección es mapeada por el elemento
+ <literal><key></literal>.
+ </para>
+
+ <para>
+ Puede haber una restricción de nulabilidad sobre la columna de
clave
+ foránea. Para la mayoría de colecciones, esto
está implicado.
+ Para asociaciones unidireccionales uno a muchos, la columna de clave
+ foránea es nulable por defecto, de modo que podrías
necesitar
+ especificar <literal>not-null="true"</literal>.
+ </para>
+
+ <programlisting><![CDATA[<key
column="productSerialNumber"
not-null="true"/>]]></programlisting>
+
+ <para>
+ La restricción de clave foránea puede usar
+ <literal>ON DELETE CASCADE</literal>.
+ </para>
+
+ <programlisting><![CDATA[<key
column="productSerialNumber"
on-delete="cascade"/>]]></programlisting>
+
+ <para>
+ Mira el capítulo anterior por una definición
completa del
+ elemento <literal><key></literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-elements" >
+ <title>Elementos de collección</title>
+
+ <para>
+ Las colecciones pueden contener casi cualquier tipo de Hibernate,
incluyendo
+ todos los tipos básicos, componentes, y por supuesto,
referencias a otras
+ entidades. Esta es una distinción importante: un objeto en una
colección
+ puede ser manejado con una semántica de "valor" (su
ciclo de vida depende
+ completamente del propietario de la colección) o
podría ser una referencia
+ a otra entidad, con su propio ciclo de vida. En el último
caso, sólo
+ el estado del "enlace" entre los dos objetos se considera
mantenido por la
+ colección.
+ </para>
+
+ <para>
+ Se hace referencia al tipo contenido como el <emphasis>tipo de
elemento
+ de la colección</emphasis>. Los elementos de
colección son
+ mapeados por <literal><element></literal> o
+ <literal><composite-element></literal>, o en el
caso de referencias
+ de entidades, con
<literal><one-to-many></literal> o
+ <literal><many-to-many></literal>. Las dos
primeras mapean elementos
+ con semántica de valor, los dos siguientes son usados para
mapear
+ asociaciones de entidades.
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-indexed">
+ <title>Colecciones indexadas</title>
+
+ <para>
+ Todos los mapeos de colección, excepto aquellos con
semántica de
+ set o bag, necesitan una <emphasis>columna
índice</emphasis> en la tabla
+ de colección, una columna que mapea a un índice de
array, o
+ índice de <literal>List</literal>, o clave de
<literal>Map</literal>.
+ El índice de un <literal>Map</literal> puede ser
de cualquier tipo
+ básico, mapeado con
<literal><map-key></literal>, o puede ser
+ una referencia de entidad, mapeada con
<literal><map-key-many-to-many></literal>,
+ o puede ser un tipo compuesto, mapeado con
<literal><composite-map-key></literal>.
+ El índice de un array o lista es siempre de tipo
<literal>integer</literal>
+ y se mapea usando el elemento
<literal><list-index></literal>. La columna
+ mapeada contiene enteros secuenciales (numerados desde cero, por
defecto).
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="index1" coords="2 45"/>
+ <area id="index2" coords="3 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<list-index
+ column="column_name"
+ base="0|1|..."/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="index1">
+ <para>
+ <literal>column_name</literal> (requerido): El nombre
de la columna que tiene
+ los valores índice de la colección.
+ </para>
+ </callout>
+ <callout arearefs="index1">
+ <para>
+ <literal>base</literal> (opcional, por defecto a
<literal>0</literal>): El valor
+ de la columna índice que corresponde al primer
elemento de la lista o
+ array.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <programlistingco>
+ <areaspec>
+ <area id="mapkey1" coords="2 45"/>
+ <area id="mapkey2" coords="3 45"/>
+ <area id="mapkey3" coords="4 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<map-key
+ column="column_name"
+ formula="any SQL expression"
+ type="type_name"
+ node="@attribute-name"
+ length="N"/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="mapkey1">
+ <para>
+ <literal>column</literal> (opcional): El nombre de la
columna que tiene
+ los valores índice de la colección.
+ </para>
+ </callout>
+ <callout arearefs="mapkey2">
+ <para>
+ <literal>formula</literal> (opcional): Una
fórmula SQL usada para
+ evaluar la clave del mapa.
+ </para>
+ </callout>
+ <callout arearefs="mapkey3">
+ <para>
+ <literal>type</literal> (requerido): el tipo de las
claves del mapa.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <programlistingco>
+ <areaspec>
+ <area id="indexmanytomany1" coords="2 45"/>
+ <area id="indexmanytomany2" coords="3 45"/>
+ <area id="indexmanytomany3" coords="3 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<map-key-many-to-many
+ column="column_name"
+ formula="any SQL expression"
+ class="ClassName"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="indexmanytomany1">
+ <para>
+ <literal>column</literal> (opcional): El nombre de la
columna clave
+ foránea para los valores índice de la
colección.
+ </para>
+ </callout>
+ <callout arearefs="indexmanytomany2">
+ <para>
+ <literal>formula</literal> (opcional): Una
fórmula SQL usada para
+ evaluar la clave foránea de la clave del mapa.
+ </para>
+ </callout>
+ <callout arearefs="indexmanytomany3">
+ <para>
+ <literal>class</literal> (requerido): La clase de
entidad usada como clave del mapa.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+
+ <para>
+ Si tu tabla no tiene una columna índice, y deseas
aún usar <literal>List</literal> como
+ tipo de propiedad, debes mapear la propiedad como un
<emphasis><bag></emphasis>
+ de Hibernate. Un bag (bolsa) no retiene su orden al ser recuperado de la
base de datos,
+ pero puede ser ordenado o clasificado opcionalmente.
+ </para>
+
+ </sect2>
+
+ <para>
+ Hay absolutamente un rango de mapeos que pueden ser generados para
colecciones,
+ cubriendo muchos modelos relacionales comunes. Te sugerimos que experimentes
con
+ la herramienta de generación de esquemas para obtener una idea de
cómo varias
+ declaraciones de mapeo se traducen a tablas de base de datos.
+ </para>
+
+ <sect2 id="collections-ofvalues" revision="1">
+ <title>Colecciones de valores y asociaciones muchos-a-muchos</title>
+
+ <para>
+ Cualquier colección de valores o asociación muchos a
muchos requiere una
+ <emphasis>tabla de colección</emphasis> dedicada con
una columna o columnas
+ de clave foránea, <emphasis>columna de elemento de
colección</emphasis> o
+ columnas y posiblemente una columna o columnas índice.
+ </para>
+
+ <para>
+ Para una colección de valores, usamos la etiqueta
<literal><element></literal>.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="element1b" coords="2 50"/>
+ <area id="element2b" coords="3 50"/>
+ <area id="element3b" coords="4 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<element
+ column="column_name"
+ formula="any SQL expression"
+ type="typename"
+ length="L"
+ precision="P"
+ scale="S"
+ not-null="true|false"
+ unique="true|false"
+ node="element-name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="element1b">
+ <para>
+ <literal>column</literal> (opcional): El nombre de la
columna que tiene
+ los valores de los elementos de la colección.
+ </para>
+ </callout>
+ <callout arearefs="element2b">
+ <para>
+ <literal>formula</literal> (opcional): Una
fórmula SQL usada para evaluar
+ el elemento.
+ </para>
+ </callout>
+ <callout arearefs="element3b">
+ <para>
+ <literal>type</literal> (requerido): El tipo del
elemento de colección.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Una <emphasis>asociación muchos-a-muchos</emphasis> se
especifica usando
+ el elemento <literal><many-to-many></literal>.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="manytomany1" coords="2 60"/>
+ <area id="manytomany2" coords="3 60"/>
+ <area id="manytomany3" coords="4 60"/>
+ <area id="manytomany4" coords="5 60"/>
+ <area id="manytomany5" coords="6 60"/>
+ <area id="manytomany6" coords="7 60"/>
+ <area id="manytomany7" coords="8 60"/>
+ </areaspec>
+ <programlisting><![CDATA[<many-to-many
+ column="column_name"
+ formula="any SQL expression"
+ class="ClassName"
+ fetch="select|join"
+ unique="true|false"
+ not-found="ignore|exception"
+ entity-name="EntityName"
+ node="element-name"
+ embed-xml="true|false"
+ />]]></programlisting>
+ <calloutlist>
+ <callout arearefs="manytomany1">
+ <para>
+ <literal>column</literal> (opcional): El nombre de la
columna de clave
+ foránea del elemento.
+ </para>
+ </callout>
+ <callout arearefs="manytomany2">
+ <para>
+ <literal>formula</literal> (opcional): Una
fórmula SQL opcional usada
+ para evaluar el valor de clave foránea del elemento.
+ </para>
+ </callout>
+ <callout arearefs="manytomany3">
+ <para>
+ <literal>class</literal> (requerido): El nombre de la
clase asociada.
+ </para>
+ </callout>
+ <callout arearefs="manytomany4">
+ <para>
+ <literal>fetch</literal> (opcional - por defecto a
<literal>join</literal>):
+ habilita la recuperación por unión externa
o selección secuencial para esta
+ asociación. Este es un caso especial; para una
recuperación completamente
+ temprana (en un solo <literal>SELECT</literal>) de
una entidad y sus relaciones
+ muchos-a-muchos a otras entidades, deberías habilitar
la recuperación
+ <literal>join</literal> no sólo de la
colección misma, sino también con este
+ atributo en el elemento anidado
<literal><many-to-many></literal>.
+ </para>
+ </callout>
+ <callout arearefs="manytomany5">
+ <para>
+ <literal>unique</literal> (opcional): Habilita la
generación DDL de una
+ restricción de unicidad para la columna clave
foránea. Esto hace la
+ multiplicidad de la asociación efectivamente uno a
muchos.
+ </para>
+ </callout>
+ <callout arearefs="manytomany6">
+ <para>
+ <literal>not-found</literal> (opcional - por defecto a
<literal>exception</literal>):
+ Especifica cómo serán manejadas las
claves foráneas que referencian
+ filas perdidas: <literal>ignore</literal>
tratará una fila perdida como
+ una asociación nula.
+ </para>
+ </callout>
+ <callout arearefs="manytomany7">
+ <para>
+ <literal>entity-name</literal> (opcional): El nombre
de entidad de la clase
+ asociada, como una alternativa a
<literal>class</literal>.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Algunos ejemplos, primero, un conjunto de cadenas:
+ </para>
+
+ <programlisting><![CDATA[<set name="names"
table="person_names">
+ <key column="person_id"/>
+ <element column="person_name" type="string"/>
+</set>]]></programlisting>
+
+ <para>
+ Un bag conteniendo enteros (con un orden de iteración determinado
por el
+ atributo <literal>order-by</literal>):
+ </para>
+
+ <programlisting><![CDATA[<bag name="sizes"
+ table="item_sizes"
+ order-by="size asc">
+ <key column="item_id"/>
+ <element column="size" type="integer"/>
+</bag>]]></programlisting>
+
+ <para>
+ Un array de entidades - en este caso, una asociación muchos a
muchos:
+ </para>
+
+ <programlisting><![CDATA[<array name="addresses"
+ table="PersonAddress"
+ cascade="persist">
+ <key column="personId"/>
+ <list-index column="sortOrder"/>
+ <many-to-many column="addressId" class="Address"/>
+</array>]]></programlisting>
+
+ <para>
+ Un mapa de índices de cadenas a fechas:
+ </para>
+
+ <programlisting><![CDATA[<map name="holidays"
+ table="holidays"
+ schema="dbo"
+ order-by="hol_name asc">
+ <key column="id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date" type="date"/>
+</map>]]></programlisting>
+
+ <para>
+ Una lista de componentes (discutidos en el próximo
capítulo):
+ </para>
+
+ <programlisting><![CDATA[<list name="carComponents"
+ table="CarComponents">
+ <key column="carId"/>
+ <list-index column="sortOrder"/>
+ <composite-element class="CarComponent">
+ <property name="price"/>
+ <property name="type"/>
+ <property name="serialNumber" column="serialNum"/>
+ </composite-element>
+</list>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="collections-onetomany">
+ <title>Asociaciones uno-a-muchos</title>
+
+ <para>
+ Una <emphasis>asociación uno a muchos</emphasis>
enlaza las tablas de dos clases
+ por medio de una clave foránea, sin intervención de
tabla de colección alguna.
+ Este mapeo pierde cierta semántica de colecciones Java normales:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Una instancia de la clase entidad contenida no puede pertenecer
+ a más de una instancia de la colección.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Una instancia de la clase entidad contenida no puede aparecer en
más
+ de un valor del índice de colección.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Una asociación de <literal>Product</literal> a
<literal>Part</literal> requiere la
+ existencia de una columna clave foránea y posiblemente una columna
índice a la tabla
+ <literal>Part</literal>. Una etiqueta
<literal><one-to-many></literal> indica
+ que ésta es una asociación uno a muchos.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="onetomany1" coords="2 60"/>
+ <area id="onetomany2" coords="3 60"/>
+ <area id="onetomany3" coords="4 60"/>
+ </areaspec>
+ <programlisting><![CDATA[<one-to-many
+ class="ClassName"
+ not-found="ignore|exception"
+ entity-name="EntityName"
+ node="element-name"
+ embed-xml="true|false"
+ />]]></programlisting>
+ <calloutlist>
+ <callout arearefs="onetomany1">
+ <para>
+ <literal>class</literal> (requerido): El nombre de la
clase asociada.
+ </para>
+ </callout>
+ <callout arearefs="onetomany2">
+ <para>
+ <literal>not-found</literal> (opcional - por defecto a
<literal>exception</literal>):
+ Especifica cómo serán manejados los
identificadores en caché que referencien
+ filas perdidas: <literal>ignore</literal>
tratará una fila perdida como una
+ asociación nula.
+ </para>
+ </callout>
+ <callout arearefs="onetomany3">
+ <para>
+ <literal>entity-name</literal> (opcional): El nombre
de entidad de la clase
+ asociada, como una alternativa a
<literal>class</literal>.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Observa que el elemento
<literal><one-to-many></literal> no necesita
+ declarar ninguna columna. Ni es necesario especificar el nombre de
<literal>table</literal>
+ en ningún sitio.
+ </para>
+
+ <para>
+ <emphasis>Nota muy importante:</emphasis> Si la columna clave
foránea de una asociación
+ <literal><one-to-many></literal> es declarada
<literal>NOT NULL</literal>, debes
+ declarar el mapeo de <literal><key></literal>
<literal>not-null="true"</literal>
+ o <emphasis>usar una asociación
bidireccional</emphasis> con el mapeo de colección
+ marcado <literal>inverse="true"</literal>. Ver la
discusión sobre asociaciones
+ bidireccionales más adelante en este capítulo.
+ </para>
+
+ <para>
+ Este ejemplo muestra un mapa de entidades <literal>Part</literal>
por nombre
+ (donde <literal>partName</literal> es una propiedad persistente
de <literal>Part</literal>).
+ Observa el uso de un índice basado en fórmula.
+ </para>
+
+ <programlisting><![CDATA[<map name="parts"
+ cascade="all">
+ <key column="productId" not-null="true"/>
+ <map-key formula="partName"/>
+ <one-to-many class="Part"/>
+</map>]]></programlisting>
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="collections-advancedmappings">
+ <title>Mapeos de colección avanzados</title>
+
+ <sect2 id="collections-sorted" revision="2">
+ <title>Colecciones ordenadas</title>
+
+ <para>
+ Hibernate soporta colecciones implementando
<literal>java.util.SortedMap</literal> y
+ <literal>java.util.SortedSet</literal>. Debes especificar un
comparador en el fichero de
+ mapeo:
+ </para>
+
+ <programlisting><![CDATA[<set name="aliases"
+ table="person_aliases"
+ sort="natural">
+ <key column="person"/>
+ <element column="name" type="string"/>
+</set>
+
+<map name="holidays" sort="my.custom.HolidayComparator">
+ <key column="year_id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date" type="date"/>
+</map>]]></programlisting>
+
+ <para>
+ Los valores permitidos del atributo <literal>sort</literal> son
<literal>unsorted</literal>,
+ <literal>natural</literal> y el nombre de una clase que
implemente
+ <literal>java.util.Comparator</literal>.
+ </para>
+
+ <para>
+ Las colecciones ordenadas realmente se comportan como
<literal>java.util.TreeSet</literal> o
+ <literal>java.util.TreeMap</literal>.
+ </para>
+
+ <para>
+ Si quieres que la misma base de datos ordene los elementos de
colección usa el
+ atributo <literal>order-by</literal> de los mapeos
<literal>set</literal>,
+ <literal>bag</literal> o <literal>map</literal>. Esta
solución está disponible sólo
+ bajo el JDK 1.4 o superior (está implementado usando
<literal>LinkedHashSet</literal> o
+ <literal>LinkedHashMap</literal>). Esto realiza la
ordenación en la consulta SQL,
+ no en memoria.
+ </para>
+
+ <programlisting><![CDATA[<set name="aliases"
table="person_aliases" order-by="lower(name) asc">
+ <key column="person"/>
+ <element column="name" type="string"/>
+</set>
+
+<map name="holidays" order-by="hol_date, hol_name">
+ <key column="year_id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date type="date"/>
+</map>]]></programlisting>
+
+ <para>
+ Observa que el valor del atributo <literal>order-by</literal> es
una ordenación SQL, no
+ una ordenación HQL!
+ </para>
+
+ <para>
+ Las asociaciones pueden incluso ser ordenadas por algún criterio
arbitrario en tiempo de
+ ejecución usando un <literal>filter()</literal> de
colección.
+ </para>
+
+ <programlisting><![CDATA[sortedUsers = s.createFilter( group.getUsers(),
"order by this.name" ).list();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="collections-bidirectional" revision="1">
+ <title>Asociaciones bidireccionales</title>
+
+ <para>
+ Una <emphasis>asociación bidireccional</emphasis>
permite la nevegación desde
+ ambos "extremos" de la asociación. Son soportados dos
tipos de asociación
+ bidireccional:
+
+ <variablelist>
+ <varlistentry>
+ <term>uno-a-muchos</term>
+ <listitem>
+ <para>
+ set o bag valorados en un extremo, monovaluados al otro
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>muchos-a-muchos</term>
+ <listitem>
+ <para>
+ set o bag valorados a ambos extremos
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </para>
+
+ <para>
+ Puedes especificar una asociación bidireccional muchos-a-muchos
simplemente
+ mapeando dos asociaciones muchos-a-muchos a la misma tabla de base de datos
+ y declarando un extremo como <emphasis>inverse</emphasis>
(cuál de ellos es tu
+ elección, pero no puede ser una colección indexada).
+ </para>
+
+ <para>
+ He aquí un ejemplo de una asociación bidireccional
muchos-a-muchos; cada categoría
+ puede tener muchos ítems y cada ítem puede estar en
muchas categorías:
+ </para>
+
+ <programlisting><![CDATA[<class name="Category">
+ <id name="id" column="CATEGORY_ID"/>
+ ...
+ <bag name="items" table="CATEGORY_ITEM">
+ <key column="CATEGORY_ID"/>
+ <many-to-many class="Item" column="ITEM_ID"/>
+ </bag>
+</class>
+
+<class name="Item">
+ <id name="id" column="ITEM_ID"/>
+ ...
+
+ <!-- inverse end -->
+ <bag name="categories" table="CATEGORY_ITEM"
inverse="true">
+ <key column="ITEM_ID"/>
+ <many-to-many class="Category" column="CATEGORY_ID"/>
+ </bag>
+</class>]]></programlisting>
+
+ <para>
+ Los cambios hechos sólo al extremo inverso de la
asociación <emphasis>no</emphasis>
+ son persistidos. Esto significa que Hibernate tiene dos representaciones en
memoria
+ para cada asociación bidireccional, una enlaza de A a B y otra
enlaza de B a A.
+ Esto es más fácil de entender si piensas en el modelo
de objetos de Java y cómo
+ creamos una relación muchos-a-muchos en Java:
+ </para>
+
+ <programlisting><![CDATA[
+category.getItems().add(item); // The category now "knows" about the
relationship
+item.getCategories().add(category); // The item now "knows" about the
relationship
+
+session.persist(item); // The relationship won't be saved!
+session.persist(category); // The relationship will be
saved]]></programlisting>
+
+ <para>
+ El lado no-inverso se usa para salvar la representación en memoria
a la base de datos.
+ </para>
+
+ <para>
+ Puedes definir una asociación bidireccional uno-a-muchos mapeando
una asociación uno-a-muchos
+ a la misma columna (o columnas) de tabla como una asociación
muchos-a-uno y declarando el
+ extremo multivaluado
<literal>inverse="true"</literal>.
+ </para>
+
+ <programlisting><![CDATA[<class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+</class>
+
+<class name="eg.Child">
+ <id name="id" column="id"/>
+ ....
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ not-null="true"/>
+</class>]]></programlisting>
+
+ <para>
+ Mapear un extremo de una asociación con
<literal>inverse="true"</literal> no afecta
+ la operación de cascadas; éstos son conceptos
ortogonales!
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-indexedbidirectional">
+ <title>Asociaciones bidireccionales con colecciones
indexadas</title>
+ <para>
+ Requiere especial consideración una asociación
bidireccional donde un extremo esté representado
+ como una <literal><list></literal> o
<literal><map></literal>. Si hay una propiedad
+ de la clase hija que mapee a la columna índice, no hay problema,
podemos seguir usando
+ <literal>inverse="true"</literal> en el mapeo de la
colección:
+ </para>
+
+ <programlisting><![CDATA[<class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <map name="children" inverse="true">
+ <key column="parent_id"/>
+ <map-key column="name"
+ type="string"/>
+ <one-to-many class="Child"/>
+ </map>
+</class>
+
+<class name="Child">
+ <id name="id" column="child_id"/>
+ ....
+ <property name="name"
+ not-null="true"/>
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ not-null="true"/>
+</class>]]></programlisting>
+
+ <para>
+ Pero, si no existe tal proiedad en la clase hija, no podemos pensar en la
asociación como
+ verdaderamente bidireccional (hay información en un extremo de la
asociación que no está
+ disponible en el otro extremo). En este caso, no podemos mapear la
colección con
+ <literal>inverse="true"</literal>. En cambio,
podríamos usar el siguiente mapeo:
+ </para>
+
+ <programlisting><![CDATA[<class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <map name="children">
+ <key column="parent_id"
+ not-null="true"/>
+ <map-key column="name"
+ type="string"/>
+ <one-to-many class="Child"/>
+ </map>
+</class>
+
+<class name="Child">
+ <id name="id" column="child_id"/>
+ ....
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ insert="false"
+ update="false"
+ not-null="true"/>
+</class>]]></programlisting>
+
+ <para>
+ Nota que, en este mapeo, el extremo de la asociación valuado en
colección es responsable de las
+ actualizaciones a la clave foránea.
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-ternary">
+ <title>Asociaciones ternarias</title>
+
+ <para>
+ Hay tres enfoques posibles para mapear una asociación ternaria.
+ Una es usar un <literal>Map</literal> con una
asociación como su índice:
+ </para>
+
+ <programlisting><![CDATA[<map name="contracts">
+ <key column="employer_id" not-null="true"/>
+ <map-key-many-to-many column="employee_id"
class="Employee"/>
+ <one-to-many class="Contract"/>
+</map>]]></programlisting>
+
+ <programlisting><![CDATA[<map name="connections">
+ <key column="incoming_node_id"/>
+ <map-key-many-to-many column="outgoing_node_id"
class="Node"/>
+ <many-to-many column="connection_id" class="Connection"/>
+</map>]]></programlisting>
+
+ <para>
+ Un segundo enfoque es simplemente remodelar la asociación como una
clase de entidad.
+ Este es el enfoque que usamos más comunmente.
+ </para>
+
+ <para>
+ Una alternativa final es usar elementos compuestos, que discutiremos
más adelante.
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-idbag" revision="1">
+ <title><literal>Usando un
<idbag></literal></title>
+
+ <para>
+ Si has adoptado completamente nuestra visión de que las claves
compuestas son una
+ cosa mala y que las entidades deben tener identificadores
sitéticos (claves delegadas),
+ entonces podrías encontrar un poco raro que todas las asociaciones
muchos a muchos y
+ las colecciones de valores que hemos mostrado hasta ahora mapeen a tablas con
claves
+ compuestas! Ahora, este punto es discutible; una tabla de pura
asociación no parece
+ beneficiarse demasiado de una clave delegada (aunque sí
<emphasis>podría</emphasis> una
+ colección de valores compuestos). Sin embargo, Hibernate provee
una funcionalidad que
+ te permite mapear asociaciones muchos a muchos y colecciones de valores a una
tabla con
+ una clave delegada.
+ </para>
+
+ <para>
+ El elemento <literal><idbag></literal> te permite
mapear una <literal>List</literal>
+ (o <literal>Collection</literal>) con semántica de
bag.
+ </para>
+
+<programlisting><![CDATA[<idbag name="lovers"
table="LOVERS">
+ <collection-id column="ID" type="long">
+ <generator class="sequence"/>
+ </collection-id>
+ <key column="PERSON1"/>
+ <many-to-many column="PERSON2" class="Person"
fetch="join"/>
+</idbag>]]></programlisting>
+
+ <para>
+ Como puedes ver, un <literal><idbag></literal>
tiene un generador de id sintético,
+ igual que una clase de entidad! Una clave delegada diferente se asigna a cada
fila
+ de la colección. Hibernate no provee ningún mecanismo
para descubrir el valor de clave
+ delegada de una fila en particular, sin embargo.
+ </para>
+
+ <para>
+ Observa que el rendimiento de actualización de un
<literal><idbag></literal> es
+ <emphasis>mucho</emphasis> mejor que el de un
<literal><bag></literal> regular!
+ Hibernate puede localizar filas individuales eficientemente y actualizarlas o
borrarlas
+ individualmente, igual que si fuese una lista, mapa o conjunto.
+ </para>
+
+ <para>
+ En la implementación actual, la estrategia de
generación de identificador
+ <literal>native</literal> no está soportada para
identificadores de colecciones
+ <literal><idbag></literal>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <!--undocumenting this stuff -->
+
+ <!--sect1 id="collections-heterogeneous">
+ <title>Heterogeneous Associations</title>
+
+ <para>
+ The <literal><many-to-any></literal> and
<literal><index-many-to-any></literal>
+ elements provide for true heterogeneous associations. These mapping elements
work in the
+ same way as the <literal><any></literal> element -
and should also be used
+ rarely, if ever.
+ </para>
+
+ </sect1-->
+
+ <sect1 id="collections-example" revision="1">
+ <title>Ejemplos de colección</title>
+
+ <para>
+ Las secciones previas son bastantes confusas. Así que miremos un
ejemplo.
+ Esta clase:
+ </para>
+
+ <programlisting><![CDATA[package eg;
+import java.util.Set;
+
+public class Parent {
+ private long id;
+ private Set children;
+
+ public long getId() { return id; }
+ private void setId(long id) { this.id=id; }
+
+ private Set getChildren() { return children; }
+ private void setChildren(Set children) { this.children=children; }
+
+ ....
+ ....
+}]]></programlisting>
+
+ <para>
+ tiene una colección de instancias de
<literal>Child</literal>.
+ Si cada hijo tiene como mucho un padre, el mapeo más natural es
+ una asociación uno-a-muchos:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Esto mapea a las siguientes definiciones de tablas:
+ </para>
+
+ <programlisting><![CDATA[create table parent ( id bigint not null
primary key )
+create table child ( id bigint not null primary key, name varchar(255), parent_id bigint
)
+alter table child add constraint childfk0 (parent_id) references
parent]]></programlisting>
+
+ <para>
+ Si el padre es <emphasis>requerido</emphasis>, usa una
asociación bidireccional
+ uno-a-muchos:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ <many-to-one name="parent" class="Parent"
column="parent_id" not-null="true"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Observa la restricción <literal>NOT NULL</literal>:
+ </para>
+
+ <programlisting><![CDATA[create table parent ( id bigint not null
primary key )
+create table child ( id bigint not null
+ primary key,
+ name varchar(255),
+ parent_id bigint not null )
+alter table child add constraint childfk0 (parent_id) references
parent]]></programlisting>
+
+ <para>
+ Alternativamente, si absolutamente insistes que esta asociación
debe ser unidireccional,
+ puedes declarar la restricción <literal>NOT
NULL</literal> en el mapeo de
+ <literal><key></literal>:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children">
+ <key column="parent_id" not-null="true"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ En la otra mano, si un hijo pudiera tener múltiples padres,
sería apropiada
+ una asociación muchos-a-muchos:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children" table="childset">
+ <key column="parent_id"/>
+ <many-to-many class="Child" column="child_id"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Definiciones de tabla:
+ </para>
+
+ <programlisting><![CDATA[create table parent ( id bigint not null
primary key )
+create table child ( id bigint not null primary key, name varchar(255) )
+create table childset ( parent_id bigint not null,
+ child_id bigint not null,
+ primary key ( parent_id, child_id ) )
+alter table childset add constraint childsetfk0 (parent_id) references parent
+alter table childset add constraint childsetfk1 (child_id) references
child]]></programlisting>
+
+ <para>
+ Para más ejemplos y un paseo completo a través del
mapeo de relaciones padre/hijo,
+ ver <xref linkend="example-parentchild"/>.
+ </para>
+
+ <para>
+ Son posibles mapeos de asociación aún más
complejos. Catalogaremos todas las posibilidades
+ en el próximo capítulo.
+ </para>
+
+ </sect1>
+
+</chapter>
Copied:
core/trunk/documentation/manual/es-ES/src/main/docbook/content/component_mapping.xml (from
rev 12794,
core/trunk/documentation/manual/es-ES/src/main/docbook/modules/component_mapping.xml)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/content/component_mapping.xml
(rev 0)
+++
core/trunk/documentation/manual/es-ES/src/main/docbook/content/component_mapping.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,403 @@
+<chapter id="components">
+ <title>Mapeo de Componentes</title>
+
+ <para>
+ La noción de un <emphasis>componente</emphasis> es reusada
en muchos contextos diferentes,
+ para propósitos diferentes, a través de Hibernate.
+ </para>
+
+ <sect1 id="components-dependentobjects">
+ <title>Objetos dependientes</title>
+
+ <para>
+ Un componente es un objeto contenido que es persistido como un tipo de valor,
no una
+ referencia de entidad. El término "componente" hace
referencia a la noción orientada a
+ objetos de composición (no a componentes a nivel de arquitectura).
Por ejemplo, podrías
+ modelar una persona como:
+ </para>
+
+ <programlisting><![CDATA[public class Person {
+ private java.util.Date birthday;
+ private Name name;
+ private String key;
+ public String getKey() {
+ return key;
+ }
+ private void setKey(String key) {
+ this.key=key;
+ }
+ public java.util.Date getBirthday() {
+ return birthday;
+ }
+ public void setBirthday(java.util.Date birthday) {
+ this.birthday = birthday;
+ }
+ public Name getName() {
+ return name;
+ }
+ public void setName(Name name) {
+ this.name = name;
+ }
+ ......
+ ......
+}]]></programlisting>
+
+<programlisting><![CDATA[public class Name {
+ char initial;
+ String first;
+ String last;
+ public String getFirst() {
+ return first;
+ }
+ void setFirst(String first) {
+ this.first = first;
+ }
+ public String getLast() {
+ return last;
+ }
+ void setLast(String last) {
+ this.last = last;
+ }
+ public char getInitial() {
+ return initial;
+ }
+ void setInitial(char initial) {
+ this.initial = initial;
+ }
+}]]></programlisting>
+
+ <para>
+ Ahora <literal>Name</literal> puede ser persistido como un
componente de
+ <literal>Person</literal>. Observa que
<literal>Name</literal> define métodos
+ getter y setter para sus propiedades persistentes, pero no necesita declarar
+ ninguna interface ni propiedades identificadoras.
+ </para>
+
+ <para>
+ Nuestro mapeo de Hibernate se vería así:
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Person"
table="person">
+ <id name="Key" column="pid" type="string">
+ <generator class="uuid.hex"/>
+ </id>
+ <property name="birthday" type="date"/>
+ <component name="Name" class="eg.Name"> <!-- class
attribute optional -->
+ <property name="initial"/>
+ <property name="first"/>
+ <property name="last"/>
+ </component>
+</class>]]></programlisting>
+
+ <para>
+ La tabla person tendría las columnas
<literal>pid</literal>,
+ <literal>birthday</literal>,
+ <literal>initial</literal>,
+ <literal>first</literal> y
+ <literal>last</literal>.
+ </para>
+
+ <para>
+ Como todos los tipos de valor, los componentes no soportan referencias
compartidas.
+ En otras palabras, dos personas pueden tener el mismo nombre, pero los dos
objetos
+ persona contendrían dos objetos nombre independientes,
sólo "iguales" en valor.
+ La semántica de valor nulo de un componente es <emphasis>ad
hoc</emphasis>.
+ Cuando se recargue el objeto contenedor, Hibernate asumirá que si
todas las columnas del
+ componente son nulas, el componente entero es nulo. Esto debe estar bien para
la mayoría
+ de propósitos.
+ </para>
+
+ <para>
+ Las propiedades de un componentes pueden ser de cualquier tipo de Hibernate
+ (colecciones, muchos-a-uno, asociaciones, otros componentes, etc). Los
componentes
+ anidados <emphasis>no</emphasis> deben ser considerados un uso
exótico. Hibernate está
+ concebido para soportar un modelo de objetos granularizado en fino.
+ </para>
+
+ <para>
+ El elemento <literal><component></literal> permite
un subelemento
+ <literal><parent></literal> que mapee una propiedad
de la clase del componente
+ como una referencia de regreso a la entidad contenedora.
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Person"
table="person">
+ <id name="Key" column="pid" type="string">
+ <generator class="uuid.hex"/>
+ </id>
+ <property name="birthday" type="date"/>
+ <component name="Name" class="eg.Name"
unique="true">
+ <parent name="namedPerson"/> <!-- reference back to the Person
-->
+ <property name="initial"/>
+ <property name="first"/>
+ <property name="last"/>
+ </component>
+</class>]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="components-incollections" revision="1">
+ <title>Colecciones de objetos dependientes</title>
+
+ <para>
+ Las colecciones de componentes están soportadas (por ejemplo,
+ un array de tipo <literal>Name</literal>). Declara tu
colección
+ de componentes remplazando la etiqueta
<literal><element></literal>
+ por una etiqueta
<literal><composite-element></literal>.
+ </para>
+
+ <programlisting><![CDATA[<set name="someNames"
table="some_names" lazy="true">
+ <key column="id"/>
+ <composite-element class="eg.Name"> <!-- class attribute required
-->
+ <property name="initial"/>
+ <property name="first"/>
+ <property name="last"/>
+ </composite-element>
+</set>]]></programlisting>
+
+ <para>
+ Nota: si defines un <literal>Set</literal> de elementos
compuestos, es muy
+ importante implementar <literal>equals()</literal> y
<literal>hashCode()</literal>
+ correctamente.
+ </para>
+
+ <para>
+ Los elementos compuestos pueden contener componentes pero no colecciones.
+ Si tu elemento compuesto contiene a su vez componentes, usa la etiqueta
+ <literal><nested-composite-element></literal>. Este
es un caso bastante
+ exótico - una colección de componentes que a su vez
tienen componentes. A esta
+ altura debes estar preguntándote si una asociación
uno-a-muchos es más
+ apropiada. Intenta remodelar el elemento compuesto como una entidad - pero
+ observa que aunque el modelo Java es el mismo, el modelo relacional y la
+ semántica de persistencia siguen siendo ligeramente diferentes.
+ </para>
+
+ <para>
+ Por favor observa que un mapeo de elemento compuesto no soporta
+ propiedades nulables si estás usando un
<literal><set></literal>.
+ Hibernate tiene que usar cada columna para identificar un registro
+ al borrar objetos (no hay una columna clave primaria separada en la tabla
del
+ elemento compuesto), lo que es imposible con valores nulos. Tienes que, o
bien usar
+ sólo propiedades no nulas en un elemento compuesto o elegir un
+ <literal><list></literal>,
<literal><map></literal>,
+ <literal><bag></literal> o
<literal><idbag></literal>.
+ </para>
+
+ <para>
+ Un caso especial de un elemento compuesto es un elemento compuesto con un
+ elemento anidado <literal><many-to-one></literal>.
Un mapeo como este
+ te permite mapear columnas extra de una tabla de asociación
muchos-a-muchos
+ a la clase del elemento compuesto. La siguiente es una asociación
muchos-a-muchos
+ de <literal>Order</literal> a <literal>Item</literal>
donde
+ <literal>purchaseDate</literal>,
<literal>price</literal> y
+ <literal>quantity</literal> son propiedades de la
asociación:
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Order" .... >
+ ....
+ <set name="purchasedItems" table="purchase_items"
lazy="true">
+ <key column="order_id">
+ <composite-element class="eg.Purchase">
+ <property name="purchaseDate"/>
+ <property name="price"/>
+ <property name="quantity"/>
+ <many-to-one name="item" class="eg.Item"/> <!--
class attribute is optional -->
+ </composite-element>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ Por supuesto, no puede haber una referencia a la compra del otro lado para
la
+ navegación bidireccional de la asociación. Recuerda que
los componentes son tipos de
+ valor no permiten referencias compartidas. Una sola
<literal>Purchase</literal> puede
+ estar en el conjunto de una <literal>Order</literal>, pero no
puede ser referenciada
+ por el <literal>Item</literal> al mismo tiempo.
+ </para>
+
+ <para>Incluso son posibles las asociaciones ternarias (o cuaternarias,
etc):</para>
+
+ <programlisting><![CDATA[<class name="eg.Order" .... >
+ ....
+ <set name="purchasedItems" table="purchase_items"
lazy="true">
+ <key column="order_id">
+ <composite-element class="eg.OrderLine">
+ <many-to-one name="purchaseDetails
class="eg.Purchase"/>
+ <many-to-one name="item" class="eg.Item"/>
+ </composite-element>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ Los elementos compuestos pueden aparecer en consultas usando la misma
+ sintáxis que las asociaciones a otras entidades.
+ </para>
+
+ </sect1>
+
+ <sect1 id="components-asmapindex">
+ <title>Componentes como índices de Map</title>
+
+ <para>
+ El elemento <literal><composite-map-key></literal>
te permite mapear
+ una clase componente como la clave de un <literal>Map</literal>.
Asegúrate que
+ sobrescribes <literal>hashCode()</literal> y
<literal>equals()</literal>
+ correctamente en la clase componente.
+ </para>
+ </sect1>
+
+ <sect1 id="components-compositeid" revision="1">
+ <title>Componentes como identificadores compuestos</title>
+
+ <para>
+ Puedes usar un componente como un identidicador de una clase entidad. Tu
clase
+ componente debe satisfacer ciertos requerimientos:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Debe implementar
<literal>java.io.Serializable</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Debe re-implementar <literal>equals()</literal> y
+ <literal>hashCode()</literal>, consistentemente con la
+ noción de base de datos de igualdad de clave compuesta.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <emphasis>Nota: en Hibernat3, el segundo requerimiento no es
absolutamente un
+ requerimiento rígido de Hibernate. Pero de todas formas,
házlo.</emphasis>
+ </para>
+
+ <para>
+ No puedes usar un <literal>IdentifierGenerator</literal> para
generar claves
+ compuestas. La aplicación debe, en cambio, asignar sus propios
identificadores.
+ </para>
+
+ <para>
+ Usa la etiqueta <literal><composite-id></literal>
(con elementos
+ anidados <literal><key-property></literal>) en
lugar de la usual
+ declaración <literal><id></literal>. Por
ejemplo, la clase
+ <literal>OrderLine</literal> tiene una clave primaria que depende
de
+ la clave primaria (compuesta) de <literal>Order</literal>.
+ </para>
+
+ <programlisting><![CDATA[<class name="OrderLine">
+
+ <composite-id name="id" class="OrderLineId">
+ <key-property name="lineId"/>
+ <key-property name="orderId"/>
+ <key-property name="customerId"/>
+ </composite-id>
+
+ <property name="name"/>
+
+ <many-to-one name="order" class="Order"
+ insert="false" update="false">
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </many-to-one>
+ ....
+
+</class>]]></programlisting>
+
+ <para>
+ Ahora, cualquier clave foránea que referencie la tabla de
<literal>OrderLine</literal>
+ es también compuesta. Debes declarar esto en tus mapeos de otras
clases. Una asociación
+ a <literal>OrderLine</literal> sería mapeado
así:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="orderLine"
class="OrderLine">
+<!-- the "class" attribute is optional, as usual -->
+ <column name="lineId"/>
+ <column name="orderId"/>
+ <column name="customerId"/>
+</many-to-one>]]></programlisting>
+
+ <para>
+ (Nota que la etiqueta <literal><column></literal>
es una alternativa al
+ atributo <literal>column</literal> en cualquier sitio.)
+ </para>
+
+ <para>
+ Una asociación <literal>muchos-a-muchos</literal> a
<literal>OrderLine</literal>
+ también usa la clave foránea compuesta:
+ </para>
+
+ <programlisting><![CDATA[<set name="undeliveredOrderLines">
+ <key column name="warehouseId"/>
+ <many-to-many class="OrderLine">
+ <column name="lineId"/>
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </many-to-many>
+</set>]]></programlisting>
+
+ <para>
+ La colección de <literal>OrderLine</literal>s en
<literal>Order</literal> usaría:
+ </para>
+
+ <programlisting><![CDATA[<set name="orderLines"
inverse="true">
+ <key>
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </key>
+ <one-to-many class="OrderLine"/>
+</set>]]></programlisting>
+
+ <para>
+ (El elemento <literal><one-to-many></literal>, como
es usual, no declara columnas.)
+ </para>
+
+ <para>
+ Si <literal>OrderLine</literal> posee una colección
por sí misma, tiene también
+ una clave foránea compuesta.
+ </para>
+
+ <programlisting><![CDATA[<class name="OrderLine">
+ ....
+ ....
+ <list name="deliveryAttempts">
+ <key> <!-- a collection inherits the composite key type -->
+ <column name="lineId"/>
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </key>
+ <list-index column="attemptId" base="1"/>
+ <composite-element class="DeliveryAttempt">
+ ...
+ </composite-element>
+ </set>
+</class>]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="components-dynamic" revision="1">
+ <title>Componentes dinámicos</title>
+
+ <para>
+ Puedes incluso mapear una propiedad de tipo
<literal>Map</literal>:
+ </para>
+
+ <programlisting><![CDATA[<dynamic-component
name="userAttributes">
+ <property name="foo" column="FOO"
type="string"/>
+ <property name="bar" column="BAR"
type="integer"/>
+ <many-to-one name="baz" class="Baz"
column="BAZ_ID"/>
+</dynamic-component>]]></programlisting>
+
+ <para>
+ La semántica de un mapeo
<literal><dynamic-component></literal> es ídentica
+ a la de <literal><component></literal>. La ventaja
de este tipo de mapeos es
+ la habilidad para determinar las propiedades reales del bean en tiempo de
despliegue,
+ sólo con editar el documento de mapeo. La manipulación
del documento de mapeo en tiempo
+ de ejecución es también posible, usando un analizador
DOM. Incluso mejor, puedes acceder
+ (y cambiar) el metamodelo de tiempo de configuración de Hibernate
por medio del objeto
+ <literal>Configuration</literal>.
+ </para>
+
+ </sect1>
+
+</chapter>
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/content/configuration.xml
(from rev 12794,
core/trunk/documentation/manual/es-ES/src/main/docbook/modules/configuration.xml)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/content/configuration.xml
(rev 0)
+++
core/trunk/documentation/manual/es-ES/src/main/docbook/content/configuration.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,1761 @@
+<chapter id="session-configuration" revision="1">
+
+ <title>Configuración</title>
+
+ <para>
+ Debido a que Hibernate está diseñado para operar en muchos
entornos
+ diferentes, hay un gran número de parámetros de
configuración.
+ Afortunadamente, la mayoría tiene valores por defecto sensibles e
Hibernate
+ se distribuye con un fichero <literal>hibernate.properties</literal>
de ejemplo en
+ <literal>etc/</literal> que muestra las diversas opciones. Tan
sólo pon el
+ fichero de ejemplo en tu classpath y personalízalo.
+ </para>
+
+ <sect1 id="configuration-programmatic" revision="1">
+ <title>Configuración programática</title>
+
+ <para>
+ Una instancia de
<literal>org.hibernate.cfg.Configuration</literal>
+ representa un conjunto entero de mapeos de los tipos Java de una
aplicación
+ a una base de datos SQL. La <literal>Configuration</literal> es
usada para
+ construir una <literal>SessionFactory</literal> (inmutable). Los
mapeos se
+ compilan de varios ficheros de mapeo XML.
+ </para>
+
+ <para>
+ Puedes obtener una instancia de <literal>Configuration</literal>
instanciándola
+ directamente y especificando documentos de mapeo XML. Si los ficheros de
mapeo
+ están en el classpath, usa
<literal>addResource()</literal>:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration()
+ .addResource("Item.hbm.xml")
+ .addResource("Bid.hbm.xml");]]></programlisting>
+
+ <para>
+ Una forma alternativa (a veces mejor) es especificar la clase mapeada,
+ y dejar que Hibernate encuentre el documento de mapeo por ti:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration()
+ .addClass(org.hibernate.auction.Item.class)
+ .addClass(org.hibernate.auction.Bid.class);]]></programlisting>
+
+ <para>
+ Entonces Hibernate buscará ficheros de mapeo llamados
+ <literal>/org/hibernate/auction/Item.hbm.xml</literal> y
+ <literal>/org/hibernate/auction/Bid.hbm.xml</literal> en el
classpath.
+ Este enfoque elimina cualquier nombre de fichero en el código.
+ </para>
+
+ <para>
+ Una <literal>Configuration</literal> también te
permite especificar
+ propiedades de configuración:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration()
+ .addClass(org.hibernate.auction.Item.class)
+ .addClass(org.hibernate.auction.Bid.class)
+ .setProperty("hibernate.dialect",
"org.hibernate.dialect.MySQLInnoDBDialect")
+ .setProperty("hibernate.connection.datasource",
"java:comp/env/jdbc/test")
+ .setProperty("hibernate.order_updates",
"true");]]></programlisting>
+
+ <para>
+ Esta no es la única forma de pasar propiedades de
configuración
+ a Hibernate. La diversas opciones incluyen:
+ </para>
+
+ <orderedlist spacing="compact">
+ <listitem>
+ <para>
+ Pasar una instancia de
<literal>java.util.Properties</literal> a
+ <literal>Configuration.setProperties()</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Colocar <literal>hibernate.properties</literal> en un
directorio
+ raíz del classpath.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Establecer propiedades <literal>System</literal>
+ usando <literal>java -Dproperty=value</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Incluir elementos
<literal><property></literal>
+ en <literal>hibernate.cfg.xml</literal> (discutido
luego).
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ <literal>hibernate.properties</literal> es el enfoque
más fácil
+ si quieres comenzar rápido.
+ </para>
+
+ <para>
+ La <literal>Configuration</literal> está concebida
como un objeto
+ de tiempo de arranque, para ser descartado una vez que una
+ <literal>SessionFactory</literal> es creada.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-sessionfactory">
+ <title>Obteniendo una SessionFactory</title>
+
+ <para>
+ Cuando todos los mapeos han sido parseados por la
<literal>Configuration</literal>,
+ la aplicación debe obtener una fábrica de instancias de
<literal>Session</literal>.
+ Esta fábrica está concebida para ser compartida por
todas las hebras de
+ aplicación:
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sessions =
cfg.buildSessionFactory();]]></programlisting>
+
+ <para>
+ Hibernate permite que tu aplicación instancie más de
una
+ <literal>SessionFactory</literal>. Esto es útil si
estás usando
+ más de una base de datos.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-hibernatejdbc" revision="1">
+ <title>Conexiones JDBC</title>
+
+ <para>
+ Usualmente, quieres que la <literal>SessionFactory</literal> cree
y almacene
+ en pool conexiones JDBC para ti. Si adoptas este enfoque, abrir una
<literal>Session</literal>
+ es tan simple como:
+ </para>
+
+ <programlisting><![CDATA[Session session = sessions.openSession(); //
open a new Session]]></programlisting>
+
+ <para>
+ En cuanto hagas algo que requiera acceso a la base de datos, se
obtendrá una
+ conexión JDBC del pool.
+ </para>
+
+ <para>
+ Para que esto funcione, necesitamos pasar algunas propiedades de
conexión
+ JDBC a Hibernate. Todos los nombres de propiedades y su semántica
+ están definidas en la clase
<literal>org.hibernate.cfg.Environment</literal>.
+ Describiremos ahora las configuraciones más importantes para la
conexión
+ JDBC.
+ </para>
+
+ <para>
+ Hibernate obtendrá (y tendrá en pool) conexiones
usando
+ <literal>java.sql.DriverManager</literal> si configuras las
siguientes propiedades:
+ </para>
+
+ <table frame="topbot">
+ <title>Propiedades JDBC de Hibernate</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nombre de propiedad</entry>
+ <entry>Propósito</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.connection.driver_class</literal>
+ </entry>
+ <entry>
+ <emphasis>clase del driver jdbc</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.url</literal>
+ </entry>
+ <entry>
+ <emphasis>URL de jdbc</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.username</literal>
+ </entry>
+ <entry>
+ <emphasis>usuario de base de datos</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.password</literal>
+ </entry>
+ <entry>
+ <emphasis>contraseña del usuario de base de
datos</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.pool_size</literal>
+ </entry>
+ <entry>
+ <emphasis>número máximo de conexiones
manejadas por pooling</emphasis>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ El algoritmo de pooling de conexiones propio de Hibernate es sin embargo
+ algo rudimentario. Está concebido para ayudarte a comenzar y
+ <emphasis>no está concebido para usar en un sistema de
producción</emphasis>
+ ni siquiera para pruebas de rendimiento. Debes usar un pool de terceros para
+ un mejor rendimiento y estabilidad. Sólo remplaza la propiedad
+ <literal>hibernate.connection.pool_size</literal> con
configuraciones
+ específicas del pool de conexiones. Esto desactivará el
pool
+ interno de Hibernate. Por ejemplo, podrías querer usar C3P0.
+ </para>
+
+ <para>
+ C3P0 es un pool de conexiones JDBC de código abierto distribuido
+ junto a Hibernate en el directorio <literal>lib</literal>.
+ Hibernate usará su
<literal>C3P0ConnectionProvider</literal>
+ para pooling de conexiones si estableces propiedades
<literal>hibernate.c3p0.*</literal>.
+ Si quieres usar Proxool refiérete al
<literal>hibernate.properties</literal>
+ empaquetado y al sitio web de Hibernate para más
información.
+ </para>
+
+ <para>
+ Aquí hay un fichero
<literal>hibernate.properties</literal> de ejemplo para C3P0:
+ </para>
+
+ <programlisting id="c3p0-configuration"
revision="1"><![CDATA[hibernate.connection.driver_class =
org.postgresql.Driver
+hibernate.connection.url = jdbc:postgresql://localhost/mydatabase
+hibernate.connection.username = myuser
+hibernate.connection.password = secret
+hibernate.c3p0.min_size=5
+hibernate.c3p0.max_size=20
+hibernate.c3p0.timeout=1800
+hibernate.c3p0.max_statements=50
+hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
+
+ <para>
+ Para su uso en un servidor de aplicaciones, casi siempre debes configurar
+ Hibernate para que obtenga conexiones de un
<literal>Datasource</literal>
+ del servidor de aplicaciones registrado en JNDI. Necesitarás
establecer
+ al menos una de las siguientes propiedades:
+ </para>
+
+ <table frame="topbot">
+ <title>Propiedades de Datasource de Hibernate</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nombre de propiedad</entry>
+ <entry>Propósito</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.connection.datasource</literal>
+ </entry>
+ <entry>
+ <emphasis>nombre del datasource JNDI</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jndi.url</literal>
+ </entry>
+ <entry>
+ <emphasis>URL del provedor JNDI</emphasis> (optional)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jndi.class</literal>
+ </entry>
+ <entry>
+ <emphasis>clase de la
<literal>InitialContextFactory</literal> de JNDI</emphasis> (opcional)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.username</literal>
+ </entry>
+ <entry>
+ <emphasis>usuario de base de datos</emphasis> (opcional)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.password</literal>
+ </entry>
+ <entry>
+ <emphasis>contraseña del usuario de base de
datos</emphasis> (opcional)
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ He aquí un fichero
<literal>hibernate.properties</literal> de ejemplo
+ para un un datasource JNDI provisto por un servidor de aplicaciones.
+ </para>
+
+ <programlisting><![CDATA[hibernate.connection.datasource =
java:/comp/env/jdbc/test
+hibernate.transaction.factory_class = \
+ org.hibernate.transaction.JTATransactionFactory
+hibernate.transaction.manager_lookup_class = \
+ org.hibernate.transaction.JBossTransactionManagerLookup
+hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
+
+ <para>
+ Las conexiones JDBC obtenidas de un datasource JNDI participarán
automáticamente
+ en las transacciones del servidor de aplicaciones manejadas por contenedor.
+ </para>
+
+ <para>
+ Pueden darse propiedades de conexión arbitrarias anteponiendo
+ "<literal>hibernate.connnection</literal>" al nombre de
propiedad.
+ Por ejemplo, puedes especificar un <literal>charSet</literal>
usando
+ <literal>hibernate.connection.charSet</literal>.
+ </para>
+
+ <para>
+ Puedes definir tu propia estrategia de plugin para obtener conexiones JDBC
implementando
+ la interface
<literal>org.hibernate.connection.ConnectionProvider</literal>. Puedes
+ seleccionar una implementación personalizada estableciendo
+ <literal>hibernate.connection.provider_class</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-optional" revision="1">
+ <title>Parámetros de configuración
opcionales</title>
+
+ <para>
+ Hay un número de otras propiedades que controlan el
comportamiento
+ de Hibernate en tiempo de ejecución. Todas son opcionales y
tienen
+ valores por defecto razonables.
+ </para>
+
+ <para>
+ <emphasis>Advertencia: algunas de estas propiedades son de
"nivel-de-sistema"
+ solamente.</emphasis>. Las propiedades a nivel de sistema
sólo pueden ser
+ establecidas por medio de <literal>java
-Dproperty=value</literal> o
+ <literal>hibernate.properties</literal>.
<emphasis>No</emphasis> pueden
+ establecerse por medio de las otras técnicas arriba descritas.
+ </para>
+
+ <table frame="topbot"
id="configuration-optional-properties" revision="8">
+ <title>Propiedades de Configuración de
Hibernate</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nombre de propiedad</entry>
+ <entry>Propósito</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.dialect</literal>
+ </entry>
+ <entry>
+ El nombre de clase de un
<literal>Dialect</literal>
+ de Hibernate que permite a Hibernate generar SQL optimizado
+ para una base de datos relacional en particular.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>full.classname.of.Dialect</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.show_sql</literal>
+ </entry>
+ <entry>
+ Escribe todas las sentencias SQL a la consola.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>true</literal> |
<literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_schema</literal>
+ </entry>
+ <entry>
+ Cualifica, en el SQL generado, los nombres de tabla sin
cualificar
+ con el esquema/tablespace dado.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>SCHEMA_NAME</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_catalog</literal>
+ </entry>
+ <entry>
+ Cualifica, en el SQL generado, los nombres de tabla sin
cualificar
+ con el catálogo dado.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>CATALOG_NAME</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+
<literal>hibernate.session_factory_name</literal>
+ </entry>
+ <entry>
+ La <literal>SessionFactory</literal>
será
+ ligada a este nombre en JNDI automáticamente
+ después de ser creada.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>jndi/composite/name</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.max_fetch_depth</literal>
+ </entry>
+ <entry>
+ Establece una "profundidad" máxima del
+ árbol de recuperación por outer join
+ para asociaciones de un extremo solo (uno-a-uno,
muchos-a-uno).
+ Un <literal>0</literal> deshabilita la
recuperación
+ por outer join por defecto.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ valores recomendados entre
<literal>0</literal> y
+ <literal>3</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+
<literal>hibernate.default_batch_fetch_size</literal>
+ </entry>
+ <entry>
+ Establece un tamaño por defecto para la
recuperación
+ en lote de asociaciones de Hibernate.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ valores recomendados <literal>4</literal>,
<literal>8</literal>,
+ <literal>16</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_entity_mode</literal>
+ </entry>
+ <entry>
+ Establece un modo por defecto de representación
de
+ entidades para todas las sesiones abiertas por esta
+ <literal>SessionFactory</literal>
+ <para>
+ <literal>dynamic-map</literal>,
<literal>dom4j</literal>,
+ <literal>pojo</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.order_updates</literal>
+ </entry>
+ <entry>
+ Fuerza a Hibernate a ordenar las actualizaciones SQL
+ por el valor de la clave primaria de los items a actualizar.
+ Esto resultará en menos bloqueos muertos de
transacción
+ en sistemas altamente concurrentes.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>true</literal> |
<literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.generate_statistics</literal>
+ </entry>
+ <entry>
+ De habilitarse, Hibernate colectará
estadísticas
+ útiles para la afinación de
rendimiento.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>true</literal> |
<literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+
<literal>hibernate.use_identifer_rollback</literal>
+ </entry>
+ <entry>
+ De habilitarse, las propiedades identificadoras
+ generadas serán reseteadas a valores por
+ defecto cuando los objetos sean borrados.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>true</literal> |
<literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.use_sql_comments</literal>
+ </entry>
+ <entry>
+ De activarse, Hibernate generará comentarios
dentro del SQL,
+ para una más fácil
depuración, por defecto a
+ <literal>false</literal>.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>true</literal> |
<literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-jdbc-properties"
revision="8">
+ <title>Propiedades de JDBC y Conexiones de Hibernate</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nombre de propiedad</entry>
+ <entry>Propoósito</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.fetch_size</literal>
+ </entry>
+ <entry>
+ Un valor distinto de cero que determina el tamaño
+ de recuperación de JDBC (llama a
+ <literal>Statement.setFetchSize()</literal>).
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.batch_size</literal>
+ </entry>
+ <entry>
+ Un valor distinto de cero habilita el uso de actualizaciones
+ en lote de JDBC2 por Hibernate.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ valores recomendados entre
<literal>5</literal> y <literal>30</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+
<literal>hibernate.jdbc.batch_versioned_data</literal>
+ </entry>
+ <entry>
+ Establece esta propiedad a
<literal>true</literal> si tu driver JDBC
+ devuelve cuentas correctas de filas desde
<literal>executeBatch()</literal>
+ (usualmente es seguro activar esta opción).
Hibernate usará
+ DML en lote para versionar automáticamente los
datos.
+ Por defecto a <literal>false</literal>.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>true</literal> |
<literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.factory_class</literal>
+ </entry>
+ <entry>
+ Selecciona un <literal>Batcher</literal>
personalizado.
+ La mayoría de las aplicaciones no
necesitarán
+ esta propiedad de configuración.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+
<literal>classname.of.BatcherFactory</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+
<literal>hibernate.jdbc.use_scrollable_resultset</literal>
+ </entry>
+ <entry>
+ Habilita el uso de resultados scrollables de JDBC2 por
Hibernate.
+ Esta propiedad sólo es necesaria cuando se usan
conexiones
+ JDBC provistas por el usuario, en caso contrario Hibernate
usa los
+ metadatos de conexión.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>true</literal> |
<literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+
<literal>hibernate.jdbc.use_streams_for_binary</literal>
+ </entry>
+ <entry>
+ Usa flujos (streams) al escribir/leer tipos
+ <literal>binary</literal> o
<literal>serializable</literal>
+ a/desde JDBC (propiedad a nivel de sistema).
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>true</literal> |
<literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+
<literal>hibernate.jdbc.use_get_generated_keys</literal>
+ </entry>
+ <entry>
+ Habilita el uso de
<literal>PreparedStatement.getGeneratedKeys()</literal>
+ de JDBC3 para traer claves generadas nativamente
después de insertar.
+ Requiere un driver JDBC3+ y un JRE1.4+.
Establécela a false si tu
+ driver tiene problemas con los generadores de identificador
de Hibernate.
+ Por defecto, se intenta determinar las capacidades del driver
usando los
+ metadatos de conexión.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+
<literal>hibernate.connection.provider_class</literal>
+ </entry>
+ <entry>
+ EL nombre de clase de un
<literal>ConnectionProvider</literal> personalizado
+ que provea conexiones JDBC a Hibernate.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+
<literal>classname.of.ConnectionProvider</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.isolation</literal>
+ </entry>
+ <entry>
+ Establece el nivel de aislamiento de transacción
JDBC.
+ Comprueba <literal>java.sql.Connection</literal> para
valores
+ significativos pero observa que la mayoría de las
bases de
+ datos no soportan todos los niveles de aislamiento.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>1, 2, 4, 8</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+
<literal>hibernate.connection.autocommit</literal>
+ </entry>
+ <entry>
+ Habilita compromiso automático (autocommit) para
+ las conexiones JDBC en pool (no recomendado).
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>true</literal> |
<literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+
<literal>hibernate.connection.release_mode</literal>
+ </entry>
+ <entry>
+ Especifica cuándo Hibernate debe liberar las
conexiones JDBC.
+ Por defecto, una conexión JDBC es retenida hasta
que la sesión
+ es cerrada explícitamente o desconectada. Para un
datasource JTA
+ del servidor de aplicaciones, debes usar
<literal>after_statement</literal>
+ para liberar agresivamente las conexiones después
de cada llamada
+ JDBC. Para una conexión no JTA, frecuentemente
tiene sentido liberar
+ la conexión al final de cada
transacción, usando
+ <literal>after_transaction</literal>.
<literal>auto</literal> eligirá
+ <literal>after_statement</literal> para las
estrategias JTA o CMT
+ de transacción y
<literal>after_transaction</literal> para la
+ estrategia JDBC de transacción.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>on_close</literal> (por defecto)|
<literal>after_transaction</literal> |
+ <literal>after_statement</literal> |
<literal>auto</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+
<literal>hibernate.connection.<emphasis><propertyName></emphasis></literal>
+ </entry>
+ <entry>
+ Pasa la propiedad JDBC
<literal>propertyName</literal>
+ a <literal>DriverManager.getConnection()</literal>.
+ </entry>
+ </row>
+ <row>
+ <entry>
+
<literal>hibernate.jndi.<emphasis><propertyName></emphasis></literal>
+ </entry>
+ <entry>
+ Pasa la propiedad
<literal>propertyName</literal>
+ a <literal>InitialContextFactory</literal> de
JNDI.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-cache-properties"
revision="7">
+ <title>Propiedades de Caché de Hibernate</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nombre de propiedad</entry>
+ <entry>Propósito</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+
<literal>hibernate.cache.provider_class</literal>
+ </entry>
+ <entry>
+ El nombre de clase de un
<literal>CacheProvider</literal> personalizado.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+
<literal>classname.of.CacheProvider</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+
<literal>hibernate.cache.use_minimal_puts</literal>
+ </entry>
+ <entry>
+ Optimiza la operación del caché de
segundo nivel
+ para minimizar escrituras, al costo de lecturas
más frecuentes.
+ Esto es más útil para cachés
en cluster y,
+ en Hibernate3, está habilitado por defecto para
implementaciones
+ de caché en cluster.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+
<literal>hibernate.cache.use_query_cache</literal>
+ </entry>
+ <entry>
+ Habilita el caché de lectura, consultas
individuales todavía
+ tienen que ponerse cachables.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+
<literal>hibernate.cache.use_second_level_cache</literal>
+ </entry>
+ <entry>
+ Puede ser usado para deshabilitar completamente el
caché
+ de segundo nivel, que está habilitado por defecto
para clases
+ que especifican un mapeo
<literal><cache></literal>.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+
<literal>hibernate.cache.query_cache_factory</literal>
+ </entry>
+ <entry>
+ El nombre de clase de una interface
<literal>QueryCache</literal>
+ personalizada, por defecto al
<literal>StandardQueryCache</literal>
+ prefabricado.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>classname.of.QueryCache</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.region_prefix</literal>
+ </entry>
+ <entry>
+ Un prefijo a usar para los nombres de región
+ del caché de segundo nivel.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>prefix</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+
<literal>hibernate.cache.use_structured_entries</literal>
+ </entry>
+ <entry>
+ Fuerza a Hibernate a almacenar los datos en el
caché
+ de segundo nivel en un formato más amigable al
humano.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot"
id="configuration-transaction-properties" revision="8">
+ <title>Propiedades de Transacción de
Hibernate</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nombre de propiedad</entry>
+ <entry>Propósito</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+
<literal>hibernate.transaction.factory_class</literal>
+ </entry>
+ <entry>
+ El nombre de clase de un
<literal>TransactionFactory</literal>
+ a usar con la API de
<literal>Transaction</literal> de Hibernate
+ (por defectoa
<literal>JDBCTransactionFactory</literal>).
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+
<literal>classname.of.TransactionFactory</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>jta.UserTransaction</literal>
+ </entry>
+ <entry>
+ Un nombre JNDI usado por
<literal>JTATransactionFactory</literal> para
+ obtener la <literal>UserTransaction</literal> JTA
del servidor
+ de aplicaciones.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>jndi/composite/name</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+
<literal>hibernate.transaction.manager_lookup_class</literal>
+ </entry>
+ <entry>
+ El nombre de clase de un
<literal>TransactionManagerLookup</literal>
+ requerido cuando el chaché a nivel de JVM
está
+ habilitado o cuando se usa un generador alto/bajo en un
+ entorno JTA.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+
<literal>classname.of.TransactionManagerLookup</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+
<literal>hibernate.transaction.flush_before_completion</literal>
+ </entry>
+ <entry>
+ De habilitarse, la sesión se limpiará
(flushed)
+ automáticamente durante la fase previa a la
compleción
+ de la transacción. (Muy útil cuando se
usa Hibernate
+ con CMT).
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>true</literal> |
<literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+
<literal>hibernate.transaction.auto_close_session</literal>
+ </entry>
+ <entry>
+ De habilitarse, la sesión será cerrada
automáticamente
+ durante la fase posterior a la compleción de la
transacción.
+ (Muy útil cuando se usa Hibernate con CMT).
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>true</literal> |
<literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-misc-properties"
revision="7">
+ <title>Propiedades Misceláneas</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nombre de propiedad</entry>
+ <entry>Propósito</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.query.factory_class</literal>
+ </entry>
+ <entry>
+ Elige la implementación de parser HQL.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+
<literal>org.hibernate.hql.ast.ASTQueryTranslatorFactory</literal> or
+
<literal>org.hibernate.hql.classic.ClassicQueryTranslatorFactory</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.query.substitutions</literal>
+ </entry>
+ <entry>
+ Mapeos de símbolos en consultas Hibernate a
+ símbolos SQL. (los símbolos puedem ser
+ nombres de función o literales, por ejemplo).
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>hqlLiteral=SQL_LITERAL,
hqlFunction=SQLFUNC</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.hbm2ddl.auto</literal>
+ </entry>
+ <entry>
+ Exporta automáticamente DDL de esquema cuando
+ al crear la <literal>SessionFactory</literal>.
+ Con <literal>create-drop</literal>, el esquema
de
+ base de datos será desechado cuando la
+ <literal>SessionFactory</literal> se cierre
explícitamente.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>update</literal> |
<literal>create</literal> | <literal>create-drop</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+
<literal>hibernate.cglib.use_reflection_optimizer</literal>
+ </entry>
+ <entry>
+ Habilita el uso de CGLIB en vez de refleccón en
tiempo de
+ ejecución (propiedad a nivel de sistema). La
reflección
+ a veces puede ser útil ante la
aparición de problemas.
+ Observa que Hibernate siempre requiere CGLIB incluso si
desactivas
+ el optimizador. No puedes establecer esta propiedad en
+ <literal>hibernate.cfg.xml</literal>.
+ <para>
+ <emphasis
role="strong">ej.</emphasis>
+ <literal>true</literal> |
<literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <sect2 id="configuration-optional-dialects"
revision="1">
+ <title>SQL Dialects</title>
+
+ <para>
+ You should always set the
<literal>hibernate.dialect</literal> property to the correct
+ <literal>org.hibernate.dialect.Dialect</literal> subclass for
your database. If you
+ specify a dialect, Hibernate will use sensible defaults for some of the
+ other properties listed above, saving you the effort of specifying them
manually.
+ </para>
+
+ <table frame="topbot" id="sql-dialects"
revision="2">
+ <title>Dialectos SQL de
Hibernate(<literal>hibernate.dialect</literal>)</title>
+ <tgroup cols="2">
+ <colspec colwidth="1*"/>
+ <colspec colwidth="2.5*"/>
+ <thead>
+ <row>
+ <entry>RDBMS</entry>
+ <entry>Dialecto</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>DB2</entry>
<entry><literal>org.hibernate.dialect.DB2Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>DB2 AS/400</entry>
<entry><literal>org.hibernate.dialect.DB2400Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>DB2 OS390</entry>
<entry><literal>org.hibernate.dialect.DB2390Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>PostgreSQL</entry>
<entry><literal>org.hibernate.dialect.PostgreSQLDialect</literal></entry>
+ </row>
+ <row>
+ <entry>MySQL</entry>
<entry><literal>org.hibernate.dialect.MySQLDialect</literal></entry>
+ </row>
+ <row>
+ <entry>MySQL con InnoDB</entry>
<entry><literal>org.hibernate.dialect.MySQLInnoDBDialect</literal></entry>
+ </row>
+ <row>
+ <entry>MySQL con MyISAM</entry>
<entry><literal>org.hibernate.dialect.MySQLMyISAMDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Oracle (cualquier
versión)</entry>
<entry><literal>org.hibernate.dialect.OracleDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Oracle 9i/10g</entry>
<entry><literal>org.hibernate.dialect.Oracle9Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>Sybase</entry>
<entry><literal>org.hibernate.dialect.SybaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Sybase Anywhere</entry>
<entry><literal>org.hibernate.dialect.SybaseAnywhereDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Microsoft SQL Server</entry>
<entry><literal>org.hibernate.dialect.SQLServerDialect</literal></entry>
+ </row>
+ <row>
+ <entry>SAP DB</entry>
<entry><literal>org.hibernate.dialect.SAPDBDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Informix</entry>
<entry><literal>org.hibernate.dialect.InformixDialect</literal></entry>
+ </row>
+ <row>
+ <entry>HypersonicSQL</entry>
<entry><literal>org.hibernate.dialect.HSQLDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Ingres</entry>
<entry><literal>org.hibernate.dialect.IngresDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Progress</entry>
<entry><literal>org.hibernate.dialect.ProgressDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Mckoi SQL</entry>
<entry><literal>org.hibernate.dialect.MckoiDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Interbase</entry>
<entry><literal>org.hibernate.dialect.InterbaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Pointbase</entry>
<entry><literal>org.hibernate.dialect.PointbaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>FrontBase</entry>
<entry><literal>org.hibernate.dialect.FrontbaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Firebird</entry>
<entry><literal>org.hibernate.dialect.FirebirdDialect</literal></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-outerjoin"
revision="4">
+ <title>Recuperación por Unión Externa (Outer Join
Fetching)</title>
+
+ <para>
+ Si tu base de datos soporta uniones externas del estilo ANSI, Oracle o
Sybase, la
+ <emphasis>recuperación por unión
externa</emphasis> aumentará
+ frecuentemente el rendimiento limitando el número de llamadas
a la base de datos
+ (al costo de más trabajo posiblemente realizado por la base de
datos misma).
+ La recuperación por unión externa permite que un
grafo completo de objetos
+ conectados por asociaciones muchos-a-uno, uno-a-muchos, muchos-a-muchos y
uno-a-uno sea
+ traído en una sola <literal>SELECT</literal> SQL.
+ </para>
+
+ <para>
+ La recuperación por unión externa puede ser
deshabilitada
+ <emphasis>globalmente</emphasis> estableciendo la propiedad
+ <literal>hibernate.max_fetch_depth</literal> a
<literal>0</literal>.
+ Un valor de <literal>1</literal> o mayor habilita la
recuperación
+ por unión externa para asociaciones uno-a-uno y muchos-a-uno
que
+ hayan sido mapeadas con
<literal>fetch="join"</literal>.
+ </para>
+
+ <para>
+ Ver <xref linkend="performance-fetching"/> para
más información.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-binarystreams"
revision="1">
+ <title>Flujos Binarios</title>
+
+ <para>
+ Oracle limita el tamaño de arrays de
<literal>byte</literal>
+ que puedan ser pasados a/desde su driver JDBC. Si deseas usar instancias
+ grandes de tipo <literal>binary</literal> o
<literal>serializable</literal>,
+ debes habilitar
<literal>hibernate.jdbc.use_streams_for_binary</literal>.
+ <emphasis>Esta es una propiedad a nivel de sistema
solamente.</emphasis>
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-cacheprovider"
revision="2">
+ <title>Caché de segundo nivel y de lectura</title>
+
+ <para>
+ Las propiedades prefijadas por
<literal>hibernate.cache</literal>
+ te permiten usar un sistema de caché de segundo nivel
+ en el ámbito de un proceso o cluster con Hibernate.
+ Ver <xref linkend="performance-cache"/> para
más detalles.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-querysubstitution">
+ <title>Sustitución de Lenguaje de Consulta</title>
+
+ <para>
+ Puedes definir nuevos símbolos de consulta de Hibernate
usando
+ <literal>hibernate.query.substitutions</literal>. Por
ejemplo:
+ </para>
+
+ <programlisting>hibernate.query.substitutions true=1,
false=0</programlisting>
+
+ <para>
+ causaría que los símbolos
<literal>true</literal> y <literal>false</literal> sean
+ traducidos a literales enteros en el SQL generado.
+ </para>
+
+ <programlisting>hibernate.query.substitutions
toLowercase=LOWER</programlisting>
+
+ <para>
+ te permitiría renombrar la función
<literal>LOWER</literal> de SQL.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-statistics"
revision="2">
+ <title>Hibernate statistics</title>
+
+ <para>
+ Si habilitas
<literal>hibernate.generate_statistics</literal>, Hibernate
+ expondrá un número de métricas que son
útiles
+ al afinar un sistema en ejecución vía
+ <literal>SessionFactory.getStatistics()</literal>. Hibernate
puede incluso ser
+ configurado para exponer estas estadísticas vía
JMX. Lee el
+ Javadoc de las interfaces en
<literal>org.hibernate.stats</literal>
+ para más información.
+ </para>
+
+ </sect2>
+ </sect1>
+
+ <sect1 id="configuration-logging">
+ <title>Registros de mensajes (Logging)</title>
+
+ <para>
+ Hibernate registra varios eventos usando commons-logging
+ de Apache.
+ </para>
+
+ <para>
+ El servicio de commons-logging saldrá directamente ya sea a
+ Log4J (si incluyes <literal>log4j.jar</literal> in your
classpath) o
+ JDK1.4 logging (al ejecutar bajo JDK1.4 o superior). Puedes descargar
+ Log4J desde <literal>http://logging.apache.org</literal>. Para
usar
+ Log4J necesitarás colocar un fichero
<literal>log4j.properties</literal>
+ en tu classpath. Un fichero de propiedades de ejemplo se distribuye con
+ Hibernate en el directorio <literal>src/</literal>.
+ </para>
+
+ <para>
+ Recomendamos fuertemente que te familiarices con los registros de mensajes
+ de Hibernate. Se ha puesto un gran trabajo en hacer los registros de
Hibernate
+ tan detallados como se puede, sin hacerlos ilegibles. Es un dispositivo
esencial
+ en la resolución de problemas. Las categorías de
registro
+ más interesantes son las siguientes:
+ </para>
+
+ <table frame="topbot" id="log-categories"
revision="2">
+ <title>Categorías de Registro de
Hibernate</title>
+ <tgroup cols="2">
+ <colspec colwidth="1*"/>
+ <colspec colwidth="2.5*"/>
+ <thead>
+ <row>
+ <entry>Categoría</entry>
+ <entry>Función</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+
<entry><literal>org.hibernate.SQL</literal></entry>
+ <entry>Registra todas las sentencias DML de SQL a
+ medida que se ejecutan</entry>
+ </row>
+ <row>
+
<entry><literal>org.hibernate.type</literal></entry>
+ <entry>Registra todos los parámetros
JDBC</entry>
+ </row>
+ <row>
+
<entry><literal>org.hibernate.tool.hbm2ddl</literal></entry>
+ <entry>Registra todas las sentencias DDL de SQL a
+ medida que se ejecutan</entry>
+ </row>
+ <row>
+
<entry><literal>org.hibernate.pretty</literal></entry>
+ <entry>
+ Registra el estado de todas las entidades
(máximo
+ de 20 entidades) asociadas con la sesión en
tiempo
+ de limpieza (flush)
+ </entry>
+ </row>
+ <row>
+
<entry><literal>org.hibernate.cache</literal></entry>
+ <entry>Registra toda la actividad del caché
de segundo nivel</entry>
+ </row>
+ <row>
+
<entry><literal>org.hibernate.transaction</literal></entry>
+ <entry>Registra la actividad relacionada con la
transacción</entry>
+ </row>
+ <row>
+
<entry><literal>org.hibernate.jdbc</literal></entry>
+ <entry>Registra toda adquisición de recursos
JDBC</entry>
+ </row>
+ <row>
+
<entry><literal>org.hibernate.hql.ast</literal></entry>
+ <entry>Regista los ASTs de HQL y SQL, así
como
+ otra información sobre análisis de
consultas.</entry>
+ </row>
+ <row>
+
<entry><literal>org.hibernate.secure</literal></entry>
+ <entry>Registra todas las peticiones de
autorización JAAS</entry>
+ </row>
+ <row>
+
<entry><literal>org.hibernate</literal></entry>
+ <entry>
+ Registra todo (mucha información, pero muy
útil
+ para la resolución de problemas)
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Al desarrollar aplicacinoes con Hibernate, casi siempre debes trabajar con
+ <literal>debug</literal> habilitado para la categoría
<literal>org.hibernate.SQL</literal>
+ o, alternativamente, la propiedad
<literal>hibernate.show_sql</literal> habilitada.
+ </para>
+
+
+ </sect1>
+
+ <sect1 id="configuration-namingstrategy">
+ <title>Implementando una
<literal>NamingStrategy</literal></title>
+
+ <para>
+ La interface <literal>org.hibernate.cfg.NamingStrategy</literal>
te permite
+ especificar un "estándar de nombrado" para objetos de la
base de datos
+ y elementos de esquema.
+ </para>
+
+ <para>
+ Puedes proveer reglas para generar automáticamente identificadores
de
+ base de datos a partir de identificadores JDBC o para procesar nombres
+ "lógicos" de columnas y tablas dados en el fichero de
mapeo en nombres
+ "físicos" de columnas y tablas. Esta funcionalidad ayuda
a reducir
+ la verborragia del documento de mapeo, eliminando ruido repetitivo
+ (prefijos <literal>TBL_</literal>, por ejemplo). La estrategia
por defecto
+ usada por Hibernate mínima en absoluto.
+ </para>
+
+ <para>
+ Puedes especificar una estrategia diferente llamando a
+ <literal>Configuration.setNamingStrategy()</literal> antes de
agregar los mapeos:
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sf = new Configuration()
+ .setNamingStrategy(ImprovedNamingStrategy.INSTANCE)
+ .addFile("Item.hbm.xml")
+ .addFile("Bid.hbm.xml")
+ .buildSessionFactory();]]></programlisting>
+
+ <para>
+ <literal>org.hibernate.cfg.ImprovedNamingStrategy</literal>
+ es una estrategia prefabricada que puede ser un punto de
+ partida útil para algunas aplicaciones.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-xmlconfig" revision="2">
+ <title>Fichero de configuración XML</title>
+
+ <para>
+ Un enfoque alternativo de configuración es especificar una
+ configuración completa en un fichero llamado
<literal>hibernate.cfg.xml</literal>.
+ Este fichero puede ser usado como un remplazo del fichero
<literal>hibernate.properties</literal> o,
+ si ambos están presentes, para sobrescribir propiedades.
+ </para>
+
+ <para>
+ El fichero de configuración XML se espera por defecto en la
+ raíz o tu <literal>CLASSPATH</literal>. He
aquí un ejemplo:
+ </para>
+
+ <programlisting><![CDATA[<?xml version='1.0'
encoding='utf-8'?>
+<!DOCTYPE hibernate-configuration PUBLIC
+ "-//Hibernate/Hibernate Configuration DTD//EN"
+ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
+
+<hibernate-configuration>
+
+ <!-- a SessionFactory instance listed as /jndi/name -->
+ <session-factory
+ name="java:hibernate/SessionFactory">
+
+ <!-- properties -->
+ <property
name="connection.datasource">java:/comp/env/jdbc/MyDB</property>
+ <property
name="dialect">org.hibernate.dialect.MySQLDialect</property>
+ <property name="show_sql">false</property>
+ <property name="transaction.factory_class">
+ org.hibernate.transaction.JTATransactionFactory
+ </property>
+ <property
name="jta.UserTransaction">java:comp/UserTransaction</property>
+
+ <!-- mapping files -->
+ <mapping resource="org/hibernate/auction/Item.hbm.xml"/>
+ <mapping resource="org/hibernate/auction/Bid.hbm.xml"/>
+
+ <!-- cache settings -->
+ <class-cache class="org.hibernate.auction.Item"
usage="read-write"/>
+ <class-cache class="org.hibernate.auction.Bid"
usage="read-only"/>
+ <collection-cache collection="org.hibernate.auction.Item.bids"
usage="read-write"/>
+
+ </session-factory>
+
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ Como puedes ver, la ventaja de este enfoque es la externalización
de los
+ nombres de los fichero de mapeo a configuración. El
+ <literal>hibernate.cfg.xml</literal> es también
más conveniente
+ una vez que hayas afinado el caché de Hibernate. Observa que
elección
+ tuya usar ya sea <literal>hibernate.properties</literal> o
+ <literal>hibernate.cfg.xml</literal>, ambos son equivalentes,
excepto por los
+ beneficios de usar la sintaxis XML arriba mencionados.
+ </para>
+
+ <para>
+ Con la configuración XML, arrancar Hibernate es tan simple como
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sf = new
Configuration().configure().buildSessionFactory();]]></programlisting>
+
+ <para>
+ Puedes tomar un fichero XML diferente usando
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sf = new Configuration()
+ .configure("catdb.cfg.xml")
+ .buildSessionFactory();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="configuration-j2ee" revision="1">
+ <title>Integració con Servidores de Aplicaciones
J2EE</title>
+
+ <para>
+ Hibernate tiene los siguientes puntos de integración con la
+ infraestructura J2EE:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Datasources manejados por contenedor</emphasis>:
Hibernate puede usar
+ conexiones JDBC manejadas por el contenedor y provistas a
través de JNDI.
+ Usualmente, un <literal>TransactionManager</literal>
compatible con JTA y un
+ <literal>ResourceManager</literal> cuidan del manejo de
transacciones (CMT),
+ esp. manejo de transacciones distribuídas a través
de varios
+ datasources. Puedes también, por supuesto, demarcar los
límites
+ de las transacciones programáticamente (BMT) o
podrías querer usar
+ para esto la API opcional de <literal>Transaction</literal>
de Hibernate para
+ mantener tu código portable.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Ligamento Automático JNDI</emphasis>:
Hibernate puede ligar sus
+ <literal>SessionFactory</literal> a JNDI después
del arranque.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Ligamento de Sesión JTA:</emphasis>
+ La <literal>Session</literal> de Hibernate puede ser ligada
automáticamente
+ al ámbito de transacciones JTA si usas EJBs. Simplemente busca
la
+ <literal>SessionFactory</literal> de JNDI y obtén
la <literal>Session</literal>
+ actual. Deja que Hibernate cuide de limpiar y cerrar la
<literal>Session</literal>
+ cuando se complete tu transacción JTA. La
demarcación de transacción
+ es declarativa, en descriptores de despliegue de EJB.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Despliegue JMX:</emphasis> Si tienes un servidor de
aplicaciones capaz
+ de JMX (por ejemplo, JBoss AS), puedes optar por desplegar Hibernate como
un MBean
+ manejado. Esto te ahorra el código de una línea de
arranque para
+ construir tu <literal>SessionFactory</literal> desde una
<literal>Configuration</literal>.
+ El contenedor arrancará tu
<literal>HibernateService</literal>, e idealmente también
+ cuidará de las dependencias entre servicios (El datasource
debe estar
+ disponible antes que arranque Hibernate, etc).
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Dependiendo de tu entorno, podrías tener que establecer la
opción
+ de configuración
<literal>hibernate.connection.aggressive_release</literal>
+ a true si tu servidor de aplicaciones muestra excepciones "connection
containment".
+ </para>
+
+ <sect2 id="configuration-optional-transactionstrategy"
revision="3">
+ <title>Configuración de la estrategia de
transacción</title>
+
+ <para>
+ La API de <literal>Session</literal> de Hibernate es
independiente de cualquier
+ demarcación de transacción en tu arquitectura. Si
dejas que Hibernate
+ use JDBC directamente, a través de un pool de conexiones.
puedes comenzar y
+ acabar tus transacciones llamando la API de JDBC. Si ejecutas en un
servidor de
+ aplicaciones J2EE, podréas querer usar transacciones manejadas
por bean y
+ llamar la API de JTA y <literal>UserTransaction</literal>
cuando sea necesario.
+ </para>
+
+ <para>
+ Para mantener tu código portable entre estos dos (y otros)
entornos recomendamos la API
+ de <literal>Transaction</literal> de Hibernate, que envuelve
y oculta el sistema subyacente.
+ Tienes que especificar una clase fábrica para las instancias
de <literal>Transaction</literal>
+ estableciendo la propiedad de configuración
<literal>hibernate.transaction.factory_class</literal>
+ de Hibernate.
+ </para>
+
+ <para>
+ Hay tres elecciones estándar (prefabricadas):
+ </para>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+
<term><literal>org.hibernate.transaction.JDBCTransactionFactory</literal></term>
+ <listitem>
+ <para>delega a transacciones de base de datos (JDBC) (por
defecto)</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+
<term><literal>org.hibernate.transaction.JTATransactionFactory</literal></term>
+ <listitem>
+ <para>
+ delega a transacciones manejadas por contenedor si una
transacción
+ existente estó por debajo en este contexto (ej.
método de un
+ bean de sesión EJB), en otro caso una nueva
transacción es
+ comenzada y se usan transacciones manejadas por bean.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+
<term><literal>org.hibernate.transaction.CMTTransactionFactory</literal></term>
+ <listitem>
+ <para>delega a transacciones JTA manejadas por
contenedor</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ Puedes definir también tus propias estrategias de
transacción
+ (para un servicio de transacción CORBA, por ejemplo).
+ </para>
+
+ <para>
+ Algunas funcionalidades en Hibernate (ej, el caché de segundo
nivel, ligamento
+ automático de JTA y Session, etc.) requieren acceso al
<literal>TransactionManager</literal>
+ de JTA en un entorno manejado. En un servidor de aplicaciones tienes que
especificar
+ cómo Hibernate debe obtener una referencia al
<literal>TransactionManager</literal>,
+ pues J2EE no estandariza un solo mecanismo:
+ </para>
+
+ <table frame="topbot" id="jtamanagerlookup"
revision="1">
+ <title>TransactionManagers de JTA</title>
+ <tgroup cols="2">
+ <colspec colwidth="2.5*"/>
+ <colspec colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Transaction Factory</entry>
+ <entry align="center">Servidor de
Aplicaciones</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+
<entry><literal>org.hibernate.transaction.JBossTransactionManagerLookup</literal></entry>
+ <entry align="center">JBoss</entry>
+ </row>
+ <row>
+
<entry><literal>org.hibernate.transaction.WeblogicTransactionManagerLookup</literal></entry>
+ <entry align="center">Weblogic</entry>
+ </row>
+ <row>
+
<entry><literal>org.hibernate.transaction.WebSphereTransactionManagerLookup</literal></entry>
+ <entry
align="center">WebSphere</entry>
+ </row>
+ <row>
+
<entry><literal>org.hibernate.transaction.WebSphereExtendedJTATransactionLookup</literal></entry>
+ <entry align="center">WebSphere
6</entry>
+ </row>
+ <row>
+
<entry><literal>org.hibernate.transaction.OrionTransactionManagerLookup</literal></entry>
+ <entry align="center">Orion</entry>
+ </row>
+ <row>
+
<entry><literal>org.hibernate.transaction.ResinTransactionManagerLookup</literal></entry>
+ <entry align="center">Resin</entry>
+ </row>
+ <row>
+
<entry><literal>org.hibernate.transaction.JOTMTransactionManagerLookup</literal></entry>
+ <entry align="center">JOTM</entry>
+ </row>
+ <row>
+
<entry><literal>org.hibernate.transaction.JOnASTransactionManagerLookup</literal></entry>
+ <entry align="center">JOnAS</entry>
+ </row>
+ <row>
+
<entry><literal>org.hibernate.transaction.JRun4TransactionManagerLookup</literal></entry>
+ <entry align="center">JRun4</entry>
+ </row>
+ <row>
+
<entry><literal>org.hibernate.transaction.BESTransactionManagerLookup</literal></entry>
+ <entry align="center">Borland
ES</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-jndi" revision="2">
+ <title><literal>SessionFactory</literal> ligada a
JNDI</title>
+
+ <para>
+ Una <literal>SessionFactory</literal> de Hibernate ligada a
JNDI puede simplificar
+ la obtención de la fábrica y la creación
de nuevas
+ <literal>Session</literal>s. Observa que esto no
está relacionado a un
+ <literal>Datasource</literal> ligado a JNDI, simplemente
ambos usan el mismo
+ registro!
+ </para>
+
+ <para>
+ Si deseas tener la <literal>SessionFactory</literal> ligada a
un espacio de nombres de JNDI,
+ especifica un nombre (ej.
<literal>java:hibernate/SessionFactory</literal>) usando la
+ propiedad <literal>hibernate.session_factory_name</literal>.
Si esta propiedad es omitida,
+ la <literal>SessionFactory</literal> no será
ligada a JNDI (Esto es especialmente
+ útil en entornos con una implementació JNDI de
sólo lectura por defecto,
+ ej. Tomcat.)
+ </para>
+
+ <para>
+ Al ligar la <literal>SessionFactory</literal> a JNDI,
Hibernate usará los valores de
+ <literal>hibernate.jndi.url</literal>,
<literal>hibernate.jndi.class</literal> para instanciar
+ un contexto inicial. Si étos no se especifican, se
usará el
+ <literal>InitialContext</literal> por defecto.
+ </para>
+
+ <para>
+ Hibernate colocará automáticamente la
<literal>SessionFactory</literal>
+ en JNDI después que llames a
<literal>cfg.buildSessionFactory()</literal>.
+ Esto significa que tendrás al menos esta llamada en
algún código
+ de arranque (o clase de utilidad) en tu aplicación, a menos
qie uses el despliegue
+ JMX con el <literal>HibernateService</literal> (discutido
luego).
+ </para>
+
+ <para>
+ Si usas una <literal>SessionFactory</literal> de JNDI, un EJB
o cualquier otra
+ clase puede obtener la <literal>SessionFactory</literal>
usando una búsqueda
+ JNDI. Observa que esta configuración no es necesaria si usas
la clase de ayuda
+ <literal>HibernateUtil</literal> introducida en el
capítulo uno, que actúa
+ como un registro Singleton. Sin embargo,
<literal>HibernateUtil</literal> es más
+ común en un entorno no manejado.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-j2ee-currentsession"
revision="1">
+ <title>Ligado automático de JTA y Session</title>
+
+ <para>
+ Para entornos no manejados hemos sugerido
<literal>HibernateUtil</literal> con una
+ <literal>SessionFactory</literal> estática, y
administración de la
+ <literal>Session</literal> de Hibernate. Este enfoque no es
fácil de usar
+ en un entorno EJB, al poder ejecutarse muchos EJBs dentro de la misma
transacción
+ pero no en la misma hebra. Recomendados que ligues la
<literal>SessionFactory</literal>
+ a JNDI en un entorno manejado.
+ </para>
+
+ <para>
+ En vez de rodar tu propia utilidad de
<literal>ThreadLocal</literal>,
+ usa el método
<literal>getCurrentSession()</literal> en la
+ <literal>SessionFactory</literal> para obtener una
<literal>Session</literal>
+ de Hibernate. Si no hubiese una <literal>Session</literal> de
Hibernate en la
+ transacción JTA actual, se arrancará y
asignará una.
+ Ambas opciones de configuración
<literal>hibernate.transaction.flush_before_completion</literal>
+ y
<literal>hibernate.transaction.auto_close_session</literal>, serán
establecidas
+ automáticamente para cada
<literal>Session</literal> que obtengas con
+ <literal>getCurrentSession()</literal>, de modo que
éstas serán
+ limpiadas (flushed) y cerradas automáticamente cuando el
contenedor complete
+ las transacciones JTA.
+ </para>
+
+ <para>
+ Si tu, por ejemplo, usas el patrón de diseño DAO
para escribir tu
+ capa de persistencia, todos los DAO's buscan la
<literal>SessionFactory</literal>
+ cuando se necesite y abren la sesión "actual". No
hay necesidad de pasar
+ las instancias de <literal>SessionFactory</literal> o
<literal>Session</literal>
+ alrededor entre el código de control y el código
DAO.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-j2ee-jmx" revision="1">
+ <title>Despliegue JMX</title>
+
+ <para>
+ La línea
<literal>cfg.buildSessionFactory()</literal> todavía tiene
+ que ser ejecutada en algun sitio para obtener una
<literal>SessionFactory</literal>
+ en JNDI. Puedes hacer esto bien en un bloque inicializador
<literal>static</literal>
+ (como aquel en <literal>HibernateUtil</literal>) o bien
despliegas Hibernate como un
+ <emphasis>servicio manejado</emphasis>.
+ </para>
+
+ <para>
+ Hibernate se distribuye con
<literal>org.hibernate.jmx.HibernateService</literal>
+ para despliegue en un servidor de aplicaciones con capacidades JMX, como
JBoss AS.
+ El despliegue y la configuracón reales son
específicos del vendedor.
+ He aquí un <literal>jboss-service.xml</literal> de
ejemplo para JBoss 4.0.x:
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<server>
+
+<mbean code="org.hibernate.jmx.HibernateService"
+ name="jboss.jca:service=HibernateFactory,name=HibernateFactory">
+
+ <!-- Required services -->
+ <depends>jboss.jca:service=RARDeployer</depends>
+ <depends>jboss.jca:service=LocalTxCM,name=HsqlDS</depends>
+
+ <!-- Bind the Hibernate service to JNDI -->
+ <attribute
name="JndiName">java:/hibernate/SessionFactory</attribute>
+
+ <!-- Datasource settings -->
+ <attribute name="Datasource">java:HsqlDS</attribute>
+ <attribute
name="Dialect">org.hibernate.dialect.HSQLDialect</attribute>
+
+ <!-- Transaction integration -->
+ <attribute name="TransactionStrategy">
+ org.hibernate.transaction.JTATransactionFactory</attribute>
+ <attribute name="TransactionManagerLookupStrategy">
+ org.hibernate.transaction.JBossTransactionManagerLookup</attribute>
+ <attribute
name="FlushBeforeCompletionEnabled">true</attribute>
+ <attribute name="AutoCloseSessionEnabled">true</attribute>
+
+ <!-- Fetching options -->
+ <attribute name="MaximumFetchDepth">5</attribute>
+
+ <!-- Second-level caching -->
+ <attribute name="SecondLevelCacheEnabled">true</attribute>
+ <attribute
name="CacheProviderClass">org.hibernate.cache.EhCacheProvider</attribute>
+ <attribute name="QueryCacheEnabled">true</attribute>
+
+ <!-- Logging -->
+ <attribute name="ShowSqlEnabled">true</attribute>
+
+ <!-- Mapping files -->
+ <attribute
name="MapResources">auction/Item.hbm.xml,auction/Category.hbm.xml</attribute>
+
+</mbean>
+
+</server>]]></programlisting>
+
+ <para>
+ Este fichero es desplegado en un directorio llamado
<literal>META-INF</literal> y
+ empaquetado en un fichero JAR con la extensión
<literal>.sar</literal>
+ (fichero de servicio). También necesitas empaquetar Hibernate,
sus bibliotecas
+ de terceros requeridas, tus clases persistentes compiladas,
así como tus ficheros de mapeo
+ en el mismo fichero. Tus beans de empresa (usualmente beans de
sesión) pueden ser
+ mantenidos en su propio fichero JAR, pero debes incluir este fichero EJB
JAR en el fichero
+ de servicio principal para obtener una unidad desplegable (en caliente).
Consulta la documentación
+ de JBoss AS para más información sobre el servicio
JMX y despliegue de EJB.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+</chapter>
+
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/content/events.xml (from
rev 12794, core/trunk/documentation/manual/es-ES/src/main/docbook/modules/events.xml)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/content/events.xml
(rev 0)
+++ core/trunk/documentation/manual/es-ES/src/main/docbook/content/events.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,233 @@
+<chapter id="events">
+ <title>Interceptores y eventos</title>
+
+ <para>
+ Frecuentemente es útil para la aplicación reaccionar a
ciertos eventos que ocurran dentro de Hibernate.
+ Esto permite la implementación de ciertos tipos de funcionalidade
genérica, y extensión de la
+ funcionalidad de Hibernate.
+ </para>
+
+ <sect1 id="objectstate-interceptors" revision="1">
+ <title>Interceptores</title>
+
+ <para>
+ La interface <literal>Interceptor</literal> provee callbacks
desde la sesión a la aplicación
+ permitiendo a ésta última inspeccionar y/o manipular
las propiedades de un objeto persistente
+ antes que sea salvado, actualizado, borrado o cargado. Un uso posible de esto
es seguir la pista
+ de información de auditoría. Por ejemplo, el siguiente
<literal>Interceptor</literal> establece
+ automáticamente el <literal>createTimestamp</literal>
cuando un <literal>Auditable</literal> es
+ creado y actualiza la propiedad
<literal>lastUpdateTimestamp</literal> cuando un
+ <literal>Auditable</literal> es acutalizado.
+ </para>
+
+ <programlisting><![CDATA[package org.hibernate.test;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Iterator;
+
+import org.hibernate.Interceptor;
+import org.hibernate.type.Type;
+
+public class AuditInterceptor implements Interceptor, Serializable {
+
+ private int updates;
+ private int creates;
+
+ public void onDelete(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+ // do nothing
+ }
+
+ public boolean onFlushDirty(Object entity,
+ Serializable id,
+ Object[] currentState,
+ Object[] previousState,
+ String[] propertyNames,
+ Type[] types) {
+
+ if ( entity instanceof Auditable ) {
+ updates++;
+ for ( int i=0; i < propertyNames.length; i++ ) {
+ if ( "lastUpdateTimestamp".equals( propertyNames[i] ) ) {
+ currentState[i] = new Date();
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean onLoad(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+ return false;
+ }
+
+ public boolean onSave(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+
+ if ( entity instanceof Auditable ) {
+ creates++;
+ for ( int i=0; i<propertyNames.length; i++ ) {
+ if ( "createTimestamp".equals( propertyNames[i] ) ) {
+ state[i] = new Date();
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public void postFlush(Iterator entities) {
+ System.out.println("Creations: " + creates + ", Updates: " +
updates);
+ }
+
+ public void preFlush(Iterator entities) {
+ updates=0;
+ creates=0;
+ }
+
+ ...
+
+}]]></programlisting>
+
+ <para>
+ El interceptor podría ser especificado cuando se crea la
sesión:
+ </para>
+
+ <programlisting><![CDATA[Session session = sf.openSession( new
AuditInterceptor() );]]></programlisting>
+
+ <para>
+ Puedes además establecer un interceptor a un nivel global, usando
la <literal>Configuration</literal>:
+ </para>
+
+ <programlisting><![CDATA[new Configuration().setInterceptor( new
AuditInterceptor() );]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="objectstate-events" revision="2">
+ <title>Sistema de eventos</title>
+
+ <para>
+ Si tienes que reaccionar a eventos particulares en tu capa de persistencia,
puedes también la
+ arquitectura de <emphasis>eventos</emphasis> de Hibernate3. El
sistema de eventos puede ser usado
+ en adición o como un remplazo a los interceptores.
+ </para>
+
+ <para>
+ Esencialmente todos los métodos de la interface
<literal>Session</literal> se correlacionan
+ con un evento. Tienes un <literal>LoadEvent</literal>, un
<literal>FlushEvent</literal>, etc
+ (consulta el DTD del fichero de configuración XML o el paquete
<literal>org.hibernate.event</literal>
+ para la lista completa de tipos de evento definidos). Cuando se hace una
petición de uno de estos
+ métodos, la <literal>Session</literal> de Hibernate
genera un evento apropiado y se lo pasa
+ al oyente (listener) de eventos configurado para ese tipo. De
fábrica, estos oyentes implementan
+ el mismo procesamiento en los que siempre resultan aquellos
métodos. Sin embargo, eres libre de
+ implementar una personalización de una de las interfaces oyentes
(es decir, el
+ <literal>LoadEvent</literal> es procesado por la
implementación registrada de la interface
+ <literal>LoadEventListener</literal>), en cuyo caso su
implementación sería responsable
+ de procesar cualquier petición
<literal>load()</literal> hecha a la <literal>Session</literal>.
+ </para>
+
+ <para>
+ Los oyentes deben ser considerados efectivamente singletons; quiere decir,
que son compartidos
+ entre las peticiones, y por lo tanto no guardan ningún estado en
variables de instancia.
+ </para>
+
+ <para>
+ Un oyente personalizado debe implementar la interface apropiada para el
evento que quiere procesar y/o
+ extender una de las clases base de conveniencia (o incluso los oyentes de
eventos por defecto
+ usados por Hibernate de fábrica al ser éstos declarados
non-final para este propósito). Los
+ oyentes personalizados pueden ser registrados programáticamente a
través del objeto
+ <literal>Configuration</literal>, o especificados en el XML de
configuración de Hibernate
+ (la declaración declarativa a través del fichero de
propiedades no está soportada).
+ He aquí un ejemplo de un oyente personalizado de eventos load:
+ </para>
+
+ <programlisting><![CDATA[public class MyLoadListener extends
DefaultLoadEventListener {
+ // this is the single method defined by the LoadEventListener interface
+ public Object onLoad(LoadEvent event, LoadEventListener.LoadType loadType)
+ throws HibernateException {
+ if ( !MySecurity.isAuthorized( event.getEntityClassName(), event.getEntityId() )
) {
+ throw MySecurityException("Unauthorized access");
+ }
+ return super.onLoad(event, loadType);
+ }
+}]]></programlisting>
+
+ <para>
+ Necesitas además una entrada de configuración
diciéndole a Hibernate que use el
+ oyente en vez del oyente por defecto:
+ </para>
+
+<programlisting><![CDATA[<hibernate-configuration>
+ <session-factory>
+ ...
+ <listener type="load" class="MyLoadListener"/>
+ </session-factory>
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ En cambio, puedes registrarlo programáticamente:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration();
+cfg.getSessionEventListenerConfig().setLoadEventListener( new MyLoadListener()
);]]></programlisting>
+
+ <para>
+ Los oyentes registrados declarativamente no pueden compartir instancias. Si
el mismo nombre de clase es
+ usado en múltiples elementos
<literal><listener/></literal>, cada referencia
resultará en una instancia
+ separada de esa clase. Si necesitas la capacidad de compartir instancias de
oyentes entre tipos de oyente
+ debes usar el enfoque de registración programática.
+ </para>
+
+ <para>
+ ¿Por qué implementar una interface y definir el tipo
espcífico durante la configuración?
+ Bueno, una implementación de oyente podría implementar
múltiples interfaces de oyente
+ de eventos. Teniendo el tipo definido adicionalmente durante la
registración lo hace más
+ fácil para activar o desactivar oyentes personalizados durante la
configuración.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-decl-security">
+ <title>Seguridad declarativa de Hibernate</title>
+ <para>
+ Usualmente, la seguridad declarativa en aplicaciones Hibernate es manejada en
una capa de fachada
+ de sesión. Ahora, Hibernate3 permite que ciertas acciones sean
permitidas vía JACC, y autorizadas vía
+ JAAS. Esta en una funcionalidad opcional construída encima de la
arquitectura de eventos.
+ </para>
+
+ <para>
+ Primero, debes configurar los oyentes de eventos apropiados, para habilitar
el uso de
+ autorización JAAS.
+ </para>
+
+ <programlisting><![CDATA[<listener type="pre-delete"
class="org.hibernate.secure.JACCPreDeleteEventListener"/>
+<listener type="pre-update"
class="org.hibernate.secure.JACCPreUpdateEventListener"/>
+<listener type="pre-insert"
class="org.hibernate.secure.JACCPreInsertEventListener"/>
+<listener type="pre-load"
class="org.hibernate.secure.JACCPreLoadEventListener"/>]]></programlisting>
+
+ <para>
+ Seguido, aún en <literal>hibernate.cfg.xml</literal>,
liga los permisos a roles:
+ </para>
+
+ <programlisting><![CDATA[<grant role="admin"
entity-name="User" actions="insert,update,read"/>
+<grant role="su" entity-name="User"
actions="*"/>]]></programlisting>
+
+ <para>
+ Los nombres de role son los roles entendidos por tu proveedor de JACC.
+ </para>
+
+ </sect1>
+
+</chapter>
+
Copied:
core/trunk/documentation/manual/es-ES/src/main/docbook/content/example_mappings.xml (from
rev 12794,
core/trunk/documentation/manual/es-ES/src/main/docbook/modules/example_mappings.xml)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/content/example_mappings.xml
(rev 0)
+++
core/trunk/documentation/manual/es-ES/src/main/docbook/content/example_mappings.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,654 @@
+<chapter id="example-mappings">
+ <title>Ejemplo: Varios Mapeos</title>
+
+ <para>
+ Este capítulo muestra mapeos de asociaciones más
complejos.
+ </para>
+
+ <sect1 id="example-mappings-emp">
+ <title>Empleador/Empleado</title>
+
+ <para>
+ El siguiente modelo de la relación entre
<literal>Employer</literal> y <literal>Employee</literal>
+ usa una clase de entidad real (<literal>Employment</literal>)
para representar la asociación.
+ Esto se ha hecho esto porque podría haber más de un
período de empleo para los mismos dos participantes.
+ Se usan componentes para modelar valores monetarios y nombres de empleado.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="../images/EmployerEmployee.gif"
format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../images/EmployerEmployee.gif"
format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ He aquí un documento de mapeo posible:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Employer" table="employers">
+ <id name="id">
+ <generator class="sequence">
+ <param name="sequence">employer_id_seq</param>
+ </generator>
+ </id>
+ <property name="name"/>
+ </class>
+
+ <class name="Employment" table="employment_periods">
+
+ <id name="id">
+ <generator class="sequence">
+ <param name="sequence">employment_id_seq</param>
+ </generator>
+ </id>
+ <property name="startDate" column="start_date"/>
+ <property name="endDate" column="end_date"/>
+
+ <component name="hourlyRate" class="MonetaryAmount">
+ <property name="amount">
+ <column name="hourly_rate" sql-type="NUMERIC(12,
2)"/>
+ </property>
+ <property name="currency" length="12"/>
+ </component>
+
+ <many-to-one name="employer" column="employer_id"
not-null="true"/>
+ <many-to-one name="employee" column="employee_id"
not-null="true"/>
+
+ </class>
+
+ <class name="Employee" table="employees">
+ <id name="id">
+ <generator class="sequence">
+ <param name="sequence">employee_id_seq</param>
+ </generator>
+ </id>
+ <property name="taxfileNumber"/>
+ <component name="name" class="Name">
+ <property name="firstName"/>
+ <property name="initial"/>
+ <property name="lastName"/>
+ </component>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Y he aquí el esquema de tablas generado por
<literal>SchemaExport</literal>.
+ </para>
+
+ <programlisting><![CDATA[create table employers (
+ id BIGINT not null,
+ name VARCHAR(255),
+ primary key (id)
+)
+
+create table employment_periods (
+ id BIGINT not null,
+ hourly_rate NUMERIC(12, 2),
+ currency VARCHAR(12),
+ employee_id BIGINT not null,
+ employer_id BIGINT not null,
+ end_date TIMESTAMP,
+ start_date TIMESTAMP,
+ primary key (id)
+)
+
+create table employees (
+ id BIGINT not null,
+ firstName VARCHAR(255),
+ initial CHAR(1),
+ lastName VARCHAR(255),
+ taxfileNumber VARCHAR(255),
+ primary key (id)
+)
+
+alter table employment_periods
+ add constraint employment_periodsFK0 foreign key (employer_id) references employers
+alter table employment_periods
+ add constraint employment_periodsFK1 foreign key (employee_id) references employees
+create sequence employee_id_seq
+create sequence employment_id_seq
+create sequence employer_id_seq]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-mappings-authorwork">
+ <title>Autor/Obra</title>
+
+ <para>
+ Considera el siguiente modelo de las relaciones entre
<literal>Work</literal>,
+ <literal>Author</literal> y
<literal>Person</literal>. Representamos la relación entre
<literal>Work</literal>
+ y <literal>Author</literal> como una asociación
muchos-a-muchos. Elegimos representar la relación entre
+ <literal>Author</literal> y <literal>Person</literal>
como una asociación uno-a-uno. Otra posibilidad
+ hubiese sido que <literal>Author</literal> extendiera
<literal>Person</literal>.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="../images/AuthorWork.gif"
format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../images/AuthorWork.gif"
format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ El siguiente documento de mapeo representa estas relaciones correctamente:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Work" table="works"
discriminator-value="W">
+
+ <id name="id" column="id">
+ <generator class="native"/>
+ </id>
+ <discriminator column="type" type="character"/>
+
+ <property name="title"/>
+ <set name="authors" table="author_work">
+ <key column name="work_id"/>
+ <many-to-many class="Author" column
name="author_id"/>
+ </set>
+
+ <subclass name="Book" discriminator-value="B">
+ <property name="text"/>
+ </subclass>
+
+ <subclass name="Song" discriminator-value="S">
+ <property name="tempo"/>
+ <property name="genre"/>
+ </subclass>
+
+ </class>
+
+ <class name="Author" table="authors">
+
+ <id name="id" column="id">
+ <!-- The Author must have the same identifier as the Person -->
+ <generator class="assigned"/>
+ </id>
+
+ <property name="alias"/>
+ <one-to-one name="person" constrained="true"/>
+
+ <set name="works" table="author_work"
inverse="true">
+ <key column="author_id"/>
+ <many-to-many class="Work" column="work_id"/>
+ </set>
+
+ </class>
+
+ <class name="Person" table="persons">
+ <id name="id" column="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Hay cuatro tablas en este mapeo. <literal>works</literal>,
<literal>authors</literal> y <literal>persons</literal>
+ tienen los datos de obra, autor y persona respectivamente.
<literal>author_work</literal> es una tabla de
+ asociación enlazando autores a obras. He aquí el esquema de
tablas, tal como fue generado por
+ <literal>SchemaExport</literal>.
+ </para>
+
+ <programlisting><![CDATA[create table works (
+ id BIGINT not null generated by default as identity,
+ tempo FLOAT,
+ genre VARCHAR(255),
+ text INTEGER,
+ title VARCHAR(255),
+ type CHAR(1) not null,
+ primary key (id)
+)
+
+create table author_work (
+ author_id BIGINT not null,
+ work_id BIGINT not null,
+ primary key (work_id, author_id)
+)
+
+create table authors (
+ id BIGINT not null generated by default as identity,
+ alias VARCHAR(255),
+ primary key (id)
+)
+
+create table persons (
+ id BIGINT not null generated by default as identity,
+ name VARCHAR(255),
+ primary key (id)
+)
+
+alter table authors
+ add constraint authorsFK0 foreign key (id) references persons
+alter table author_work
+ add constraint author_workFK0 foreign key (author_id) references authors
+alter table author_work
+ add constraint author_workFK1 foreign key (work_id) references
works]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-mappings-customerorderproduct">
+ <title>Cliente/Orden/Producto</title>
+
+ <para>
+ Ahora considera un modelo de las relaciones entre
<literal>Customer</literal>,
+ <literal>Order</literal> y
<literal>LineItem</literal> y <literal>Product</literal>.
+ Hay una asociación uno-a-muchos entre
<literal>Customer</literal> y <literal>Order</literal>,
+ pero, ¿cómo deberíamos representar
<literal>Order</literal> / <literal>LineItem</literal> /
<literal>Product</literal>?
+ He elegido mapear <literal>LineItem</literal> como una clase de
asociación representando la
+ asociación muchos-a-muchos entre
<literal>Order</literal> y <literal>Product</literal>. En
Hibernate,
+ esto se llama un elemento compuesto.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="../images/CustomerOrderProduct.gif"
format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../images/CustomerOrderProduct.gif"
format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ El documento de mapeo:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Customer" table="customers">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ <set name="orders" inverse="true">
+ <key column="customer_id"/>
+ <one-to-many class="Order"/>
+ </set>
+ </class>
+
+ <class name="Order" table="orders">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="date"/>
+ <many-to-one name="customer" column="customer_id"/>
+ <list name="lineItems" table="line_items">
+ <key column="order_id"/>
+ <list-index column="line_number"/>
+ <composite-element class="LineItem">
+ <property name="quantity"/>
+ <many-to-one name="product"
column="product_id"/>
+ </composite-element>
+ </list>
+ </class>
+
+ <class name="Product" table="products">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="serialNumber"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ <literal>customers</literal>, <literal>orders</literal>,
<literal>line_items</literal> y
+ <literal>products</literal> tienen los datos de cliente, orden,
ítem de línea de orden y producto
+ respectivamente. Además <literal>line_items</literal>
actúa como una tabla de asociación enlazando
+ órdenes con productos.
+ </para>
+
+ <programlisting><![CDATA[create table customers (
+ id BIGINT not null generated by default as identity,
+ name VARCHAR(255),
+ primary key (id)
+)
+
+create table orders (
+ id BIGINT not null generated by default as identity,
+ customer_id BIGINT,
+ date TIMESTAMP,
+ primary key (id)
+)
+
+create table line_items (
+ line_number INTEGER not null,
+ order_id BIGINT not null,
+ product_id BIGINT,
+ quantity INTEGER,
+ primary key (order_id, line_number)
+)
+
+create table products (
+ id BIGINT not null generated by default as identity,
+ serialNumber VARCHAR(255),
+ primary key (id)
+)
+
+alter table orders
+ add constraint ordersFK0 foreign key (customer_id) references customers
+alter table line_items
+ add constraint line_itemsFK0 foreign key (product_id) references products
+alter table line_items
+ add constraint line_itemsFK1 foreign key (order_id) references
orders]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="misc">
+ <title>Mapeos misceláneos de ejemplo</title>
+
+ <para>
+ Todos estos ejemplos están tomados de la batería de
pruebas de Hibernate.
+ Encontrarás muchos otros mapeos de ejemplo útiles
allí. Mira en la carpeta
+ <literal>test</literal> de la distribución de
Hibernate.
+ </para>
+
+ <para>POR HACER: poner palabras alrededor de este material</para>
+
+ <sect2 id="example-mappings-typed-onetone">
+ <title>Asociación uno-a-uno
"Tipificada"</title>
+<programlisting><![CDATA[<class name="Person">
+ <id name="name"/>
+ <one-to-one name="address"
+ cascade="all">
+ <formula>name</formula>
+ <formula>'HOME'</formula>
+ </one-to-one>
+ <one-to-one name="mailingAddress"
+ cascade="all">
+ <formula>name</formula>
+ <formula>'MAILING'</formula>
+ </one-to-one>
+</class>
+
+<class name="Address" batch-size="2"
+ check="addressType in ('MAILING', 'HOME',
'BUSINESS')">
+ <composite-id>
+ <key-many-to-one name="person"
+ column="personName"/>
+ <key-property name="type"
+ column="addressType"/>
+ </composite-id>
+ <property name="street" type="text"/>
+ <property name="state"/>
+ <property name="zip"/>
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-composite-key">
+ <title>Ejemplo de clave compuesta</title>
+<programlisting><![CDATA[<class name="Customer">
+
+ <id name="customerId"
+ length="10">
+ <generator class="assigned"/>
+ </id>
+
+ <property name="name" not-null="true"
length="100"/>
+ <property name="address" not-null="true"
length="200"/>
+
+ <list name="orders"
+ inverse="true"
+ cascade="save-update">
+ <key column="customerId"/>
+ <index column="orderNumber"/>
+ <one-to-many class="Order"/>
+ </list>
+
+</class>
+
+<class name="Order" table="CustomerOrder"
lazy="true">
+ <synchronize table="LineItem"/>
+ <synchronize table="Product"/>
+
+ <composite-id name="id"
+ class="Order$Id">
+ <key-property name="customerId" length="10"/>
+ <key-property name="orderNumber"/>
+ </composite-id>
+
+ <property name="orderDate"
+ type="calendar_date"
+ not-null="true"/>
+
+ <property name="total">
+ <formula>
+ ( select sum(li.quantity*p.price)
+ from LineItem li, Product p
+ where li.productId = p.productId
+ and li.customerId = customerId
+ and li.orderNumber = orderNumber )
+ </formula>
+ </property>
+
+ <many-to-one name="customer"
+ column="customerId"
+ insert="false"
+ update="false"
+ not-null="true"/>
+
+ <bag name="lineItems"
+ fetch="join"
+ inverse="true"
+ cascade="save-update">
+ <key>
+ <column name="customerId"/>
+ <column name="orderNumber"/>
+ </key>
+ <one-to-many class="LineItem"/>
+ </bag>
+
+</class>
+
+<class name="LineItem">
+
+ <composite-id name="id"
+ class="LineItem$Id">
+ <key-property name="customerId" length="10"/>
+ <key-property name="orderNumber"/>
+ <key-property name="productId" length="10"/>
+ </composite-id>
+
+ <property name="quantity"/>
+
+ <many-to-one name="order"
+ insert="false"
+ update="false"
+ not-null="true">
+ <column name="customerId"/>
+ <column name="orderNumber"/>
+ </many-to-one>
+
+ <many-to-one name="product"
+ insert="false"
+ update="false"
+ not-null="true"
+ column="productId"/>
+
+</class>
+
+<class name="Product">
+ <synchronize table="LineItem"/>
+
+ <id name="productId"
+ length="10">
+ <generator class="assigned"/>
+ </id>
+
+ <property name="description"
+ not-null="true"
+ length="200"/>
+ <property name="price" length="3"/>
+ <property name="numberAvailable"/>
+
+ <property name="numberOrdered">
+ <formula>
+ ( select sum(li.quantity)
+ from LineItem li
+ where li.productId = productId )
+ </formula>
+ </property>
+
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-composite-key-manytomany">
+ <title>Muchos-a-muchos con atributo de clave compuesta
compartido</title>
+<programlisting><![CDATA[<class name="User"
table="`User`">
+ <composite-id>
+ <key-property name="name"/>
+ <key-property name="org"/>
+ </composite-id>
+ <set name="groups" table="UserGroup">
+ <key>
+ <column name="userName"/>
+ <column name="org"/>
+ </key>
+ <many-to-many class="Group">
+ <column name="groupName"/>
+ <formula>org</formula>
+ </many-to-many>
+ </set>
+</class>
+
+<class name="Group" table="`Group`">
+ <composite-id>
+ <key-property name="name"/>
+ <key-property name="org"/>
+ </composite-id>
+ <property name="description"/>
+ <set name="users" table="UserGroup"
inverse="true">
+ <key>
+ <column name="groupName"/>
+ <column name="org"/>
+ </key>
+ <many-to-many class="User">
+ <column name="userName"/>
+ <formula>org</formula>
+ </many-to-many>
+ </set>
+</class>
+]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-content-discrimination">
+ <title>Discriminación basada en contenido</title>
+<programlisting><![CDATA[<class name="Person"
+ discriminator-value="P">
+
+ <id name="id"
+ column="person_id"
+ unsaved-value="0">
+ <generator class="native"/>
+ </id>
+
+
+ <discriminator
+ type="character">
+ <formula>
+ case
+ when title is not null then 'E'
+ when salesperson is not null then 'C'
+ else 'P'
+ end
+ </formula>
+ </discriminator>
+
+ <property name="name"
+ not-null="true"
+ length="80"/>
+
+ <property name="sex"
+ not-null="true"
+ update="false"/>
+
+ <component name="address">
+ <property name="address"/>
+ <property name="zip"/>
+ <property name="country"/>
+ </component>
+
+ <subclass name="Employee"
+ discriminator-value="E">
+ <property name="title"
+ length="20"/>
+ <property name="salary"/>
+ <many-to-one name="manager"/>
+ </subclass>
+
+ <subclass name="Customer"
+ discriminator-value="C">
+ <property name="comments"/>
+ <many-to-one name="salesperson"/>
+ </subclass>
+
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-association-alternatekeys" >
+ <title>Asociaciones sobre claves alternativas</title>
+<programlisting><![CDATA[<class name="Person">
+
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+
+ <property name="name" length="100"/>
+
+ <one-to-one name="address"
+ property-ref="person"
+ cascade="all"
+ fetch="join"/>
+
+ <set name="accounts"
+ inverse="true">
+ <key column="userId"
+ property-ref="userId"/>
+ <one-to-many class="Account"/>
+ </set>
+
+ <property name="userId" length="8"/>
+
+</class>
+
+<class name="Address">
+
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+
+ <property name="address" length="300"/>
+ <property name="zip" length="5"/>
+ <property name="country" length="25"/>
+ <many-to-one name="person" unique="true"
not-null="true"/>
+
+</class>
+
+<class name="Account">
+ <id name="accountId" length="32">
+ <generator class="uuid.hex"/>
+ </id>
+
+ <many-to-one name="user"
+ column="userId"
+ property-ref="userId"/>
+
+ <property name="type" not-null="true"/>
+
+</class>]]></programlisting>
+ </sect2>
+
+ </sect1>
+
+</chapter>
+
Copied:
core/trunk/documentation/manual/es-ES/src/main/docbook/content/example_parentchild.xml
(from rev 12794,
core/trunk/documentation/manual/es-ES/src/main/docbook/modules/example_parentchild.xml)
===================================================================
---
core/trunk/documentation/manual/es-ES/src/main/docbook/content/example_parentchild.xml
(rev 0)
+++
core/trunk/documentation/manual/es-ES/src/main/docbook/content/example_parentchild.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,362 @@
+<chapter id="example-parentchild">
+ <title>Ejemplo: Padre/Hijo</title>
+
+ <para>
+ Una de las primerísimas cosas que los usuarios nuevos intentan hacer
con Hibernate es modelar una relación de
+ tipo padre / hijo. Para esto hay dos enfoques diferentes. Por varias razones, el
enfoque más conveniente,
+ especialmente para usuarios nuevos, es modelar tanto
<literal>Parent</literal> como <literal>Child</literal>
+ como clases de entidad con una asociación
<literal><one-to-many></literal> desde
<literal>Parent</literal>
+ a <literal>Child</literal>. (El enfoque alternativo es declarar el
<literal>Child</literal> como un
+ <literal><composite-element></literal>.) Ahora, resulta
que la semántica por defecto de una asociación
+ uno a muchos (en Hibernate) es mucho menos cercana a la semántica
usual de una relación padre / hijo que aquellas
+ de un mapeo de elementos compuestos. Explicaremos cómo usar una
<emphasis>asociación uno a muchos bidireccional
+ con tratamiento en cascada</emphasis> para modelar una relación
padre / hijo eficiente y elegantemente.
+ ¡No es para nada difícil!
+ </para>
+
+ <sect1 id="example-parentchild-collections">
+ <title>Una nota sobre las colecciones</title>
+
+ <para>
+ Se considera que las colecciones de Hibernate son una parte lógica
de la entidad que las posee; nunca de
+ las entidades contenidas. ¡Esta es una distinción
crucial! Esto tiene las siguientes consecuencias:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ Cuando se quita / añade un objeto desde / a una
colección, se incrementa el número de versión del
+ dueño de la colección.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Si un objeto que fue quitado de una colección es una instancia
de un tipo de valor (por ejemplo, un
+ elemento compuesto), ese objeta cesará de ser persistente y su
estado será completamente quitado de la
+ base de datos. Asimismo, añadir una instancia de tipo de valor
a la colección causará que su estado
+ sea inmediatamente persistente.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Por otro lado, si se quita una entidad de una colección (una
asociación uno-a-muchos o muchos-a-muchos),
+ no será borrado, por defecto. Este comportamiento es
completamente consistente. ¡Un cambio en el
+ estado interno de otra entidad no hace desaparecer la entidad asociada!
Asimismo, añadir una entidad a
+ una colección no causa que la entidad se vuelva persistente,
por defecto.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ En cambio, el comportamiento por defecto es que al añadir una
entidad a una colección se crea meramente
+ un enlace entre las dos entidades, mientras que al quitarla se quita el
enlace. Esto es muy apropiado para
+ todos los tipos de casos. Donde no es para nada apropiado es en el caso de
una relación padre / hijo. donde
+ la vida del hijo está ligada al ciclo de vida del padre.
+ </para>
+
+ </sect1>
+
+ <sect1 id="example-parentchild-bidir">
+ <title>Uno-a-muchos bidirectional</title>
+
+ <para>
+ Supón que empezamos con una asociación simple
<literal><one-to-many></literal> desde
+ <literal>Parent</literal> a
<literal>Child</literal>.
+ </para>
+
+ <programlisting><![CDATA[<set name="children">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ Si ejecutásemos el siguiente código
+ </para>
+
+ <programlisting><![CDATA[Parent p = .....;
+Child c = new Child();
+p.getChildren().add(c);
+session.save(c);
+session.flush();]]></programlisting>
+
+ <para>
+ Hibernate publicaría dos sentencias SQL:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>un <literal>INSERT</literal> para crear el registro
de <literal>c</literal></para>
+ </listitem>
+ <listitem>
+ <para>
+ un <literal>UPDATE</literal> para crear el enlace desde
<literal>p</literal> a
+ <literal>c</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Esto no es sólo ineficiente, sino que además viola
cualquier restricción <literal>NOT NULL</literal> en la
+ columna <literal>parent_id</literal>. Podemos reparar la
violación de restricción de nulabilidad
+ especificando <literal>not-null="true"</literal> en el
mapeo de la colección:
+ </para>
+
+ <programlisting><![CDATA[<set name="children">
+ <key column="parent_id" not-null="true"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ Sin embargo, esta no es la solución recomendada.
+ </para>
+ <para>
+ El caso subyacente de este comportamiento es que el enlace (la clave
foránea <literal>parent_id</literal>)
+ de <literal>p</literal> a <literal>c</literal> no es
considerado parte del estado del objeto
+ <literal>Child</literal> y por lo tanto no es creada en el
<literal>INSERT</literal>. De modo que la
+ solución es hacer el enlace parte del mapeo del
<literal>Child</literal>.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="parent"
column="parent_id" not-null="true"/>]]></programlisting>
+
+ <para>
+ (Necesitamos además añadir la propiedad
<literal>parent</literal> a la clase <literal>Child</literal>.)
+ </para>
+
+ <para>
+ Ahora que la entidad <literal>Child</literal> está
gestionando el estado del enlace, le decimos a la
+ colección que no actualice el enlace. Usamos el atributo
<literal>inverse</literal>.
+ </para>
+
+ <programlisting><![CDATA[<set name="children"
inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ El siguiente código podría ser usado para
añadir un nuevo <literal>Child</literal>
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class,
pid);
+Child c = new Child();
+c.setParent(p);
+p.getChildren().add(c);
+session.save(c);
+session.flush();]]></programlisting>
+
+ <para>
+ Y ahora, ¡Sólo se publicaría un
<literal>INSERT</literal> de SQL!
+ </para>
+
+ <para>
+ Para ajustar un poco más las cosas, podríamos crear un
método <literal>addChild()</literal> en
+ <literal>Parent</literal>.
+ </para>
+
+ <programlisting><![CDATA[public void addChild(Child c) {
+ c.setParent(this);
+ children.add(c);
+}]]></programlisting>
+
+ <para>
+ Ahora, el código para añadir un
<literal>Child</literal> se ve así
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class,
pid);
+Child c = new Child();
+p.addChild(c);
+session.save(c);
+session.flush();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-parentchild-cascades">
+ <title>Ciclo de vida en cascada</title>
+
+ <para>
+ La llamada explícita a <literal>save()</literal> es
aún molesta. Apuntaremos a esto usando tratamientos
+ en cascada.
+ </para>
+
+ <programlisting><![CDATA[<set name="children"
inverse="true" cascade="all">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ Esto simplifica el código anterior a
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class,
pid);
+Child c = new Child();
+p.addChild(c);
+session.flush();]]></programlisting>
+
+ <para>
+ Similarmente, no necesitamos iterar los hijos al salvar o borrar un
<literal>Parent</literal>.
+ Lo siguiente quita <literal>p</literal> y todos sus hijos de la
base de datos.
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class,
pid);
+session.delete(p);
+session.flush();]]></programlisting>
+
+ <para>
+ Sin embargo, este código
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class,
pid);
+Child c = (Child) p.getChildren().iterator().next();
+p.getChildren().remove(c);
+c.setParent(null);
+session.flush();]]></programlisting>
+
+ <para>
+ no quitará <literal>c</literal> de la base de datos;
sólo quitará el enlace a <literal>p</literal>
+ (y causará una violación a una restricción
<literal>NOT NULL</literal>). Necesitas borrar el hijo
+ explícitamente llamando a
<literal>delete()</literal>.
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class,
pid);
+Child c = (Child) p.getChildren().iterator().next();
+p.getChildren().remove(c);
+session.delete(c);
+session.flush();]]></programlisting>
+
+ <para>
+ Ahora, en nuestro caso, un <literal>Child</literal> no puede
existir realmente sin su padre. De modo que
+ si quitamos un <literal>Child</literal> de la
colección, realmente queremos que sea borrado. Para esto,
+ debemos usar
<literal>cascade="all-delete-orphan"</literal>.
+ </para>
+
+ <programlisting><![CDATA[<set name="children"
inverse="true" cascade="all-delete-orphan">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ Nota: aunque el mapeo de la colección especifique
<literal>inverse="true"</literal>, el tratamiento en
+ cascada se procesa aún al iterar los elementos de
colección. De modo que si requieres que un objeto sea
+ salvado, borrado o actualizado en cascada, debes añadirlo a la
colección. No es suficiente con simplemente
+ llamar a <literal>setParent()</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="example-parentchild-update">
+ <title>Tratamiento en cascada y
<literal>unsaved-value</literal></title>
+
+ <para>
+ Supón que hemos cargado un <literal>Parent</literal>
en una <literal>Session</literal>, hemos hecho algunos
+ cambios en una acción de UI y deseamos hacer persistentes estos
cambios en una nueva sesión llamando a
+ <literal>update()</literal>. El
<literal>Parent</literal> contendrá una colección de
hijos y, ya que
+ está habilitado el tratamiento en cascada, Hibernate necesita
saber qué hijos están recién instanciados
+ y cuáles representan filas existentes en la base de datos.
Asumamos que tanto <literal>Parent</literal> como
+ <literal>Child</literal> tienen propiedades identificadoras
generadas de tipo <literal>Long</literal>.
+ Hibernate usará el identificador y el valor de la propiedad de
versión/timestamp para determinar cuáles de
+ los hijos son nuevos. (Ver <xref
linkend="objectstate-saveorupdate"/>.) <emphasis>En Hibernate3, no es
+ más necesario especificar un
<literal>unsaved-value</literal> explícitamente.</emphasis>
+ </para>
+
+ <para>
+ The following code will update <literal>parent</literal> and
<literal>child</literal> and insert
+ <literal>newChild</literal>.
+ </para>
+
+ <programlisting><![CDATA[//parent and child were both loaded in a
previous session
+parent.addChild(child);
+Child newChild = new Child();
+parent.addChild(newChild);
+session.update(parent);
+session.flush();]]></programlisting>
+
+ <para>
+ Bueno, todo eso está muy bien para el caso de un identificador
generado, pero ¿qué de los
+ identificadores asignados y de los identificadores compuestos? Esto es
más difícil, ya que Hibernate
+ no puede usar la propiedad identificadora para distinguir entre un objeto
recién instanciado (con un
+ identificador asignado por el usuario) y un objeto cargado en una
sesión previa. En este caso, Hibernate
+ bien usará la propiedad de versión o timestamp, o bien
consultará realmente el caché de segundo nivel,
+ o bien, en el peor de los casos, la base de datos, para ver si existe la
fila.
+ </para>
+
+ <!-- undocumenting
+ <para>
+ There is one further possibility. The
<literal>Interceptor</literal> method named
+ <literal>isUnsaved()</literal> lets the application implement
its own strategy for distinguishing
+ newly instantiated objects. For example, you could define a base class for
your persistent classes.
+ </para>
+
+ <programlisting><![CDATA[public class Persistent {
+ private boolean _saved = false;
+ public void onSave() {
+ _saved=true;
+ }
+ public void onLoad() {
+ _saved=true;
+ }
+ ......
+ public boolean isSaved() {
+ return _saved;
+ }
+}]]></programlisting>
+
+ <para>
+ (The <literal>saved</literal> property is non-persistent.)
+ Now implement <literal>isUnsaved()</literal>, along with
<literal>onLoad()</literal>
+ and <literal>onSave()</literal> as follows.
+ </para>
+
+ <programlisting><![CDATA[public Boolean isUnsaved(Object entity) {
+ if (entity instanceof Persistent) {
+ return new Boolean( !( (Persistent) entity ).isSaved() );
+ }
+ else {
+ return null;
+ }
+}
+
+public boolean onLoad(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+
+ if (entity instanceof Persistent) ( (Persistent) entity ).onLoad();
+ return false;
+}
+
+public boolean onSave(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+
+ if (entity instanceof Persistent) ( (Persistent) entity ).onSave();
+ return false;
+}]]></programlisting>
+
+ <para>
+ Don't worry; in Hibernate3 you don't need to write any of this kind of code if
you don't want to.
+ </para>
+ -->
+ </sect1>
+
+ <sect1 id="example-parentchild-conclusion">
+ <title>Conclusión</title>
+
+ <para>
+ Hay que resumir un poco aquí y podría parecer confuso
a la primera vez. Sin embargo, en la práctica,
+ todo funciona muy agradablemente. La mayoría de las aplicaciones
de Hibernate usan el patrón
+ padre / hijo en muchos sitios.
+ </para>
+
+ <para>
+ Hemos mencionado una alternativa en el primer párrafo. Ninguno de
los temas anteriores existe en el caso
+ de los mapeos
<literal><composite-element></literal>, que tienen exactamente
la semántica de una
+ relación padre / hijo. Desafortunadamente, hay dos grandes
limitaciones para las clases de elementos
+ compuestos: los elementos compuestos no pueden poseer sus propias
colecciones, y no deben ser el hijo
+ de cualquier otra entidad que no sea su padre único.
+ </para>
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/content/example_weblog.xml
(from rev 12794,
core/trunk/documentation/manual/es-ES/src/main/docbook/modules/example_weblog.xml)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/content/example_weblog.xml
(rev 0)
+++
core/trunk/documentation/manual/es-ES/src/main/docbook/content/example_weblog.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,429 @@
+<chapter id="example-weblog">
+ <title>Ejemplo: Aplicación de Weblog</title>
+
+ <sect1 id="example-weblog-classes">
+ <title>Clases Persistentes</title>
+
+ <para>
+ Las clases persistentes representan un weblog, y un ítem enviado a
un weblog. Van a ser modelados como una
+ relación padre/hijo estñndar, pero usaremos un bag
ordenado, en vez de un conjunto (set).
+ </para>
+
+ <programlisting><![CDATA[package eg;
+
+import java.util.List;
+
+public class Blog {
+ private Long _id;
+ private String _name;
+ private List _items;
+
+ public Long getId() {
+ return _id;
+ }
+ public List getItems() {
+ return _items;
+ }
+ public String getName() {
+ return _name;
+ }
+ public void setId(Long long1) {
+ _id = long1;
+ }
+ public void setItems(List list) {
+ _items = list;
+ }
+ public void setName(String string) {
+ _name = string;
+ }
+}]]></programlisting>
+
+ <programlisting><![CDATA[package eg;
+
+import java.text.DateFormat;
+import java.util.Calendar;
+
+public class BlogItem {
+ private Long _id;
+ private Calendar _datetime;
+ private String _text;
+ private String _title;
+ private Blog _blog;
+
+ public Blog getBlog() {
+ return _blog;
+ }
+ public Calendar getDatetime() {
+ return _datetime;
+ }
+ public Long getId() {
+ return _id;
+ }
+ public String getText() {
+ return _text;
+ }
+ public String getTitle() {
+ return _title;
+ }
+ public void setBlog(Blog blog) {
+ _blog = blog;
+ }
+ public void setDatetime(Calendar calendar) {
+ _datetime = calendar;
+ }
+ public void setId(Long long1) {
+ _id = long1;
+ }
+ public void setText(String string) {
+ _text = string;
+ }
+ public void setTitle(String string) {
+ _title = string;
+ }
+}]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-weblog-mappings">
+ <title>Mapeos de Hibernate</title>
+
+ <para>
+ Los mapeos XML ahora deben ser absolutamente directos.
+ </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 package="eg">
+
+ <class
+ name="Blog"
+ table="BLOGS">
+
+ <id
+ name="id"
+ column="BLOG_ID">
+
+ <generator class="native"/>
+
+ </id>
+
+ <property
+ name="name"
+ column="NAME"
+ not-null="true"
+ unique="true"/>
+
+ <bag
+ name="items"
+ inverse="true"
+ order-by="DATE_TIME"
+ cascade="all">
+
+ <key column="BLOG_ID"/>
+ <one-to-many class="BlogItem"/>
+
+ </bag>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <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 package="eg">
+
+ <class
+ name="BlogItem"
+ table="BLOG_ITEMS"
+ dynamic-update="true">
+
+ <id
+ name="id"
+ column="BLOG_ITEM_ID">
+
+ <generator class="native"/>
+
+ </id>
+
+ <property
+ name="title"
+ column="TITLE"
+ not-null="true"/>
+
+ <property
+ name="text"
+ column="TEXT"
+ not-null="true"/>
+
+ <property
+ name="datetime"
+ column="DATE_TIME"
+ not-null="true"/>
+
+ <many-to-one
+ name="blog"
+ column="BLOG_ID"
+ not-null="true"/>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-weblog-code">
+ <title>Código Hibernate</title>
+
+ <para>
+ La siguiente clase demuestra algunos de los tipos de cosas que podemos haces
con estas clases,
+ usando Hibernate.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hibernate.HibernateException;
+import org.hibernate.Query;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.tool.hbm2ddl.SchemaExport;
+
+public class BlogMain {
+
+ private SessionFactory _sessions;
+
+ public void configure() throws HibernateException {
+ _sessions = new Configuration()
+ .addClass(Blog.class)
+ .addClass(BlogItem.class)
+ .buildSessionFactory();
+ }
+
+ public void exportTables() throws HibernateException {
+ Configuration cfg = new Configuration()
+ .addClass(Blog.class)
+ .addClass(BlogItem.class);
+ new SchemaExport(cfg).create(true, true);
+ }
+
+ public Blog createBlog(String name) throws HibernateException {
+
+ Blog blog = new Blog();
+ blog.setName(name);
+ blog.setItems( new ArrayList() );
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ session.persist(blog);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return blog;
+ }
+
+ public BlogItem createBlogItem(Blog blog, String title, String text)
+ throws HibernateException {
+
+ BlogItem item = new BlogItem();
+ item.setTitle(title);
+ item.setText(text);
+ item.setBlog(blog);
+ item.setDatetime( Calendar.getInstance() );
+ blog.getItems().add(item);
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ session.update(blog);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return item;
+ }
+
+ public BlogItem createBlogItem(Long blogid, String title, String text)
+ throws HibernateException {
+
+ BlogItem item = new BlogItem();
+ item.setTitle(title);
+ item.setText(text);
+ item.setDatetime( Calendar.getInstance() );
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ Blog blog = (Blog) session.load(Blog.class, blogid);
+ item.setBlog(blog);
+ blog.getItems().add(item);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return item;
+ }
+
+ public void updateBlogItem(BlogItem item, String text)
+ throws HibernateException {
+
+ item.setText(text);
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ session.update(item);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ }
+
+ public void updateBlogItem(Long itemid, String text)
+ throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ BlogItem item = (BlogItem) session.load(BlogItem.class, itemid);
+ item.setText(text);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ }
+
+ public List listAllBlogNamesAndItemCounts(int max)
+ throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ List result = null;
+ try {
+ tx = session.beginTransaction();
+ Query q = session.createQuery(
+ "select blog.id, blog.name, count(blogItem) " +
+ "from Blog as blog " +
+ "left outer join blog.items as blogItem " +
+ "group by blog.name, blog.id " +
+ "order by max(blogItem.datetime)"
+ );
+ q.setMaxResults(max);
+ result = q.list();
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return result;
+ }
+
+ public Blog getBlogAndAllItems(Long blogid)
+ throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ Blog blog = null;
+ try {
+ tx = session.beginTransaction();
+ Query q = session.createQuery(
+ "from Blog as blog " +
+ "left outer join fetch blog.items " +
+ "where blog.id = :blogid"
+ );
+ q.setParameter("blogid", blogid);
+ blog = (Blog) q.uniqueResult();
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return blog;
+ }
+
+ public List listBlogsAndRecentItems() throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ List result = null;
+ try {
+ tx = session.beginTransaction();
+ Query q = session.createQuery(
+ "from Blog as blog " +
+ "inner join blog.items as blogItem " +
+ "where blogItem.datetime > :minDate"
+ );
+
+ Calendar cal = Calendar.getInstance();
+ cal.roll(Calendar.MONTH, false);
+ q.setCalendar("minDate", cal);
+
+ result = q.list();
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return result;
+ }
+}]]></programlisting>
+
+ </sect1>
+
+</chapter>
+
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/content/filters.xml (from
rev 12794, core/trunk/documentation/manual/es-ES/src/main/docbook/modules/filters.xml)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/content/filters.xml
(rev 0)
+++ core/trunk/documentation/manual/es-ES/src/main/docbook/content/filters.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,130 @@
+<chapter id="filters">
+ <title>Filtrando datos</title>
+
+ <para>
+ Hibernate3 provee un nuevo enfoque innovador para manejar datos con reglas de
"visibilidad".
+ Un <emphasis>filtro de Hibernate</emphasis> es un filtro global, con
nombre y parametrizado
+ que puede ser habilitado o deshabilitado para una sesión de Hibernate en
particular.
+ </para>
+
+ <sect1 id="objectstate-filters">
+ <title>Filtros de Hibernate</title>
+
+ <para>
+ Hibernate3 añade la habilidad de predefinir criterios de filtros y unir esos
filtros tanto a
+ nivel de una clase como de una colección. Un criterio de filtro es la
habilidad de definir una
+ cláusula de restricción muy similar al atributo existente "where"
disponible en el elemento
+ class y varios elementos de colección. Excepto en que estos filtros pueden
ser parametrizados.
+ La aplicación puede tomar la decisión en tiempo de ejecución de qué filtros
deben estar
+ habilitados y cuáles deben ser sus parámetros. Los filtros pueden ser usados
como vistas de
+ base de datos, pero parametrizados dentro de la aplicación.
+ </para>
+
+ <para>
+ Para usar los filtros, éstos deben primero ser definidos y luego unidos a los
elementos de mapeo
+ apropiados. Para definir un filtro, usa el elemento
<literal><filter-def/></literal> dentro
+ de un elemento
<literal><hibernate-mapping/></literal>:
+ </para>
+
+ <programlisting><![CDATA[<filter-def name="myFilter">
+ <filter-param name="myFilterParam" type="string"/>
+</filter-def>]]></programlisting>
+
+ <para>
+ Entonces este filtro puede ser unido a una clase:
+ </para>
+
+ <programlisting><![CDATA[<class name="myClass" ...>
+ ...
+ <filter name="myFilter" condition=":myFilterParam =
MY_FILTERED_COLUMN"/>
+</class>]]></programlisting>
+
+ <para>
+ o a una colección:
+ </para>
+
+ <programlisting><![CDATA[<set ...>
+ <filter name="myFilter" condition=":myFilterParam =
MY_FILTERED_COLUMN"/>
+</set>]]></programlisting>
+
+ <para>
+ o incluso a ambos (o muchos de cada uno) al mismo tiempo.
+ </para>
+
+ <para>
+ Los métodos en <literal>Session</literal> son:
<literal>enableFilter(String filterName)</literal>,
+ <literal>getEnabledFilter(String filterName)</literal>, y
<literal>disableFilter(String filterName)</literal>.
+ Por defecto, los filtros <emphasis>no</emphasis> están
habilitados para una sesión dada; deben ser
+ habilitados explícitamente por medio del uso del método
<literal>Session.enableFilter()</literal>,
+ que devuelve una instancia de la interface
<literal>Filter</literal>. Usando el filtro simple definido
+ arriba, esto se vería así:
+ </para>
+
+
<programlisting><![CDATA[session.enableFilter("myFilter").setParameter("myFilterParam",
"some-value");]]></programlisting>
+
+ <para>
+ Nota que los métodos en la interface org.hibernate.Filter permiten el
encadenamiento de métodos
+ común en gran parte de Hibernate.
+ </para>
+
+ <para>
+ Un ejemplo completo, usando datos temporales con un patrón efectivo de fechas
de registro:
+ </para>
+
+ <programlisting><![CDATA[<filter-def
name="effectiveDate">
+ <filter-param name="asOfDate" type="date"/>
+</filter-def>
+
+<class name="Employee" ...>
+...
+ <many-to-one name="department" column="dept_id"
class="Department"/>
+ <property name="effectiveStartDate" type="date"
column="eff_start_dt"/>
+ <property name="effectiveEndDate" type="date"
column="eff_end_dt"/>
+...
+ <!--
+ Note that this assumes non-terminal records have an eff_end_dt set to
+ a max db date for simplicity-sake
+ -->
+ <filter name="effectiveDate"
+ condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
+</class>
+
+<class name="Department" ...>
+...
+ <set name="employees" lazy="true">
+ <key column="dept_id"/>
+ <one-to-many class="Employee"/>
+ <filter name="effectiveDate"
+ condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ Entonces, en orden de asegurar que siempre tendrás de vuelta registros
actualmente efectivos,
+ simplemente habilita el filtro en la sesión previo a recuperar los datos de
empleados:
+ </para>
+
+<programlisting><![CDATA[Session session = ...;
+session.enabledFilter("effectiveDate").setParameter("asOfDate", new
Date());
+List results = session.createQuery("from Employee as e where e.salary >
:targetSalary")
+ .setLong("targetSalary", new Long(1000000))
+ .list();
+]]></programlisting>
+
+ <para>
+ En el HQL de arriba, aunque sólo hemos mencionado explícitamente una
restricción de salario en
+ los resultados, debido al filtro habilitado la consulta sólo devolverá
empleados actualmente activos
+ que tengan un salario mayor que un millón de dólares.
+ </para>
+
+ <para>
+ Nota: si planeas usar filtros con unión externa (outer joining) (bien a
través de HQL, o bien
+ de recuperación de carga) sé cuidadoso en la dirección de expresión de la
condición. Lo más seguro
+ es establecer esto para unión externa izquierda (left outer joining). En
general, coloca el primer
+ parámetro seguido del nombre(s) de columna(s) después del operador.
+ </para>
+
+ </sect1>
+
+</chapter>
+
Copied:
core/trunk/documentation/manual/es-ES/src/main/docbook/content/inheritance_mapping.xml
(from rev 12794,
core/trunk/documentation/manual/es-ES/src/main/docbook/modules/inheritance_mapping.xml)
===================================================================
---
core/trunk/documentation/manual/es-ES/src/main/docbook/content/inheritance_mapping.xml
(rev 0)
+++
core/trunk/documentation/manual/es-ES/src/main/docbook/content/inheritance_mapping.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,464 @@
+<chapter id="inheritance">
+ <title>Mapeo de Herencia</title>
+
+ <sect1 id="inheritance-strategies" revision="2">
+ <title>Las Tres Estrategias</title>
+
+ <para>
+ Hibernate soporta las tres estrategias básicas de mapeo de
herencia:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ tabla por jerarquía de clases
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ tabla por subclase
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ tabla por clase concreta
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ En adición, Hibernate soporta un cuarto, ligeramente diferente
tipo
+ de polimorfismo:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ polimorfismo implícito
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Es posible usar estrategias de mapeo diferentes para diferentes
+ ramificaciones de la misma jerarquía de herencia, y entonces usar
+ polimorfismo implícito para conseguir polimorfismo a
través de
+ toda la jerarquía. Sin embargo, Hibernate no soporta la mezcla de
+ mapeos <literal><subclass></literal>,
+ y <literal><joined-subclass></literal>
+ y <literal><union-subclass></literal> bajo el mismo
elemento
+ <literal><class></literal> raíz. Es
posible mezclar juntas las
+ estrategias de tabla por jerarquía y tabla por subclase, bajo el
mismo
+ elemento <literal><class></literal>, combinando los
elementos
+ <literal><subclass></literal> y
<literal><join></literal>
+ (ver debajo).
+ </para>
+
+ <sect2 id="inheritance-tableperclass" >
+ <title>Tabla por jerarquía de clases</title>
+
+ <para>
+ Supón que tenemos una interface
<literal>Payment</literal>, con
+ los implementadores <literal>CreditCardPayment</literal>,
+ <literal>CashPayment</literal>,
<literal>ChequePayment</literal>.
+ El mapeo de tabla por jerarquía se vería
así:
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment"
table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="PAYMENT_TYPE" type="string"/>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <subclass name="CreditCardPayment"
discriminator-value="CREDIT">
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </subclass>
+ <subclass name="CashPayment" discriminator-value="CASH">
+ ...
+ </subclass>
+ <subclass name="ChequePayment"
discriminator-value="CHEQUE">
+ ...
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ Se requiere exactamente una tabla. Hay una gran limitación de esta
estrategia de mapeo:
+ las columnas declaradas por las subclases, como
<literal>CCTYPE</literal>, no pueden
+ tener restricciones <literal>NOT NULL</literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-tablepersubclass">
+ <title>Tabla por subclase</title>
+
+ <para>
+ Un mapeo de tabla por sublclase se vería así:
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment"
table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <joined-subclass name="CreditCardPayment"
table="CREDIT_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </joined-subclass>
+ <joined-subclass name="CashPayment" table="CASH_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ ...
+ </joined-subclass>
+ <joined-subclass name="ChequePayment"
table="CHEQUE_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ ...
+ </joined-subclass>
+</class>]]></programlisting>
+
+ <para>
+ Se requieren cuatro tablas. Las tres tablas de subclase tienen
+ asociaciones de clave primaria a la tabla de superclase (de modo
+ que en el modelo relacional es realmente una asociación
uno-a-uno).
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-tablepersubclass-discriminator"
revision="2">
+ <title>Tabla por subclase, usando un discriminador</title>
+
+ <para>
+ Observa que la implementación de Hibernate de tabla por subclase
+ no requiere ninguna columna discriminadora. Otros mapeadores
+ objeto/relacional usan una implementación diferente de tabla por
+ subclase que requiere una columna discriminadora de tipo en la tabla
+ de superclase. Este enfoque es mucho más difícil de
implementar
+ pero discutiblemente más correcto desde un punto de vista
relacional.
+ Si quisieras usar una columna discriminadora con la estrategia de
+ tabla por subclase, puedes combinar el uso de
<literal><subclass></literal>
+ y <literal><join></literal>, como sigue:
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment"
table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="PAYMENT_TYPE" type="string"/>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <subclass name="CreditCardPayment"
discriminator-value="CREDIT">
+ <join table="CREDIT_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </join>
+ </subclass>
+ <subclass name="CashPayment" discriminator-value="CASH">
+ <join table="CASH_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ ...
+ </join>
+ </subclass>
+ <subclass name="ChequePayment"
discriminator-value="CHEQUE">
+ <join table="CHEQUE_PAYMENT" fetch="select">
+ <key column="PAYMENT_ID"/>
+ ...
+ </join>
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ la declaración opcional
<literal>fetch="select"</literal> dice a Hibernate
+ que no recupere los datos de la subclase
<literal>ChequePayment</literal>
+ usando una unión externa (outer join) al consultar la superclase.
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-mixing-tableperclass-tablepersubclass">
+ <title>Mezclando tabla por jerarquía de clases con tabla por
subclase</title>
+
+ <para>
+ Puedes incluso mezclar las estrategias de tabla po jerarquía y
tabla por
+ subclase usando este enfoque:
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment"
table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="PAYMENT_TYPE" type="string"/>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <subclass name="CreditCardPayment"
discriminator-value="CREDIT">
+ <join table="CREDIT_PAYMENT">
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </join>
+ </subclass>
+ <subclass name="CashPayment" discriminator-value="CASH">
+ ...
+ </subclass>
+ <subclass name="ChequePayment"
discriminator-value="CHEQUE">
+ ...
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ Para cualquiera de estas estrategias de mapeo, una asociación
polimórfica
+ a la clase raíz <literal>Payment</literal> es mapeada
usando <literal><many-to-one></literal>.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="payment"
column="PAYMENT_ID" class="Payment"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="inheritance-tableperconcrete" revision="1">
+ <title>Tabla por clase concreta</title>
+
+ <para>
+ Podríamos ir de dos maneras a la estrategia de mapeo de tabla por
clase
+ concreta. La primera es usar
<literal><union-subclass></literal>.
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="sequence"/>
+ </id>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <union-subclass name="CreditCardPayment"
table="CREDIT_PAYMENT">
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </union-subclass>
+ <union-subclass name="CashPayment" table="CASH_PAYMENT">
+ ...
+ </union-subclass>
+ <union-subclass name="ChequePayment"
table="CHEQUE_PAYMENT">
+ ...
+ </union-subclass>
+</class>]]></programlisting>
+
+ <para>
+ Están implicadas tres tablas. Cada tabla define columnas para
todas las
+ propiedades de la clase, inccluyendo las propiedades heredadas.
+ </para>
+
+ <para>
+ La limitación de este enfoque es que si una propiedad es mapeada
en la
+ superclase, el nombre de columna debe ser el mismo en todas las tablas
+ de subclase. (Podríamos relajar esto en un lanzamiento futuro de
Hibernate.)
+ La estrategia de generador de indentidad no está permitida en la
herencia
+ de unión de subclase, de hecho la semilla de clave primaria tiene
que ser
+ compartida a través de todas las subclases unidas de una
jerarquía.
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-tableperconcreate-polymorphism">
+ <title>Tabla por clase concreta, usando polimorfismo
implícito</title>
+
+ <para>
+ Un enfoque alternativo es hacer uso de polimorfismo implícito:
+ </para>
+
+ <programlisting><![CDATA[<class name="CreditCardPayment"
table="CREDIT_PAYMENT">
+ <id name="id" type="long"
column="CREDIT_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="CREDIT_AMOUNT"/>
+ ...
+</class>
+
+<class name="CashPayment" table="CASH_PAYMENT">
+ <id name="id" type="long"
column="CASH_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="CASH_AMOUNT"/>
+ ...
+</class>
+
+<class name="ChequePayment" table="CHEQUE_PAYMENT">
+ <id name="id" type="long"
column="CHEQUE_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="CHEQUE_AMOUNT"/>
+ ...
+</class>]]></programlisting>
+
+ <para>
+ Nota que en ningún sitio mencionamos la interface
<literal>Payment</literal>
+ explícitamente. Nota además que las propiedades de
<literal>Payment</literal>
+ son mapeadas en cada una de las subclases. Si quieres evitar
duplicación,
+ considera usar entidades XML. (por ejemplo,
+ <literal>[ <!ENTITY allproperties SYSTEM
"allproperties.xml"> ]</literal>
+ en la declaración <literal>DOCTYPE</literal> y
<literal>&allproperties;</literal>
+ en el mapeo).
+ </para>
+
+ <para>
+ La desventaja de este enfoque es que Hibernate no genera
<literal>UNION</literal>s
+ de SQL al realizar consultas polimórficas.
+ </para>
+
+ <para>
+ Para esta estrategia de mapeo, una asociación
polimórfica a <literal>Payment</literal>
+ es mapeada generalmente usando
<literal><any></literal>.
+ </para>
+
+ <programlisting><![CDATA[<any name="payment"
meta-type="string" id-type="long">
+ <meta-value value="CREDIT" class="CreditCardPayment"/>
+ <meta-value value="CASH" class="CashPayment"/>
+ <meta-value value="CHEQUE" class="ChequePayment"/>
+ <column name="PAYMENT_CLASS"/>
+ <column name="PAYMENT_ID"/>
+</any>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="inheritace-mixingpolymorphism">
+ <title>Mezclando polimorfismo implícito con otros mapeos de
herencia</title>
+
+ <para>
+ Hay una cosa más por notar acerca de este mapeo. Ya que las
subclases se mapean
+ cada una en su propio elemento
<literal><class></literal> (y ya que
+ <literal>Payment</literal> es sólo una interface),
cada una de las subclases
+ podría ser parte de otra jerarquía de herencia! (Y
todavía puedes seguir usando
+ consultas polimórficas contra la interface
<literal>Payment</literal>.)
+ </para>
+
+ <programlisting><![CDATA[<class name="CreditCardPayment"
table="CREDIT_PAYMENT">
+ <id name="id" type="long"
column="CREDIT_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="CREDIT_CARD" type="string"/>
+ <property name="amount" column="CREDIT_AMOUNT"/>
+ ...
+ <subclass name="MasterCardPayment"
discriminator-value="MDC"/>
+ <subclass name="VisaPayment" discriminator-value="VISA"/>
+</class>
+
+<class name="NonelectronicTransaction"
table="NONELECTRONIC_TXN">
+ <id name="id" type="long" column="TXN_ID">
+ <generator class="native"/>
+ </id>
+ ...
+ <joined-subclass name="CashPayment" table="CASH_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="amount" column="CASH_AMOUNT"/>
+ ...
+ </joined-subclass>
+ <joined-subclass name="ChequePayment"
table="CHEQUE_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="amount" column="CHEQUE_AMOUNT"/>
+ ...
+ </joined-subclass>
+</class>]]></programlisting>
+
+ <para>
+ Una vez más, no mencionamos a
<literal>Payment</literal> explícitamente.
+ Si ejecutamos una consulta contra la interface
<literal>Payment</literal>
+ - por ejemplo, <literal>from Payment</literal> - Hibernate
devuelve
+ automáticamente instancias de
<literal>CreditCardPayment</literal>
+ (y sus subclases, ya que ellas también implementan
<literal>Payment</literal>),
+ <literal>CashPayment</literal> y
<literal>ChequePayment</literal> pero
+ no instancias de <literal>NonelectronicTransaction</literal>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="inheritance-limitations">
+ <title>Limitaciones</title>
+
+ <para>
+ Existen ciertas limitaciones al enfoque de "polimorfismo
implícito" en
+ la estrategia de mapeo de tabla por clase concreta. Existen limitaciones
+ algo menos restrictivas a los mapeos
<literal><union-subclass></literal>.
+ </para>
+
+ <para>
+ La siguiente tabla muestra las limitaciones de mapeos de tabla por
+ clase concreta, y de polmorfismo implícito, en Hibernate.
+ </para>
+
+ <table frame="topbot">
+ <title>Funcionalidades de mapeo de herencia</title>
+ <tgroup cols='8' align='left' colsep='1'
rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <colspec colname='c3' colwidth="1*"/>
+ <colspec colname='c4' colwidth="1*"/>
+ <colspec colname='c5' colwidth="1*"/>
+ <colspec colname='c6' colwidth="1*"/>
+ <colspec colname='c7' colwidth="1*"/>
+ <colspec colname='c8' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Estrategia de herencia</entry>
+ <entry>muchos-a-uno polimórfica</entry>
+ <entry>uno-a-uno polimórfica</entry>
+ <entry>uno-a-muchos polimórfica</entry>
+ <entry>mushos-a-muchos polimórfica</entry>
+ <entry><literal>load()/get()</literal>
polimórficos</entry>
+ <entry>Consultas polimórficas</entry>
+ <entry>Uniones polimórficas</entry>
+ <entry>Recuperación por unión externa (outer
join)</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>tabla por jerarquía de clases</entry>
+
<entry><literal><many-to-one></literal></entry>
+
<entry><literal><one-to-one></literal></entry>
+
<entry><literal><one-to-many></literal></entry>
+
<entry><literal><many-to-many></literal></entry>
+ <entry><literal>s.get(Payment.class,
id)</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><literal>from Order o join o.payment
p</literal></entry>
+ <entry><emphasis>soportada</emphasis></entry>
+ </row>
+ <row>
+ <entry>tabla por subclase</entry>
+
<entry><literal><many-to-one></literal></entry>
+
<entry><literal><one-to-one></literal></entry>
+
<entry><literal><one-to-many></literal></entry>
+
<entry><literal><many-to-many></literal></entry>
+ <entry><literal>s.get(Payment.class,
id)</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><literal>from Order o join o.payment
p</literal></entry>
+ <entry><emphasis>soportada</emphasis></entry>
+ </row>
+ <row>
+ <entry>tabla por clase concreta (union-subclass)</entry>
+
<entry><literal><many-to-one></literal></entry>
+
<entry><literal><one-to-one></literal></entry>
+ <entry><literal><one-to-many></literal>
(para <literal>inverse="true"</literal> solamente)</entry>
+
<entry><literal><many-to-many></literal></entry>
+ <entry><literal>s.get(Payment.class,
id)</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><literal>from Order o join o.payment
p</literal></entry>
+ <entry><emphasis>soportada</emphasis></entry>
+ </row>
+ <row>
+ <entry>tabla por clase concreta (polimorfismo
implícito)</entry>
+
<entry><literal><any></literal></entry>
+ <entry><emphasis>no soportada</emphasis></entry>
+ <entry><emphasis>no soportada</emphasis></entry>
+
<entry><literal><many-to-any></literal></entry>
+ <entry><literal>s.createCriteria(Payment.class).add(
Restrictions.idEq(id) ).uniqueResult()</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><emphasis>no
suportadas</emphasis></entry>
+ <entry><emphasis>no soportada</emphasis></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect1>
+
+</chapter>
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/content/performance.xml
(from rev 12794,
core/trunk/documentation/manual/es-ES/src/main/docbook/modules/performance.xml)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/content/performance.xml
(rev 0)
+++
core/trunk/documentation/manual/es-ES/src/main/docbook/content/performance.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,1334 @@
+<chapter id="performance">
+ <title>Mejorando el rendimiento</title>
+
+ <sect1 id="performance-fetching">
+ <title>Estrategias de recuperación</title>
+
+ <para>
+ Una <emphasis>estrategia de recuperación</emphasis> es
la estrategia que usará Hibernate para recuperar
+ los objetos asociados cuando la aplicación necesite navegar la
asociación. Las estrategias de recuperación
+ pueden ser declaradas en los metadatos de mapeo O/R, o sobrescritas por una
consulta HQL o
+ <literal>Criteria</literal> en particular.
+ </para>
+
+ <para>
+ Hibernate3 define las siguientes estrategias de recuperación:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Recuperación por unión (join
fetching)</emphasis> - Hibernate recupera la
+ instancia asociada o colección en la misma
<literal>SELECT</literal>, usando una
+ <literal>OUTER JOIN</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Recuperación por selección
(select fetching)</emphasis> - se usa una segunda
+ <literal>SELECT</literal> para recuperar la entidad
asociada o colección. A menos que
+ deshabilites explícitamente la recuperación
perezosa especificando <literal>lazy="false"</literal>,
+ la segunda selección sólo será
ejecutada cuando realmente accedas a la asociación.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Recuperación por subselección
(subselect fetching)</emphasis> - se usa una segunda
+ <literal>SELECT</literal> para recuperar las colecciones
asociadas de todas las entidades
+ recuperadas en una consulta o recuperación previa. A menos
que deshabilites explícitamente la
+ recuperación perezosa especificando
<literal>lazy="false"</literal>, esta segunda selección
sólo
+ será ejecutada cuando realmente accedas a la
asociación.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Recuperación en lote</emphasis> -
una estrategia de optimización para la recuperación
+ por selección - Hibernate recupera un lote de instancias
de entidad o colecciones en una sola
+ <literal>SELECT</literal>, especificando una lista de
claves primarias o de claves foráneas.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernate también distingue entre:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Recuperación inmediata</emphasis> -
una asociación, colección o atributo es recuperado
+ inmediatamente, cuando el dueño es cargado.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Recuperación perezosa de
colecciones</emphasis> - se recupera una colección cuando la
+ aplicación invoca una operación sobre la
colección. (Esto es por defecto para las colecciones.)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Recuperación por proxy</emphasis> -
se recupera una asociación monovaluada cuando se
+ invoca un método que no sea el getter del identificador
sobre el objeto asociado.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Recuperación perezosa de
atributos</emphasis> - se recupera un atributo o una asociación
+ monovaluada cuando se accede a la variable de instancia (requiere
instrumentación del bytecode en
+ tiempo de ejecución). Este enfoque es raramente
necesario.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Aquí tenemos dos nociones ortogonales:
<emphasis>cuándo</emphasis> se recupera la aplicación,
+ y <emphasis>cómo</emphasis> es recuperada
(qué SQL es usado). ¡No las confundas! Usamos
+ <literal>fetch</literal> para afinar el rendimiento. Podemos usar
<literal>lazy</literal> para
+ definir un contrato sobre qué datos están siempre
disponibles en cualquier instancia separada de
+ una clase en particular.
+ </para>
+
+ <sect2 id="performance-fetching-lazy">
+ <title>Trabajando con asociaciones perezosas</title>
+
+ <para>
+ Por defecto, Hibernate3 usa una recuperación perezosa por
selección para colecciones
+ y una recuperación por proxy perezosa para asociaciones
monovaluadas. Estas políticas por
+ defecto tienen sentido para casi todas las asociaciones en casi todas las
aplicaciones.
+ </para>
+
+ <para>
+ <emphasis>Nota:</emphasis> si estableces
<literal>hibernate.default_batch_fetch_size</literal>, Hibernate
+ usará la optimización de recuperación en
lotes para recuperación perezosa (esta optimización
también puede
+ ser habilitada a un nivel más granularizado).
+ </para>
+
+ <para>
+ Sin embargo, la recuperación perezosa plantea un problema del
que tienes que estar al tanto. Acceder
+ a una asociación perezosa fuera del contexto de una
sesión de Hibernate abierta resultará en una
+ excepción. Por ejemplo:
+ </para>
+
+ <programlisting><![CDATA[s = sessions.openSession();
+Transaction tx = s.beginTransaction();
+
+User u = (User) s.createQuery("from User u where u.name=:userName")
+ .setString("userName", userName).uniqueResult();
+Map permissions = u.getPermissions();
+
+tx.commit();
+s.close();
+
+Integer accessLevel = (Integer) permissions.get("accounts"); //
Error!]]></programlisting>
+
+ <para>
+ Ya que la colección de permisos no fue inicializada cuando se
cerró la <literal>Session</literal>,
+ la colección no será capaz de cargar su estado.
<emphasis>Hibernate no soporta la inicialización
+ perezosa de objetos separados</emphasis>. La solución es
mover el código que lee de la colección
+ a justo antes que la transacción sea comprometida.
+ </para>
+
+ <para>
+ Alternativamente, podríamos usar una colección no
perezosa o asociación, especificando
+ <literal>lazy="false"</literal> para el mapeo de
asociación. Sin embargo, está pensado
+ que la inicialización perezosa sea usada para casi todas las
colecciones y asociaciones.
+ ¡Si defines demasiadas asociaciones no perezosas en tu modelo
de objetos, Hibernate terminará
+ necesitando recuperar la base de datos entera en cada
transacción!
+ </para>
+
+ <para>
+ Por otro lado, frecuentemente necesitamos elegir la
recuperación por unión (que es no perezosa
+ por naturaleza) en vez de la recuperación por
selección en una transacción en particular. Veremos
+ ahora cómo personalizar la estrategia de
recuperación. En Hibernate3, los mecanismos para elegir una
+ estrategia de recuperación son idénticas a las de
las asociaciones monovaluadas y colecciones.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-custom" revision="3">
+ <title>Afinando las estrategias de
recuperación</title>
+
+ <para>
+ La recuperación por selección (la preestablecida)
es extremadamente vulnerable a problemas de
+ selección N+1, de modo querríamos habilitar la
recuperación por unión (join fetching) en el
+ documento de mapeo:
+ </para>
+
+ <programlisting><![CDATA[<set name="permissions"
+ fetch="join">
+ <key column="userId"/>
+ <one-to-many class="Permission"/>
+</set]]></programlisting>
+
+ <programlisting><![CDATA[<many-to-one name="mother"
class="Cat" fetch="join"/>]]></programlisting>
+
+ <para>
+ La estrategia de recuperación definida en el documento de
mapeo afecta a:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ las recuperaciones vía
<literal>get()</literal> o <literal>load()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ las recuperaciones que ocurren implícitamente cuando se
navega una asociación
+ (recuperación perezosa)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ las consultas de <literal>Criteria</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Usualmente, no usamos el documento de mapeo para personalizar la
recuperación. En cambio, mantenemos el
+ comportamiento por defecto, y lo sobrescribimos para una
transacción en particular, usando
+ <literal>left join fetch</literal> en HQL. Esto le dice a
Hibernate que recupere la asociación
+ tempranamente en la primera selección, usando una
unión externa. En la API de consulta de
+ <literal>Criteria</literal>, usarías
<literal>setFetchMode(FetchMode.JOIN)</literal>.
+ </para>
+
+ <para>
+ Si acaso lo deseases, podrías cambiar la estrategia de
recuperación usada por
+ <literal>get()</literal> or
<literal>load()</literal>; simplemente usa una consulta
+ <literal>Criteria</literal>, por ejemplo:
+ </para>
+
+ <programlisting><![CDATA[User user = (User)
session.createCriteria(User.class)
+ .setFetchMode("permissions", FetchMode.JOIN)
+ .add( Restrictions.idEq(userId) )
+ .uniqueResult();]]></programlisting>
+
+ <para>
+ (Esto es el equivalente de Hibernate de lo que otras soluciones ORM
llaman un "plan de recuperación".)
+ </para>
+
+ <para>
+ Una forma completamente diferente de evitar problemas con selecciones N+1
es usar el caché de
+ segundo nivel.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-proxies" revision="2">
+ <title>Proxies de asociaciones de un solo extremo</title>
+
+ <para>
+ La recuperación perezosa de colecciones está
implementada usando la implementación de colecciones
+ persistentes propia de Hibernate. Sin embargo, se necesita un mecanismo
diferente para un comportamiento
+ perezoso en las asociaciones de un solo extremo. La entidad objetivo de
la asociación debe ser tratada
+ con proxies. Hibernate implementa proxies de inicialización
perezosa para objetos persistentes usando
+ mejora del bytecode en tiempo de ejecución (por medio de la
excelente biblioteca CGLIB).
+ </para>
+
+ <para>
+ Por defecto, Hibernate3 genera proxies (en el arranque) para todas las
clases persistentes y los usa
+ para habilitar la recuperación perezosa de asociaciones
<literal>muchos-a-uno</literal> y
+ <literal>uno-a-uno</literal>.
+ </para>
+
+ <para>
+ El fichero de mapeo puede declarar una interface a usar como interface de
proxy para esa clase,
+ con el atributo <literal>proxy</literal>. Por defecto,
Hibernate usa una subclase de la clase.
+ <emphasis>Nota que la clase tratada con proxies debe implementar un
constructor por defecto con al
+ menos visibilidad de paquete. ¡Recomendamos este constructor
para todas las clases persistentes!</emphasis>
+ </para>
+
+ <para>
+ Hay algunos puntos a tener en cuenta al extender este enfoque a clases
polimórficas, por ejemplo.
+ </para>
+
+ <programlisting><![CDATA[<class name="Cat"
proxy="Cat">
+ ......
+ <subclass name="DomesticCat">
+ .....
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ Primero, las instancias de <literal>Cat</literal> nunca
serán objeto de un cast a
+ <literal>DomesticCat</literal>, incluso aunque la instancia
subyacente sea instancia de
+ <literal>DomesticCat</literal>:
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) session.load(Cat.class,
id); // instantiate a proxy (does not hit the db)
+if ( cat.isDomesticCat() ) { // hit the db to initialize the proxy
+ DomesticCat dc = (DomesticCat) cat; // Error!
+ ....
+}]]></programlisting>
+
+ <para>
+ Segundo, es posible romper con el operador
<literal>==</literal> de un proxy.
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) session.load(Cat.class,
id); // instantiate a Cat proxy
+DomesticCat dc =
+ (DomesticCat) session.load(DomesticCat.class, id); // acquire new DomesticCat
proxy!
+System.out.println(cat==dc); //
false]]></programlisting>
+
+ <para>
+ Sin embargo, la situación no en absoluta tan mala como parece.
Aunque tenemos ahora dos referencias
+ a objetos proxy diferentes, la instancia subyacente será
aún el mismo objeto:
+ </para>
+
+ <programlisting><![CDATA[cat.setWeight(11.0); // hit the db to
initialize the proxy
+System.out.println( dc.getWeight() ); // 11.0]]></programlisting>
+
+ <para>
+ Tercero, no debes usar un proxy CGLIB para una clase
<literal>final</literal> o una clase
+ con algún método
<literal>final</literal>.
+ </para>
+
+ <para>
+ Finalmente, si tu objeto persistente adquiere cualquier recurso bajo
instanciación
+ (por ejemplo, en inicializadores o constructores por defecto), entonces
esos recursos
+ serán adquiridos también por el proxy. La clase del
proxy es una subclase real de la clase
+ persistente.
+ </para>
+
+ <para>
+ Estos problemas se deben a limitaciones fundamentales en el modelo de
herencia única de Java.
+ Si deseas evitar estos problemas cada una de tus clases persistentes
deben implementar una
+ interface que declare sus métodos de negocio. Debes
especificar estas interfaces en el fichero
+ de mapeo. Por ejemplo:
+ </para>
+
+ <programlisting><![CDATA[<class name="CatImpl"
proxy="Cat">
+ ......
+ <subclass name="DomesticCatImpl" proxy="DomesticCat">
+ .....
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ donde <literal>CatImpl</literal> implementa la interface
<literal>Cat</literal> y
+ <literal>DomesticCatImpl</literal> implementa la interface
<literal>DomesticCat</literal>.
+ Entonces <literal>load()</literal> o
<literal>iterate()</literal> pueden devolver instancias de
+ <literal>Cat</literal> y
<literal>DomesticCat</literal>. (Nota que
<literal>list()</literal>
+ usualmente no devuelve proxies.)
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) session.load(CatImpl.class,
catid);
+Iterator iter = session.createQuery("from CatImpl as cat where
cat.name='fritz'").iterate();
+Cat fritz = (Cat) iter.next();]]></programlisting>
+
+ <para>
+ Las relaciones también son inicializadas perezosamente. Esto
significa que debes declarar
+ cualquier propiedad como de tipo <literal>Cat</literal>, no
<literal>CatImpl</literal>.
+ </para>
+
+ <para>
+ Ciertas operaciones <emphasis>no</emphasis> requieren
inicialización de proxies.
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>equals()</literal>, si la clase persistente
no sobrescribe <literal>equals()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>hashCode()</literal>, si la clase
persistente no sobrescribe
+ <literal>hashCode()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ El método getter del identificador
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernate detectará las clase persistentes que sobrescriban
<literal>equals()</literal> o
+ <literal>hashCode()</literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-initialization">
+ <title>Inicializando colecciones y proxies</title>
+
+ <para>
+ Una <literal>LazyInitializationException</literal>
será lanzada por Hibernate si una colección
+ o proxy sin inicializar es accedido fuera del ámbito de la
<literal>Session</literal>, es decir,
+ cuando la entidad que posee la colección o que tiene la
referencia al proxy esté en el estado
+ separada.
+ </para>
+
+ <para>
+ A veces necesitamos asegurarnos que un proxy o colección
esté inicializado antes de cerrar la
+ <literal>Session</literal>. Por supuesto, siempre podemos
forzar la inicialización llamando a
+ <literal>cat.getSex()</literal> o
<literal>cat.getKittens().size()</literal>, por ejemplo.
+ Pero esto es confuso a lectores del código y no es conveniente
para código genérico.
+ </para>
+
+ <para>
+ Los métodos estáticos
<literal>Hibernate.initialize()</literal> y
+ <literal>Hibernate.isInitialized()</literal> proveen a la
aplicación de una forma conveniente de
+ trabajar con colecciones o proxies inicializados perezosamente.
+ <literal>Hibernate.initialize(cat)</literal>
forzará la inicialización de un proxy,
+ <literal>cat</literal>, en tanto su
<literal>Session</literal> esté todavía abierta.
+ <literal>Hibernate.initialize( cat.getKittens() )</literal>
tiene un efecto similar para la colección
+ de gatitos.
+ </para>
+
+ <para>
+ Otra opción es mantener la
<literal>Session</literal> abierta hasta que todas las colecciones
+ y proxies necesarios hayan sido cargados. En algunas arquitecturas de
aplicación, particularmente
+ en aquellas donde el código que accede a los datos usando
Hibernate, y el código que los usa están
+ en capas de aplicación diferentes o procesos
físicos diferentes, puede ser un problema asegurar que
+ la <literal>Session</literal> esté abierta cuando
se inicializa una colección. Existen dos formas
+ básicas de tratar este tema:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ En una aplicación basada web, puede usarse un filtro
de servlets para cerrar la
+ <literal>Session</literal> sólo bien al
final de una petición de usuario, una
+ vez que el rendering de la vista esté completa (el
patrón <emphasis>Sesión Abierta en
+ Vista (Open Session in View)</emphasis>). Por supuesto,
estos sitios requieren una
+ fuerte demanda de corrección del manejo de excepciones
de tu infraestructura de
+ aplicación. Es de una importancia vital que la
<literal>Session</literal> esté
+ cerrada y la transacción terminada antes de volver al
usuario, incluso cuando ocurra
+ una excepción durante el rendering de la
página. Para este enfoque, el filtro de servlet tiene
+ que ser capaz de accceder la
<literal>Session</literal>. Recomendamos que se use una variable
+ <literal>ThreadLocal</literal> para tener la
<literal>Session</literal> actual (ver
+ el capítulo 1, <xref
linkend="quickstart-playingwithcats"/>, para una implementación de
ejemplo).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ En una aplciación con una grada de negocios separada,
la lógica de negocio debe "preparar"
+ todas las colecciones que se vayan a necesitar por la grada web
antes de volver.
+ Esto significa que la grada de negocios debe cargar todos los
datos y devolver a la grada de
+ presentación web todos los datos que se requieran para
un caso de uso en particular
+ ya inicializados. Usualmente, la aplicación llama a
<literal>Hibernate.initialize()</literal>
+ para cada colección que se necesitará en la
grada web (esta llamada debe ocurrir antes que la
+ sesión sea cerrada) o recupera la colección
tempranamente usando una consulta de Hibernate con una
+ cláusula <literal>FETCH</literal> o una
<literal>FetchMode.JOIN</literal> en
+ <literal>Criteria</literal>. Esto es usualmente
más fácil si adoptas el patrón
+ <emphasis>Comando</emphasis> en vez de un
<emphasis>Fachada de Sesión</emphasis>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Puedes también adjuntar un objeto cargado previamente
a una nueva <literal>Session</literal>
+ con <literal>merge()</literal> o
<literal>lock()</literal> antes de acceder a colecciones
+ no inicializadas (u otros proxies). ¡No, Hibernate no,
y ciertamente <emphasis>no
+ debe</emphasis> hacer esto automáticamente, ya
que introduciría semánticas de transacción ad hoc!
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ A veces no quieres inicializar una colección grande, pero
necesitas aún alguna informacion sobre
+ ella (como su tamaño) o un subconjunto de los datos.
+ </para>
+
+ <para>
+ Puedes usar un filtro de colecciones para obtener el tamaño de
una colección sin inicializarla:
+ </para>
+
+ <programlisting><![CDATA[( (Integer) s.createFilter( collection,
"select count(*)" ).list().get(0) ).intValue()]]></programlisting>
+
+ <para>
+ El método <literal>createFilter()</literal> se usa
también para recuperar eficientemente subconjuntos
+ de una colección sin necesidad de inicializar toda la
colección:
+ </para>
+
+ <programlisting><![CDATA[s.createFilter( lazyCollection,
"").setFirstResult(0).setMaxResults(10).list();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-batch">
+ <title>Usando recuperación en lotes</title>
+
+ <para>
+ Hibernate puede hacer un uso eficiente de la recuperación en
lotes, esto es, Hibernate puede cargar
+ muchos proxies sin inicializar si se accede a un proxy (o colecciones).
La recuperación en lotes es una
+ optimización de la estrategia de recuperación por
selección perezosa. Hay dos formas en que puedes afinar
+ la recuperación en lotes: a nivel de la clase o de la
colección.
+ </para>
+
+ <para>
+ La recuperación en lotes para clases/entidades es
más fácil de entender. Imagina que tienes la siguiente
+ situación en tiempo de ejecución: Tienes 25
instancias de <literal>Cat</literal> cargadas en una
+ <literal>Session</literal>, cada
<literal>Cat</literal> tiene una referencia a su
<literal>owner</literal>,
+ una <literal>Person</literal>. La clase
<literal>Person</literal> está mapeada con un proxy,
+ <literal>lazy="true"</literal>. Si ahora iteras a
través de todos los gatos y llamas a
+ <literal>getOwner()</literal> para cada uno, Hibernate por
defecto ejecutará 25 sentencias
+ <literal>SELECT</literal> para traer los dueños
tratados con proxies. Puedes afinar este comportamiento
+ especificando un <literal>batch-size</literal> en el mapeo de
<literal>Person</literal>:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person"
batch-size="10">...</class>]]></programlisting>
+
+ <para>
+ Hibernate ahora ejecutará sólo tres consultas, el
patrón es 10, 10, 5.
+ </para>
+
+ <para>
+ También puedes habilitar la recuperación en lotes
para colecciones. Por ejemplo, si cada
+ <literal>Person</literal> tiene una colección
perezosa de <literal>Cat</literal>s, y hay 10
+ personas actualmente cargadas en la
<literal>Session</literal>, iterar a través de las 10 personas
+ generará 10 <literal>SELECT</literal>s, una para
cada llamada a <literal>getCats()</literal>.
+ Si habilitas la recuperación en lotes para la
colección de <literal>cats</literal> en el mapeo de
+ <literal>Person</literal>, Hibernate puede recuperar por
adelantado las colecciones:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <set name="cats" batch-size="3">
+ ...
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ Con un <literal>batch-size</literal> de 3, Hibernate
cargará 3, 3, 3, 1 colecciones en cuatro
+ <literal>SELECT</literal>s. Una vez más, el valor
del atributo depende del número esperado de
+ colecciones sin inicializar en una <literal>Session</literal>
en particular.
+ </para>
+
+ <para>
+ La recuperación de coleccione en lotes es particularmente
útil si tienes un árbol anidado de
+ ítems, es decir, el típico patrón de
cuenta de materiales. (Aunque un <emphasis>conjunto
+ anidado</emphasis> o una <emphasis>ruta
materializada</emphasis> podría ser una mejor opción
+ para árboles que sean de lectura en la mayoría de
los casos.)
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-subselect">
+ <title>Usando recuperación por
subselección</title>
+
+ <para>
+ Si una colección perezosa o proxy monovaluado tiene que ser
recuperado, Hibernate los carga a todos,
+ volviendo a ejecutar la consulta original en una subselección.
Esto funciona de la misma forma que
+ la recuperación en lotes, sin carga fragmentaria.
+ </para>
+
+ <!-- TODO: Write more about this -->
+
+ </sect2>
+
+ <sect2 id="performance-fetching-lazyproperties">
+ <title>Usando recuperación perezosa de
propiedades</title>
+
+ <para>
+ Hibernate3 soporta la recuperación perezosa de propiedades
individuales. Esta técnica de optimización
+ es también conocida como <emphasis>grupos de
recuperación (fetch groups)</emphasis>. Por favor, nota
+ que éste es mayormente un aspecto de marketing, ya que en la
práctica, optimizar lecturas de filas es
+ mucho más importante que la optimización de lectura
de columnas. Sin embargo, cargar sólo algunas
+ propiedades de una clase podría ser útil en casos
extremos, cuando tablas heredadas tienen cientos de
+ columnas y el modelo de datos no puede ser mejorado.
+ </para>
+
+ <para>
+ Para habilitar la carga perezosa de propiedades, establece el atributo
<literal>lazy</literal> en tus
+ mapeos de propiedades:
+ </para>
+
+ <programlisting><![CDATA[<class name="Document">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name" not-null="true"
length="50"/>
+ <property name="summary" not-null="true"
length="200" lazy="true"/>
+ <property name="text" not-null="true" length="2000"
lazy="true"/>
+</class>]]></programlisting>
+
+ <para>
+ ¡La carga perezosa de propiedades requiere la
instrumentación del bytecode en tiempo
+ de construcción! Si tus clases persistentes no son mejoradas,
Hibernate ignorará silenciosamente
+ la configuración perezosa de propiedades y caerá en
recuperación inmediata.
+ </para>
+
+ <para>
+ Para la instrumentación del bytecode, usa la siguiente tarea
Ant:
+ </para>
+
+ <programlisting><![CDATA[<target name="instrument"
depends="compile">
+ <taskdef name="instrument"
classname="org.hibernate.tool.instrument.InstrumentTask">
+ <classpath path="${jar.path}"/>
+ <classpath path="${classes.dir}"/>
+ <classpath refid="lib.class.path"/>
+ </taskdef>
+
+ <instrument verbose="true">
+ <fileset dir="${testclasses.dir}/org/hibernate/auction/model">
+ <include name="*.class"/>
+ </fileset>
+ </instrument>
+</target>]]></programlisting>
+
+ <para>
+ Una forma diferente (¿mejor?) de evitar lecturas innecesarias
de columnas, al menos para
+ transacciones de sólo lectura es usar las funcionalidades de
proyección de consultas HQL o Criteria.
+ Esto evita la necesidad de procesar el bytecode en tiempo de
construcción y ciertamente es una solución
+ preferida.
+ </para>
+
+ <para>
+ Puedes forzar la usual recuperación temprana de propiedades
usando <literal>fetch all properties</literal>
+ en HQL.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="performance-cache" revision="1">
+ <title>El Caché de Segundo Nivel</title>
+
+ <para>
+ Una <literal>Session</literal> de Hibernate es una
caché de datos persistentes a nivel de transacción.
+ Es posible configurar un cluster o caché a nivel de JVM (a nivel
de <literal>SessionFactory</literal>)
+ sobre una base de clase-a-clase o
colección-a-colección. Puedes incluso enchufar una caché
en cluster.
+ Sé cuidadoso. Las cachés nunca están al
tanto de los cambios hechos por otra aplicación al almacén
persistente
+ (aunque pueden ser configurados para expirar regularmente los datos en
caché).
+ </para>
+
+ <para>
+ Por defecto, Hibernate usa EHCache para caching a nivel de JVM. (El soporte a
JCS ahora está despreciado
+ y será quitado en una futura versión de Hibernate.)
Puedes elegir una implementación diferente estableciendo
+ el nombre de una clase que implemente
<literal>org.hibernate.cache.CacheProvider</literal> usando la propiedad
+ <literal>hibernate.cache.provider_class</literal>.
+ </para>
+
+ <table frame="topbot" id="cacheproviders"
revision="1">
+ <title>Proveedores de Caché</title>
+ <tgroup cols='5' align='left' colsep='1'
rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="3*"/>
+ <colspec colname='c3' colwidth="1*"/>
+ <colspec colname='c4' colwidth="1*"/>
+ <colspec colname='c5' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Caché</entry>
+ <entry>clase del Provedor</entry>
+ <entry>Tipo</entry>
+ <entry>Cluster Seguro</entry>
+ <entry>Caché de Consultas Soportado</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Hashtable (no pensado para uso en
producción)</entry>
+
<entry><literal>org.hibernate.cache.HashtableCacheProvider</literal></entry>
+ <entry>memoria</entry>
+ <entry></entry>
+ <entry>sí</entry>
+ </row>
+ <row>
+ <entry>EHCache</entry>
+
<entry><literal>org.hibernate.cache.EhCacheProvider</literal></entry>
+ <entry>memoria, disco</entry>
+ <entry></entry>
+ <entry>sí</entry>
+ </row>
+ <row>
+ <entry>OSCache</entry>
+
<entry><literal>org.hibernate.cache.OSCacheProvider</literal></entry>
+ <entry>memoria, disco</entry>
+ <entry></entry>
+ <entry>sí</entry>
+ </row>
+ <row>
+ <entry>SwarmCache</entry>
+
<entry><literal>org.hibernate.cache.SwarmCacheProvider</literal></entry>
+ <entry>clusterizado (ip multicast)</entry>
+ <entry>sí (invalidación en
cluster)</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>TreeCache de JBoss</entry>
+
<entry><literal>org.hibernate.cache.TreeCacheProvider</literal></entry>
+ <entry>clusterizado (ip multicast), transaccional</entry>
+ <entry>sí (replicación)</entry>
+ <entry>sí (requiere sincronización de
reloj)</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <sect2 id="performance-cache-mapping">
+ <title>Mapeos de caché</title>
+
+ <para>
+ El elemento <literal><cache></literal> de una
mapeo de clase o colección tiene la siguiente
+ forma:
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="cache1" coords="2 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<cache
+ usage="transactional|read-write|nonstrict-read-write|read-only"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="cache1">
+ <para>
+ <literal>usage</literal> especifica la estrategia
de caching:
+ <literal>transactional</literal>,
+ <literal>read-write</literal>,
+ <literal>nonstrict-read-write</literal> o
+ <literal>read-only</literal>
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Alternativamente (¿preferiblemente?), puedes especificar los
elementos
+ <literal><class-cache></literal> y
<literal><collection-cache></literal> en
+ <literal>hibernate.cfg.xml</literal>.
+ </para>
+
+ <para>
+ El atributo <literal>usage</literal> especifica una
<emphasis>estrategia de concurrencia al
+ caché</emphasis>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-cache-readonly">
+ <title>Estrategia: sólo lectura (read only)</title>
+
+ <para>
+ Si tu aplicación necesita leer pero nunca modificar las
instancias de una clase persistente,
+ puede usarse un caché
<literal>read-only</literal>. Esta es la mejor y más simple
estrategia.
+ Es incluso perfectamente segura de usar en un cluster.
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Immutable"
mutable="false">
+ <cache usage="read-only"/>
+ ....
+</class>]]></programlisting>
+
+ </sect2>
+
+
+ <sect2 id="performance-cache-readwrite">
+ <title>Estrategia: lectura/escritura (read/write)</title>
+
+ <para>
+ Si la aplicación necesita actualizar datos, un
caché <literal>read-write</literal> podría ser
apropiado.
+ Esta estrategia de caché nunca debe ser usada si se requiere
nivel de aislamiento serializable de
+ transacciones. Si el caché es usado en un entorno JTA, debes
especificar la propiedad
+
<literal>hibernate.transaction.manager_lookup_class</literal>, mencionando una
estrategia para obtener
+ el <literal>TransactionManager</literal> de JTA. En otros
entornos, debes asegurarte que la transacción
+ esté completada cuando se llame a
<literal>Session.close()</literal> o
+ <literal>Session.disconnect()</literal>. Si deseas usar esta
estrategia en un cluster, debes asegurarte
+ que la implementación de caché subyacente soporta
bloqueos. Los provedores de caché internos
+ predeterminados <emphasis>no</emphasis> no lo soportan.
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Cat" ....
>
+ <cache usage="read-write"/>
+ ....
+ <set name="kittens" ... >
+ <cache usage="read-write"/>
+ ....
+ </set>
+</class>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="performance-cache-nonstrict">
+ <title>Estrategia: lectura/escritura no estricta (nonstrict
read/write)</title>
+
+ <para>
+ Si la aplicación necesita sólo ocasionalmente
actualizar datos (es decir, es extremadamente inprobable
+ que dos transacciones intenten actualizar el mismo ítem
simultáneamente) y no se requiere de un
+ aislamiento de transacciones estricto, un caché
<literal>nonstrict-read-write</literal> podría ser
+ apropiado. Si se usa el caché en un entorno JTA, debes
especificar
+
<literal>hibernate.transaction.manager_lookup_class</literal>. En otros
entornos, debes asegurarte que la
+ transacción se haya completado cuando se llame a
<literal>Session.close()</literal> o
+ <literal>Session.disconnect()</literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-cache-transactional">
+ <title>Estrategia: transaccional</title>
+
+ <para>
+ La estrategia de caché
<literal>transactional</literal> brinda soporte a provedores de
cachés
+ completamente transaccionales como TreeCache de JBoss. Un
caché así, puede sólo ser usado en un
+ entorno JTA y debes especificar
<literal>hibernate.transaction.manager_lookup_class</literal>.
+ </para>
+
+ </sect2>
+
+ <para>
+ Ninguno de los provedores de caché soporta todas las estrategias
de concurrencia al caché. La siguiente
+ tabla muestra qué provedores son compatibles con qué
estrategias de concurrencia.
+ </para>
+
+ <table frame="topbot">
+ <title>Soporte a Estrategia de Concurrencia a
Caché</title>
+ <tgroup cols='5' align='left' colsep='1'
rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <colspec colname='c3' colwidth="1*"/>
+ <colspec colname='c4' colwidth="1*"/>
+ <colspec colname='c5' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Caché</entry>
+ <entry>read-only</entry>
+ <entry>nonstrict-read-write</entry>
+ <entry>read-write</entry>
+ <entry>transactional</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Hashtable (no pensado para uso en
producción)</entry>
+ <entry>sí</entry>
+ <entry>sí</entry>
+ <entry>sí</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>EHCache</entry>
+ <entry>sí</entry>
+ <entry>sí</entry>
+ <entry>sí</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>OSCache</entry>
+ <entry>sí</entry>
+ <entry>sí</entry>
+ <entry>sí</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>SwarmCache</entry>
+ <entry>sí</entry>
+ <entry>sí</entry>
+ <entry></entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>JBoss TreeCache</entry>
+ <entry>sí</entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>sí</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect1>
+
+ <sect1 id="performance-sessioncache" revision="2">
+ <title>Gestionando los cachés</title>
+
+ <para>
+ Siempre que pases un objeto a <literal>save()</literal>,
<literal>update()</literal>
+ o <literal>saveOrUpdate()</literal> y siempre que recuperes un
objeto usando
+ <literal>load()</literal>, <literal>get()</literal>,
<literal>list()</literal>,
+ <literal>iterate()</literal> o
<literal>scroll()</literal>, ese objeto es agregado
+ al caché interno de la <literal>Session</literal>.
+ </para>
+ <para>
+ Cuando subsecuentemente se llame a <literal>flush()</literal>, el
estado de ese objeto será
+ sincronizado con la base de datos. Si no quieres que ocurra esta
sincronización o si estás
+ procesando un número enorme de objetos y necesitas gestionar la
memoria eficientemente,
+ puede usarse el método <literal>evict()</literal> para
quitar el objeto y sus colecciones
+ del caché de primer nivel.
+ </para>
+
+ <programlisting><![CDATA[ScrollableResult cats =
sess.createQuery("from Cat as cat").scroll(); //a huge result set
+while ( cats.next() ) {
+ Cat cat = (Cat) cats.get(0);
+ doSomethingWithACat(cat);
+ sess.evict(cat);
+}]]></programlisting>
+
+ <para>
+ La <literal>Session</literal> también provee un
método <literal>contains()</literal> para determinar
+ si una instancia pertenece al caché de la sesión.
+ </para>
+
+ <para>
+ Para desahuciar (evict) todos los objetos del caché de
sesión, llama a <literal>Session.clear()</literal>.
+ </para>
+
+ <para>
+ Para el caché de segundo nivel, hay métodos definidos
en <literal>SessionFactory</literal> para
+ desahuciar el estado en caché de una instancia, clase entera,
instancia de colección o rol
+ enter de colección.
+ </para>
+
+ <programlisting><![CDATA[sessionFactory.evict(Cat.class, catId); //evict
a particular Cat
+sessionFactory.evict(Cat.class); //evict all Cats
+sessionFactory.evictCollection("Cat.kittens", catId); //evict a particular
collection of kittens
+sessionFactory.evictCollection("Cat.kittens"); //evict all kitten
collections]]></programlisting>
+
+ <para>
+ El <literal>CacheMode</literal> controla cómo una
sesión en particular interactúa con el caché de segundo
+ nivel.
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>CacheMode.NORMAL</literal> - lee ítems desde
y escribe ítems hacia el caché de segundo nivel
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>CacheMode.GET</literal> - lee ítems del
caché de segundo nivel, pero no escribe al caché de
+ segundo nivel excepto al actualizar datos
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>CacheMode.PUT</literal> - escribe ítems al
caché de segundo nivel, pero no lee del caché de segundo
+ nivel
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>CacheMode.REFRESH</literal> - escribe ítems
al caché de segundo nivel, pero no lee del caché de
+ segundo nivel, saltándose el efecto de
<literal>hibernate.cache.use_minimal_puts</literal>, forzando
+ un refresco del caché de segundo nivel para todos los
ítems leídos de la base de datos
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Para navegar por los contenidos de una región de caché
de segundo nivel o de consultas, usa la API de
+ <literal>Statistics</literal>:
+ </para>
+
+ <programlisting><![CDATA[Map cacheEntries =
sessionFactory.getStatistics()
+ .getSecondLevelCacheStatistics(regionName)
+ .getEntries();]]></programlisting>
+
+ <para>
+ Necesitarás habilitar las estadísticas y,
opcionalmente, forzar a Hibernate para que guarde las
+ entradas del caché en un formato más entendible por
humanos:
+ </para>
+
+ <programlisting><![CDATA[hibernate.generate_statistics true
+hibernate.cache.use_structured_entries true]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="performance-querycache" revision="1">
+ <title>El Caché de Consultas</title>
+
+ <para>
+ Los conjuntos resultado de consultas también pueden tratarse en
caché. Esto sólo es útil para
+ consultas que se ejecutan frecuentemente con los mismos
parámetros. Para usar el caché de consultas
+ primero debes habilitarlo:
+ </para>
+
+ <programlisting><![CDATA[hibernate.cache.use_query_cache
true]]></programlisting>
+
+ <para>
+ Esta configuración causa la creación de dos nuevas
regiones de caché - una teniendo en caché
+ conjuntos resultado de consulta
(<literal>org.hibernate.cache.StandardQueryCache</literal>),
+ el otro teniendo timestamps de las actualizaciones más recientes a
tablas consultables
+ (<literal>org.hibernate.cache.UpdateTimestampsCache</literal>).
Nota que el caché de consultas
+ no pone en caché el estado de las entidades reales en el conjunto
resultado; sólo tiene en caché
+ valores indentificadores y resultados de tipo de valor. De modo que el
caché de consultas siempre
+ debe ser usado en conjunción con el caché de segundo
nivel.
+ </para>
+
+ <para>
+ La mayoría de consultas no se benefician del tratamiento en
caché, de modo que por defecto las
+ consultas no son tratadas en caché. Para habilitar el tratamiento
en caché, llama a
+ <literal>Query.setCacheable(true)</literal>. Esta llamada permite
a la consulta buscar
+ resultados existentes en caché o agregar sus resultados al
caché cuando se ejecuta.
+ </para>
+
+ <para>
+ Si requieres un control finamente granularizado sobre las
políticas de expiración del caché de
+ consultas, puedes especificar una región de caché con
nombre para una consulta en particular
+ llamando a <literal>Query.setCacheRegion()</literal>.
+ </para>
+
+ <programlisting><![CDATA[List blogs = sess.createQuery("from Blog
blog where blog.blogger = :blogger")
+ .setEntity("blogger", blogger)
+ .setMaxResults(15)
+ .setCacheable(true)
+ .setCacheRegion("frontpages")
+ .list();]]></programlisting>
+
+ <para>
+ Si la consulta debe forzar un refresco de si región del
caché de consultas, debes llamar a
+ <literal>Query.setCacheMode(CacheMode.REFRESH)</literal>. Esto es
particularmente útil en casos donde
+ los datos subyacentes pueden haber sido actualizados por medio de un proceso
separado (es decir,
+ no modificados a través de Hibernate) y permite a la
aplicación refrescar selectivamente conjuntos
+ resultado de consultas en particular. Esto es una alternativa más
eficient al desahuciamiento de una
+ región del caché de consultas vía
<literal>SessionFactory.evictQueries()</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="performance-collections">
+ <title>Entendiendo el rendimiento de Colecciones</title>
+
+ <para>
+ Ya hemos llevado un buen tiempo hablando sobre colecciones.
+ En esta sección resaltaremos un par de temas más sobre
cómo las colecciones
+ se comportan en tiempo de ejecución.
+ </para>
+
+ <sect2 id="performance-collections-taxonomy">
+ <title>Taxonomia</title>
+
+ <para>Hibernate define tres tipos básicos de
colecciones:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>colecciones de valores</para>
+ </listitem>
+ <listitem>
+ <para>asociaciones uno a muchos</para>
+ </listitem>
+ <listitem>
+ <para>asociaciones muchos a muchos</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Esta clasificación distingue las varias tablas y relaciones de
clave foránea pero no nos
+ dice absolutamente todo lo que necesitamos saber sobre el modelo
relacional. Para entender
+ completamente la estructura relacional y las características
de rendimiento, debemos considerar
+ la estructura de la clave primaria que es usada por Hibernate para
actualizar o borrar filas de
+ colección. Esto sugiere la siguiente
clasificación:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>colecciones indexadas</para>
+ </listitem>
+ <listitem>
+ <para>conjuntos (sets)</para>
+ </listitem>
+ <listitem>
+ <para>bolsas (bags)</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Todas las colecciones indexadas (mapas, listas, arrays) tienen una clave
primaria
+ consistente de las columnas
<literal><key></literal> y
<literal><index></literal>.
+ En este caso las actualizaciones de colecciones son usualmente
extremadamente eficientes.
+ La clave primaria puede ser indexada fácilmente y una fila en
particular puede ser localizada
+ cuando Hibernate intenta actualizarla o borrarla.
+ </para>
+
+ <para>
+ Los conjuntos (sets) tienen una clave primaria consistente en
<literal><key></literal>
+ y columnas de elemento. Esto puede ser menos eficiente para algunos tipos
de elemento de
+ colección, particularmente elementos compuestos o texto largo,
o campos binarios. La base de datos
+ puede no ser capaz de indexar una clave primaria compleja eficientemente.
Por otra parte,
+ para asociaciones uno a muchos o muchos a muchos, particularmente en el
caso de identificadores
+ sintéticos, es probable que sólo sea tan eficiente.
(Nota al márgen: si quieres que
+ <literal>SchemaExport</literal> realmente cree la clave
primaria de un <literal><set></literal>
+ por ti, debes declarar todas las columnas como
<literal>not-null="true"</literal>.)
+ </para>
+
+ <para>
+ Los mapeos de <literal><idbag></literal>
definen una clave delegada, de modo que siempre
+ resulten eficientes de actualizar. De hecho, son el mejor caso.
+ </para>
+
+ <para>
+ Los bags son el peor caso. Ya que un bag permite valores de elementos
duplicados y no tiene
+ ninguna columna índice, no puede definirse ninguna clave
primaria. Hibernate no tiene forma de
+ distinguir entre filas duplicadas. Hibernate resuelve este problema
quitando completamente
+ (en un solo <literal>DELETE</literal>) y recreando la
colección siempre que cambia.
+ Esto podría ser muy ineficiente.
+ </para>
+
+ <para>
+ Nota que para una asociación uno-a-muchos, la "clave
primaria" puede no ser la clave
+ primaria física de la tabla de base de datos; pero incluso en
este caso, la clasificación
+ anterior es útil todavía. (Aún refleja
cómo Hibernate "localiza" filas individuales de la
+ colección.)
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-collections-mostefficientupdate">
+ <title>Las listas, mapas, idbags y conjuntos son las colecciones
más eficientes de actualizar</title>
+
+ <para>
+ Desde la discusión anterior, debe quedar claro que las
colecciones indexadas y
+ (usualmente) los conjuntos permiten la operación
más eficiente en términos de añadir,
+ quitar y actualizar elementos.
+ </para>
+
+ <para>
+ Hay, discutiblemente, una ventaja más que las colecciones
indexadas tienen sobre otros
+ conjuntos para las asociaciones muchos a muchos o colecciones de valores.
Debido a la
+ estructura de un <literal>Set</literal>, Hibernate ni
siquiera actualiza una fila con
+ <literal>UPDATE</literal> cuando se "cambia" un
elemento. Los cambios a un <literal>Set</literal>
+ siempre funcionan por medio de <literal>INSERT</literal> y
<literal>DELETE</literal>
+ (de filas individuales). Una vez más, esta
consideración no se aplica a las asociaciones
+ uno a muchos.
+ </para>
+
+ <para>
+ Después de observar que los arrays no pueden ser perezosos,
podríamos concluir que las
+ listas, mapas e idbags son los tipos más eficientes de
colecciones (no inversas), con los
+ conjuntos (sets) no muy por detrás. Se espera que los sets
sean el tipo más común de colección
+ en las aplicaciones de Hibernate. Esto es debido a que la
semántica de los sets es la más
+ natural en el modelo relacional.
+ </para>
+
+ <para>
+ Sin embargo, en modelos de dominio de Hibernate bien dieñados,
usualmente vemos que la mayoría
+ de las colecciones son de hecho asociaciones uno-a-muchos con
<literal>inverse="true"</literal>.
+ Para estas asociaciones, la actualización es manejada por el
extremo muchos-a-uno de la asociación,
+ y las consideraciones de este tipo sobre el rendimiento de
actualización de colecciones simplemente
+ no se aplican.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-collections-mostefficentinverse">
+ <title>Los Bags y las listas son las colecciones inversas
más eficientes</title>
+
+ <para>
+ Justo antes que tires a la zanja los bags para siempre, hay un caso en
particular en el que
+ los bags son muchos más eficientes que los conjuntos. Para una
colección con
+ <literal>inverse="true"</literal> (el idioma
estándar de relaciones uno-a-muchos bidireccionales,
+ por ejemplo) ¡podemos añadir elementos a un bag o
lista sin necesidad de inicializar (fetch)
+ los elementos del bag! Esto se debe a que
<literal>Collection.add()</literal> o
+ <literal>Collection.addAll()</literal> siempre deben devolver
true para un bag o <literal>List</literal>
+ (no como un <literal>Set</literal>). Esto puede hacer el
siguiente código común mucho más rápido.
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) sess.load(Parent.class,
id);
+ Child c = new Child();
+ c.setParent(p);
+ p.getChildren().add(c); //no need to fetch the collection!
+ sess.flush();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="performance-collections-oneshotdelete">
+ <title>Borrado de un solo tiro</title>
+
+ <para>
+ Ocasionalmente, borrar los elementos de una colección uno a
uno puede ser extremadamente ineficiente.
+ Hibernate no es completamente estúpido, de modo que sabe no
hacer eso, en el caso de una colección
+ nueva-vacía (si has llamado a
<literal>list.clear()</literal>, por ejemplo). En este caso, Hibernate
+ publicará una sola <literal>DELETE</literal>,
¡y listo!
+ </para>
+
+ <para>
+ Supón que añadimos un solo elemento a una
colección de tamaño veinte y luego quitamos dos elementos.
+ Hibernate publicará una sentencia
<literal>INSERT</literal> y dos sentencias
<literal>DELETE</literal>
+ (a menos que la colección sea un bag). Esto es ciertamente
deseable.
+ </para>
+
+ <para>
+ Sin embargo, supón que quitamos dieciocho elementos, dejando
dos y luego añadimos tres nuevos elementos.
+ Hay dos formas posibles de proceder
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>borrar dieciocho filas una a una y luego insertar tres
filas</para>
+ </listitem>
+ <listitem>
+ <para>quitar toda la colección (en un solo
<literal>DELETE</literal> de SQL) e insertar todos los
+ cinco elementos actuales (uno a uno)</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernate no es lo suficientemente inteligente para saber que la segunda
opción es probablemente más
+ rápida en este caso. (Y que sería probablemente
indeseable para Hibernate ser tan inteligente;
+ este comportamiento podría confundir a disparadores de base de
datos, etc.)
+ </para>
+
+ <para>
+ Afortunadamente, puedes forzar este comportamiento (es decir, la segunda
estrategia) en cualquier
+ momento descartando (es decir, desreferenciando) la colección
original y devolviendo una colección
+ nuevamente instanciada con todos los elementos actuales. Esto puede ser
muy útil y potente de vez en
+ cuando.
+ </para>
+
+ <para>
+ Por supuesto, el borrado-de-un-solo-tiro no se aplica a colecciones
mapeadas
+ <literal>inverse="true"</literal>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="performance-monitoring" revision="1">
+ <title>Monitoreando el rendimiento</title>
+
+ <para>
+ La optimización no es de mucho uso sin el monitoreo y el acceso a
números de rendimiento. Hibernate provee
+ un rango completo de figuras sobre sus operaciones internas. Las
estadísticas en Hibernate están disponibles
+ por <literal>SessionFactory</literal>.
+ </para>
+
+ <sect2 id="performance-monitoring-sf" revision="2">
+ <title>Monitoreando una SessionFactory</title>
+
+ <para>
+ Puedes acceder a las métricas de
<literal>SessionFactory</literal> de dos formas.
+ Tu primera opción es llamar a
<literal>sessionFactory.getStatistics()</literal> y
+ leer o mostrar por pantalla la <literal>Statistics</literal>
por ti mismo.
+ </para>
+
+ <para>
+ Hibernate puede también usar JMX para publicar las
métricas si habilitas el MBean
+ <literal>StatisticsService</literal>. Puede habilitar un solo
MBean para todas tus
+ <literal>SessionFactory</literal> o una por
fábrica. Mira el siguiente código para
+ ejemplos de configuración minimalistas:
+ </para>
+
+ <programlisting><![CDATA[// MBean service registration for a
specific SessionFactory
+Hashtable tb = new Hashtable();
+tb.put("type", "statistics");
+tb.put("sessionFactory", "myFinancialApp");
+ObjectName on = new ObjectName("hibernate", tb); // MBean object name
+
+StatisticsService stats = new StatisticsService(); // MBean implementation
+stats.setSessionFactory(sessionFactory); // Bind the stats to a SessionFactory
+server.registerMBean(stats, on); // Register the Mbean on the
server]]></programlisting>
+
+
+<programlisting><![CDATA[// MBean service registration for all
SessionFactory's
+Hashtable tb = new Hashtable();
+tb.put("type", "statistics");
+tb.put("sessionFactory", "all");
+ObjectName on = new ObjectName("hibernate", tb); // MBean object name
+
+StatisticsService stats = new StatisticsService(); // MBean implementation
+server.registerMBean(stats, on); // Register the MBean on the
server]]></programlisting>
+
+ <para>
+ POR HACER: Esto no tiene sentido: En el primer caso, recuperamos y usamos
el MBean directamente.
+ En el segundo, debemos proporcionar el nombre JNDI en el que se guarda la
fábrica de sesiones antes
+ de usarlo. Usa
<literal>hibernateStatsBean.setSessionFactoryJNDIName("my/JNDI/Name")</literal>
+ </para>
+ <para>
+ Puedes (des)activar el monitoreo de una
<literal>SessionFactory</literal>
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ en tiempo de configuración, establece
<literal>hibernate.generate_statistics</literal> a
+ <literal>false</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+ <itemizedlist>
+ <listitem>
+ <para>
+ en tiempo de ejecución:
<literal>sf.getStatistics().setStatisticsEnabled(true)</literal>
+ o
<literal>hibernateStatsBean.setStatisticsEnabled(true)</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Las estadísticas pueden ser reajustadas
programáticamente usando el método
<literal>clear()</literal>.
+ Puede enviarse un resumen a un logger (nivel info) usando el
método <literal>logSummary()</literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-monitoring-metrics"
revision="1">
+ <title>Métricas</title>
+
+ <para>
+ Hibernate provee un número de métricas, desde
información muy básica a la especializada
+ sólo relevante en ciertos escenarios. Todos los contadores
disponibles se describen en la
+ API de la interface <literal>Statistics</literal>, en tres
categorías:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ Métricas relacionadas al uso general de
<literal>Session</literal> usage, tales como
+ número de sesiones abiertas, conexiones JDBC
recuperadas, etc,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Métricas relacionadas a las entidades, colecciones,
consultas, y cachés como un todo.
+ (también conocidas como métricas
globales).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Métricas detalladas relacionadas a una entidad,
colección, consulta o región de caché
+ en particular.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Por ejemplo, puedes comprobar el acceso, pérdida, y radio de
colocación de entidades, colecciones
+ y consultas en el caché, y el tiempo promedio que necesita una
consulta. Ten en cuenta que el número
+ de milisegundos está sujeto a aproximación en Java.
Hibernate está pegado a la precisión de la JVM,
+ en algunas plataformas esto podría incuso ser tener
sólo una exactitud de 10 segundos.
+ </para>
+
+ <para>
+ Se usan getters simples para acceder a las métricas globales
(es decir, no pegadas a una entidad,
+ colección, región de caché, etc, en
particular). Puedes acceder a las métricas de una entidad,
+ colección, región de caché en particular
a través de su nombre, y a través de su representación
HQL
+ o SQL para las consultas. Por favor refiérete al Javadoc de la
API de <literal>Statistics</literal>,
+ <literal>EntityStatistics</literal>,
<literal>CollectionStatistics</literal>,
+ <literal>SecondLevelCacheStatistics</literal>, y
<literal>QueryStatistics</literal> para más
información.
+ El siguiente código muestra un ejemplo sencillo:
+ </para>
+
+ <programlisting><![CDATA[Statistics stats =
HibernateUtil.sessionFactory.getStatistics();
+
+double queryCacheHitCount = stats.getQueryCacheHitCount();
+double queryCacheMissCount = stats.getQueryCacheMissCount();
+double queryCacheHitRatio =
+ queryCacheHitCount / (queryCacheHitCount + queryCacheMissCount);
+
+log.info("Query Hit ratio:" + queryCacheHitRatio);
+
+EntityStatistics entityStats =
+ stats.getEntityStatistics( Cat.class.getName() );
+long changes =
+ entityStats.getInsertCount()
+ + entityStats.getUpdateCount()
+ + entityStats.getDeleteCount();
+log.info(Cat.class.getName() + " changed " + changes + "times"
);]]></programlisting>
+
+ <para>
+ Para trabajar sobre todas las entidades, colecciones, consultas y
regiones de cachés, puedes recuperar la
+ lista de nombres de entidades, colecciones, consultas y regiones de
cachés con los siguientes métodos:
+ <literal>getQueries()</literal>,
<literal>getEntityNames()</literal>,
+ <literal>getCollectionRoleNames()</literal>, y
<literal>getSecondLevelCacheRegionNames()</literal>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Copied:
core/trunk/documentation/manual/es-ES/src/main/docbook/content/persistent_classes.xml
(from rev 12794,
core/trunk/documentation/manual/es-ES/src/main/docbook/modules/persistent_classes.xml)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/content/persistent_classes.xml
(rev 0)
+++
core/trunk/documentation/manual/es-ES/src/main/docbook/content/persistent_classes.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,478 @@
+<chapter id="persistent-classes" revision="2">
+ <title>Clases Persistentes</title>
+
+ <para>
+ Clases presistentes son clases en una aplicación que implementan las
+ entidades del problema de negocio (por ejemplo, Customer y Order en una
+ aplicación de comercio electrónico). No todas las
instancias de una
+ clase persistente se considera que estén en el estado persistente,
+ una instancia puede en cambio ser transitoria o estar separada.
+ </para>
+
+ <para>
+ Hibernate funciona mejor si las clases siguen algunas simples reglas,
también
+ conocidas como el modelo de programación de Viejas Clases Java Planas
+ (Plain Old Java Object o POJO). Sin embargo, ninguna de estas reglas son
+ requerimientos rígidos. En cambio, Hibernate3 asume muy poco acerca
de
+ la naturaleza de tus objetos persistentes. Puedes expresar un modelo de dominio
en
+ otras formas: usando árboles de instancias de
<literal>Map</literal>,
+ por ejemplo.
+ </para>
+
+ <sect1 id="persistent-classes-pojo">
+ <title>Un ejemplo simple de POJO</title>
+
+ <para>
+ La mayoría de aplicaciones Java requieren una clase
+ representando felinos.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+import java.util.Set;
+import java.util.Date;
+
+public class Cat {
+ private Long id; // identifier
+
+ private Date birthdate;
+ private Color color;
+ private char sex;
+ private float weight;
+ private int litterId;
+
+ private Cat mother;
+ private Set kittens = new HashSet();
+
+ private void setId(Long id) {
+ this.id=id;
+ }
+ public Long getId() {
+ return id;
+ }
+
+ void setBirthdate(Date date) {
+ birthdate = date;
+ }
+ public Date getBirthdate() {
+ return birthdate;
+ }
+
+ void setWeight(float weight) {
+ this.weight = weight;
+ }
+ public float getWeight() {
+ return weight;
+ }
+
+ public Color getColor() {
+ return color;
+ }
+ void setColor(Color color) {
+ this.color = color;
+ }
+
+ void setSex(char sex) {
+ this.sex=sex;
+ }
+ public char getSex() {
+ return sex;
+ }
+
+ void setLitterId(int id) {
+ this.litterId = id;
+ }
+ public int getLitterId() {
+ return litterId;
+ }
+
+ void setMother(Cat mother) {
+ this.mother = mother;
+ }
+ public Cat getMother() {
+ return mother;
+ }
+ void setKittens(Set kittens) {
+ this.kittens = kittens;
+ }
+ public Set getKittens() {
+ return kittens;
+ }
+
+ // addKitten not needed by Hibernate
+ public void addKitten(Cat kitten) {
+ kitten.setMother(this);
+ kitten.setLitterId( kittens.size() );
+ kittens.add(kitten);
+ }
+}]]></programlisting>
+
+ <para>
+ Aquí hay cuatro reglas principales a seguir:
+ </para>
+
+ <sect2 id="persistent-classes-pojo-constructor"
revision="1">
+ <title>Implementa un constructor sin argumentos</title>
+
+ <para>
+ <literal>Cat</literal> tiene un contructor sin argumentos.
Todas las clases persistentes
+ deben tener un constructor por defecto (que puede no ser
público) de modo que Hibernate
+ pueda instanciarlas usando
<literal>Constructor.newInstance()</literal>. Recomendamos fuertemente tener
+ un constructor por defecto con al menos visibilidad de
<emphasis>package</emphasis> para la
+ generación de proxies en tiempo de ejecución en
Hibernate.
+ </para>
+ </sect2>
+
+ <sect2 id="persistent-classes-pojo-identifier"
revision="2">
+ <title>Provee una propiedad identificadora (opcional)</title>
+
+ <para>
+ <literal>Cat</literal> tiene una propiedad llamada
<literal>id</literal>. Esta
+ propiedad mapea a la columna clave primaria de la tabla de base de datos.
La propiedad
+ podría llamarse cualquierCosa, y su tipo podría
haber sido cualquier tipo
+ primitivo, cualquier tipo de "envoltura" primitivo,
<literal>java.lang.String</literal>
+ o <literal>java.util.Date</literal>. (Si tu tabla de base de
datos heredada tiene claves
+ compuestas, puedes incluso usar una clase definida por el usuario con
propiedades de
+ estos tipos, ver la sección sobre identificadores compuestos
luego.)
+ </para>
+
+ <para>
+ La propiedad identificadora es estrictamente opcional. Puedes olvidarla y
dejar que Hibernate
+ siga internamente la pista de los identificadores del objeto. Sin
embargo, no recomendamos esto.
+ </para>
+
+ <para>
+ De hecho, alguna funcionalidad está disponible sólo
para clases que
+ declaran una propiedad identificadora:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Reasociación transitiva de objetos separados
(actualizaciones o
+ fusiones en cascada) - ver <xref
linkend="objectstate-transitive"/>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Session.saveOrUpdate()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Session.merge()</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Recomendamos que declares propiedades identificadoras
nombradas-consistentemente
+ en clases persistentes. Mas aún, recomendamos que uses un tipo
nulable
+ (es decir, no primitivo).
+ </para>
+ </sect2>
+
+ <sect2 id="persistent-classes-pojo-final">
+ <title>Prefiere las clases no finales (opcional)</title>
+ <para>
+ Un aspecto central de Hibernate,
<emphasis>proxies</emphasis>, depende de que
+ las clases persistentes sean ya no finales, o sean ya la
implementación
+ de una interface que declare todos los métodos
públicos.
+ </para>
+ <para>
+ Puedes persistir con Hibernate clases
<literal>final</literal> que no implementen una
+ interface, pero no serás capaz de usar proxies para
recuperación perezosa
+ de asociaciones, lo que limitará tus opciones para afinar el
rendimiento.
+ </para>
+ <para>
+ Debes también evitar declarar métodos
<literal>public final</literal>
+ en clases non-final. Si quieres usar una clase con un método
<literal>public
+ final</literal>, debes deshabilitar explícitamente el
uso de proxies estableciendo
+ <literal>lazy="false"</literal>.
+ </para>
+ </sect2>
+
+ <sect2 id="persistent-classes-pojo-accessors"
revision="2">
+ <title>Declara métodos de acceso y modificación
para los campos persistentes (opcional)</title>
+ <para>
+ <literal>Cat</literal> declara métodos de acceso
para todos sus campos persistente.
+ Muchas otras herramientas ORM persisten directamente variables de
instancia. Creemos que
+ es mejor proveer una indirección entre el esquema relacional y
las estructuras internas de la clase.
+ Por defecto, Hibernate persiste propiedades del estilo JavaBeans, y
reconoce nombres de método
+ de la forma <literal>getFoo</literal>,
<literal>isFoo</literal> y <literal>setFoo</literal>.
+ Puedes cambiar a acceso directo a campos para propiedades en particular,
de ser necesario.
+ </para>
+
+ <para>
+ Las propiedades <emphasis>no</emphasis> necesitan ser
declaradas públicas. Hibernate puede
+ persistir una propiedad con un par get / set
<literal>protected</literal> o <literal>private</literal>.
+ </para>
+ </sect2>
+ </sect1>
+
+ <sect1 id="persistent-classes-inheritance">
+ <title>Implementando herencia</title>
+
+ <para>
+ Una subclase puede a su vez observar la primera y segunda regla. Hereda su
+ propiedad identificadora de la superclase,
<literal>Cat</literal>.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+
+public class DomesticCat extends Cat {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+ protected void setName(String name) {
+ this.name=name;
+ }
+}]]></programlisting>
+ </sect1>
+
+ <sect1 id="persistent-classes-equalshashcode"
revision="1">
+ <title>Implementando <literal>equals()</literal> y
<literal>hashCode()</literal></title>
+
+
+ <para>
+ Tienes que sobrescribir los métodos
<literal>equals()</literal> y <literal>hashCode()</literal>
+ si :
+ </para>
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ piensas poner instancias de clases persistentes en un
<literal>Set</literal>
+ (la forma recomendada de representar asociaciones multivaluadas)
+ <emphasis>y</emphasis>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ piensas usar reasociación de instancias separadas.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernate garantiza la equivalencia de identidad persistente (fila de base de
datos) y
+ identidad Java sólo dentro del ámbito de una
sesión en particular.
+ De modo que en el momento que mezclamos instancias recuperadas en sesiones
diferentes,
+ debemos implementar <literal>equals()</literal> y
<literal>hashCode()</literal> si
+ deseamos tener una semántica significativa de
<literal>Set</literal>s.
+ </para>
+
+ <para>
+ La forma más obvia es implementar
<literal>equals()</literal>/<literal>hashCode()</literal>
+ comparando el valor identificador de ambos objetos. Si el valor es el mismo,
ambos deben ser
+ la misma fila de base de datos, por lo tanto son iguales (si ambos son
agregados a un
+ <literal>Set</literal>, sólo tendremos un elemento en
el <literal>Set</literal>).
+ Desafortunadamente, no podemos usar este enfoque con identificadores
generados! Hibernate sólo
+ asignará valores identificadores a objetos que son persistentes,
una instancia recién
+ creada no tendrá ningún valor identificador!
Además, si una instancia no está
+ salvada y está actualmente en un
<literal>Set</literal>, salvarla asignará un
+ valor identificador al objeto. Si <literal>equals()</literal> and
<literal>hashCode()</literal>
+ están basados en el valor identificador, el código hash
podría cambiar,
+ rompiendo el contrato de <literal>Set</literal>. Ver el sitio web
de Hibernate para una
+ discusión completa de este problema. Observa que esto no es una
incidencia de Hibernate,
+ sino la semántica normal de Java de identidad de objeto e
igualdad.
+ </para>
+
+ <para>
+ Recomendamos implementar <literal>equals()</literal> y
<literal>hashCode()</literal>
+ usando <emphasis>igualdad de clave de negocio (Business key
equality)</emphasis>.
+ Igualdad de clave de negocio significa que el método
<literal>equals()</literal>
+ compara sólo las propiedades que forman la clave de negocio, una
clave que podría
+ identificar nuestra instancia en el mundo real (una clave candidata
+ <emphasis>natural</emphasis>):
+ </para>
+
+ <programlisting><![CDATA[public class Cat {
+
+ ...
+ public boolean equals(Object other) {
+ if (this == other) return true;
+ if ( !(other instanceof Cat) ) return false;
+
+ final Cat cat = (Cat) other;
+
+ if ( !cat.getLitterId().equals( getLitterId() ) ) return false;
+ if ( !cat.getMother().equals( getMother() ) ) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = getMother().hashCode();
+ result = 29 * result + getLitterId();
+ return result;
+ }
+
+}]]></programlisting>
+
+ <para>
+ Nota que una clave de negocio no tiene que ser tan sólida como
+ una clave primaria candidata de base de datos (ver
+ <xref linkend="transactions-basics-identity"/>). Las
propiedades inmutables o
+ únicas son usualmente buenas candidatas para una clave de
negocio.
+ </para>
+
+ </sect1>
+
+ <sect1 id="persistent-classes-dynamicmodels">
+ <title>Modelos dinámicos</title>
+
+ <para>
+ <emphasis>Ten en cuenta que las siguientes funcionalidades
están
+ consideradas actualmente experimentales y pueden cambiar en el futuro
+ cercano.</emphasis>
+ </para>
+
+ <para>
+ Las entidades persistentes no necesariamente tienen que estar representadas
+ como clases POJO o como objetos JavaBean en tiempo de ejecución.
Hibernate
+ soporta además modelos dinámicos (usando
<literal>Map</literal>s de
+ <literal>Map</literal>s en tiempo de ejecución) y la
representación
+ de entidades como árboles de DOM4J. Con este enfoque no escribes
clases
+ persistentes, sólo ficheros de mapeo.
+ </para>
+
+ <para>
+ Por defecto, Hibernate funciona en modo POJO normal. Puedes establecer una
+ representación de entidad por defecto para una
<literal>SessionFactory</literal>
+ en particular usando la opción de configuración
+ <literal>default_entity_mode</literal>
+ (ver <xref linkend="configuration-optional-properties"/>).
+ </para>
+
+ <para>
+ Los siguientes ejemplos demuestran la representación usando
+ <literal>Map</literal>s. Primero, en el fichero de mapeo,
+ tiene que declararse un <literal>entity-name</literal> en vez de
+ (o como agregado a) un nombre de clase:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class entity-name="Customer">
+
+ <id name="id"
+ type="long"
+ column="ID">
+ <generator class="sequence"/>
+ </id>
+
+ <property name="name"
+ column="NAME"
+ type="string"/>
+
+ <property name="address"
+ column="ADDRESS"
+ type="string"/>
+
+ <many-to-one name="organization"
+ column="ORGANIZATION_ID"
+ class="Organization"/>
+
+ <bag name="orders"
+ inverse="true"
+ lazy="false"
+ cascade="all">
+ <key column="CUSTOMER_ID"/>
+ <one-to-many class="Order"/>
+ </bag>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Ten en cuenta que aunque las asociaciones se declaran usando nombres
+ de clase objetivo, el tipo objetivo de una asociación puede
+ ser además una entidad dinámica en vez de un POJO.
+ </para>
+
+ <para>
+ Después de establecer el modo de entidad por defecto a
+ <literal>dynamic-map</literal> para la
<literal>SessionFactory</literal>,
+ podemos trabajar en tiempo de ejecución con
<literal>Map</literal>s
+ de <literal>Map</literal>s:
+ </para>
+
+ <programlisting><![CDATA[Session s = openSession();
+Transaction tx = s.beginTransaction();
+Session s = openSession();
+
+// Create a customer
+Map david = new HashMap();
+david.put("name", "David");
+
+// Create an organization
+Map foobar = new HashMap();
+foobar.put("name", "Foobar Inc.");
+
+// Link both
+david.put("organization", foobar);
+
+// Save both
+s.save("Customer", david);
+s.save("Organization", foobar);
+
+tx.commit();
+s.close();]]></programlisting>
+
+ <para>
+ Las ventajas de un mapeo dinámico es rápido tiempo de
ciclo
+ de prototipado sin la necesidad de implementación de clases de
entidad.
+ Sin embargo, pierdes chequeo de tipos en tiempo de compilación y
+ muy probablemente tratarás con muchas excepciones en tiempo de
ejecución.
+ Gracias al mapeo de Hibernate, el esquema de base de datos puede estar
facilmente
+ sano y normalizado, permitiendo agregar una implementación
apropiada del
+ modelo de dominio más tarde.
+ </para>
+
+ <para>
+ Los modos de representación de entidad pueden ser establecidos
+ por <literal>Session</literal>:
+ </para>
+
+ <programlisting><![CDATA[Session dynamicSession =
pojoSession.getSession(EntityMode.MAP);
+
+// Create a customer
+Map david = new HashMap();
+david.put("name", "David");
+dynamicSession.save("Customer", david);
+...
+dynamicSession.flush();
+dynamicSession.close()
+...
+// Continue on pojoSession
+]]></programlisting>
+
+
+ <para>
+ Por favor, ten en cuenta que la llamada a
<literal>getSession()</literal>
+ usando un <literal>EntityMode</literal> está en la API
de
+ <literal>Session</literal>, no en la de
<literal>SessionFactory</literal>.
+ De esta forma, la nueva <literal>Session</literal> comparte la
conexión
+ JDBC, transacción y otra información de contexto. Esto
significa
+ que no tienes que llamar a <literal>flush()</literal> ni a
<literal>close()</literal>
+ en la <literal>Session</literal> secundaria, y tembién
dejar el manejo
+ de la transacción y de la conexión a la unidad de
trabajo primaria.
+ </para>
+
+ <para>
+ Puede encontrarse más información sobre las capacidades
de
+ representación XML en <xref linkend="xml"/>.
+ </para>
+
+ </sect1>
+
+ <para>
+ PORHACER: Documentar el framework de extensiones del usuario en los paquetes
+ de propiedad y proxies.
+ </para>
+
+</chapter>
+
Added: core/trunk/documentation/manual/es-ES/src/main/docbook/content/preface.xml
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/content/preface.xml
(rev 0)
+++ core/trunk/documentation/manual/es-ES/src/main/docbook/content/preface.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,112 @@
+<?xml version='1.0'?>
+
+<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
+
+ <preface id="preface" revision="2">
+ <title>Prefacio</title>
+
+
+ <para>
+ Trabajar con software orientado a objetos y una base de datos relacional
puede ser
+ incómodo y consumir tiempo en los entornos de empresa de hoy.
Hibernate es una
+ herramienta de mapeo objeto/relacional para entornos Java. El
término mapeo
+ objeto/relacional (MOR) hace referencia a la técnica de mapear
una
+ representación de datos desde un modelo de objetos a un modelo de
datos relacional
+ con un esquema basado en SQL.
+ </para>
+
+ <para>
+ Hibernate no sólo se encarga de mapear de clases Java a tablas de
base de datos
+ (y de tipos de datos de Java a tipos de datos SQL), sino que
también provee
+ facilidades de consulta y recuperación de datos y puede reducir
significativamente
+ el tiempo de desarrollo que de otra forma se gasta en el manejo de los datos
en SQL y JDBC.
+ </para>
+
+ <para>
+ La meta de Hibernate es relevar al desarrollador del 95 por ciento de las
tareas comunes
+ relacionadas a la programación de la persistencia de los datos.
+ Hibernate puede no ser la mejor solución para aplicaciones que
usan solamente
+ procedimientos almacenados para implementar la lógica de negocio
en la base de
+ datos, es mas útil con modelos de dominio orientados a objetos y
lógica de
+ negocio en middle-tier basada en Java. Sin embargo, Hibernate ciertamente
puede ayudarte
+ a quitar o encapsular código SQL específico de vendedor
y ayudará
+ con la tarea común de traducción de resultados desde
una representación
+ tabular a un grafo de objetos.
+ </para>
+
+ <para>
+ Si eres nuevo en Hibernate y lo del Mapeo Objeto/Relacional o incluso en
Java,
+ sigue por favor estos pasos:
+ </para>
+
+ <orderedlist>
+ <listitem>
+ <para>
+ Lee <xref linkend="quickstart"/> para un tutorial de
30 minutos, usando Tomcat.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Lee <xref linkend="architecture"/> para entender los
entornos en los que
+ puede ser usado Hibernate.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Dale una mirada al directorio <literal>eg/</literal> en
la distribución
+ de Hibernate, contiene una aplicación independiente
simple.
+ Copia tu driver JDBC al directorio
<literal>lib/</literal> y edita
+ <literal>etc/hibernate.properties</literal>,
especificando los valores
+ correctos para tu base de datos. Desde línea de comandos
en el
+ directorio de la distribución, tipea <literal>ant
eg</literal>
+ (usando Ant), o bajo Windows, tipea <literal>build
eg</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Usa esta documentación de referencia como tu fuente de
información
+ primaria. Ten en consideración leer <emphasis>Java
Persistence with Hibernate</emphasis>
+ (
http://www.manning.com/bauer2) si necesitas mas ayuda con el
diseño
+ de aplicaciones o si prefieres un tutorial paso a paso.
+ Visita también
http://caveatemptor.hibernate.org y
descarga la aplicación
+ de ejemplo para Java Persistence with Hibernate.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Los FAQs son respondidos en el sitio web de Hibernate.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ En el sitio web de Hibernate hay enlaces a demos
de terceros, ejemplos
+ y tutoriales.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ El Area de Comunidad en el sitio web de Hibernate es una buena
fuente
+ de patrones de diseño y varias soluciones de
integración
+ (Tomcat, JBoss, Struts, EJB, etc.).
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ Si tienes preguntas, usa el foro de usuarios enlazado en el sitio web de
Hibernate.
+ También proveemos un sistema de seguimiento JIRA para reportes de
defectos y
+ peticiones de nuevas características.
+ Si estas interesado en el desarrollo de Hibernate, únete a la
lista de correo
+ de desarrolladores. Si estas interesado en traducir esta
documentación a tu
+ lenguaje, contáctanos en la lista de correo de desarrolladores.
+ </para>
+
+ <para>
+ A través de JBoss Inc. (see
http://www.hibernate.org/SupportTraining/) hay
+ disponibilidad de soporte comercial de desarrollo, soporte de
producción y
+ entrenamiento en Hibernate.
+ Hibernate es un proyecto de la suite de productos de código
abierto
+ JBoss Professional.
+ </para>
+
+ </preface>
\ No newline at end of file
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/content/query_criteria.xml
(from rev 12794,
core/trunk/documentation/manual/es-ES/src/main/docbook/modules/query_criteria.xml)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/content/query_criteria.xml
(rev 0)
+++
core/trunk/documentation/manual/es-ES/src/main/docbook/content/query_criteria.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,431 @@
+<chapter id="querycriteria">
+ <title>Consultas por Criterios</title>
+
+ <para>
+ Acompaña a Hibernate una API de consultas por criterios intuitiva y
extensible.
+ </para>
+
+ <sect1 id="querycriteria-creating">
+ <title>Creando una instancia de
<literal>Criteria</literal></title>
+
+ <para>
+ La interface <literal>org.hibernate.Criteria</literal> representa
una consulta contra
+ una clase persistente en particular. La
<literal>Session</literal> es una fábrica de instancias
+ de <literal>Criteria</literal>.
+ </para>
+
+ <programlisting><![CDATA[Criteria crit =
sess.createCriteria(Cat.class);
+crit.setMaxResults(50);
+List cats = crit.list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-narrowing">
+ <title>Estrechando el conjunto resultado</title>
+
+ <para>
+ Un criterio individual de consulta es una instancia de la interface
+ <literal>org.hibernate.criterion.Criterion</literal>. La clase
+ <literal>org.hibernate.criterion.Restrictions</literal> define
métodos de fábrica para obtener ciertos tipos
+ prefabricados de <literal>Criterion</literal>.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "Fritz%") )
+ .add( Restrictions.between("weight", minWeight, maxWeight) )
+ .list();]]></programlisting>
+
+ <para>
+ Las restricciones pueden ser agrupadas lógicamente.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "Fritz%") )
+ .add( Restrictions.or(
+ Restrictions.eq( "age", new Integer(0) ),
+ Restrictions.isNull("age")
+ ) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.in( "name", new String[] { "Fritz",
"Izi", "Pk" } ) )
+ .add( Restrictions.disjunction()
+ .add( Restrictions.isNull("age") )
+ .add( Restrictions.eq("age", new Integer(0) ) )
+ .add( Restrictions.eq("age", new Integer(1) ) )
+ .add( Restrictions.eq("age", new Integer(2) ) )
+ ) )
+ .list();]]></programlisting>
+
+ <para>
+ Hay un gran rango de tipos de criterio prefabricados (subclases de
<literal>Restrictions</literal>),
+ pero uno que es especialmente útil te deja especificar SQL directamente.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.sql("lower({alias}.name) like lower(?)",
"Fritz%", Hibernate.STRING) )
+ .list();]]></programlisting>
+
+ <para>
+ El sitio <literal>{alias}</literal> será remplazado
por el alias de fila de la entidad consultada.
+ </para>
+
+ <para>
+ Un enfoque alternativo para obtener un criterio es tomarlo de una instancia
de
+ <literal>Property</literal>. Puedes crear una
<literal>Property</literal> llamando a
+ <literal>Property.forName()</literal>.
+ </para>
+
+ <programlisting><![CDATA[
+Property age = Property.forName("age");
+List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.disjunction()
+ .add( age.isNull() )
+ .add( age.eq( new Integer(0) ) )
+ .add( age.eq( new Integer(1) ) )
+ .add( age.eq( new Integer(2) ) )
+ ) )
+ .add( Property.forName("name").in( new String[] { "Fritz",
"Izi", "Pk" } ) )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-ordering">
+ <title>Ordenando los resultados</title>
+
+ <para>
+ Puedes ordenar los resultados usando
<literal>org.hibernate.criterion.Order</literal>.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "F%")
+ .addOrder( Order.asc("name") )
+ .addOrder( Order.desc("age") )
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Property.forName("name").like("F%") )
+ .addOrder( Property.forName("name").asc() )
+ .addOrder( Property.forName("age").desc() )
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-associations">
+ <title>Asociaciones</title>
+
+ <para>
+ Puedes especificar fácilmente restricciones sobre las entidades
relacionadas al navegar asociaciones
+ usando <literal>createCriteria()</literal>.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "F%")
+ .createCriteria("kittens")
+ .add( Restrictions.like("name", "F%")
+ .list();]]></programlisting>
+
+ <para>
+ nota que el segundo <literal>createCriteria()</literal> devuelve
una nueva instancia de
+ <literal>Criteria</literal>, que hace referencia a los elementos
de la colección
+ <literal>kittens</literal>.
+ </para>
+
+ <para>
+ La siguiente forma alternativa es útil en ciertas circunstancias.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .createAlias("kittens", "kt")
+ .createAlias("mate", "mt")
+ .add( Restrictions.eqProperty("kt.name", "mt.name") )
+ .list();]]></programlisting>
+
+ <para>
+ (<literal>createAlias()</literal> no crea una nueva instancia de
+ <literal>Criteria</literal>.)
+ </para>
+
+ <para>
+ ¡Observa que las colecciones de gatitos tenidas por las instancias
de <literal>Cat</literal> devueltas
+ por las dos consultas previas <emphasis>no</emphasis>
están prefiltradas por los criterios! Si deseas
+ recuperar sólo los gatitos que emparejen los criterios, debes usar
<literal>returnMaps()</literal>.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .createCriteria("kittens", "kt")
+ .add( Restrictions.eq("name", "F%") )
+ .returnMaps()
+ .list();
+Iterator iter = cats.iterator();
+while ( iter.hasNext() ) {
+ Map map = (Map) iter.next();
+ Cat cat = (Cat) map.get(Criteria.ROOT_ALIAS);
+ Cat kitten = (Cat) map.get("kt");
+}]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-dynamicfetching" revision="1">
+ <title>Recuperación dinámica de
asociaciones</title>
+
+ <para>
+ Puedes especificar la semántica de recuperación de
asociaciones en tiempo de ejecución usando
+ <literal>setFetchMode()</literal>.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "Fritz%") )
+ .setFetchMode("mate", FetchMode.EAGER)
+ .setFetchMode("kittens", FetchMode.EAGER)
+ .list();]]></programlisting>
+
+ <para>
+ Esta consulta recuperará tanto <literal>mate</literal>
como <literal>kittens</literal> por
+ unión exterior (outer join). Ver <xref
linkend="performance-fetching"/> para más información.
+ </para>
+
+ </sect1>
+
+ <sect1 id="querycriteria-examples">
+ <title>Consultas por ejemplos</title>
+
+ <para>
+ La clase <literal>org.hibernate.criterion.Example</literal> te
permite construir un criterio de consulta
+ a partir de una instancia dada.
+ </para>
+
+ <programlisting><![CDATA[Cat cat = new Cat();
+cat.setSex('F');
+cat.setColor(Color.BLACK);
+List results = session.createCriteria(Cat.class)
+ .add( Example.create(cat) )
+ .list();]]></programlisting>
+
+ <para>
+ Las propiedades de versión, los identificadores y las asociaciones
son ignorados. Por defecto,
+ las propiedades valuadas a nulo son excluídas.
+ </para>
+
+ <para>
+ Puedes ajustar cómo se aplica el
<literal>Example</literal>.
+ </para>
+
+ <programlisting><![CDATA[Example example = Example.create(cat)
+ .excludeZeroes() //exclude zero valued properties
+ .excludeProperty("color") //exclude the property named "color"
+ .ignoreCase() //perform case insensitive string comparisons
+ .enableLike(); //use like for string comparisons
+List results = session.createCriteria(Cat.class)
+ .add(example)
+ .list();]]></programlisting>
+
+ <para>
+ Puedes incluso usar ejemplos para colocar criterios sobre objetos asociados.
+ </para>
+
+ <programlisting><![CDATA[List results =
session.createCriteria(Cat.class)
+ .add( Example.create(cat) )
+ .createCriteria("mate")
+ .add( Example.create( cat.getMate() ) )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-projection">
+ <title>Proyecciones, agregación y agrupamiento</title>
+ <para>
+ La clase <literal>org.hibernate.criterion.Projections</literal>
es una fábrica de instancias de
+ <literal>Projection</literal>. Aplicamos una
proyección a una consulta llamando a
+ <literal>setProjection()</literal>.
+ </para>
+
+ <programlisting><![CDATA[List results =
session.createCriteria(Cat.class)
+ .setProjection( Projections.rowCount() )
+ .add( Restrictions.eq("color", Color.BLACK) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results =
session.createCriteria(Cat.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.rowCount() )
+ .add( Projections.avg("weight") )
+ .add( Projections.max("weight") )
+ .add( Projections.groupProperty("color") )
+ )
+ .list();]]></programlisting>
+
+ <para>
+ No es necesario ningún "group by" explícito en una
consulta por criterios.
+ Ciertos tipos de proyecciones son definidos para ser
<emphasis>proyecciones agrupadas</emphasis>,
+ que además aparecen en la cláusula SQL
<literal>group by</literal>.
+ </para>
+
+ <para>
+ Puede opcionalmente asignarse un alias a una proyección, de modo
que el valor proyectado pueda
+ ser referido en restricciones u ordenamientos. Aquí hay dos formas
diferentes de hacer esto:
+ </para>
+
+ <programlisting><![CDATA[List results =
session.createCriteria(Cat.class)
+ .setProjection( Projections.alias( Projections.groupProperty("color"),
"colr" ) )
+ .addOrder( Order.asc("colr") )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results =
session.createCriteria(Cat.class)
+ .setProjection( Projections.groupProperty("color").as("colr") )
+ .addOrder( Order.asc("colr") )
+ .list();]]></programlisting>
+
+ <para>
+ Los métodos <literal>alias()</literal> y
<literal>as()</literal> simplemente envuelven una instancia
+ de proyección en otra instancia de
<literal>Projection</literal> con alias. Como un atajo, puedes asignar
+ un alias cuando agregas la proyección a una lista de
proyecciones:
+ </para>
+
+ <programlisting><![CDATA[List results =
session.createCriteria(Cat.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.rowCount(), "catCountByColor" )
+ .add( Projections.avg("weight"), "avgWeight" )
+ .add( Projections.max("weight"), "maxWeight" )
+ .add( Projections.groupProperty("color"), "color" )
+ )
+ .addOrder( Order.desc("catCountByColor") )
+ .addOrder( Order.desc("avgWeight") )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results =
session.createCriteria(Domestic.class, "cat")
+ .createAlias("kittens", "kit")
+ .setProjection( Projections.projectionList()
+ .add( Projections.property("cat.name"), "catName" )
+ .add( Projections.property("kit.name"), "kitName" )
+ )
+ .addOrder( Order.asc("catName") )
+ .addOrder( Order.asc("kitName") )
+ .list();]]></programlisting>
+
+ <para>
+ Puedes también usar
<literal>Property.forName()</literal> para expresar proyecciones:
+ </para>
+
+ <programlisting><![CDATA[List results =
session.createCriteria(Cat.class)
+ .setProjection( Property.forName("name") )
+ .add( Property.forName("color").eq(Color.BLACK) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results =
session.createCriteria(Cat.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.rowCount().as("catCountByColor") )
+ .add( Property.forName("weight").avg().as("avgWeight") )
+ .add( Property.forName("weight").max().as("maxWeight") )
+ .add( Property.forName("color").group().as("color" )
+ )
+ .addOrder( Order.desc("catCountByColor") )
+ .addOrder( Order.desc("avgWeight") )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-detachedqueries">
+ <title>Consultas y subconsultas separadas</title>
+ <para>
+ La clase <literal>DetachedCriteria</literal> te deja crear una
consulta fuera del ámbito de una sesión,
+ y entonces ejecutarla luego usando alguna
<literal>Session</literal> arbitraria.
+ </para>
+
+ <programlisting><![CDATA[DetachedCriteria query =
DetachedCriteria.forClass(Cat.class)
+ .add( Property.forName("sex").eq('F') );
+
+Session session = ....;
+Transaction txn = session.beginTransaction();
+List results = query.getExecutableCriteria(session).setMaxResults(100).list();
+txn.commit();
+session.close();]]></programlisting>
+
+ <para>
+ También una <literal>DetachedCriteria</literal> puede
usarse para expresar una subconsulta.
+ Las instancias de Criterion implicando subconsultas pueden obtenerse
vía <literal>Subqueries</literal> o
+ <literal>Property</literal>.
+ </para>
+
+ <programlisting><![CDATA[DetachedCriteria avgWeight =
DetachedCriteria.forClass(Cat.class)
+ .setProjection( Property.forName("weight").avg() );
+session.createCriteria(Cat.class)
+ .add( Property.forName("weight").gt(avgWeight) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[DetachedCriteria weights =
DetachedCriteria.forClass(Cat.class)
+ .setProjection( Property.forName("weight") );
+session.createCriteria(Cat.class)
+ .add( Subqueries.geAll("weight", weights) )
+ .list();]]></programlisting>
+
+ <para>
+ Incluso son posibles las subconsultas correlacionadas:
+ </para>
+
+ <programlisting><![CDATA[DetachedCriteria avgWeightForSex =
DetachedCriteria.forClass(Cat.class, "cat2")
+ .setProjection( Property.forName("weight").avg() )
+ .add( Property.forName("cat2.sex").eqProperty("cat.sex") );
+session.createCriteria(Cat.class, "cat")
+ .add( Property.forName("weight").gt(avgWeightForSex) )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <!--TODO: ResultSetTransformer + aliasing. AliasToBeanTransformer allow
returning arbitrary
+ user objects - similar to setResultClass in JDO2. General use of
ResultTransformer
+ could also be explained. -->
+
+ <sect1 id="query-criteria-naturalid">
+ <title>Consultas por identificador natural</title>
+
+ <para>
+ Para la mayoría de consultas, incluyendo las consultas por
criterios, el caché de consulta no es
+ muy eficiente, debido a que la invalidación del caché
de consulta ocurre demasiado frecuentemente.
+ Sin embargo, hay un tipo especial de consulta donde podemos optimizar el
algoritmo de invalidación
+ de caché: búsquedas por una clave natural constante. En algunas
aplicaciones, este tipo de consulta,
+ ocurre frecuentemente. La API de criterios brinda especial
provisión para este caso de uso.
+ </para>
+
+ <para>
+ Primero, debes mapear la clave natural de tu entidad usando
+ <literal><natural-id></literal>, y habilitar el uso
del caché de segundo nivel.
+ </para>
+
+ <programlisting><![CDATA[<class name="User">
+ <cache usage="read-write"/>
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <natural-id>
+ <property name="name"/>
+ <property name="org"/>
+ </natural-id>
+ <property name="password"/>
+</class>]]></programlisting>
+
+ <para>
+ Nota que esta funcionalidad no está pensada para uso con entidades
con claves naturales
+ <emphasis>mutable</emphasis>.
+ </para>
+
+ <para>
+ Seguido, habilita el caché de consulta de Hibernate.
+ </para>
+
+ <para>
+ Ahora, <literal>Restrictions.naturalId()</literal> nos permite
hacer uso de el algoritmo de caché
+ más eficiente.
+ </para>
+
+ <programlisting><![CDATA[session.createCriteria(User.class)
+ .add( Restrictions.naturalId()
+ .set("name", "gavin")
+ .set("org", "hb")
+ ).setCacheable(true)
+ .uniqueResult();]]></programlisting>
+
+ </sect1>
+
+</chapter>
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/content/query_hql.xml (from
rev 12794, core/trunk/documentation/manual/es-ES/src/main/docbook/modules/query_hql.xml)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/content/query_hql.xml
(rev 0)
+++
core/trunk/documentation/manual/es-ES/src/main/docbook/content/query_hql.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,1107 @@
+<chapter id="queryhql">
+ <title>HQL: El Lenguaje de Consulta de Hibernate</title>
+
+ <para>
+ Hibernate está equipado con un lenguaje de consulta extremadamente
potente que
+ (intencionalmente en absoluto) se parece muchísimo a SQL. Pero no te
engañes por la sintaxis;
+ HQL es completamente orientado a objetos, entendiendo nociones como herencia,
polimorfismo
+ y asociación.
+ </para>
+
+ <sect1 id="queryhql-casesensitivity">
+ <title>Sensibilidad a Mayúsculas</title>
+
+ <para>
+ Las consultas son insensibles a mayúsculas, excepto para nombres
de clases Java y propiedades. De modo que
+ <literal>SeLeCT</literal> es lo mismo que
<literal>sELEct</literal> e igual a <literal>SELECT</literal>,
+ pero <literal>org.hibernate.eg.FOO</literal> no lo es a
<literal>org.hibernate.eg.Foo</literal> y
+ <literal>foo.barSet</literal> no es igual a
<literal>foo.BARSET</literal>.
+ </para>
+
+ <para>
+ Este manual usa palabras clave HQL en minúsculas. Algunos usuarios
encuentran las consultas con
+ palabras clave en mayúsculas más leíbles,
pero encontramos esta convención fea cuando se encaja
+ en código Java.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-from">
+ <title>La cláusula from</title>
+
+ <para>
+ La consulta más simple posible de Hibernate es de la forma:
+ </para>
+
+ <programlisting><![CDATA[from eg.Cat]]></programlisting>
+
+ <para>
+ que simplemente devuelve todas las instancias de la clase
<literal>eg.Cat</literal>.
+ Usualmente no necesitamos cualificar el nombre de la clase, ya que
<literal>auto-import</literal>
+ está por defecto. De modo que casi siempre escribimos solamente:
+ </para>
+
+ <programlisting><![CDATA[from Cat]]></programlisting>
+
+ <para>
+ La mayoría del tiempo, necesitarás asignar un
<emphasis>alias</emphasis>, ya que querrás referirte al
+ <literal>Cat</literal> en otras partes de la consulta.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat]]></programlisting>
+
+ <para>
+ Esta consulta asigna el alias <literal>cat</literal> a las
instancias de <literal>Cat</literal>,
+ de modo que podríamos usar ese alias luego en la consulta. La
palabra clave <literal>as</literal>
+ es opcional; también podríamos escribir:
+ </para>
+
+ <programlisting><![CDATA[from Cat cat]]></programlisting>
+
+ <para>
+ Pueden aparecer múltiples clases, resultando en un producto
cartesiano o unión "cruzada" (cross join).
+ </para>
+
+ <programlisting><![CDATA[from Formula,
Parameter]]></programlisting>
+ <programlisting><![CDATA[from Formula as form, Parameter as
param]]></programlisting>
+
+ <para>
+ Se considera buena práctica el nombrar los alias de consulta
usando una inicial en minúsculas,
+ consistente con los estándares de nombrado de Java para variables
locales
+ (por ejemplo, <literal>domesticCat</literal>).
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-joins" revision="1">
+ <title>Asociaciones y uniones (joins)</title>
+
+ <para>
+ Podemos también asignar aliases a entidades asociadas, e incluso a
elementos de una colección de valores,
+ usando una <literal>join</literal>.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ inner join cat.mate as mate
+ left outer join cat.kittens as kitten]]></programlisting>
+
+ <programlisting><![CDATA[from Cat as cat left join cat.mate.kittens as
kittens]]></programlisting>
+
+ <programlisting><![CDATA[from Formula form full join form.parameter
param]]></programlisting>
+
+ <para>
+ Los tipos de join soportados son prestados de ANSI SQL
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>inner join</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>left outer join</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>right outer join</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>full join</literal> (no útil
usualmente)
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Las construcciones <literal>inner join</literal>,
<literal>left outer join</literal> y
+ <literal>right outer join</literal> pueden ser abreviadas.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ join cat.mate as mate
+ left join cat.kittens as kitten]]></programlisting>
+
+ <para>
+ Puedes proveer condiciones de unión extra usando la palabra clave
<literal>with</literal> de HQL.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ left join cat.kittens as kitten
+ with kitten.bodyWeight > 10.0]]></programlisting>
+
+ <para>
+ En adición, un "fetch" join permite a las asociaciones o
colecciones de valores ser inicializadas
+ junto a sus objetos padres, usando una sola selección. Esto es
particularmente útil en el case de una
+ colección. Efectivamente sobrescribe el outer join y las
declaraciones perezosas (lazy) del fichero
+ de mapeo para asociaciones y colecciones. Ver <xref
linkend="performance-fetching"/> para más
+ información.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ inner join fetch cat.mate
+ left join fetch cat.kittens]]></programlisting>
+
+ <para>
+ Usualmente a un fetch join no se necesita asignársele un alias,
porque los objetos asociados no deben
+ ser usados en la cláusula <literal>where</literal> (ni
en cualquier otra cláusula). Además, los objetos
+ asociados no son devueltos directamente en los resultados de consulta. En
cambio, pueden ser accedidos
+ vía el objeto padre. La única razón por la
que necesitaríamos un alias es estamos uniendo recursivamente
+ otra colección:
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ inner join fetch cat.mate
+ left join fetch cat.kittens child
+ left join fetch child.kittens]]></programlisting>
+
+ <para>
+ Nota que la construcción <literal>fetch</literal> no
puede usarse en consultas llamadas usando
+ <literal>scroll()</literal> o
<literal>iterate()</literal>. Ni debe usarse
<literal>fetch</literal>
+ junto con <literal>setMaxResults()</literal> o
<literal>setFirstResult()</literal>. Tampoco puede usarse
+ <literal>fetch</literal> junto a una condición
<literal>with</literal> ad hoc. Es posible crear
+ un producto cartesiano trayendo por join más de una
colección en una colección, así que ten cuidado en
+ este caso. Traer por join múltiples roles de colección
también da a veces resultados inesperados para mapeos
+ de bag, así que sé cuidadoso sobre cómo
formular tus consultas en este caso. Finalmente, nota que
+ <literal>full join fetch</literal> y <literal>right join
fetch</literal> no son significativos.
+ </para>
+
+ <para>
+ Si estás usando recuperación perezosa a nivel de
propiedad (con instrumentación de bytecode), es posible
+ forzar a Hibernate a traer las propiedades perezosas inmediatamente (en la
primera consulta) usando
+ <literal>fetch all properties</literal>.
+ </para>
+
+ <programlisting><![CDATA[from Document fetch all properties order by
name]]></programlisting>
+ <programlisting><![CDATA[from Document doc fetch all properties where
lower(doc.name) like '%cats%']]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-select">
+ <title>La cláusula select</title>
+
+ <para>
+ La cláusula <literal>select</literal> escoge
qué objetos y propiedades devolver in el conjunto resultado
+ de la consulta. Considera:
+ </para>
+
+ <programlisting><![CDATA[select mate
+from Cat as cat
+ inner join cat.mate as mate]]></programlisting>
+
+ <para>
+ La consulta seleccionará <literal>mate</literal>s de
otros <literal>Cat</literal>s.
+ Realmente, puedes expresar esta consulta en un forma más compacta
como:
+ </para>
+
+ <programlisting><![CDATA[select cat.mate from Cat
cat]]></programlisting>
+
+ <para>
+ Las consultas pueden devolver propiedades de cualquier tipo de valor
incluyendo propiedades de
+ tipo componente:
+ </para>
+
+ <programlisting><![CDATA[select cat.name from DomesticCat cat
+where cat.name like 'fri%']]></programlisting>
+
+ <programlisting><![CDATA[select cust.name.firstName from Customer as
cust]]></programlisting>
+
+ <para>
+ Las consultas pueden devolver múltiples objetos y/o propiedades
como un array de tipo
+ <literal>Object[]</literal>,
+ </para>
+
+ <programlisting><![CDATA[select mother, offspr, mate.name
+from DomesticCat as mother
+ inner join mother.mate as mate
+ left outer join mother.kittens as offspr]]></programlisting>
+
+ <para>
+ o como una <literal>List</literal>,
+ </para>
+
+ <programlisting><![CDATA[select new list(mother, offspr, mate.name)
+from DomesticCat as mother
+ inner join mother.mate as mate
+ left outer join mother.kittens as offspr]]></programlisting>
+
+ <para>
+ o como un objeto real Java de tipo seguro,
+ </para>
+
+ <programlisting><![CDATA[select new Family(mother, mate, offspr)
+from DomesticCat as mother
+ join mother.mate as mate
+ left join mother.kittens as offspr]]></programlisting>
+
+ <para>
+ asumiendo que la clase <literal>Family</literal> tiene un
constructor apropiado.
+ </para>
+
+ <para>
+ Puedes asignar aliases para seleccionar expresiones usando
<literal>as</literal>:
+ </para>
+
+ <programlisting><![CDATA[select max(bodyWeight) as max, min(bodyWeight)
as min, count(*) as n
+from Cat cat]]></programlisting>
+
+ <para>
+ Esto es lo más útil cuando se usa junto con
<literal>select new map</literal>:
+ </para>
+
+ <programlisting><![CDATA[select new map( max(bodyWeight) as max,
min(bodyWeight) as min, count(*) as n )
+from Cat cat]]></programlisting>
+
+ <para>
+ Esta consulta devuelve un <literal>Map</literal> de aliases a
valores seleccionados.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-aggregation">
+ <title>Funciones de agregación</title>
+
+ <para>
+ Las consultas HQL pueden incluso devolver resultados de funciones de
agregación sobre propiedades:
+ </para>
+
+ <programlisting><![CDATA[select avg(cat.weight), sum(cat.weight),
max(cat.weight), count(cat)
+from Cat cat]]></programlisting>
+
+<!-- NO LONGER SUPPORTED
+ <para>
+ Collections may also appear inside aggregate functions in the
<literal>select</literal>
+ clause.
+ </para>
+
+ <programlisting><![CDATA[select cat, count( elements(cat.kittens) )
+from Cat cat group by cat]]></programlisting>
+-->
+
+ <para>
+ Las funciones de agregación soportadas son
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>avg(...), sum(...), min(...),
max(...)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>count(*)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>count(...), count(distinct ...),
count(all...)</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Puedes usar operadores aritméticos, concatenación, y
funciones SQL reconocidas en la cláusula select:
+ </para>
+
+ <programlisting><![CDATA[select cat.weight + sum(kitten.weight)
+from Cat cat
+ join cat.kittens kitten
+group by cat.id, cat.weight]]></programlisting>
+
+ <programlisting><![CDATA[select firstName||' '||initial||'
'||upper(lastName) from Person]]></programlisting>
+
+ <para>
+ Las palabras clave <literal>distinct</literal> y
<literal>all</literal> pueden ser usadas y tienen las misma
+ semántica que en SQL.
+ </para>
+
+ <programlisting><![CDATA[select distinct cat.name from Cat cat
+
+select count(distinct cat.name), count(cat) from Cat cat]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-polymorphism">
+ <title>Consultas polimórficas</title>
+
+ <para>
+ Una consulta como:
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat]]></programlisting>
+
+ <para>
+ devuelve instancias no sólo de <literal>Cat</literal>,
sino también de subclases como
+ <literal>DomesticCat</literal>. Las consultas de Hibernate pueden
mencionar <emphasis>cualquier</emphasis>
+ clase o interface Java en la cláusula
<literal>from</literal>. La consulta devolverá instancias de todas
+ las clases persistentes que extiendan esa clase o implementen la interface.
La siguiente consulta devolvería
+ todos los objetos persistentes.
+ </para>
+
+ <programlisting><![CDATA[from java.lang.Object
o]]></programlisting>
+
+ <para>
+ La interface <literal>Named</literal> podría ser
implementada por varias clases persistentes:
+ </para>
+
+ <programlisting><![CDATA[from Named n, Named m where n.name =
m.name]]></programlisting>
+
+ <para>
+ Nota que estas dos últimas consultas requerirán
más de un <literal>SELECT</literal> SQL. Esto significa
+ que la cláusula <literal>order by</literal> no
ordenará correctamente todo el conjunto resultado.
+ (Significa además que no puedes llamar estas consulta usando
<literal>Query.scroll()</literal>.)
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-where">
+ <title>La cláusula where</title>
+
+ <para>
+ La cláusula where te permite estrechar la lista de instancias
devueltas. Si no existe ningún alias.
+ puedes referirte a las propiedades por nombre:
+ </para>
+
+ <programlisting><![CDATA[from Cat where
name='Fritz']]></programlisting>
+
+ <para>
+ Si existe un alias, usan un nombre cualificado de propiedad:
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat where
cat.name='Fritz']]></programlisting>
+
+ <para>
+ devuelve las instancias de <literal>Cat</literal> llamadas
'Fritz'.
+ </para>
+
+ <programlisting><![CDATA[select foo
+from Foo foo, Bar bar
+where foo.startDate = bar.date]]></programlisting>
+
+ <para>
+ devolverá todas las instancias de
<literal>Foo</literal> para las cuales exista una instancia
+ de <literal>bar</literal> con una propiedad
<literal>date</literal> igual a la propiedad
+ <literal>startDate</literal> del
<literal>Foo</literal>. Las expresiones de ruta compuestas hacen
+ la cláusula <literal>where</literal> extremadamente
potente. Considera:
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.mate.name is not
null]]></programlisting>
+
+ <para>
+ Esta consulta se traduce en una consulta SQL con una unión de
tabla (interna). Si fueses a escribir algo como
+ </para>
+
+ <programlisting><![CDATA[from Foo foo
+where foo.bar.baz.customer.address.city is not null]]></programlisting>
+
+ <para>
+ terminarías con una consulta que requeriría cuatro
uniones de tablas en SQL.
+ </para>
+
+ <para>
+ El operador <literal>=</literal> puede ser usado para comparar no
sólo propiedades, sino también instancias:
+ </para>
+
+ <programlisting><![CDATA[from Cat cat, Cat rival where cat.mate =
rival.mate]]></programlisting>
+
+ <programlisting><![CDATA[select cat, mate
+from Cat cat, Cat mate
+where cat.mate = mate]]></programlisting>
+
+ <para>
+ La propiedad especial (en minúsculas)
<literal>id</literal> puede ser usada para referenciar el identificador
+ único de un objeto. (También puedes usar su nombre de
propiedad.)
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat where cat.id = 123
+
+from Cat as cat where cat.mate.id = 69]]></programlisting>
+
+ <para>
+ La segunda consulta es eficiente. ¡No se requiere ninguna
unión de tablas!
+ </para>
+
+ <para>
+ También pueden ser usadas las propiedades de identificadores
compuestos. Supón que <literal>Person</literal>
+ tiene un identificador compuesto consistente en
<literal>country</literal> y <literal>medicareNumber</literal>.
+ </para>
+
+ <programlisting><![CDATA[from bank.Person person
+where person.id.country = 'AU'
+ and person.id.medicareNumber = 123456]]></programlisting>
+
+ <programlisting><![CDATA[from bank.Account account
+where account.owner.id.country = 'AU'
+ and account.owner.id.medicareNumber = 123456]]></programlisting>
+
+ <para>
+ Una vez más, la segunda consulta no requiere ninguna
unión de tablas.
+ </para>
+
+ <para>
+ Asimismo, la propiedad especial <literal>class</literal> acccede
al valor discriminador de una instancia en
+ el caso de persistencia polimórfica. Un nombre de clase Java
embebido en la cláusula where será
+ traducido a su valor discriminador.
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.class =
DomesticCat]]></programlisting>
+
+ <para>
+ Puedes también especificar propiedades de componentes o tipos
compuestos de usuario (y de componentes
+ de componentes, etc). Nunca intentes usar una expresión de ruta
que termine en una propiedad de tipo
+ componente (al contrario de una propiedad de un componente). Por ejemplo, si
<literal>store.owner</literal>
+ es una entidad con un componente <literal>address</literal>
+ </para>
+
+ <programlisting><![CDATA[store.owner.address.city // okay
+store.owner.address // error!]]></programlisting>
+
+ <para>
+ Un tipo "any" tiene las propiedades especiales
<literal>id</literal> y <literal>class</literal>,
+ permiténdonos expresar un join en la siguiente forma (donde
<literal>AuditLog.item</literal> es una
+ propiedad mapeada con <literal><any></literal>).
+ </para>
+
+ <programlisting><![CDATA[from AuditLog log, Payment payment
+where log.item.class = 'Payment' and log.item.id =
payment.id]]></programlisting>
+
+ <para>
+ Nota que <literal>log.item.class</literal> y
<literal>payment.class</literal> harían referencia a
+ los valores de columnas de base de datos completamente diferentes en la
consulta anterior.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-expressions">
+ <title>Expresiones</title>
+
+ <para>
+ Las expresiones permitidas en la cláusula
<literal>where</literal> incluyen la mayoría del tipo de cosas
+ que podrías escribir en SQL:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ operadores matemáticos <literal>+, -, *,
/</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ operadores de comparación binarios <literal>=,
>=, <=, <>, !=, like</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ operadores lógicos <literal>and, or,
not</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Paréntesis <literal>( )</literal>, indicando
agrupación
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>in</literal>,
+ <literal>not in</literal>,
+ <literal>between</literal>,
+ <literal>is null</literal>,
+ <literal>is not null</literal>,
+ <literal>is empty</literal>,
+ <literal>is not empty</literal>,
+ <literal>member of</literal> y
+ <literal>not member of</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Caso "simple", <literal>case ... when ... then ...
else ... end</literal>,
+ y caso "buscado", <literal>case when ... then ...
else ... end</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ concatenación de cadenas
<literal>...||...</literal> o <literal>concat(...,...)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>current_date()</literal>,
<literal>current_time()</literal>,
+ <literal>current_timestamp()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>second(...)</literal>,
<literal>minute(...)</literal>,
+ <literal>hour(...)</literal>, <literal>day(...)</literal>,
+ <literal>month(...)</literal>,
<literal>year(...)</literal>,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Cualquier función u operador definido por EJB-QL 3.0:
<literal>substring(), trim(),
+ lower(), upper(), length(), locate(), abs(), sqrt(), bit_length(),
mod()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>coalesce()</literal> y
<literal>nullif()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>str()</literal> para convertir valores
numéricos o temporales a una cadena legible.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>cast(... as ...)</literal>, donde el segundo
argumento es el nombre de un tipo Hibernate
+ , y <literal>extract(... from ...)</literal> si
<literal>cast()</literal> y
+ <literal>extract()</literal> fuesen soportados por la
base de datos subyacente.
+ </para>
+ </listitem>
+
+
+ <listitem>
+ <para>
+ la función <literal>index()</literal> de HQL,
que se aplica a alias de una colección
+ indexada unida.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ funciones de HQL que tomen expresiones de ruta valuadas en
colecciones: <literal>size(),
+ minelement(), maxelement(), minindex(), maxindex()</literal>,
junto a las funciones especiales
+ <literal>elements()</literal> and
<literal>indices</literal> que pueden ser cuantificadas usando
+ <literal>some, all, exists, any, in</literal>.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Cualquier función escalar SQL soportada por la base de
datos como <literal>sign()</literal>,
+ <literal>trunc()</literal>,
<literal>rtrim()</literal>, <literal>sin()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ parámetros posicionales JDBC
<literal>?</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ parámetros con nombre
<literal>:name</literal>, <literal>:start_date</literal>,
<literal>:x1</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ literales SQL <literal>'foo'</literal>,
<literal>69</literal>, <literal>6.66E+2</literal>,
+ <literal>'1970-01-01 10:00:01.0'</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ constantes Java <literal>public static final</literal>
<literal>eg.Color.TABBY</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <literal>in</literal> y <literal>between</literal>
pueden usarse como sigue:
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name between
'A' and 'B']]></programlisting>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name in (
'Foo', 'Bar', 'Baz' )]]></programlisting>
+
+ <para>
+ y pueden escribirse las formas negadas
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name not between
'A' and 'B']]></programlisting>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name not in (
'Foo', 'Bar', 'Baz' )]]></programlisting>
+
+ <para>
+ Asimismo, <literal>is null</literal> y <literal>is not
null</literal> pueden ser usadas para comprobar
+ valores nulos.
+ </para>
+
+ <para>
+ Los booleanos pueden ser fácilmente usados en expresiones
declarando substituciones de consulta HQL
+ en la configuración de Hibernate:
+ </para>
+
+ <programlisting><![CDATA[<property
name="hibernate.query.substitutions">true 1, false
0</property>]]></programlisting>
+
+ <para>
+ Esto remplazará las palabras clave
<literal>true</literal> y <literal>false</literal> con los
literales
+ <literal>1</literal> y <literal>0</literal> en el SQL
traducido de este HQL:
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.alive =
true]]></programlisting>
+
+ <para>
+ Puedes comprobar el tamaño de una colección con la
propiedad especial <literal>size</literal>, o la función
+ especial <literal>size()</literal>.
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.kittens.size >
0]]></programlisting>
+
+ <programlisting><![CDATA[from Cat cat where size(cat.kittens) >
0]]></programlisting>
+
+ <para>
+ Para colecciones indexadas, puedes referirte a los índices
máximo y mínimo usando las funciones
+ <literal>minindex</literal> y
<literal>maxindex</literal>. Similarmente, puedes referirte a los elementos
+ máximo y mínimo de una colección de tipo
básico usando las funciones
+ <literal>minelement</literal> y
<literal>maxelement</literal>.
+ </para>
+
+ <programlisting><![CDATA[from Calendar cal where
maxelement(cal.holidays) > current_date]]></programlisting>
+
+ <programlisting><![CDATA[from Order order where maxindex(order.items)
> 100]]></programlisting>
+
+ <programlisting><![CDATA[from Order order where minelement(order.items)
> 10000]]></programlisting>
+
+ <para>
+ Las funciones SQL <literal>any, some, all, exists, in</literal>
están soportadas cuando se les pasa
+ el conjunto de elementos o índices de una colección
(funciones <literal>elements</literal> y
+ <literal>indices</literal>) o el resultado de una subconsulta
(ver debajo).
+ </para>
+
+ <programlisting><![CDATA[select mother from Cat as mother, Cat as kit
+where kit in elements(foo.kittens)]]></programlisting>
+
+ <programlisting><![CDATA[select p from NameList list, Person p
+where p.name = some elements(list.names)]]></programlisting>
+
+ <programlisting><![CDATA[from Cat cat where exists
elements(cat.kittens)]]></programlisting>
+
+ <programlisting><![CDATA[from Player p where 3 > all
elements(p.scores)]]></programlisting>
+
+ <programlisting><![CDATA[from Show show where 'fizard' in
indices(show.acts)]]></programlisting>
+
+ <para>
+ Nota que estas construcciones - <literal>size</literal>,
<literal>elements</literal>,
+ <literal>indices</literal>,
<literal>minindex</literal>, <literal>maxindex</literal>,
+ <literal>minelement</literal>,
<literal>maxelement</literal> - pueden ser usadas solamente
+ en la cláusula where en Hibernate3.
+ </para>
+
+ <para>
+ Los elementos de colecciones indexadas (arrays, listas, mapas) pueden ser
referidos por índice
+ (en una cláusula where solamente):
+ </para>
+
+ <programlisting><![CDATA[from Order order where order.items[0].id =
1234]]></programlisting>
+
+ <programlisting><![CDATA[select person from Person person, Calendar
calendar
+where calendar.holidays['national day'] = person.birthDay
+ and person.nationality.calendar = calendar]]></programlisting>
+
+ <programlisting><![CDATA[select item from Item item, Order order
+where order.items[ order.deliveredItemIndices[0] ] = item and order.id =
11]]></programlisting>
+
+ <programlisting><![CDATA[select item from Item item, Order order
+where order.items[ maxindex(order.items) ] = item and order.id =
11]]></programlisting>
+
+ <para>
+ La expresión dentro de <literal>[]</literal> puede
incluso ser una expresión aritmética.
+ </para>
+
+ <programlisting><![CDATA[select item from Item item, Order order
+where order.items[ size(order.items) - 1 ] = item]]></programlisting>
+
+ <para>
+ HQL provee además el función prefabricada
<literal>index()</literal>, para elementos de una
+ asociación uno-a-muchos o colección de valores.
+ </para>
+
+ <programlisting><![CDATA[select item, index(item) from Order order
+ join order.items item
+where index(item) < 5]]></programlisting>
+
+ <para>
+ Pueden usarse las funciones SQL escalares soportadas por la base de datos
subyacente
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat where upper(cat.name) like
'FRI%']]></programlisting>
+
+ <para>
+ Si aún no estás convencido de todo esto, piensa
cuánto más largo y menos leíble sería la
siguiente
+ consulta en SQL:
+ </para>
+
+ <programlisting><![CDATA[select cust
+from Product prod,
+ Store store
+ inner join store.customers cust
+where prod.name = 'widget'
+ and store.location.name in ( 'Melbourne', 'Sydney' )
+ and prod = all elements(cust.currentOrder.lineItems)]]></programlisting>
+
+ <para>
+ <emphasis>Ayuda:</emphasis> algo como
+ </para>
+
+ <programlisting><![CDATA[SELECT cust.name, cust.address, cust.phone,
cust.id, cust.current_order
+FROM customers cust,
+ stores store,
+ locations loc,
+ store_customers sc,
+ product prod
+WHERE prod.name = 'widget'
+ AND store.loc_id = loc.id
+ AND loc.name IN ( 'Melbourne', 'Sydney' )
+ AND sc.store_id = store.id
+ AND sc.cust_id = cust.id
+ AND prod.id = ALL(
+ SELECT item.prod_id
+ FROM line_items item, orders o
+ WHERE item.order_id = o.id
+ AND cust.current_order = o.id
+ )]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-ordering">
+ <title>La cláusula order by</title>
+
+ <para>
+ La lista devuelta por una consulta puede ser ordenada por cualquier propiedad
de una clase devuelta
+ o componentes:
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat
+order by cat.name asc, cat.weight desc, cat.birthdate]]></programlisting>
+
+ <para>
+ Los <literal>asc</literal> o <literal>desc</literal>
opcionales indican ordenamiento ascendente o
+ descendente respectivamente.
+ </para>
+ </sect1>
+
+ <sect1 id="queryhql-grouping">
+ <title>La cláusula group by</title>
+
+ <para>
+ Una consulta que devuelve valores agregados puede ser agrupada por cualquier
propiedad de una clase
+ devuelta o componentes:
+ </para>
+
+ <programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
+from Cat cat
+group by cat.color]]></programlisting>
+
+ <programlisting><![CDATA[select foo.id, avg(name), max(name)
+from Foo foo join foo.names name
+group by foo.id]]></programlisting>
+
+ <para>
+ Se permite también una cláusula
<literal>having</literal>.
+ </para>
+
+ <programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
+from Cat cat
+group by cat.color
+having cat.color in (eg.Color.TABBY, eg.Color.BLACK)]]></programlisting>
+
+ <para>
+ Las funciones y funciones de agregación SQL están
permitidas en las cláusulas
+ <literal>having</literal> y <literal>order
by</literal>, si están soportadas por la base de datos
+ subyacente (por ejemplo, no en MySQL).
+ </para>
+
+ <programlisting><![CDATA[select cat
+from Cat cat
+ join cat.kittens kitten
+group by cat
+having avg(kitten.weight) > 100
+order by count(kitten) asc, sum(kitten.weight) desc]]></programlisting>
+
+ <para>
+ Nota que ni la cláusula <literal>group by</literal> ni
la cláusula <literal>order by</literal> pueden
+ contener expresiones aritméticas.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-subqueries">
+ <title>Subconsultas</title>
+
+ <para>
+ Para bases de datos que soportan subconsultas, Hibernate soporta subconsultas
dentro de consultas. Una
+ subconsulta debe ser encerrada entre paréntesis (frecuentemente
por una llamada a una función de agregación
+ SQL). Incluso se permiten subconsultas correlacionadas (subconsultas que
hacen referencia a un alias en la
+ consulta exterior).
+ </para>
+
+ <programlisting><![CDATA[from Cat as fatcat
+where fatcat.weight > (
+ select avg(cat.weight) from DomesticCat cat
+)]]></programlisting>
+
+ <programlisting><![CDATA[from DomesticCat as cat
+where cat.name = some (
+ select name.nickName from Name as name
+)]]></programlisting>
+
+ <programlisting><![CDATA[from Cat as cat
+where not exists (
+ from Cat as mate where mate.mate = cat
+)]]></programlisting>
+
+ <programlisting><![CDATA[from DomesticCat as cat
+where cat.name not in (
+ select name.nickName from Name as name
+)]]></programlisting>
+
+ <para>
+ Para las subconsultas con más de una expresión en la
lista de selección, puedes usar un constructor
+ de tuplas:
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+where not ( cat.name, cat.color ) in (
+ select cat.name, cat.color from DomesticCat cat
+)]]></programlisting>
+
+ <para>
+ Nota que en algunas bases de datos (pero no en Oracle o HSQL), puedes usar
constructores de tuplar en
+ otros contextos, por ejemplo al consultar componentes o tipos de usuario
compuestos:
+ </para>
+
+ <programlisting><![CDATA[from Person where name = ('Gavin',
'A', 'King')]]></programlisting>
+
+ <para>
+ Que es equivalente a la más verborrágica:
+ </para>
+
+ <programlisting><![CDATA[from Person where name.first = 'Gavin'
and name.initial = 'A' and name.last =
'King')]]></programlisting>
+
+ <para>
+ Existen dos buenas razones por las cuales podrías no querer hacer
este tipo de cosa: primero, no es
+ completamente portable entre plataformas de base de datos; segundo, la
consulta ahora es dependiente
+ del orden de propiedades en el documento de mapeo.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-examples">
+ <title>Ejemplos de HQL</title>
+
+ <para>
+ Las consultas de Hibernate pueden ser abolutamente potentes y complejas, De
hecho, el poder del lenguaje
+ de consulta es uno de los puntos principales de venta de Hibernate. He
aquí algunos consultas de ejemplo
+ muy similares a consultas que he usado en un proyecto reciente.
¡Nota que la mayoría de las consultas
+ que escribirás som mucho más simples que estas!
+ </para>
+
+ <para>
+ La siguiente consulta devuelve el order id, número de items y
valor total de la orden para todas
+ las ordenes inpagas de un cliente en particular y valor total
mínimo dados, ordenando los resultados
+ por valor total. Al determinar los precios, usa el catálogo
actual. La consulta SQL resultante,
+ contra las tablas <literal>ORDER</literal>,
<literal>ORDER_LINE</literal>, <literal>PRODUCT</literal>,
+ <literal>CATALOG</literal> and
<literal>PRICE</literal> tiene cuatro joins interiores y una subselect
+ (no correlacionada).
+ </para>
+
+ <programlisting><![CDATA[select order.id, sum(price.amount),
count(item)
+from Order as order
+ join order.lineItems as item
+ join item.product as product,
+ Catalog as catalog
+ join catalog.prices as price
+where order.paid = false
+ and order.customer = :customer
+ and price.product = product
+ and catalog.effectiveDate < sysdate
+ and catalog.effectiveDate >= all (
+ select cat.effectiveDate
+ from Catalog as cat
+ where cat.effectiveDate < sysdate
+ )
+group by order
+having sum(price.amount) > :minAmount
+order by sum(price.amount) desc]]></programlisting>
+
+ <para>
+ ¡Qué monstruo! Realmente, en la vida real, no estoy muy
afilado en subconsultas, de modo que mi
+ consulta fue realmente algo como esto:
+ </para>
+
+ <programlisting><![CDATA[select order.id, sum(price.amount),
count(item)
+from Order as order
+ join order.lineItems as item
+ join item.product as product,
+ Catalog as catalog
+ join catalog.prices as price
+where order.paid = false
+ and order.customer = :customer
+ and price.product = product
+ and catalog = :currentCatalog
+group by order
+having sum(price.amount) > :minAmount
+order by sum(price.amount) desc]]></programlisting>
+
+ <para>
+ La próxima consulta cuenta el número de pagos en cada
estado, excluyendo todos los pagos
+ en el estado <literal>AWAITING_APPROVAL</literal> donde el estado
más reciente fue hecho por el
+ usuario actual. Se traduce en una consulta SQL con dos joins interiores y una
subselect
+ correlacionada contra las tablas <literal>PAYMENT</literal>,
<literal>PAYMENT_STATUS</literal> y
+ <literal>PAYMENT_STATUS_CHANGE</literal>.
+ </para>
+
+ <programlisting><![CDATA[select count(payment), status.name
+from Payment as payment
+ join payment.currentStatus as status
+ join payment.statusChanges as statusChange
+where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
+ or (
+ statusChange.timeStamp = (
+ select max(change.timeStamp)
+ from PaymentStatusChange change
+ where change.payment = payment
+ )
+ and statusChange.user <> :currentUser
+ )
+group by status.name, status.sortOrder
+order by status.sortOrder]]></programlisting>
+
+ <para>
+ Si hubiese mapeado la colección
<literal>statusChanges</literal> como una lista, en vez de un conjunto,
+ la consulta habría sido mucho más simple de escribir.
+ </para>
+
+ <programlisting><![CDATA[select count(payment), status.name
+from Payment as payment
+ join payment.currentStatus as status
+where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
+ or payment.statusChanges[ maxIndex(payment.statusChanges) ].user <>
:currentUser
+group by status.name, status.sortOrder
+order by status.sortOrder]]></programlisting>
+
+ <para>
+ La próxima consulta usa la función
<literal>isNull()</literal> de MS SQL Server para devolver
+ todas las cuentas y pagos inpagos de la organización a la que
pertenece el usuario actual.
+ Se traduce en una consulta SQL con tres joins interiores, un join exterior y
una subconsulta
+ contra las tablas <literal>ACCOUNT</literal>,
<literal>PAYMENT</literal>, <literal>PAYMENT_STATUS</literal>,
+ <literal>ACCOUNT_TYPE</literal>,
<literal>ORGANIZATION</literal> y <literal>ORG_USER</literal>.
+ </para>
+
+ <programlisting><![CDATA[select account, payment
+from Account as account
+ left outer join account.payments as payment
+where :currentUser in elements(account.holder.users)
+ and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
+order by account.type.sortOrder, account.accountNumber,
payment.dueDate]]></programlisting>
+
+ <para>
+ Para algunas bases de datos, necesitaríamos eliminar la subselect
(correlacionada).
+ </para>
+
+ <programlisting><![CDATA[select account, payment
+from Account as account
+ join account.holder.users as user
+ left outer join account.payments as payment
+where :currentUser = user
+ and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
+order by account.type.sortOrder, account.accountNumber,
payment.dueDate]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-bulk">
+ <title>Sentencias UPDATE y DELETE masivas</title>
+
+ <para>
+ HQL soporta ahora sentencias UPDATE y DELETE en HQL.
+ Ver <xref linkend="batch-direct"/> para detalles.
+ </para>
+ </sect1>
+
+ <sect1 id="queryhql-tipstricks">
+ <title>Consejos y Trucos</title>
+
+ <para>
+ Puedes contar el número de resultados de una consulta sin
devolverlos realmente:
+ </para>
+
+ <programlisting><![CDATA[( (Integer) session.createQuery("select
count(*) from ....").iterate().next() ).intValue()]]></programlisting>
+
+ <para>
+ Para ordenar un resultado por el tamaño de una
colección, usa la siguiente consulta:
+ </para>
+
+ <programlisting><![CDATA[select usr.id, usr.name
+from User as usr
+ left join usr.messages as msg
+group by usr.id, usr.name
+order by count(msg)]]></programlisting>
+
+ <para>
+ Si tu base de datos soporta subselects, puedes colocar una
condición sobre el tamaño de selección
+ en la cláusula where de tu consulta:
+ </para>
+
+ <programlisting><![CDATA[from User usr where size(usr.messages) >=
1]]></programlisting>
+
+ <para>
+ Si tu base de datos no soporta subselects, usa la siguiente consulta:
+ </para>
+
+ <programlisting><![CDATA[select usr.id, usr.name
+from User usr.name
+ join usr.messages msg
+group by usr.id, usr.name
+having count(msg) >= 1]]></programlisting>
+
+ <para>
+ Como esta solución no puede devolver un
<literal>User</literal> con cero mensajes debido a la unión
interior,
+ la siguiente forma es también útil:
+ </para>
+
+ <programlisting><![CDATA[select usr.id, usr.name
+from User as usr
+ left join usr.messages as msg
+group by usr.id, usr.name
+having count(msg) = 0]]></programlisting>
+
+ <para>
+ Las propiedades de un JavaBean pueden ser ligadas al parámetros de
consulta con nombre:
+ </para>
+
+ <programlisting><![CDATA[Query q = s.createQuery("from foo Foo as
foo where foo.name=:name and foo.size=:size");
+q.setProperties(fooBean); // fooBean has getName() and getSize()
+List foos = q.list();]]></programlisting>
+
+ <para>
+ Las colecciones son paginables usando la interface
<literal>Query</literal> con un filtro:
+ </para>
+
+ <programlisting><![CDATA[Query q = s.createFilter( collection,
"" ); // the trivial filter
+q.setMaxResults(PAGE_SIZE);
+q.setFirstResult(PAGE_SIZE * pageNumber);
+List page = q.list();]]></programlisting>
+
+ <para>
+ Los elementos de colección pueden ser ordenados o agrupados usando
un filtro de consulta:
+ </para>
+
+ <programlisting><![CDATA[Collection orderedCollection = s.filter(
collection, "order by this.amount" );
+Collection counts = s.filter( collection, "select this.type, count(this) group by
this.type" );]]></programlisting>
+
+ <para>
+ Puedes hallar el tamaño de una colección sin
inicializarla:
+ </para>
+
+ <programlisting><![CDATA[( (Integer) session.createQuery("select
count(*) from ....").iterate().next() ).intValue();]]></programlisting>
+
+ </sect1>
+
+</chapter>
+
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/content/query_sql.xml (from
rev 12794, core/trunk/documentation/manual/es-ES/src/main/docbook/modules/query_sql.xml)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/content/query_sql.xml
(rev 0)
+++
core/trunk/documentation/manual/es-ES/src/main/docbook/content/query_sql.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,477 @@
+<chapter id="querysql" revision="2">
+ <title>SQL Nativo</title>
+
+ <para>
+ Puedes también expresar consultas en el dialecto SQL nativo de tu base
de datos. Esto es útil si quieres
+ utilizar aspectos específicos de base de datos tal como consejos
(hints) de consulta o la palabra clave
+ <literal>CONNECT</literal> en Oracle. Provee además una
clara ruta de migración desde una aplicación
+ basada en SQL/JDBC directo a Hibernate.
+ </para>
+
+ <para>
+ Hibernate3 te permite especificar SQL escrito a mano (incluyendo procedimientos
almacenados) para todas
+ las operaciones de creación, actualización, borrado y
carga.
+ </para>
+
+ <sect1 id="querysql-creating">
+ <title>Creando una <literal>Query</literal> de SQL
nativo</title>
+
+ <para>
+ Las consultas SQL se controlan por medio de la interface
<literal>SQLQuery</literal>, que se obtiene
+ llamando a <literal>Session.createSQLQuery()</literal>.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createSQLQuery("select
{cat.*} from cats cat")
+ .addEntity("cat", Cat.class)
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ <para>
+ Esta consulta especificada:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ la cadena de consulta SQL, con un lugar para que Hibernate inyecte los alias
de columnas
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ la entidad devuelta por la consulta, y sus alias de tablas SQL
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ El método <literal>addEntity()</literal> asocia alias
de tablas SQL con clases de entidad,
+ y determina la forma del conjunto resultado de la consulta.
+ </para>
+
+ <para>
+ El método <literal>addJoin()</literal> puede ser usado
para cargar asociaciones a otras entidades y
+ colecciones.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createSQLQuery(
+ "select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother =
cat.id"
+ )
+ .addEntity("cat", Cat.class)
+ .addJoin("kitten", "cat.kittens")
+ .list();]]></programlisting>
+
+ <para>
+ Una consulta SQL nativa podría devolver un valor escalar simple o
una combinación de escalares y entidades.
+ </para>
+
+ <programlisting><![CDATA[Double max = (Double)
sess.createSQLQuery("select max(cat.weight) as maxWeight from cats cat")
+ .addScalar("maxWeight", Hibernate.DOUBLE);
+ .uniqueResult();]]></programlisting>
+
+
+ </sect1>
+
+ <sect1 id="querysql-aliasreferences">
+ <title>Alias y referencias de propiedad</title>
+
+ <para>
+ La notación <literal>{cat.*}</literal> usada arriba es
un atajo para "todas las propiedades".
+ Alternativamente, puedes listar las columnas explícitamente, pero
incluso en este caso dejamos
+ que Hibernate inyecte los alias de columnas SQL para cada propiedad. El lugar
para un alias de columna
+ es sólo el nombre de propiedad cualificado por el alias de la
tabla. En el siguiente ejemplo,
+ recuperamos <literal>Cat</literal>s de una tabla diferente
(<literal>cat_log</literal>) a una
+ declarada en los metadatos de mapeo. Nota que podríamos incluso
usar los alias de propiedad en la
+ cláusula where si quisieramos.
+ </para>
+ <para>
+ La sintáxis <literal>{}</literal>
<emphasis>no</emphasis> es requerida para consultas con nombre.
+ Ver <xref linkend="querysql-namedqueries"/>
+ </para>
+
+ <programlisting><![CDATA[String sql = "select cat.originalId as
{cat.id}, " +
+ "cat.mateid as {cat.mate}, cat.sex as {cat.sex}, " +
+ "cat.weight*10 as {cat.weight}, cat.name as {cat.name} " +
+ "from cat_log cat where {cat.mate} = :catId"
+
+List loggedCats = sess.createSQLQuery(sql)
+ .addEntity("cat", Cat.class)
+ .setLong("catId", catId)
+ .list();]]></programlisting>
+
+ <para>
+ <emphasis>Nota:</emphasis> si listas cada propiedad
explícitamente, ¡debes incluir todas las
+ propiedades de la clase <emphasis>y sus subclases</emphasis>!
+ </para>
+
+ </sect1>
+
+ <sect1 id="querysql-namedqueries" revision="2">
+ <title>Consultas SQL con nombre</title>
+
+ <para>
+ Las consultas SQL con nombre pueden definirse en el documento de mapeo y
llamadas exactamente
+ en la misma forma en que a una consulta HQL con nombre. En este caso,
<emphasis>no</emphasis>
+ necesitamos llamar a <literal>addEntity()</literal>.
+ </para>
+
+ <programlisting><![CDATA[<sql-query name="persons">
+ <return alias="person" class="eg.Person"/>
+ SELECT person.NAME AS {person.name},
+ person.AGE AS {person.age},
+ person.SEX AS {person.sex}
+ FROM PERSON person
+ WHERE person.NAME LIKE :namePattern
+</sql-query>]]></programlisting>
+
+ <programlisting><![CDATA[List people =
sess.getNamedQuery("persons")
+ .setString("namePattern", namePattern)
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ <para>
+ Los elementos <literal><return-join></literal> y
<literal><load-collection></literal>
+ se usan para unir asociaciones y definir consultas que inicialicen
colecciones, respectivamente.
+ </para>
+
+ <programlisting><![CDATA[<sql-query
name="personsWith">
+ <return alias="person" class="eg.Person"/>
+ <return-join alias="address"
property="person.mailingAddress"/>
+ SELECT person.NAME AS {person.name},
+ person.AGE AS {person.age},
+ person.SEX AS {person.sex},
+ address.STREET AS {address.street},
+ address.CITY AS {address.city},
+ address.STATE AS {address.state},
+ address.ZIP AS {address.zip}
+ FROM PERSON person
+ JOIN ADDRESS address
+ ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
+ WHERE person.NAME LIKE :namePattern
+</sql-query>]]></programlisting>
+
+ <para>
+ Una consulta SQL con nombre puede devolver un valor escalar. Debes
especificar el alias de columna y
+ tipo Hibernate usando el elementp
<literal><return-scalar></literal>:
+ </para>
+
+ <programlisting><![CDATA[<sql-query name="mySqlQuery">
+ <return-scalar column="name" type="string"/>
+ <return-scalar column="age" type="long"/>
+ SELECT p.NAME AS name,
+ p.AGE AS age,
+ FROM PERSON p WHERE p.NAME LIKE 'Hiber%'
+</sql-query>]]></programlisting>
+
+ <sect2 id="propertyresults">
+ <title>Usando return-property para especificar
explícitamente nombres de columna/alias</title>
+
+ <para>
+ Con <literal><return-property></literal>
puedes decirle explícitamente a Hibernate qué
+ alias de columna usar, en vez de usar la sintáxis
<literal>{}</literal> para dejar que Hibernate
+ inyecte sus propios alias.
+ </para>
+
+ <programlisting><![CDATA[<sql-query
name="mySqlQuery">
+ <return alias="person" class="eg.Person">
+ <return-property name="name" column="myName"/>
+ <return-property name="age" column="myAge"/>
+ <return-property name="sex" column="mySex"/>
+ </return>
+ SELECT person.NAME AS myName,
+ person.AGE AS myAge,
+ person.SEX AS mySex,
+ FROM PERSON person WHERE person.NAME LIKE :name
+</sql-query>
+]]></programlisting>
+
+ <para>
+ <literal><return-property></literal>
también trabaja con múltiples columnas. Esto resuelve una
+ limitación de la sintáxis
<literal>{}</literal>, la cual no puede permitir un control fino de
propiedades
+ multi-columna.
+ </para>
+
+ <programlisting><![CDATA[<sql-query
name="organizationCurrentEmployments">
+ <return alias="emp" class="Employment">
+ <return-property name="salary">
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ <return-property name="endDate" column="myEndDate"/>
+ </return>
+ SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
+ STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
+ REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY
+ FROM EMPLOYMENT
+ WHERE EMPLOYER = :id AND ENDDATE IS NULL
+ ORDER BY STARTDATE ASC
+</sql-query>]]></programlisting>
+
+ <para>
+ Nota que en este ejemplo hemos usado
<literal><return-property></literal> en combinación
con
+ la sintáxis <literal>{}</literal> para
inyección, permitiendo a los usuarios elejir cómo quieren
+ referirse a las columnas y propiedades.
+ </para>
+
+ <para>
+ Si tu mapeo tiene un discriminador debes usar
<literal><return-discriminator></literal>
+ para especificar la columna discriminadora.
+ </para>
+ </sect2>
+
+ <sect2 id="sp_query">
+ <title>Usando procedimientos almacenados para consultar</title>
+
+ <para>
+ Hibernate3 introduce soporte para consultas vía
procedimientos almacenados. Los procedimientos
+ almacenados deben devolver un conjunto resultado como el primer
parámetro de salida para ser
+ capaces de funcionar con Hibernate. Un ejemplo de uno procedimiento
almacenado en Oracle 9
+ o superior es así:
+ </para>
+
+ <programlisting><![CDATA[CREATE OR REPLACE FUNCTION
selectAllEmployments
+ RETURN SYS_REFCURSOR
+AS
+ st_cursor SYS_REFCURSOR;
+BEGIN
+ OPEN st_cursor FOR
+ SELECT EMPLOYEE, EMPLOYER,
+ STARTDATE, ENDDATE,
+ REGIONCODE, EID, VALUE, CURRENCY
+ FROM EMPLOYMENT;
+ RETURN st_cursor;
+ END;]]></programlisting>
+
+ <para>
+ Para usar esta consulta en Hibernate necesitas mapearla por medio de una
consulta con nombre.
+ </para>
+
+ <programlisting><![CDATA[<sql-query
name="selectAllEmployees_SP" callable="true">
+ <return alias="emp" class="Employment">
+ <return-property name="employee" column="EMPLOYEE"/>
+ <return-property name="employer" column="EMPLOYER"/>
+ <return-property name="startDate" column="STARTDATE"/>
+ <return-property name="endDate" column="ENDDATE"/>
+ <return-property name="regionCode"
column="REGIONCODE"/>
+ <return-property name="id" column="EID"/>
+ <return-property name="salary">
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ </return>
+ { ? = call selectAllEmployments() }
+</sql-query>]]></programlisting>
+
+ <para>
+ Nota que los procedimientos almacenados sólo devuelven
escalares y entidades.
+ No están soportados
<literal><return-join></literal> y
<literal><load-collection></literal>.
+ </para>
+
+ <sect3 id="querysql-limits-storedprocedures">
+ <title>Reglas/limitaciones para usar procedimientos
almacenados</title>
+
+ <para>
+ Para usar procedimientos almacenados con Hibernate los procedimientos
tienen que seguir algunas reglas.
+ Si no siguen esas reglas no son usables por Hibernate. Si
aún quisieras usar estos procedimientos
+ tendrías que ejecutarlos por medio de
<literal>session.connection()</literal>. Las reglas son
+ diferentes para cada base de datos, ya que los vendedores de base de
datos tienen diferentes
+ semánticas/sintáxis de procedimientos
almacenados.
+ </para>
+
+ <para>
+ Las consultas de procedimientos almacenados no pueden ser paginadas
con
+ <literal>setFirstResult()/setMaxResults()</literal>.
+ </para>
+
+ <para>
+ Para Oracle se aplican las siguientes reglas:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ El procedimiento debe devolver un conjunto resultado. Esto se hace
devolviendo un
+ <literal>SYS_REFCURSOR</literal> en Oracle 9 o 10. En
Oracle necesitas definir un
+ tipo <literal>REF CURSOR</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ La forma recomendada es <literal>{ ? = call
procName(<parameters>) }</literal> o
+ <literal>{ ? = call procName }</literal> (esto es
más una regla de Oracle que una regla de Hibernate).
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Para Sybase o MS SQL server se aplican las siguientes reglas:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ El procedimiento debe devolver un conjunto resultado. Nota que ya que
estos servidores pueden
+ y devolverán múltiples conjuntos resultados y
cuentas de actualización, Hibernate iterará
+ los resultados y tomará el primer resultado que sea un
conjunto resultado como su valor
+ a devolver. Todo lo demás será descartado.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Si habilitas <literal>SET NOCOUNT ON</literal> en tu
procedimiento será probablemente más
+ eficiente, pero esto no es un requerimiento.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </sect3>
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="querysql-cud">
+ <title>SQL personalizado para crear, actualizar y borrar</title>
+
+ <para>
+ Hibernate3 puede usar sentencias SQL personalizadas para las operaciones de
+ crear, actualizar y borrar. Los persistidores de clases y colecciones en
Hibernate
+ ya contienen un conjunto de cadenas generadas en tiempo de
configuración (insertsql,
+ deletesql, updatesql, etc.). Las etiquetas de mapeo
<literal><sql-insert></literal>,
+ <literal><sql-delete></literal>, y
<literal><sql-update></literal> sobrescriben
+ estas cadenas:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ?
)</sql-insert>
+ <sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
+ <sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
+</class>]]></programlisting>
+
+ <para>
+ El SQL se ejecuta directamente en tu base de datos, de modo que eres libre de
usar cualquier
+ dialecto que quieras. Esto reducirá, por supuesto, la portabilidad
de tu mapeo si usas SQL
+ específico de la base de datos.
+ </para>
+
+ <para>
+ Los procedimientos almacenados son soportados si está establecido
el atributo
+ <literal>callable</literal>:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <sql-insert callable="true">{call createPerson (?,
?)}</sql-insert>
+ <sql-delete callable="true">{? = call deletePerson
(?)}</sql-delete>
+ <sql-update callable="true">{? = call updatePerson (?,
?)}</sql-update>
+</class>]]></programlisting>
+
+ <para>
+ El orden de los parámetros posicionales son actualmente vitales, ya
que deben estar en la
+ misma secuencia en que las espera Hibernate.
+ </para>
+
+ <para>
+ Puedes ver el orden esperado habilitando el registro de depuración
para el nivel
+ <literal>org.hibernate.persister.entity</literal>. Con este nivel
habilitado, Hibernate
+ imprimirá el SQL estático que se usa para crear,
actualizar, borrar, etc. las entidades.
+ (Para ver la secuencia esperada, recuerda no incluir tu SQL personalizado en
los ficheros
+ de mapeo ya que sobrescribirán el sql estático generado
por Hibernate.)
+ </para>
+
+ <para>
+ Los procedimientos almacenados son, en la mayoría de los casos
(léase, mejor hacerlo que no hacerlo),
+ obligados a devolver el número de filas
insertadas/actualizadas/borradas, ya que Hibernate tiene algunas
+ comprobaciones en tiempo de ejecución del éxito de la
sentencia. Hibernate siempre registra el primer
+ parámetro de la sentencia como un parámetro de salida
numérico para las operaciones CUD:
+ </para>
+
+ <programlisting><![CDATA[CREATE OR REPLACE FUNCTION updatePerson (uid IN
NUMBER, uname IN VARCHAR2)
+ RETURN NUMBER IS
+BEGIN
+
+ update PERSON
+ set
+ NAME = uname,
+ where
+ ID = uid;
+
+ return SQL%ROWCOUNT;
+
+END updatePerson;]]></programlisting>
+
+
+ </sect1>
+
+ <sect1 id="querysql-load">
+ <title>SQL personalizado para carga</title>
+
+ <para>
+ Puedes también declarar tu propias consultas SQL (o HQL) para
cargar entidades:
+ </para>
+
+ <programlisting><![CDATA[<sql-query name="person">
+ <return alias="pers" class="Person"
lock-mode="upgrade"/>
+ SELECT NAME AS {pers.name}, ID AS {pers.id}
+ FROM PERSON
+ WHERE ID=?
+ FOR UPDATE
+</sql-query>]]></programlisting>
+
+ <para>
+ Esto es sólo una declaración de consulta con nombrem
como se ha discutido anteriormente.
+ Puedes hacer referencia a esta consulta con nombre en un mapeo de clase:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <loader query-ref="person"/>
+</class>]]></programlisting>
+
+ <para>
+ Esto incluso funciona con procedimientos almacenados.
+ </para>
+
+ <para>
+ Puedes incluso definit una consulta para la carga de colecciones:
+ </para>
+
+<programlisting><![CDATA[<set name="employments"
inverse="true">
+ <key/>
+ <one-to-many class="Employment"/>
+ <loader query-ref="employments"/>
+</set>]]></programlisting>
+
+ <programlisting><![CDATA[<sql-query name="employments">
+ <load-collection alias="emp" role="Person.employments"/>
+ SELECT {emp.*}
+ FROM EMPLOYMENT emp
+ WHERE EMPLOYER = :id
+ ORDER BY STARTDATE ASC, EMPLOYEE ASC
+</sql-query>]]></programlisting>
+
+ <para>
+ Podrías incluso definir un cargador de entidades que cargue una
colección por
+ recuperación por unión (join fetching):
+ </para>
+
+ <programlisting><![CDATA[<sql-query name="person">
+ <return alias="pers" class="Person"/>
+ <return-join alias="emp" property="pers.employments"/>
+ SELECT NAME AS {pers.*}, {emp.*}
+ FROM PERSON pers
+ LEFT OUTER JOIN EMPLOYMENT emp
+ ON pers.ID = emp.PERSON_ID
+ WHERE ID=?
+</sql-query>]]></programlisting>
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/content/quickstart.xml
(from rev 12891,
core/trunk/documentation/manual/es-ES/src/main/docbook/modules/quickstart.xml)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/content/quickstart.xml
(rev 0)
+++
core/trunk/documentation/manual/es-ES/src/main/docbook/content/quickstart.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,666 @@
+<chapter id="quickstart">
+ <title>Comienzo rápido con Tomcat</title>
+
+ <sect1 id="quickstart-intro" revision="2">
+ <title>Empezando con Hibernate</title>
+
+ <para>
+ Este tutorial explica una instalación de Hibernate con el
+ contenedor de servlets Apache Tomcat (hemos usado la versión 4.1,
+ las diferencias con la 5.0 deben ser mínimas) para una
aplicación
+ basada en web. Hibernate trabaja bien en un entorno manejado con
+ todos los servidores de aplicaciones J2EE importantes, o incluso en
aplicaciones
+ Java independientes. El sistema de base de datos es sólo una
cuestión
+ de cambiar la configuración del dialecto SQL de Hibernate y las
+ propiedades de conexión.
+ </para>
+
+ <para>
+ Primero, tenemos que copiar todas las bibliotecas requeridas a la
instalación
+ de Tomcat. Usamos un contexto web separado
(<literal>webapps/quickstart</literal>)
+ para este tutorial, de modo que tenemos que considerar tanto la ruta de
búsqueda
+ de bibliotecas global (<literal>TOMCAT/common/lib</literal>) como
también
+ el cargador de clases a nivel de contexto en
<literal>webapps/quickstart/WEB-INF/lib</literal>
+ (para ficheros JAR) y
<literal>webapps/quickstart/WEB-INF/classes</literal>.
+ Nos referiremos a ambos niveles de cargador de clases como el classpath
global y el classpath
+ de contexto, respectivamente.
+ </para>
+
+ <para>
+ Ahora, copia las bibliotecas a los dos classpaths:
+ </para>
+
+ <orderedlist>
+ <listitem>
+ <para>
+ Copia el driver JDBC para la base de datos al classpath global. Esto
se
+ requiere para el software de pool de conexiones DBCP que se
distribuye
+ con Tomcat. Hibernate usa conexiones JDBC para ejecutar SQL sobre la
base de
+ datos, de modo que, o bien tienes que proveer conexiones JDBC en
pool,
+ o bien configurar Hibernate para que use uno de los pools soportados
+ directamente (C3P0, Proxool). Para este tutorial, copia la
biblioteca
+ <literal>pg74jdbc3.jar</literal> (para PostgreSQL 7.4 y
JDK 1.4) al
+ classpath del cargador global. Si quisieras usar una base de datos
diferente,
+ simplemente copia su apropiado driver JDBC.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Nunca copies nada más dentro de la ruta del cargador de
clases global
+ en Tomcat, o tendrás problemas con varias herramientas,
incluyendo
+ Log4J, commons-logging y otras. Siempre usa el classpath de contexto
para
+ cada aplicación web, esto es, copia las bibliotecas a
+ <literal>WEB-INF/lib</literal> y tus propias clases y
ficheros de
+ configuración/propiedades a
<literal>WEB-INF/classes</literal>.
+ Ambos directorios están a nivel del classpath de contexto
por defecto.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Hibernate está empaquetado como una biblioteca JAR. El
fichero
+ <literal>hibernate3.jar</literal> debe ser copiado en el
classpath de contexto
+ junto a las otras clases de la aplicación. Hibernate
requiere algunas
+ bibliotecas de terceros en tiempo de ejecución;
éstas vienen
+ incluídas con la distribución de Hibernate en
el directorio
+ <literal>lib/</literal>. Ver <xref
linkend="3rdpartylibs"/>. Copia las
+ bibliotecas de terceros requeridas al classpath de contexto.
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <table frame="topbot" id="3rdpartylibs">
+ <title>
+ Bibliotecas de terceros de Hibernate
+ </title>
+ <tgroup cols="2" rowsep="1" colsep="1">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="2*"/>
+ <thead>
+ <row>
+ <entry align="center">
+ Biblioteca
+ </entry>
+ <entry align="center">
+ Descripción
+ </entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ antlr (requerida)
+ </entry>
+ <entry>
+ Hibernate usa ANTLR para producir analizadores de consultas,
+ esta biblioteca también se necesita en tiempo de
ejecución.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ dom4j (requerida)
+ </entry>
+ <entry>
+ Hibernate usa dom4j para analizar ficheros de
configuración
+ XML y ficheros de metadatos de mapeo XML.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ CGLIB, asm (requerida)
+ </entry>
+ <entry>
+ Hibernate usa la biblioteca de generación de
código
+ para aumentar las clases en tiempo de ejecución
+ (en combinación con reflección Java).
+ </entry>
+ </row>
+ <row>
+ <entry>
+ Commons Collections, Commons Logging (requeridas)
+ </entry>
+ <entry>
+ Hibernate usa varias bibliotecas de utilidad del proyecto
+ Jakarta Commons de Apache.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ EHCache (requerida)
+ </entry>
+ <entry>
+ Hibernate puede usar varios provedores de caché
para
+ el caché de segundo nivel. EHCache es el provedor
de
+ caché por defecto si no se cambia en la
configuración.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ Log4j (opcional)
+ </entry>
+ <entry>
+ Hibernate usa la API de Commons Logging, que a su vez puede
+ usar Log4J como el mecanismo de logging subyacente. Si la
+ biblioteca Log4J está disponible en el directorio
de
+ bibliotecas del contexto, Commons Logging usará
Log4J
+ y la configuración
<literal>log4j.properties</literal>
+ en el classpath de contexto. Un fichero de propiedades de
ejemplo
+ para Log4J se incluye con la distribución de
Hibernate.
+ Así que copia log4j.jar y el fichero de
configuración
+ (de <literal>src/</literal>) a tu classpath de
contexto si quieres
+ ver que ocurre tras escénas.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ ¿Requerida o no?
+ </entry>
+ <entry>
+ Echa una mirada al fichero
<literal>lib/README.txt</literal> en la
+ distribución de Hibernate. Esta es una lista
actualizada
+ de bibliotecas de terceros distribuídas con
Hibernate.
+ Encontrarás listadas ahí todas las
bibliotecas
+ requeridas y opcionales (Observa que "buildtame
required" significa
+ aquí para la construcción de Hibernate,
no de tu
+ aplicación).
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Ahora instalamos el pooling y modo compartido de conexiones de base de datos
+ tanto en Tomcat como Hibernate. Esto significa que Tomcat
proveerá
+ conexiones JDBC en pool (usando su funcionalidad prefabricada de pooling
DBCP).
+ Hibernate pide esas conexiones a través de JNDI.
Alternativamente,
+ puedes dejar que Hibernate maneje el pool de conexiones. Tomcat liga su pool
+ de conexiones a JNDI; agregamos una declaración de recurso al
fichero
+ de configuración principal de Tomcat,
<literal>TOMCAT/conf/server.xml</literal>:
+ </para>
+
+ <programlisting><![CDATA[<Context path="/quickstart"
docBase="quickstart">
+ <Resource name="jdbc/quickstart" scope="Shareable"
type="javax.sql.DataSource"/>
+ <ResourceParams name="jdbc/quickstart">
+ <parameter>
+ <name>factory</name>
+ <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
+ </parameter>
+
+ <!-- DBCP database connection settings -->
+ <parameter>
+ <name>url</name>
+ <value>jdbc:postgresql://localhost/quickstart</value>
+ </parameter>
+ <parameter>
+
<name>driverClassName</name><value>org.postgresql.Driver</value>
+ </parameter>
+ <parameter>
+ <name>username</name>
+ <value>quickstart</value>
+ </parameter>
+ <parameter>
+ <name>password</name>
+ <value>secret</value>
+ </parameter>
+
+ <!-- DBCP connection pooling options -->
+ <parameter>
+ <name>maxWait</name>
+ <value>3000</value>
+ </parameter>
+ <parameter>
+ <name>maxIdle</name>
+ <value>100</value>
+ </parameter>
+ <parameter>
+ <name>maxActive</name>
+ <value>10</value>
+ </parameter>
+ </ResourceParams>
+</Context>]]></programlisting>
+
+ <para>
+ El contexto que configuramos en este ejemplo se llama
<literal>quickstart</literal>,
+ su base es el directorio
<literal>TOMCAT/webapp/quickstart</literal>. Para acceder
+ a cualquier servlet, llama a la ruta
<literal>http://localhost:8080/quickstart</literal>
+ en tu navegador (por supuesto, agregando el nombre del servlet como se mapee
en tu
+ <literal>web.xml</literal>). Puedes también ir
más allá y crear
+ ahora un servlet simple que tenga un método
<literal>process()</literal>
+ vacío.
+ </para>
+
+ <para>
+ Tomcat provee ahora conexiones a través de JNDI en
+ <literal>java:comp/env/jdbc/quickstart</literal>. Si tienes
problemas obteniendo
+ el pool de conexiones en ejecución, refiérete a la
documentación
+ de Tomcat. Si obtienes mensajes de excepción del driver JDBC,
intenta instalar
+ primero el pool de conexiones JDBC sin Hibernate. Hay disponibles en la Web
+ tutoriales de Tomcat y JDBC.
+ </para>
+
+ <para>
+ Tu próximo paso es configurar Hibernate. Hibernate tiene que saber
cómo
+ debe obtener conexiones JDBC. Usamos la configuración de Hibernate
basada en XML.
+ El otro enfoque, usando un ficheros de propiedad, es casi equivalente pero
pierde unas
+ pocas funcionalidades que sí permite la sintaxis XML. El fichero
de configuración
+ XML se ubica en el classpath de contexto
(<literal>WEB-INF/classes</literal>), como
+ <literal>hibernate.cfg.xml</literal>:
+ </para>
+
+ <programlisting><![CDATA[<?xml version='1.0'
encoding='utf-8'?>
+<!DOCTYPE hibernate-configuration PUBLIC
+ "-//Hibernate/Hibernate Configuration DTD//EN"
+ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
+
+<hibernate-configuration>
+
+ <session-factory>
+
+ <property
name="connection.datasource">java:comp/env/jdbc/quickstart</property>
+ <property name="show_sql">false</property>
+ <property
name="dialect">org.hibernate.dialect.PostgreSQLDialect</property>
+
+ <!-- Mapping files -->
+ <mapping resource="Cat.hbm.xml"/>
+
+ </session-factory>
+
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ Desactivamos el registro (logging) de comandos SQL y decimos a Hibernate
+ qué dialecto SQL de base de datos se usa y dónde
obtener
+ conexiones JDBC (declarando la dirección JNDI del pool ligado a
+ Tomcat). El dialecto es una configuración requerida, las bases de
+ datos difieren en su interpretación del
"estándar" de SQL.
+ Hibernate cuidará de las diferencias y viene con dialectos
incluídos
+ para todas las principales bases de datos comerciales y de código
+ abierto.
+ </para>
+
+ <para>
+ Una <literal>SessionFactory</literal> es el concepto de
Hibernate
+ de un almacén de datos solo. Pueden usarse múltiples
+ bases de datos creando múltiples ficheros de
configuración
+ XML y creando múltiples objetos
<literal>Configuration</literal>
+ y <literal>SessionFactory</literal> en tu aplicación.
+ </para>
+
+ <para>
+ El último elemento del
<literal>hibernate.cfg.xml</literal>
+ declara <literal>Cat.hbm.xml</literal> como el nombre de un
fichero
+ de mapeo XML para la clase persistente <literal>Cat</literal>.
Este
+ fichero contiene los metadatos para el mapeo de la clase POJO
+ <literal>Cat</literal> a una tabla (o tablas) de base de datos.
+ Volveremos a este fichero pronto. Escribamos primero la clase POJO
+ y luego declaremos los metadatos de mapeo para ella.
+ </para>
+
+ </sect1>
+
+ <sect1 id="quickstart-persistentclass" revision="1">
+ <title>Primera clase persistente</title>
+
+ <para>
+ Hibernate trabaja mejor con el modelo de programación de los
+ Viejos Objetos Planos de Java (POJOs, a veces llamados Ordinarios Objetos
Planos de Java)
+ para clases persistentes. Un POJO es como un JavaBean, con las propiedades
+ de la clase accesible vía métodos getter y setter,
+ encapsulando la representación interna de la interfaz
publicamente
+ visible (Hibernate puede también acceder a los campos
directamente, si se
+ necesita):
+ </para>
+
+ <programlisting><![CDATA[package org.hibernate.examples.quickstart;
+
+public class Cat {
+
+ private String id;
+ private String name;
+ private char sex;
+ private float weight;
+
+ public Cat() {
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ private void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public char getSex() {
+ return sex;
+ }
+
+ public void setSex(char sex) {
+ this.sex = sex;
+ }
+
+ public float getWeight() {
+ return weight;
+ }
+
+ public void setWeight(float weight) {
+ this.weight = weight;
+ }
+
+}]]></programlisting>
+
+ <para>
+ Hibernate no está restringido en su uso de tipos de propiedad,
todos
+ los tipos y tipos primitivos del JDK de Java (como
<literal>String</literal>,
+ <literal>char</literal> y <literal>Date</literal>)
pueden ser mapeados, incluyendo
+ clases del framework de colecciones de Java. Puedes mapearlos como valores,
+ colecciones de valores, o asociaciones a otras entidades. El
<literal>id</literal>
+ es una propiedad especial que representa el identificador de base de datos
(clave
+ primaria) de la clase. Es altamente recomendado para entidades como un
+ <literal>Cat</literal>. Hibernate puede usar identificadores
sólo
+ internamente, pero perderíamos algo de la flexibilidad en nuestra
+ arquitectura de aplicación.
+ </para>
+
+ <para>
+ No tiene que implementarse ninguna interface especial para las clases
persistentes
+ ni tienes que subclasear de una clase persistente raíz en
especial. Hibernate
+ tampoco requiere ningún procesamiento en tiempo de
construcción,
+ como manipulación del byte-code. Se basa solamente en
reflección de Java
+ y aumentación de clases en tiempo de ejecución (a
través de CGLIB).
+ De modo que, sin ninguna dependencia de la clase POJO en Hibernate, podemos
mapearla
+ a una tabla de base de datos.
+ </para>
+
+ </sect1>
+
+ <sect1 id="quickstart-mapping" revision="1">
+ <title>Mapeando el gato</title>
+
+ <para>
+ El fichero de mapeo <literal>Cat.hbm.xml</literal> contiene los
+ metadatos requeridos para el mapeo objeto/relacional. Los metadatos
+ incluyen la declaración de clases persistentes y el mapeo de
+ propiedades (a columnas y relaciones de claves foráneas a otras
+ entidades) a tablas de base de datos.
+ </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>
+
+ <class name="org.hibernate.examples.quickstart.Cat"
table="CAT">
+
+ <!-- A 32 hex character is our surrogate key. It's automatically
+ generated by Hibernate with the UUID pattern. -->
+ <id name="id" type="string" unsaved-value="null"
>
+ <column name="CAT_ID" sql-type="char(32)"
not-null="true"/>
+ <generator class="uuid.hex"/>
+ </id>
+
+ <!-- A cat has to have a name, but it shouldn' be too long. -->
+ <property name="name">
+ <column name="NAME" length="16"
not-null="true"/>
+ </property>
+
+ <property name="sex"/>
+
+ <property name="weight"/>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Cada clase persistente debe tener un atributo identificador (realmente,
+ sólo las clases que representen entidades, no las clases
dependientes
+ de tipo-valor, que son mapeadas como componentes de una entidad). Esta
propiedad
+ es usada para distinguir los objetos persistentes: Dos gatos son iguales si
+ <literal>catA.getId().equals(catB.getId())</literal> es
verdadero. Este concepto
+ se llama <emphasis>identidad de base de datos (database
identity)</emphasis>.
+ Hibernate viene empaquetado con varios generadores de identificador para
diferentes
+ escenarios (incluyendo generadores nativos para secuencias de base de datos,
tablas
+ de identificadores alto/bajo, e identificadores asignados por
aplicación).
+ Usamos el generador UUID (recomendado sólo para pruebas, pues
deben
+ preferirse las claves enteras delegadas generadas por la base de datos) y
+ también especificamos la columna
<literal>CAT_ID</literal> de la tabla
+ <literal>CAT</literal> para el valor identificador generado por
Hibernate
+ (como una clave primaria de la tabla).
+ </para>
+
+ <para>
+ Todas las demás propiedades de <literal>Cat</literal>
son mapeadas a la
+ misma tabla. En el caso de la propiedad <literal>name</literal>,
la hemos mapeado
+ con una declaración explícita de columna de base de
datos. Esto es
+ especialmente útil cuando el esquema de base de datos es generado
+ automáticamente (como sentencias DDL de SQL) desde la
declaración
+ de mapeo con la herramienta <emphasis>SchemaExport</emphasis> de
Hibernate.
+ Todas las demás propiedades son mapeadas usando la
configuración
+ por defecto de Hibernate, que es lo que necesitas la mayoría del
tiempo.
+ La tabla <literal>CAT</literal> en la base de datos se ve
así como:
+ </para>
+
+ <programlisting><![CDATA[ Columna | Tipo |
Modificadores
+--------+-----------------------+-----------
+ cat_id | character(32) | not null
+ name | character varying(16) | not null
+ sex | character(1) |
+ weight | real |
+Indexes: cat_pkey primary key btree (cat_id)]]></programlisting>
+
+ <para>
+ Ahora debes crear esta tabla manualmente en tu base de datos, y luego leer
el
+ <xref linkend="toolsetguide"/> si quieres automatizar este
paso con la
+ herramienta <literal>hbm2ddl</literal>. Esta herramienta puede
crear un
+ DDL SQL completo, incluyendo definición de tablas, restricciones
+ personalizadas de tipo de columnas, restricciones de unicidad e
índices.
+ </para>
+
+ </sect1>
+
+ <sect1 id="quickstart-playingwithcats" revision="2">
+ <title>Jugando con gatos</title>
+
+ <para>
+ Ahora estamos listos para comenzar la <literal>Session</literal>
de Hibernate.
+ Es el <emphasis>manejador de persistencia</emphasis> que usamos
para almacenar
+ y traer <literal>Cat</literal>s hacia y desde la base de datos.
Pero primero,
+ tenemos que obtener una <literal>Session</literal> (unidad de
trabajo de Hibernate)
+ de la <literal>SessionFactory</literal>:
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sessionFactory =
+ new
Configuration().configure().buildSessionFactory();]]></programlisting>
+
+ <para>
+ La llamada a <literal>configure()</literal> carga el
fichero de
+ configuración
<literal>hibernate.cfg.xml</literal> e
+ inicializa la instancia de
<literal>Configuration</literal>.
+ Puedes establecer otras propiedades (e incluso cambiar los
metadatos de mapeo)
+ accediendo a la <literal>Configuration</literal>
<emphasis>antes</emphasis>
+ que construyas la <literal>SessionFactory</literal>
(que es inmutable).
+ ¿Dónde creamos la
<literal>SessionFactory</literal> y cómo
+ accedemos a ella en nuestra aplicación?
+ </para>
+
+ <para>
+ Una <literal>SessionFactory</literal> usualmente se construye una
vez,
+ por ejemplo, al arrancar con un servlet
<emphasis>load-on-startup</emphasis>.
+ Esto significa también que no debes mantenerla en una variable de
instancia
+ en tus servlets, sino en alguna otro sitio. Además, necesitamos
algún
+ tipo de <emphasis>Singleton</emphasis>, de modo que podamos
acceder a la
+ <literal>SessionFactory</literal> fácilmente en el
código de
+ aplicación. El siguiente enfoque mostrado resuelve ambos
problemas:
+ configuración de arranque y fácil acceso a una
+ <literal>SessionFactory</literal>.
+ </para>
+
+ <para>
+ Implementamos una clase de ayuda
<literal>HibernateUtil</literal>:
+ </para>
+
+ <programlisting><![CDATA[import org.hibernate.*;
+import org.hibernate.cfg.*;
+
+public class HibernateUtil {
+
+ private static Logger log = LoggerFactory.getLogger(HibernateUtil.class);
+
+ private static final SessionFactory sessionFactory;
+
+ static {
+ try {
+ // Create the SessionFactory
+ sessionFactory = new Configuration().configure().buildSessionFactory();
+ } catch (Throwable ex) {
+ // Make sure you log the exception, as it might be swallowed
+ log.error("Initial SessionFactory creation failed.", ex);
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ public static final ThreadLocal session = new ThreadLocal();
+
+ public static Session currentSession() {
+ Session s = (Session) session.get();
+ // Open a new Session, if this Thread has none yet
+ if (s == null) {
+ s = sessionFactory.openSession();
+ session.set(s);
+ }
+ return s;
+ }
+
+ public static void closeSession() {
+ Session s = (Session) session.get();
+ if (s != null)
+ s.close();
+ session.set(null);
+ }
+}]]></programlisting>
+
+ <para>
+ Esta clase no sólo cuida de la
<literal>SessionFactory</literal>
+ con su inicializador static, sino que además tiene una variable
+ <literal>ThreadLocal</literal> que tiene la
<literal>Session</literal>
+ para la hebra actual. Asegúrate de entender el concepto Java de
una
+ variable local a una hebra antes de intentar usar esta ayuda. Una clase
+ <literal>HibernateUtil</literal> más compleja y
potente puede
+ encontrarse en <literal>CaveatEmptor</literal>,
http://caveatemptor.hibernate.org/
+ </para>
+
+ <para>
+ Una <literal>SessionFactory</literal> es segura entre hebras,
muchas hebras pueden
+ acceder a ella concurrentemente y pedirle
<literal>Session</literal>s. Una
+ <literal>Session</literal> no es un objeto seguro entre hebras
que representa
+ una sola unidad-de-trabajo con la base de datos. Las
<literal>Session</literal>s
+ se abren desde una <literal>SessionFactory</literal> y son
cerradas cuando
+ todo el trabajo está completo. Un ejemplo en el método
+ <literal>process()</literal> de tu servlet podría
parecerse a esto
+ (sin manejo de excepciones):
+ </para>
+
+ <programlisting><![CDATA[Session session =
HibernateUtil.currentSession();
+Transaction tx = session.beginTransaction();
+
+Cat princess = new Cat();
+princess.setName("Princess");
+princess.setSex('F');
+princess.setWeight(7.4f);
+
+session.save(princess);
+
+tx.commit();
+HibernateUtil.closeSession();]]></programlisting>
+
+ <para>
+ En una <literal>Session</literal>, cada operación de
base de datos
+ ocurre dentro de una transacción que aísla las
operaciones
+ de base de datos (incluso operaciones de sólo lectura).
+ Usamos la API de <literal>Transaction</literal> de Hibernate
para
+ abstraer de la estrategia de transacciones subyacente (en nuestro caso,
+ transacciones JDBC). Esto permite que nuestro código sea
desplegado
+ con transacciones manejadas por contenedor (usando JTA) sin cambio alguno.
+ </para>
+
+ <para>
+ Observa que puedes llamar
<literal>HibernateUtil.currentSession();</literal>
+ tantas veces como quieras, siempre obtendrás la
<literal>Session</literal>
+ actual de esta hebra. Tienes que asegurarte que la
<literal>Session</literal>
+ sea cerrada después que se complete tu unidad-de-trabajo, ya sea
en
+ código de tu servlet o en un filtro de servlet antes que la
respuesta HTTP
+ sea enviada. El bonito efecto colateral de la segunda opción es
la
+ fácil inicialización perezosa: la
<literal>Session</literal> todavía
+ está abierta cuando se dibuja la vista, de modo que Hibernate
puede cargar
+ objetos no inicializados mientras navegas tu actual grafo de objetos.
+ </para>
+
+ <para>
+ Hibernate tiene varios métodos que pueden ser usados para traer
+ objetos desde la base de datos. La forma más flexible es usando
+ el Lenguaje de Consulta de Hibernate (Hibernate Query Language o HQL),
+ que es una extensión orientada a objetos de SQL fácil
de
+ aprender:
+ </para>
+
+ <programlisting><![CDATA[Transaction tx = session.beginTransaction();
+
+Query query = session.createQuery("select c from Cat as c where c.sex =
:sex");
+query.setCharacter("sex", 'F');
+for (Iterator it = query.iterate(); it.hasNext();) {
+ Cat cat = (Cat) it.next();
+ out.println("Female Cat: " + cat.getName() );
+}
+
+tx.commit();]]></programlisting>
+
+ <para>
+ Hibernate también ofrece una API <emphasis>consulta por
criterios</emphasis>
+ orientada a objetos que puede ser usada para formular consultas de tipo
seguro.
+ Por supuesto, Hibernate usa <literal>PreparedStatement</literal>s
y ligado de
+ parámetros para toda la comunicación SQL con la base de
datos.
+ También puedes usar la funcionalidad de consulta SQL directa de
Hibernate
+ u obtener una conexión plana de JDBC de una
<literal>Session</literal>
+ en casos raros.
+ </para>
+
+ </sect1>
+
+ <sect1 id="quickstart-summary" revision="1">
+ <title>Finalmente</title>
+
+ <para>
+ Rasguñamos solamente la superficie de Hibernate en este
pequeño
+ tutorial. Por favor, observa que no incluimos ningún
código
+ específico de servlet en nuestros ejemplos. Tienes que crear un
servlet
+ por tí mismo e insertar el código de Hibernate como lo
veas
+ ubicado.
+ </para>
+
+ <para>
+ Ten en mente que Hibernate, como capa de acceso a datos, está
firmemente
+ integrado dentro de tu aplicación. Usualmente, todas las otras
capas dependen
+ del mecanismo de persistencia. Asegúrate de entender las
implicaciones
+ de este diseño.
+ </para>
+
+ <para>
+ Para un ejemplo de aplicación más compleja, ver
+
http://caveatemptor.hibernate.org/ y echa una mirada a los
+ otros tutoriales con links en
http://www.hibernate.org/Documentation
+ </para>
+
+ </sect1>
+
+</chapter>
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/content/session_api.xml
(from rev 12794,
core/trunk/documentation/manual/es-ES/src/main/docbook/modules/session_api.xml)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/content/session_api.xml
(rev 0)
+++
core/trunk/documentation/manual/es-ES/src/main/docbook/content/session_api.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,1224 @@
+<chapter id="objectstate">
+ <title>Trabajando con objetos</title>
+
+ <para>
+ Hibernate es una solución completa de mapeo objeto/relacional que no
sólo
+ abstrae al desarrollador de los detalles del sistema de manejo de base datos
+ subyacente, sino que además ofrece <emphasis>manejo de
estado</emphasis> de
+ objetos. Esto es, al contrario del manejo de
<literal>sentencias</literal>
+ SQL en capas comunes de persistencia JDBC/SQL, una vista de la persistencia
+ en aplicaciones Java muy natural y orientada a objetos.
+ </para>
+
+ <para>
+ En otras palabras, los desarroladores de aplicaciones Hibernate deben siempre
+ pensar en el <emphasis>estado</emphasis> de sus objetos, y no
necesariamente
+ en la ejecución de sentencias SQL. Esta parte es cuidada por Hibernate
y es
+ sólo relevante para el desarrollador de la aplicación al
afinar el rendimiento
+ del sistema.
+ </para>
+
+ <sect1 id="objectstate-overview">
+ <title>Estados de objeto de Hibernate</title>
+
+ <para>
+ Hibernate define y soporta los siguientes estados de objeto:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Transitorio</emphasis> - un objeto es
transitorio si ha sido
+ recién instanciado usando el operador
<literal>new</literal>, y no está
+ asociado a una <literal>Session</literal> de Hibernate.
No tiene una
+ representación persistente en la base de datos y no se le
ha asignado un
+ valor identificador. Las instancias transitorias serán
destruídas por el
+ recolector de basura si la aplicación no mantiene
más una referencia.
+ Usa la <literal>Session</literal> de Hibernate para hacer
un objeto
+ persistente (y deja que Hibernate cuide de las sentencias SQL que
necesitan
+ ejecutarse para esta transición).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Persistente</emphasis> - una instancia
persistente tiene una
+ representación en la base de datos y un valor
identificador. Puede haber
+ sido salvado o cargado, sin embargo, está por
definición en el ámbito de
+ una <literal>Session</literal>. Hibernate
detectará cualquier cambio hecho
+ a un objeto en estado persistentey sincronizará el estado
con la base de
+ datos cuando se complete la unidad de trabajo. Los desarrolladores no
ejecutan
+ sentencias <literal>UPDATE</literal> manuales, o
sentencias <literal>DELETE</literal>
+ cuando un objeto debe ser hecho transitorio.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Separado (detached)</emphasis> - una instancia
separada es un objeto
+ que ha sido hecho persistente, pero su
<literal>Session</literal> ha sido cerrada.
+ La referencia al objeto todavía es válida, por
supuesto, y la instancia separada
+ podría incluso ser modificada en este estado. Una
instancia separada puede ser
+ re-unida a una nueva <literal>Session</literal> en un
punto posterior en el tiempo,
+ haciéndola persistente de nuevo (con todas las
modificaciones). Este aspecto
+ habilita un modelo de programación para unidades de
trabajo de ejecución larga
+ que requieren tiempo-para-pensar del usuario. Las llamamos
<emphasis>transaccciones
+ de aplicación</emphasis>, es decir, una unidad de
trabajo desde el punto de vista
+ del usuario.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Discutiremos ahora los estados y transiciones de estados (y los
métodos de Hibernate que
+ disparan una transición) en más detalle:
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-makingpersistent" revision="1">
+ <title>Haciendo los objetos persistentes</title>
+
+ <para>
+ Las instancias recién instanciadas de una clase persistente son
consideradas
+ <emphasis>transitorias</emphasis> por Hibernate. Podemos hacer
una instancia
+ transitoria <emphasis>persistente</emphasis>
asociándola con una sesión:
+ </para>
+
+ <programlisting><![CDATA[DomesticCat fritz = new DomesticCat();
+fritz.setColor(Color.GINGER);
+fritz.setSex('M');
+fritz.setName("Fritz");
+Long generatedId = (Long) sess.save(fritz);]]></programlisting>
+
+ <para>
+ Si <literal>Cat</literal> tiene un identificador generado, el
identificador es
+ generado y asignado al <literal>cat</literal> cuando se llama a
<literal>save()</literal>.
+ Si <literal>Cat</literal> tiene un identificador
<literal>assigned</literal>,
+ o una clave compuesta, el identificador debe ser asignado a la instancia de
+ <literal>cat</literal> antes de llamar a
<literal>save()</literal>. Puedes también
+ usar <literal>persist()</literal> en vez de
<literal>save()</literal>, con la semántica
+ definida en el temprano borrador de EJB3.
+ </para>
+
+ <para>
+ Alternativamente, puedes asignar el identificador usando una
versión sobrecargada
+ de <literal>save()</literal>.
+ </para>
+
+<programlisting><![CDATA[DomesticCat pk = new DomesticCat();
+pk.setColor(Color.TABBY);
+pk.setSex('F');
+pk.setName("PK");
+pk.setKittens( new HashSet() );
+pk.addKitten(fritz);
+sess.save( pk, new Long(1234) );]]></programlisting>
+
+ <para>
+ Si el objeto que haces persistente tiene objetos asociados (por ejemplo,
+ la colección <literal>kittens</literal> en el ejemplo
anterior), estos
+ objetos pueden ser hechos persistentes en cualquier orden que quieras
+ a menos que tengas una restricción <literal>NOT
NULL</literal> sobre una
+ columna clave foránea. Nunca hay riesgo de violar restricciones de
clave
+ foránea. Sin embargo, podrías violar una
restricción <literal>NOT NULL</literal>
+ si llamas a <literal>save()</literal> sobre objetos en orden
erróneo.
+ </para>
+
+ <para>
+ Usualmente no te preocupas con este detalle, pues muy probablemente
usarás
+ la funcionalidad de <emphasis>persistencia transitiva</emphasis>
de Hibernate
+ para salvar los objetos asociados automáticamente. Entonces, ni
siquiera ocurren
+ violaciones de restricciones <literal>NOT NULL</literal> -
Hibernate cuidará de todo.
+ La persistencia transitiva se discute más adelante en este
capítulo.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-loading">
+ <title>Cargando un objeto</title>
+
+ <para>
+ Los métodos <literal>load()</literal> de
<literal>Session</literal> te brindan
+ una forma de traer una instancia persistente si ya saves su identificador.
+ <literal>load()</literal> toma un objeto clase y
cargará el estado dentro de
+ una instancia recién instanciada de esta clase, en estado
persistente.
+ </para>
+
+ <programlisting><![CDATA[Cat fritz = (Cat) sess.load(Cat.class,
generatedId);]]></programlisting>
+
+<programlisting><![CDATA[// you need to wrap primitive identifiers
+long id = 1234;
+DomesticCat pk = (DomesticCat) sess.load( DomesticCat.class, new Long(id)
);]]></programlisting>
+
+ <para>
+ Alternativamente, puedes cargar estado dentro de una instancia dada:
+ </para>
+
+<programlisting><![CDATA[Cat cat = new DomesticCat();
+// load pk's state into cat
+sess.load( cat, new Long(pkId) );
+Set kittens = cat.getKittens();]]></programlisting>
+
+ <para>
+ Nota que <literal>load()</literal> lanzará una
excepción irrecuperable si no
+ hay una fila correspondiente en base de datos. Si la clase es mapeada con un
+ proxy, <literal>load()</literal> sólo devuelve un
proxy no inicializado y no
+ llamará realmente a la base de datos hasta que invoques un
método del proxy.
+ Este comportamiento es muy útil si deseas crear una
asociación a un objeto
+ sin cargarlo realmente de la base de datos. Permite además que
múltiples
+ instancias sean cargadas como un lote si se define
<literal>batch-size</literal>
+ para el mapeo de la clase.
+ </para>
+
+ <para>
+ Si no tienes certeza que exista una fila correspondiente, debes usar el
+ método <literal>get()</literal>, que llama a la base
de datos inmediatamente
+ y devuelve nulo si no existe una fila correspondiente.
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) sess.get(Cat.class, id);
+if (cat==null) {
+ cat = new Cat();
+ sess.save(cat, id);
+}
+return cat;]]></programlisting>
+
+ <para>
+ Puedes incluso cargar un objeto usando un <literal>SELECT ... FOR
UPDATE</literal> de SQL,
+ usando un <literal>LockMode</literal>. Ver la
documentación de la API para más
+ información.
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) sess.get(Cat.class, id,
LockMode.UPGRADE);]]></programlisting>
+
+ <para>
+ Ten en cuenta que <emphasis>ninguna</emphasis> instancia asociada
o colección contenida es
+ selecciona <literal>FOR UPDATE</literal>, a menos que decidas
especificar
+ <literal>lock</literal> o <literal>all</literal> como
un estilo de cascada para la
+ asociación.
+ </para>
+
+ <para>
+ Es posible volver a cargar un objeto y todas sus colecciones en cualquier
momento,
+ usando el método <literal>refresh()</literal>. Esto es
útil cuando se usan disparadores de
+ base de datos para inicializar algunas de las propiedades del objeto.
+ </para>
+
+ <programlisting><![CDATA[sess.save(cat);
+sess.flush(); //force the SQL INSERT
+sess.refresh(cat); //re-read the state (after the trigger
executes)]]></programlisting>
+
+ <para>
+ Una cuestión importante aparece usualmente en este punto:
¿Cuánto carga Hibernate de
+ la base de datos y cuántos <literal>SELECT</literal>s
de SQL usará? Esto depende de la
+ <emphasis>estrategia de recuperación</emphasis> y se
explica en <xref linkend="performance-fetching"/>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-querying" revision="1">
+ <title>Consultando</title>
+
+ <para>
+ Si no sabes los identificadores de los objetos que estás
buscando,
+ necesitas una consulta. Hibernate soporta un lenguaje de consulta
+ orientado a objetos (HQL) fácil de usar pero potente. Para la
creación
+ de consultas programáticas, Hibernate soporta una funcionalidad
sofisticada
+ de consulta de Criteria y Example (QBC and QBE). También puedes
expresar tu
+ consulta en el SQL nativo de tu base de datos, con soporte opcional de
Hibernate
+ para la conversión del conjunto resultado en objetos.
+ </para>
+
+ <sect2 id="objectstate-querying-executing">
+ <title>Ejecutando consultas</title>
+
+ <para>
+ Las consultas HQL y SQL nativas son representadas con una instancia de
+ <literal>org.hibernate.Query</literal>. Esta interface ofrece
métodos para
+ la ligación de parámetros, manejo del conjunto
resultado, y para la
+ ejecución de la consulta real. Siempre obtienes una
<literal>Query</literal>
+ usando la <literal>Session</literal> actual:
+ </para>
+
+ <programlisting><![CDATA[List cats = session.createQuery(
+ "from Cat as cat where cat.birthdate < ?")
+ .setDate(0, date)
+ .list();
+
+List mothers = session.createQuery(
+ "select mother from Cat as cat join cat.mother as mother where cat.name =
?")
+ .setString(0, name)
+ .list();
+
+List kittens = session.createQuery(
+ "from Cat as cat where cat.mother = ?")
+ .setEntity(0, pk)
+ .list();
+
+Cat mother = (Cat) session.createQuery(
+ "select cat.mother from Cat as cat where cat = ?")
+ .setEntity(0, izi)
+ .uniqueResult();]]></programlisting>
+
+ <para>
+ Una consulta se ejecuta usualmente invocando a
<literal>list()</literal>,
+ el resultado de la consulta será cargado completamente dentro
de una
+ colección en memoria. Las instancias de entidad
traídas por una consulta
+ están en estado persistente. El método
<literal>uniqueResult()</literal>
+ ofrece un atajo si sabes que tu consulta devolverá
sólo un objeto.
+ </para>
+
+ <sect3 id="objectstate-querying-executing-iterate">
+ <title>Iterando los resultados</title>
+
+ <para>
+ Ocasionalmente, podrías ser capaz de lograr mejor
rendimiento al ejecutar la consulta
+ usando el método <literal>iterate()</literal>.
Esto sólo será en el caso que esperes
+ que las instancias reales de entidad devueltas por la consulta
estén ya en la sesión
+ o caché de segundo nivel. Si todavía no
están en caché, <literal>iterate()</literal>
+ será más lento que
<literal>list()</literal> y podría requerir muchas llamadas a la
+ base de datos para una consulta simple, usualmente
<emphasis>1</emphasis> para la
+ selección inicial que solamente devuelve identificadores,
y <emphasis>n</emphasis>
+ selecciones adicionales para inicializar las instancias reales.
+ </para>
+
+ <programlisting><![CDATA[// fetch ids
+Iterator iter = sess.createQuery("from eg.Qux q order by
q.likeliness").iterate();
+while ( iter.hasNext() ) {
+ Qux qux = (Qux) iter.next(); // fetch the object
+ // something we couldnt express in the query
+ if ( qux.calculateComplicatedAlgorithm() ) {
+ // delete the current instance
+ iter.remove();
+ // dont need to process the rest
+ break;
+ }
+}]]></programlisting>
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-tuples">
+ <title>Consultas que devuelven tuplas</title>
+
+ <para>
+ Las consultas de Hibernate a veces devuelven tuplas de objetos, en
cuyo caso
+ cada tupla se devuelve como un array:
+ </para>
+
+ <programlisting><![CDATA[Iterator kittensAndMothers =
sess.createQuery(
+ "select kitten, mother from Cat kitten join kitten.mother mother")
+ .list()
+ .iterator();
+
+while ( kittensAndMothers.hasNext() ) {
+ Object[] tuple = (Object[]) kittensAndMothers.next();
+ Cat kitten = (Cat) tuple[0];
+ Cat mother = (Cat) tuple[1];
+ ....
+}]]></programlisting>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-scalar">
+ <title>Resultados escalares</title>
+
+ <para>
+ Las consultas pueden especificar una propiedad de una clase en la
cláusula
+ <literal>select</literal>. Pueden incluso llamar a
funciones de agregación SQL.
+ Las propiedades o agregaciones son considerados resultados
"escalares"
+ (y no entidades en estado persistente).
+ </para>
+
+ <programlisting><![CDATA[Iterator results = sess.createQuery(
+ "select cat.color, min(cat.birthdate), count(cat) from Cat cat " +
+ "group by cat.color")
+ .list()
+ .iterator();
+
+while ( results.hasNext() ) {
+ Object[] row = results.next();
+ Color type = (Color) row[0];
+ Date oldest = (Date) row[1];
+ Integer count = (Integer) row[2];
+ .....
+}]]></programlisting>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-parameters">
+ <title>Ligación de parámetros</title>
+
+ <para>
+ Se proveen métodos en <literal>Query</literal>
para ligar valores a
+ parámetros con nombre o parámetros
<literal>?</literal> de estilo JDBC.
+ <emphasis>Al contrario de JDBC, Hibernate numera los
parámetros desde cero.</emphasis>
+ Los parámetros con nombre son identificadores de la forma
<literal>:name</literal>
+ en la cadena de la consulta. Las ventajas de los
parámetros con nombre son:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ los parámetros con nombre son insensibles al orden
en que aparecen
+ en la cadena de consulta
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ pueden aparecer múltiples veces en la misma
consulta
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ son auto-documentados
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <programlisting><![CDATA[//named parameter (preferred)
+Query q = sess.createQuery("from DomesticCat cat where cat.name = :name");
+q.setString("name", "Fritz");
+Iterator cats = q.iterate();]]></programlisting>
+
+ <programlisting><![CDATA[//positional parameter
+Query q = sess.createQuery("from DomesticCat cat where cat.name = ?");
+q.setString(0, "Izi");
+Iterator cats = q.iterate();]]></programlisting>
+
+ <programlisting><![CDATA[//named parameter list
+List names = new ArrayList();
+names.add("Izi");
+names.add("Fritz");
+Query q = sess.createQuery("from DomesticCat cat where cat.name in
(:namesList)");
+q.setParameterList("namesList", names);
+List cats = q.list();]]></programlisting>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-pagination">
+ <title>Paginación</title>
+
+ <para>
+ Si necesitas especificar límites sobre tu conjunto
resultado (el número máximo de filas
+ que quieras traer y/o la primera fila que quieras traer) debes usar
los métodos de la
+ interface <literal>Query</literal>:
+ </para>
+
+ <programlisting><![CDATA[Query q = sess.createQuery("from
DomesticCat cat");
+q.setFirstResult(20);
+q.setMaxResults(10);
+List cats = q.list();]]></programlisting>
+
+ <para>
+ Hibernate sabe cómo traducir este límite de
consulta al SQL nativo de tu
+ DBMS.
+ </para>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-scrolling">
+ <title>Iteración scrollable</title>
+
+ <para>
+ Si tu driver JDBC soporta <literal>ResultSet</literal>s
scrollables, la
+ interface <literal>Query</literal> puede ser usada para
obtener un objeto
+ <literal>ScrollableResults</literal>, que permite una
navegación flexible
+ de los resultados de consulta.
+ </para>
+
+ <programlisting><![CDATA[Query q = sess.createQuery("select
cat.name, cat from DomesticCat cat " +
+ "order by cat.name");
+ScrollableResults cats = q.scroll();
+if ( cats.first() ) {
+
+ // find the first name on each page of an alphabetical list of cats by name
+ firstNamesOfPages = new ArrayList();
+ do {
+ String name = cats.getString(0);
+ firstNamesOfPages.add(name);
+ }
+ while ( cats.scroll(PAGE_SIZE) );
+
+ // Now get the first page of cats
+ pageOfCats = new ArrayList();
+ cats.beforeFirst();
+ int i=0;
+ while( ( PAGE_SIZE > i++ ) && cats.next() ) pageOfCats.add( cats.get(1)
);
+
+}
+cats.close()]]></programlisting>
+
+ <para>
+ Nota que se requiere una conexión de base de datos abierta
(y cursor) para esta
+ funcionalidad, usa
<literal>setMaxResult()</literal>/<literal>setFirstResult()</literal>
+ si necesitas la funcionalidad de paginación fuera de
línea.
+ </para>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-named">
+ <title>Externalizando consultas con nombre</title>
+
+ <para>
+ Puedes además definir consultas con nombre en el documento
de mapeo.
+ (Recuerda usar una sección
<literal>CDATA</literal> si tu consulta
+ contiene caracteres que puedan ser interpretados como etiquetado.)
+ </para>
+
+ <programlisting><![CDATA[<query
name="eg.DomesticCat.by.name.and.minimum.weight"><![CDATA[
+ from eg.DomesticCat as cat
+ where cat.name = ?
+ and cat.weight > ?
+] ]></query>]]></programlisting>
+
+ <para>
+ La ligación de parámetros y
ejecución se hace programáticamente:
+ </para>
+
+ <programlisting><![CDATA[Query q =
sess.getNamedQuery("eg.DomesticCat.by.name.and.minimum.weight");
+q.setString(0, name);
+q.setInt(1, minWeight);
+List cats = q.list();]]></programlisting>
+
+ <para>
+ Nota que el código real del programa es independiente del
lenguaje de consulta
+ usado; puedes además definir consultas SQL nativas en
metadatos, o migrar
+ consultas existentes a Hibernate colocándolas en ficheros
de mapeo.
+ </para>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2 id="objectstate-filtering" revision="1">
+ <title>Filtrando colecciones</title>
+ <para>
+ Un <emphasis>filtro</emphasis> de colección es un
tipo especial de consulta que puede ser
+ aplicado a una colección persistente o array. La cadena de
consulta puede referirse a
+ <literal>this</literal>, significando el elemento de
colección actual.
+ </para>
+
+ <programlisting><![CDATA[Collection blackKittens =
session.createFilter(
+ pk.getKittens(),
+ "where this.color = ?")
+ .setParameter( Color.BLACK, Hibernate.custom(ColorUserType.class) )
+ .list()
+);]]></programlisting>
+
+ <para>
+ La colección devuelta es considerada un bag, y es una copia de
la colección
+ dada. La colección original no es modificada (esto es
contrario a la implicación
+ del nombre "filtro", pero consistente con el comportamiento
esperado).
+ </para>
+
+ <para>
+ Observa que los filtros no requieren una cláusula
<literal>from</literal> (aunque pueden
+ tener uno si se requiere). Los filtros no están limitados a
devolver los elementos de
+ colección por sí mismos.
+ </para>
+
+ <programlisting><![CDATA[Collection blackKittenMates =
session.createFilter(
+ pk.getKittens(),
+ "select this.mate where this.color = eg.Color.BLACK.intValue")
+ .list();]]></programlisting>
+
+ <para>
+ Incluso una consulta de filtro vacío es útil, por
ejemplo, para cargar un
+ subconjunto de elementos en una colección enorme:
+ </para>
+
+ <programlisting><![CDATA[Collection tenKittens =
session.createFilter(
+ mother.getKittens(), "")
+ .setFirstResult(0).setMaxResults(10)
+ .list();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="objecstate-querying-criteria" revision="1">
+ <title>Consultas de criterios</title>
+
+ <para>
+ HQL es extremadamente potente pero algunos desarrolladores prefieren
construir
+ consultas dinámicamente usando una API orientada a objetos, en
vez construir
+ cadenas de consulta. Hibernate provee una API intuitiva de consulta
<literal>Criteria</literal>
+ para estos casos:
+ </para>
+
+ <programlisting><![CDATA[Criteria crit =
session.createCriteria(Cat.class);
+crit.add( Restrictions.eq( "color", eg.Color.BLACK ) );
+crit.setMaxResults(10);
+List cats = crit.list();]]></programlisting>
+
+ <para>
+ Las APIs de <literal>Criteria</literal> y la asociada
<literal>Example</literal>
+ son discutidas en más detalle en <xref
linkend="querycriteria"/>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="objectstate-querying-nativesql"
revision="2">
+ <title>Consultas en SQL nativo</title>
+
+ <para>
+ Puedes expresar una consulta en SQL, usando
<literal>createSQLQuery()</literal> y
+ dejando que Hibernate cuide del mapeo de los conjuntos resultado a
objetos.
+ Nota que puedes llamar en cualquier momento a
<literal>session.connection()</literal> y
+ usar la <literal>Connection</literal> JDBC directamente. Si
eliges usar la API de
+ Hibernate, debes encerrar los alias de SQL entre llaves:
+ </para>
+
+ <programlisting><![CDATA[List cats =
session.createSQLQuery("SELECT {cat.*} FROM CAT {cat} WHERE ROWNUM<10")
+ .addEntity("cat", Cat.class)
+.list();]]></programlisting>
+
+ <programlisting><![CDATA[List cats = session.createSQLQuery(
+ "SELECT {cat}.ID AS {cat.id}, {cat}.SEX AS {cat.sex}, " +
+ "{cat}.MATE AS {cat.mate}, {cat}.SUBCLASS AS {cat.class}, ... " +
+ "FROM CAT {cat} WHERE ROWNUM<10")
+ .addEntity("cat", Cat.class)
+.list()]]></programlisting>
+
+ <para>
+ Las consultas SQL pueden contener parámetros con nombre y
posicionales, al igual que
+ las consultas de Hibernate. Puede encontrarse más
información sobre consultas en SQL
+ nativo en <xref linkend="querysql"/>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="objectstate-modifying" revision="1">
+ <title>Modificando objetos persistentes</title>
+
+ <para>
+ Las <emphasis>instancias persistentes transaccionales</emphasis>
(es decir, objetos cargados,
+ creados o consultados por la <literal>Session</literal>) pueden
ser manipulados por la
+ aplicación y cualquier cambio al estado persistente
será persistido cuando la <literal>Session</literal>
+ sea <emphasis>limpiada (flushed)</emphasis> (discutido
más adelante en este capítulo). No hay
+ necesidad de llamar un método en particular (como
<literal>update()</literal>, que tiene un
+ propósito diferente) para hacer persistentes tus modificaciones.
De modo que la forma más
+ directa de actualizar el estado de un objeto es cargarlo con
<literal>load()</literal>,
+ y entonces manipularlo directamente, mientras la
<literal>Session</literal> está abierta:
+ </para>
+
+ <programlisting><![CDATA[DomesticCat cat = (DomesticCat) sess.load(
Cat.class, new Long(69) );
+cat.setName("PK");
+sess.flush(); // changes to cat are automatically detected and
persisted]]></programlisting>
+
+ <para>
+ A veces este modelo de programación es ineficiente pues
podría requerir una
+ <literal>SELECT</literal> de SQL (para cargar un objeto) y un
<literal>UPDATE</literal>
+ de SQL (para hacer persistentes sus datos actualizados) en la misma
sesión. Por lo tanto,
+ Hibernate ofrece un enfoque alternativo, usando instancias separadas
(detached).
+ </para>
+
+ <para>
+ <emphasis>Nota que Hibernate no ofreve su propia API para
ejecución directa de
+ sentencias <literal>UPDATE</literal> o
<literal>DELETE</literal>. Hibernate es un
+ servicio de <emphasis>gestión de estado</emphasis>, no
tienes que pensar en
+ <literal>sentencias</literal> para usarlo. JDBC es una API
perfecta para ejecutar
+ sentencias SQL; puedes obtener una <literal>Connection</literal>
JDBC en cualquier
+ momento llamando a <literal>session.connection()</literal>.
Además, la noción de
+ operaciones masivas entra en conflicto con el mapeo objeto/relacional en
aplicaciones
+ en línea orientadas al procesamiento de transacciones. Versiones
futuras de Hibernate
+ pueden, sin embargo, proveer funciones de operación masiva
especiales. Ver
+ <xref linkend="batch"/> por algunos trucos de
operación en lote (batch) posibles.
+ </emphasis>
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-detached" revision="2">
+ <title>Modificando objetos separados</title>
+
+ <para>
+ Muchas aplicaciones necesitan recuperar un objeto en una
transacción, enviarla
+ a la capa de UI para su manipulación, y entonces salvar los
cambios en una nueva
+ transacción. Las aplicaciones que usan este tipo de enfoque en un
entorno de
+ alta concurrencia usualmente usan datos versionados para asegurar el
aislamiento
+ de la unidad de trabajo "larga".
+ </para>
+
+ <para>
+ Hibernate soporta este modelo al proveer re-unión de instancias
separadas usando
+ los métodos <literal>Session.update()</literal> o
<literal>Session.merge()</literal>:
+ </para>
+
+ <programlisting><![CDATA[// in the first session
+Cat cat = (Cat) firstSession.load(Cat.class, catId);
+Cat potentialMate = new Cat();
+firstSession.save(potentialMate);
+
+// in a higher layer of the application
+cat.setMate(potentialMate);
+
+// later, in a new session
+secondSession.update(cat); // update cat
+secondSession.update(mate); // update mate]]></programlisting>
+
+ <para>
+ Si el <literal>Cat</literal> con identificador
<literal>catId</literal> ya hubiera
+ sido cargado por <literal>secondSession</literal> cuando la
aplicación intentó
+ volver a unirlo, se habría lanzado una excepción.
+ </para>
+
+ <para>
+ Usa <literal>update()</literal> si no estás seguro que
la sesión tenga
+ una instancia ya persistente con el mismo identificador, y
<literal>merge()</literal>
+ si quieres fusionar tus modificaciones en cualquier momento sin
consideración del
+ estado de la sesión. En otras palabras,
<literal>update()</literal> es usualmente
+ el primer método que llamarías en una sesión
fresca, asegurando que la re-unión de
+ tus instancias separadas es la primera operación que se ejecuta.
+ </para>
+
+ <para>
+ La aplicación debe actualizar individualmente las instancias
separadas alcanzables
+ por la instancia separada dada llamando a
<literal>update()</literal>, si y
+ <emphasis>sólo</emphasis> si quiere que sus estados
sean también actualizados.
+ Esto puede, por supuesto, ser automatizado usando
<emphasis>persistencia transitiva</emphasis>,
+ ver <xref linkend="objectstate-transitive"/>.
+ </para>
+
+ <para>
+ El método <literal>lock()</literal> también
permite a una aplicación reasociar
+ un objeto con una sesión nueva. Sin embargo, la instancia separada
no puede
+ haber sido modificada!
+ </para>
+
+ <programlisting><![CDATA[//just reassociate:
+sess.lock(fritz, LockMode.NONE);
+//do a version check, then reassociate:
+sess.lock(izi, LockMode.READ);
+//do a version check, using SELECT ... FOR UPDATE, then reassociate:
+sess.lock(pk, LockMode.UPGRADE);]]></programlisting>
+
+ <para>
+ Nota que <literal>lock()</literal> puede ser usado con varios
<literal>LockMode</literal>s,
+ ver la documentación de la API y el capítulo sobre
manejo de transacciones para más
+ información. La re-unión no es el único caso
de uso para <literal>lock()</literal>.
+ </para>
+
+ <para>
+ Se discuten otros modelos para unidades de trabajo largas en <xref
linkend="transactions-optimistic"/>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-saveorupdate">
+ <title>Detección automática de estado</title>
+
+ <para>
+ Los usuarios de Hibernate han pedido un método de
propósito general que bien
+ salve una instancia transitoria generando un identificador nuevo, o bien
+ actualice/reúna las instancias separadas asociadas con su
identificador actual.
+ El método <literal>saveOrUpdate()</literal> implementa
esta funcionalidad.
+ </para>
+
+ <programlisting><![CDATA[// in the first session
+Cat cat = (Cat) firstSession.load(Cat.class, catID);
+
+// in a higher tier of the application
+Cat mate = new Cat();
+cat.setMate(mate);
+
+// later, in a new session
+secondSession.saveOrUpdate(cat); // update existing state (cat has a non-null id)
+secondSession.saveOrUpdate(mate); // save the new instance (mate has a null
id)]]></programlisting>
+
+ <para>
+ El uso y semántica de
<literal>saveOrUpdate()</literal> parece ser confuso para
+ usuarios nuevos. Primeramente, en tanto no estés intentando usar
instancias de una
+ sesión en otra sesión nueva, no debes necesitar usar
<literal>update()</literal>,
+ <literal>saveOrUpdate()</literal>, o
<literal>merge()</literal>. Algunas aplicaciones
+ enteras nunca usarán ninguno de estos métodos.
+ </para>
+
+ <para>
+ Usualmente <literal>update()</literal> o
<literal>saveOrUpdate()</literal> se usan en
+ el siguiente escenario:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ la aplicación carga un objeto en la primera
sesión
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ el objeto es pasado a la capa de UI
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ se hacen algunas modificaciones al objeto
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ el objeto se pasa abajo de regreso a la capa de negocio
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ la aplicación hace estas modificaciones persistentes
llamando
+ a <literal>update()</literal> en una segunda
sesión
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <literal>saveOrUpdate()</literal> hace lo siguiente:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ si el objeto ya es persistente en esta sesión, no hace
nada
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ si otro objeto asociado con la sesión tiene el mismo
identificador,
+ lanza una excepción
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ si el objeto no tiene ninguna propiedad identificadora, lo salva
llamando a
+ <literal>save()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ si el identificador del objeto tiene el valor asignado a un objeto
recién
+ instanciado, lo salva llamando a
<literal>save()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ si el objeto está versionado (por un
<literal><version></literal> o
+ <literal><timestamp></literal>), y el valor
de la propiedad de versión
+ es el mismo valor asignado a una objeto recién
instanciado, lo salva llamando
+ a <literal>save()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ en cualquier otro caso se actualiza el objeto llamando a
<literal>update()</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ y <literal>merge()</literal> es muy diferente:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ si existe una instancia persistente con el mismo identificador
asignado actualmente con la
+ sesión, copia el estado del objeto dado en la instancia
persistente
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ si no existe ninguna instancia persistente actualmente asociada a la
sesión,
+ intente cargarla de la base de datos, o crear una nueva instancia
persistente
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ la instancia persistente es devuelta
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ la instancia dada no resulta ser asociada a la sesión,
permanece separada
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect1>
+
+ <sect1 id="objectstate-deleting" revision="1">
+ <title>Borrando objetos persistentes</title>
+
+ <para>
+ <literal>Session.delete()</literal> quitará el estado
de un objeto de la base de datos.
+ Por supuesto, tu aplicación podría tener aún
una referencia a un objeto borrado. Lo mejor
+ es pensar en <literal>delete()</literal> como hacer transitoria
una instancia persistente.
+ </para>
+
+ <programlisting><![CDATA[sess.delete(cat);]]></programlisting>
+
+ <para>
+ Puedes borrar los objetos en el orden que gustes, sin riesgo de violaciones
+ de restricción de clave foránea. Aún es
posible violar una restricción
+ <literal>NOT NULL</literal> sobre una columna clave
foránea borrando objetos
+ en un orden erróneo, por ejemplo, si borras el padre, pero olvidas
borrar los
+ hijos.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-replicating" revision="1">
+ <title>Replicando objetos entre dos almacénes de datos
diferentes</title>
+
+ <para>
+ Es ocasionalmente útil ser capaz de tomar un grafo de instancias
persistentes
+ y hacerlas persistentes en un almacén de datos diferente, sin
regenerar los valores
+ identificadores.
+ </para>
+
+ <programlisting><![CDATA[//retrieve a cat from one database
+Session session1 = factory1.openSession();
+Transaction tx1 = session1.beginTransaction();
+Cat cat = session1.get(Cat.class, catId);
+tx1.commit();
+session1.close();
+
+//reconcile with a second database
+Session session2 = factory2.openSession();
+Transaction tx2 = session2.beginTransaction();
+session2.replicate(cat, ReplicationMode.LATEST_VERSION);
+tx2.commit();
+session2.close();]]></programlisting>
+
+ <para>
+ El <literal>ReplicationMode</literal> determina cómo
<literal>replicate()</literal>
+ tratará los conflictos con filas existentes en la base de datos.
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>ReplicationMode.IGNORE</literal> - ignora el
objeto cuando existe una fila
+ de base de datos con el mismo identificador
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ReplicationMode.OVERWRITE</literal> -
sobrescribe cualquier fila de base de
+ datos existente con el mismo identificador
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ReplicationMode.EXCEPTION</literal> - lanza una
excepción si existe una fila
+ de base de datos con el mismo identificador
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ReplicationMode.LATEST_VERSION</literal> -
sobrescribe la fila si su número
+ de versión es anterior al número de
versión del objeto, o en caso contrario ignora el objeto
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Los casos de uso para esta funcionalidad incluyen reconciliar datos
ingresados en
+ instancias diferentes de bases de datos, actualizar información de
configuración de
+ sistema durante actualizaciones de producto, deshacer cambios producidos
durante
+ transacciones no-ACID y más.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-flushing">
+ <title>Limpiando (flushing) la sesión</title>
+
+ <para>
+ Cada tanto, la <literal>Session</literal> ejecutará
las sentencias SQL necesarias para
+ sincronizar el estado de la conexión JDBC con el estado de los
objetos mantenidos en menoria.
+ Este proceso, <emphasis>limpieza (flush)</emphasis>, ocurre por
defecto en los siguientes
+ puntos
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ antes de algunas ejecuciones de consulta
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ desde
<literal>org.hibernate.Transaction.commit()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ desde <literal>Session.flush()</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Las sentencias SQL son liberadas en el siguiente orden
+ </para>
+
+ <orderedlist spacing="compact">
+ <listitem>
+ <para>
+ todas las inserciones de entidades, en el mismo orden que los
objetos
+ correspondientes fueron salvados usando
<literal>Session.save()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ todas las actualizaciones de entidades
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ todas los borrados de colecciones
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ todos los borrados, actualizaciones e inserciones de elementos de
colección
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ todas las inserciones de colecciones
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ todos los borrados de entidades, en el mismo orden que los objetos
+ correspondientes fueron borrados usando
<literal>Session.delete()</literal>
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ (Una excepción es que los objetos que usan generación
de ID <literal>native</literal>
+ se insertan cuando son salvados.)
+ </para>
+
+ <para>
+ Excepto cuando llamas explícitamente a
<literal>flush()</literal>, no hay en absoluto
+ garantías sobre <emphasis>cuándo</emphasis>
la <literal>Session</literal> ejecuta las
+ llamadas JDBC. sólo sobre el
<emphasis>orden</emphasis> en que son ejecutadas. Sin embargo,
+ Hibernate garantiza que los métodos
<literal>Query.list(..)</literal> nunca devolverán datos
+ añejos o erróneos.
+ </para>
+
+ <para>
+ Es posible cambiar el comportamiento por defecto de modo que la limpieza
(flush)
+ ocurra menos frecuentemente. La clase
<literal>FlushMode</literal> tres modos
+ diferentes: sólo en tiempo de compromiso (y sólo cuando
se use la API de
+ <literal>Transaction</literal> de Hibernate), limpieza
automática usando la rutina
+ explicada, o nunca limpiar a menos que se llame a
<literal>flush()</literal>
+ explícitamente. El último modo es útil para
unidades de trabajo largas, donde una
+ <literal>Session</literal> se mantiene abierta y desconectada por
largo tiempo
+ (ver <xref linkend="transactions-optimistic-longsession"/>).
+ </para>
+
+ <programlisting><![CDATA[sess = sf.openSession();
+Transaction tx = sess.beginTransaction();
+sess.setFlushMode(FlushMode.COMMIT); // allow queries to return stale state
+
+Cat izi = (Cat) sess.load(Cat.class, id);
+izi.setName(iznizi);
+
+// might return stale data
+sess.find("from Cat as cat left outer join cat.kittens kitten");
+
+// change to izi is not flushed!
+...
+tx.commit(); // flush occurs]]></programlisting>
+
+ <para>
+ Durante la limpieza, puede ocurrir una excepción (por ejemplo, si
una operación DML
+ violase una restricción). Ya que el manejo de excepciones implica
alguna comprensión
+ del comportamiento transaccional de Hibernate, lo discutimos en <xref
linkend="transactions"/>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-transitive">
+ <title>Persistencia transitiva</title>
+
+ <para>
+ Es absolutamente incómodo dalvar, borrar, o reunir objetos
individuales,
+ especialmente si tratas con un grafo de objetos asociados. Un caso
común es
+ una relación padre/hijo. Considera el siguiente ejemplo:
+ </para>
+
+ <para>
+ Si los hijos en una relación padre/hijo pudieran ser tipificados
en valor
+ (por ejemplo, una colección de direcciones o cadenas), sus ciclos
de vida
+ dependerían del padre y se requeriría ninguna otra
acción para el tratamiento
+ en "cascada" de cambios de estado. Cuando el padre es salvado, los
objetos hijo
+ tipificados en valor son salvados también, cuando se borra el
padre, se borran
+ los hijos, etc. Esto funciona incluso para operaciones como el retiro de un
+ hijo de la colección. Hibernate detectará esto y, ya
que los objetos tipificados
+ en valor no pueden tener referencias compartidas, borrará el hijo
de la base
+ de datos.
+ </para>
+
+ <para>
+ Ahora considera el mismo escenario con los objetos padre e hijos siendo
entidades,
+ no tipos de valor (por ejemplo, categorías e ítems, o
gatos padre e hijos).
+ Las entidades tienen su propio ciclo de vida, soportan referencias
compartidas
+ (de modo que quitar una entidad de una colección no significa que
sea borrada),
+ y no hay por defecto ningún tratamiento en "cascada" de
estado de una entidad
+ a otras entidades asociadas. Hibernate no implementa
<emphasis>persistencia por
+ alcance</emphasis>.
+ </para>
+
+ <para>
+ Para cada operación básica de la sesión de
Hibernate - incluyendo <literal>persist(), merge(),
+ saveOrUpdate(), delete(), lock(), refresh(), evict(),
replicate()</literal> - hay un estilo
+ de cascada correspondiente. Respectivamente, los estilos de cascada se llaman
<literal>create,
+ merge, save-update, delete, lock, refresh, evict, replicate</literal>.
Si quieres que una
+ operación sea tratada en cascada a lo largo de una
asociación, debes indicar eso en el
+ documento de mapeo. Por ejemplo:
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name="person"
cascade="persist"/>]]></programlisting>
+
+ <para>
+ Los estilos de cascada pueden combinarse:
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name="person"
cascade="persist,delete,lock"/>]]></programlisting>
+
+ <para>
+ Puedes incluso usar <literal>cascade="all"</literal>
para especificar que <emphasis>todas</emphasis>
+ las operaciones deben ser tratadas en cascada a lo largo de la
asociación. El por defecto
+ <literal>cascade="none"</literal> especifica que
ninguna operación será tratada en cascada.
+ </para>
+
+ <para>
+ Un estilo de cascada especial, <literal>delete-orphan</literal>,
se aplica sólo a
+ asociaciones uno-a-muchos, e indica que la operación
<literal>delete()</literal> debe
+ aplicarse a cualquier objeto hijo que sea quitado de la
asociación.
+ </para>
+
+
+ <para>
+ Recomendaciones:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Usualmente no tiene sentido habilitar el tratamiento en cascada a una
asociación
+ <literal><many-to-one></literal> o
<literal><many-to-many></literal>.
+ El tratamiento en cascada es frecuentemente útil para las
asociaciones
+ <literal><one-to-one></literal> y
<literal><one-to-many></literal>.
+ associations.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Si la esperanza de vida de los objetos hijos está ligada a
la eesperanza de
+ vida del objeto padre, házlo un <emphasis>objeto de
ciclo de vida</emphasis>
+ especificando
<literal>cascade="all,delete-orphan"</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ En otro caso, puede que no necesites tratamiento en cascada en
absoluto. Pero
+ si piensas que estarás trabajando frecuentemente con padre
e hijos juntos en la
+ misma transacción, y quieres ahorrarte algo de tipeo,
considera usar
+
<literal>cascade="persist,merge,save-update"</literal>.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Mapear una asociación (ya sea una asociación
monovaluada, o una colección) con
+ <literal>cascade="all"</literal> marca la
asociación como una relación del estilo
+ <emphasis>padre/hijo</emphasis> donde save/update/delete en el
padre resulta
+ en save/update/delete del hijo o hijos.
+ </para>
+ <para>
+ Además, una mera referencia a un hijo desde un padre persistente
resultará en
+ un save/update del hijo. Esta metáfora está incompleta,
sin embargo. Un hijo
+ que deje de ser referenciado por su padre <emphasis>no</emphasis>
es borrado
+ automáticamente, excepto en el caso de una asociación
<literal><one-to-many></literal>
+ mapeada con <literal>cascade="delete-orphan"</literal>.
La semántica precisa de
+ las operaciones en cascada para una relación padre/hijo es:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Si un padre le es pasado a <literal>persist()</literal>,
todos los hijos le son
+ pasados a <literal>persist()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Si un padre le es pasado a <literal>merge()</literal>,
todos los hijos le son
+ pasados a <literal>merge()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Si un padre le es pasado a <literal>save()</literal>,
<literal>update()</literal> o
+ <literal>saveOrUpdate()</literal>, todos los hijos le son
pasados a <literal>saveOrUpdate()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Si un hijo transitorio o separado se vuelve referenciado por un
padre
+ persistente, le es pasado a
<literal>saveOrUpdate()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Si un padre es borrado, todos los hijos le son pasados a
<literal>delete()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Si un hijo deja de ser referenciado por un padre persistente,
+ <emphasis>no ocurre nada especial</emphasis> - la
aplicación debe
+ borrar explícitamente el hijo de ser necesario - a menos
que
+ <literal>cascade="delete-orphan"</literal>, en
cuyo caso el hijo
+ "huérfano" es borrado.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect1>
+
+ <sect1 id="objectstate-metadata">
+ <title>Usando metadatos</title>
+
+ <para>
+ Hibernate requiere de un modelo de meta-nivel muy rico de todas las entidades
y tipos de valor.
+ De vez en cuando, este modelo es muy útil para la
aplicación misma. Por ejemplo, la aplicación
+ podría usar los metadatos de Hibernate para implementar un
algoritmo "inteligente" de copia
+ en profundidad que entienda qué objetos deben ser copiados (por
ejemplo, tipo de valor mutables)
+ y cuáles no (por ejemplo, tipos de valor inmutables y,
posiblemente, entidades asociadas).
+ </para>
+ <para>
+ Hibernate expone los metadatos vía las interfaces
<literal>ClassMetadata</literal> y
+ <literal>CollectionMetadata</literal> y la jerarquía
<literal>Type</literal>. Las instancias
+ de las interfaces de metadatos pueden obtenerse de
<literal>SessionFactory</literal>.
+ </para>
+
+ <programlisting><![CDATA[Cat fritz = ......;
+ClassMetadata catMeta = sessionfactory.getClassMetadata(Cat.class);
+
+Object[] propertyValues = catMeta.getPropertyValues(fritz);
+String[] propertyNames = catMeta.getPropertyNames();
+Type[] propertyTypes = catMeta.getPropertyTypes();
+
+// get a Map of all properties which are not collections or associations
+Map namedValues = new HashMap();
+for ( int i=0; i<propertyNames.length; i++ ) {
+ if ( !propertyTypes[i].isEntityType() && !propertyTypes[i].isCollectionType()
) {
+ namedValues.put( propertyNames[i], propertyValues[i] );
+ }
+}]]></programlisting>
+
+ </sect1>
+
+</chapter>
+
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/content/toolset_guide.xml
(from rev 12794,
core/trunk/documentation/manual/es-ES/src/main/docbook/modules/toolset_guide.xml)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/content/toolset_guide.xml
(rev 0)
+++
core/trunk/documentation/manual/es-ES/src/main/docbook/content/toolset_guide.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,459 @@
+<chapter id="toolsetguide" revision="2">
+ <title>Guía del Conjunto de Herramientas</title>
+
+ <para>
+ La ingeniería de ida y vuelta con Hibernate es posible usando un
conjunto de plugins de Eclipse,
+ herramientas de línea de comandos, así como tareas de Ant.
+ </para>
+
+ <para>
+ Las <emphasis>Herramientas de Hibernate</emphasis> actualmente
incluyen plugins para la IDE de
+ Eclipse así como tareas de Ant para la ingeniería inversa
de bases de datos existentes:
+ </para>
+
+ <itemizedlist>
+ <listitem><para>
+ <emphasis>Editor de Mapeo:</emphasis> Un editor de ficheros de
mapeo XML, que soporta autocompleción
+ y resaltado de sintáxis. Soporta también
autocompleción semántica de nombres de clases y nombres de
+ campos/propiedades, haciéndolo mucho más
versátil que un editor de XML normal.
+ </para></listitem>
+ <listitem><para>
+ <emphasis>Consola:</emphasis> La consola es una nueva vista en
Eclipse. Además de la vista de
+ árbol de tus configuraciones de consola, tienes también
una vista interactiva de tus clases
+ persistentes y sus relaciones. La console te permite ejecutar consultas HQL
contra tu base de datos y
+ navegar el resultado directamente en Eclipse.
+ </para></listitem>
+ <listitem><para>
+ <emphasis>Asistentes de Desarrollo:</emphasis> Se proveen muchos
asistentes con las herramientas
+ de Eclipse. Puedes usar un asistente para generar rápidamente
ficheros de configuración de Hibernate
+ (cfg.xml), o incluso puedes haceruna ingeniería inversa completa
de un esquema de base de datos existente
+ en ficheros de código de POJO y ficheros de mapeo de Hibernate. El
asistente de ingeniería inversa soporta
+ plantillas personalizables.
+ </para></listitem>
+ <listitem><para>
+ <emphasis>Tareas de Ant:</emphasis>
+ </para></listitem>
+
+ </itemizedlist>
+
+ <para>
+ Por favor refiérete al paquete <emphasis>Herramientas de
Hibernate</emphasis> y su documentación para
+ más información.
+ </para>
+
+ <para>
+ Sin embargo, el paquete principal de Hibernate viene incluyendo una herramienta
integrada
+ (puede ser usada incluso "dentro" de Hibernate on-the-fly):
<emphasis>SchemaExport</emphasis>
+ también conocido como <literal>hbm2ddl</literal>.
+ </para>
+
+ <sect1 id="toolsetguide-s1" revision="2">
+ <title>Generación automática de
esquemas</title>
+
+ <para>
+ Una utilidad de Hibernate puede generar DDL desde tus ficheros de mapeo. El
esquema generado incluye
+ restricciones de integridad referencial (claves primarias y
foráneas) para las tablas de entidades y
+ colecciones. Las tablas y secuencias también son creadas para los
generadores de identificadores mapeados.
+ </para>
+
+ <para>
+ <emphasis>Debes</emphasis> especificar un
<literal>Dialecto</literal> SQL vía la propiedad
+ <literal>hibernate.dialect</literal> al usar esta herramienta, ya
que el DDL es altamente específico del
+ vendedor.
+ </para>
+
+ <para>
+ First, customize your mapping files to improve the generated schema.
+ </para>
+
+ <sect2 id="toolsetguide-s1-2" revision="1">
+ <title>Personalizando el esquema</title>
+
+ <para>
+ Muchos elementos de mapeo de Hibernate definen un atributo opcional
llamado <literal>length</literal>.
+ Con este atributo puedes establecer el tamaño de una columna.
(O, para tipos de datos
+ numéricos/decimales, la precisión.)
+ </para>
+
+ <para>
+ Algunas etiquetas también aceptan un atributo
<literal>not-null</literal> (para generar una restricción
+ <literal>NOT NULL</literal> en columnas de tablas) y y un
atributo <literal>unique</literal> (para generar
+ restricciones <literal>UNIQUE</literal> en columnas de
tablas).
+ </para>
+
+ <para>
+ Algunas etiquetas aceptan un atributo
<literal>index</literal> para especificar el nombre de un índice
+ para esa columna. Se puede usar un atributo
<literal>unique-key</literal> para agrupar columnas en una
+ restricción de clave de una sola unidad. Actualmente, el valor
especificado del atributo
+ <literal>unique-key</literal>
<emphasis>no</emphasis> es usado para nombrar la restricción,
sólo para
+ agrupar las columnas en el fichero de mapeo.
+ </para>
+
+ <para>
+ Ejemplos:
+ </para>
+
+ <programlisting><![CDATA[<property name="foo"
type="string" length="64" not-null="true"/>
+
+<many-to-one name="bar" foreign-key="fk_foo_bar"
not-null="true"/>
+
+<element column="serial_number" type="long"
not-null="true" unique="true"/>]]></programlisting>
+
+ <para>
+ Alternativamente, estos elementos aceptan tambíen un elemento
hijo <literal><column></literal>.
+ Esto es particularmente útil para tipos multicolumnas:
+ </para>
+
+ <programlisting><![CDATA[<property name="foo"
type="string">
+ <column name="foo" length="64" not-null="true"
sql-type="text"/>
+</property>]]></programlisting>
+
+ <programlisting><![CDATA[<property name="bar"
type="my.customtypes.MultiColumnType"/>
+ <column name="fee" not-null="true"
index="bar_idx"/>
+ <column name="fi" not-null="true"
index="bar_idx"/>
+ <column name="fo" not-null="true"
index="bar_idx"/>
+</property>]]></programlisting>
+
+ <para>
+ El atributo <literal>sql-type</literal> permite al usuario
sobrescribir el mapeo por defecto de
+ tipo Hibernate a tipo de datos SQL.
+ </para>
+
+ <para>
+ El atributo <literal>check</literal> te permite especificar
una comprobación de restricción.
+ </para>
+
+ <programlisting><![CDATA[<property name="foo"
type="integer">
+ <column name="foo" check="foo > 10"/>
+</property>]]></programlisting>
+
+ <programlisting><![CDATA[<class name="Foo"
table="foos" check="bar < 100.0">
+ ...
+ <property name="bar" type="float"/>
+</class>]]></programlisting>
+
+
+ <table frame="topbot" id="schemattributes-summary"
revision="2">
+ <title>Resumen</title>
+ <tgroup cols="3">
+ <colspec colwidth="1*"/>
+ <colspec colwidth="1*"/>
+ <colspec colwidth="2.5*"/>
+ <thead>
+ <row>
+ <entry>Atributo</entry>
+ <entry>Valores</entry>
+ <entry>Interpretación</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+
<entry><literal>length</literal></entry>
+ <entry>number</entry>
+ <entry>largo de columna/precisión
decimal</entry>
+ </row>
+ <row>
+
<entry><literal>not-null</literal></entry>
+
<entry><literal>true|false</literal></entry>
+ <entry>especifica que la columna debe ser no
nulable</entry>
+ </row>
+ <row>
+
<entry><literal>unique</literal></entry>
+
<entry><literal>true|false</literal></entry>
+ <entry>especifica que la columna debe tener una
restricción de unicidad</entry>
+ </row>
+ <row>
+
<entry><literal>index</literal></entry>
+
<entry><literal>index_name</literal></entry>
+ <entry>especifica el nombre de un índice
(multicolumna)</entry>
+ </row>
+ <row>
+
<entry><literal>unique-key</literal></entry>
+
<entry><literal>unique_key_name</literal></entry>
+ <entry>especifica el nombre de una
restricción de unicidad multicolumna</entry>
+ </row>
+ <row>
+
<entry><literal>foreign-key</literal></entry>
+
<entry><literal>foreign_key_name</literal></entry>
+ <entry>
+ especifica el nombre de la restricción de
clave foránea generada por una
+ asociación, úsalo en los elementos
de mapeo <one-to-one>, <many-to-one>,
+ <key>, y <many-to-many>. Nota
que los lados
+ <literal>inverse="true"</literal>
no serán considerados por
+ <literal>SchemaExport</literal>.
+ </entry>
+ </row>
+ <row>
+
<entry><literal>sql-type</literal></entry>
+
<entry><literal>column_type</literal></entry>
+ <entry>
+ sobrescribe el tipo de columna por defecto
(sólo atributo del elemento
+ <literal><column></literal>)
+ </entry>
+ </row>
+ <row>
+
<entry><literal>check</literal></entry>
+ <entry>expresión SQL</entry>
+ <entry>
+ crea una restricción de
comprobación SQL en columna o tabla
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ El elemento <literal><comment></literal> te
permite especificar un comentario para el esquema
+ generado.
+ </para>
+
+ <programlisting><![CDATA[<class name="Customer"
table="CurCust">
+ <comment>Current customers only</comment>
+ ...
+</class>]]></programlisting>
+
+ <programlisting><![CDATA[<property name="balance">
+ <column name="bal">
+ <comment>Balance in USD</comment>
+ </column>
+</property>]]></programlisting>
+
+ <para>
+ Esto resulta en una sentencia <literal>comment on
table</literal> o <literal>comment on column</literal>
+ en el DDL generado (donde esté soportado).
+ </para>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-3">
+ <title>Ejecutando la herramienta</title>
+
+ <para>
+ La herramienta <literal>SchemaExport</literal> escribe un
guión DDL a la salida estándar y/o
+ ejecuta las sentencias DDL.
+ </para>
+
+ <para>
+ <literal>java -cp
</literal><emphasis>classpaths_de_hibernate</emphasis>
+ <literal>org.hibernate.tool.hbm2ddl.SchemaExport</literal>
<emphasis>opciones ficheros_de_mapeo</emphasis>
+ </para>
+
+ <table frame="topbot">
+ <title>Opciones de Línea de Comandos de
<literal>SchemaExport</literal></title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>Opción</entry>
+ <entry>Descripción</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+
<entry><literal>--quiet</literal></entry>
+ <entry>no enviar a salida estándar el
guión</entry>
+ </row>
+ <row>
+
<entry><literal>--drop</literal></entry>
+ <entry>sólo desechar las
tablas</entry>
+ </row>
+ <row>
+
<entry><literal>--text</literal></entry>
+ <entry>no exportar a la base de datos</entry>
+ </row>
+ <row>
+
<entry><literal>--output=my_schema.ddl</literal></entry>
+ <entry>enviar la salida del guión ddl a un
fichero</entry>
+ </row>
+ <row>
+
<entry><literal>--config=hibernate.cfg.xml</literal></entry>
+ <entry>lee la configuración de Hibernate de
un fichero XML</entry>
+ </row>
+ <row>
+
<entry><literal>--properties=hibernate.properties</literal></entry>
+ <entry>lee las propiedades de base de datos de un
fichero</entry>
+ </row>
+ <row>
+
<entry><literal>--format</literal></entry>
+ <entry>formatea agradablemente el SQL generado en el
guión</entry>
+ </row>
+ <row>
+
<entry><literal>--delimiter=x</literal></entry>
+ <entry>establece un delimitador de fin de
línea para el guión</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Puedes incluso encajar <literal>SchemaExport</literal> en tu
aplicación:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = ....;
+new SchemaExport(cfg).create(false, true);]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-4">
+ <title>Propiedades</title>
+
+ <para>
+ Las propiedades de base de datos pueden especificarse
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>como propiedades de sistema con
<literal>-D</literal><emphasis><property></emphasis></para>
+ </listitem>
+ <listitem>
+ <para>en
<literal>hibernate.properties</literal></para>
+ </listitem>
+ <listitem>
+ <para>en un fichero de propiedades mencionado con
<literal>--properties</literal></para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Las propiedades necesarias son:
+ </para>
+
+ <table frame="topbot">
+ <title>Propiedades de Conexión de
SchemaExport</title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>Nombre de Propiedad</entry>
+ <entry>Descripción</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+
<entry><literal>hibernate.connection.driver_class</literal></entry>
+ <entry>clase del driver jdbc</entry>
+ </row>
+ <row>
+
<entry><literal>hibernate.connection.url</literal></entry>
+ <entry>url de jdbc</entry>
+ </row>
+ <row>
+
<entry><literal>hibernate.connection.username</literal></entry>
+ <entry>usuario de base de datos</entry>
+ </row>
+ <row>
+
<entry><literal>hibernate.connection.password</literal></entry>
+ <entry>contraseña de usuario</entry>
+ </row>
+ <row>
+
<entry><literal>hibernate.dialect</literal></entry>
+ <entry>dialecto</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-5">
+ <title>Usando Ant</title>
+
+ <para>
+ Puedes llamar a <literal>SchemaExport</literal> desde tu
guión de construcción de Ant:
+ </para>
+
+ <programlisting><![CDATA[<target
name="schemaexport">
+ <taskdef name="schemaexport"
+ classname="org.hibernate.tool.hbm2ddl.SchemaExportTask"
+ classpathref="class.path"/>
+
+ <schemaexport
+ properties="hibernate.properties"
+ quiet="no"
+ text="no"
+ drop="no"
+ delimiter=";"
+ output="schema-export.sql">
+ <fileset dir="src">
+ <include name="**/*.hbm.xml"/>
+ </fileset>
+ </schemaexport>
+</target>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-6">
+ <title>Actualizaciones incrementales de esquema</title>
+
+ <para>
+ La herramienta <literal>SchemaUpdate</literal>
actualizará un esquema existente con cambios
+ "incrementales". Nota que
<literal>SchemaUpdate</literal> depende fuertemente de la API de metadatos
+ de JDBC, de modo que no funcionará con todos los drivers
JDBC.
+ </para>
+
+ <para>
+ <literal>java -cp
</literal><emphasis>classpaths_de_hibernate</emphasis>
+ <literal>org.hibernate.tool.hbm2ddl.SchemaUpdate</literal>
<emphasis>opciones ficheros_de_mapeo</emphasis>
+ </para>
+
+ <table frame="topbot">
+ <title>Opciones de Línea de Comandos de
<literal>SchemaUpdate</literal></title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>Opción</entry>
+ <entry>Descripción</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+
<entry><literal>--quiet</literal></entry>
+ <entry>no enviar a salida estándar el
guión</entry>
+ </row>
+ <row>
+
<entry><literal>--properties=hibernate.properties</literal></entry>
+ <entry>lee las propiedades de base de datos de un
fichero</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Puedes encajar <literal>SchemaUpdate</literal> en tu
aplicación:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = ....;
+new SchemaUpdate(cfg).execute(false);]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-7">
+ <title>Usando Ant para actualizaciones incrementales de
esquema</title>
+
+ <para>
+ Puedes llamar a <literal>SchemaUpdate</literal> desde el
guión de Ant:
+ </para>
+
+ <programlisting><![CDATA[<target
name="schemaupdate">
+ <taskdef name="schemaupdate"
+ classname="org.hibernate.tool.hbm2ddl.SchemaUpdateTask"
+ classpathref="class.path"/>
+
+ <schemaupdate
+ properties="hibernate.properties"
+ quiet="no">
+ <fileset dir="src">
+ <include name="**/*.hbm.xml"/>
+ </fileset>
+ </schemaupdate>
+</target>]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+</chapter>
+
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/content/transactions.xml
(from rev 12794,
core/trunk/documentation/manual/es-ES/src/main/docbook/modules/transactions.xml)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/content/transactions.xml
(rev 0)
+++
core/trunk/documentation/manual/es-ES/src/main/docbook/content/transactions.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,925 @@
+<chapter id="transactions" revision="1">
+ <title>Transacciones y Concurrencia</title>
+
+ <para>
+ El punto más importante sobre Hibernate y el control de concurrencia
es que muy fácil
+ de comprender. Hibernate usa directamente conexiones JDBC y recursos JTA sin
agregar
+ ningún comportamiento de bloqueo adicional. Recomendamos altamente que
gastes algo de
+ tiempo con la especificación de JDBC, ANSI, y el aislamiento de
transacciones de tu sistema
+ de gestión de base de datos. Hibernate sólo
añade versionado automático pero no bloquea
+ objetos en memoria ni cambia el nivel de aislamiento de tus transacciones de base
de datos.
+ Básicamente, usa Hibernate como usarías JDBC directo (o
JTA/CMT) con tus recursos de base de
+ datos.
+ </para>
+
+ <para>
+ Sin embargo, además del versionado automático, Hibernate
ofrece una API (menor) para
+ bloqueo pesimista de filas, usando la sintáxis <literal>SELECT
FOR UPDATE</literal>.
+ Esta API se discute más adelante en este capítulo:
+ </para>
+
+ <para>
+ Comenzamos la discusión del control de concurrencia en Hibernate con
la granularidad
+ de <literal>Configuration</literal>,
<literal>SessionFactory</literal>, y
+ <literal>Session</literal>, así como la base de datos y
las transacciones de aplicación
+ largas.
+ </para>
+
+ <sect1 id="transactions-basics">
+ <title>Ámbitos de sesión y de
transacción</title>
+
+ <para>
+ Una <literal>SessionFactory</literal> es un objeto seguro entre
hebras caro-de-crear
+ pensado para ser compartido por todas las hebras de la aplicación.
Es creado una sola vez,
+ usualmente en el arranque de la aplicación, a partir de una
instancia de <literal>Configuration</literal>.
+ </para>
+
+ <para>
+ Una <literal>Session</literal> es un objeto barato, inseguro
entre hebras que debe
+ ser usado una sola vez, para un solo proceso de negocio, una sola unidad de
trabajo,
+ y luego descartado. Una <literal>Session</literal> no
obtendrá una <literal>Connection</literal>
+ JDBC (o un <literal>Datasource</literal>) a menos que sea
necesario, de modo que puedas
+ abrir y cerrar seguramente una <literal>Session</literal> incluso
si no estás seguro
+ que se necesitará acceso a los datos para servir una
petición en particular. (Esto se
+ vuelve importante en cuanto estés implementando alguno de los
siguientes patrones usando
+ intercepción de peticiones).
+ </para>
+
+ <para>
+ Para completar este cuadro tienes que pensar también en las
transacciones de base de
+ datos. Una transacción de base de datos tiene que ser tan corta
como sea posible, para
+ reducir la contención de bloqueos en la base de datos. Las
transacciones largas de base de
+ datos prevendrán a tu aplicación de escalar a una carga
altamente concurrente.
+ </para>
+
+ <para>
+ ¿Qué es el ámbito de una unidad de trabajo?
¿Puede una sola <literal>Session</literal> de Hibernate
+ extenderse a través de varias transacciones de base de datos o es
ésta una relación uno-a-uno
+ de ámbitos? ¿Cuándo debes abrir y cerrar una
<literal>Session</literal> y cómo demarcas los
+ límites de la transacción de base de datos?
+ </para>
+
+ <sect2 id="transactions-basics-uow">
+ <title>Unidad de trabajo</title>
+
+ <para>
+ Primero, no uses el antipatrón
<emphasis>sesión-por-operación</emphasis>, esto es,
+ ¡no abras y cierres una <literal>Session</literal>
para cada simple llamada a la base
+ de datos en una sola hebra! Por supuesto, lo mismo es verdad para
transacciones de base de
+ datos. Las llamadas a base de datos en una aplicación se hacen
usando una secuencia
+ prevista, que están agrupadas dentro de unidades de trabajo
atómicas. (Nota que esto
+ también significa que el auto-commit después de
cada una de las sentencias SQL es inútil
+ en una aplicación, este modo está pensado para
trabajo ad-hoc de consola SQL.
+ Hibernate deshabilita, o espera que el servidor de aplicaciones lo haga,
el modo
+ auto-commit inmediatamente.)
+ </para>
+
+ <para>
+ El patrón más común en una
aplicación mutiusuario cliente/servidor es
+
<emphasis>sesión-por-petición</emphasis>. En este
modelo, una petición del cliente
+ es enviada al servidor (en donde se ejecuta la capa de persistencia de
Hibernate),
+ se abre una nueva <literal>Session</literal> de Hibernate, y
todas las operaciones
+ de base de datos se ejecutan en esta unidad de trabajo. Una vez
completado el trabajo
+ (y se ha preparado la respuesta para el cliente) la sesión es
limpiada y cerrada.
+ Podrías usar una sola transacción de base de datos
para servir a petición del cliente,
+ comenzándola y comprometiéndola cuando abres y
cierras la <literal>Session</literal>.
+ La relación entre las dos es uno-a-uno y este modelo es a la
medida perfecta de muchas
+ aplicaciones.
+ </para>
+
+ <para>
+ El desafío yace en la implementación: no
sólo tienen que comenzarse y terminarse correctamente
+ la <literal>Session</literal> y la transacción,
sino que además tienen que estar accesibles
+ para las operaciones de acceso a datos. La demarcación de una
unidad de trabajo se implementa
+ idealmente usando un interceptor que se ejecuta cuando una
petición llama al servidor y anter que
+ la respuesta sea enviada (es decir, un
<literal>ServletFilter</literal>). Recomendamos ligar la
+ <literal>Session</literal> a la hebra que atiende la
petición, usando una variable
+ <literal>ThreadLocal</literal>. Esto permite un
fácil acceso (como acceder a una variable static)
+ en tódo el código que se ejecuta en esta hebra.
Dependiendo del mecanismo de demarcación de
+ transacciones de base de datos que elijas, podrías mantener
también el contexto de la transacción
+ en una variable <literal>ThreadLocal</literal>. Los patrones
de implementación para esto son
+ conocidos como <emphasis>Sesión Local de Hebra
(ThreadLocal Session)</emphasis> y
+ <emphasis>Sesión Abierta en Vista (Open Session in
View)</emphasis>. Puedes extender fácilmente
+ la clase de ayuda <literal>HibernateUtil</literal> mostrada
anteriormente para encontrar
+ una forma de implementar un interceptor e instalarlo en tu entorno. Ver
el sitio web de Hibernate
+ para consejos y ejemplos.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-basics-apptx">
+ <title>Transacciones de aplicación</title>
+
+ <para>
+ El patrón sesión-por-petición no es el
único concepto útil que puedes usar para diseñar
unidades
+ de trabajo. Muchos procesos de negocio requiere una serie completa de
interacciones con el
+ usuario intercaladas con accesos a base de datos. En aplicaciones web y
de empresa no es aceptable
+ que una transacción de base de datos se extienda a
través de la interacción de un usuario.
+ Considera el siguiente ejemplo:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ Se abre la primera pantalla de un diálogo, los datos
vistos por el usuario han sido
+ cargados en una <literal>Session</literal> y
transacción de base de datos particular.
+ El usuario es libre de modificar los objetos.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ El usuario hace click en "Salvar" después de
5 minutos y espera que sus modificaciones
+ sean hechas persistentes. También espera que
él sea la única persona editando esta
+ información y que no puede ocurrir ninguna
modificación en conflicto.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Llamamos a esto unidad de trabajo, desde el punto de vista del usuario,
una larga
+ <emphasis>transacción de
aplicación</emphasis> ejecutándose. Hay muchas formas en
+ que puedes implementar esto en tu aplicación.
+ </para>
+
+ <para>
+ Una primera implementación ingenua podría mantener
abierta la <literal>Session</literal>
+ y la transacción de base de datos durante el tiempo de pensar
del usuario, con bloqueos
+ tomados en la base de datos para prevenir la modificación
concurrente, y para garantizar
+ aislamiento y atomicidad. Esto es, por supuesto, un
antipatrón, ya que la contención de
+ bloqueo no permitiría a la aplicación escalar con
el número de usuarios concurrentes.
+ </para>
+
+ <para>
+ Claramente, tenemos que usar muchas transacciones de base de datos para
implementar la transacción
+ de aplicación. En este caso, mantener el aislamiento de los
procesos de negocio se vuelve una
+ responsabilidad parcial de la capa de aplicación. Una sola
transacción de aplicación usualmente
+ abarca varias transacciones de base de datos. Será
atómica si sólo una de estas transacciones de
+ base de datos (la última) almacena los datos actualizados,
todas las otras simplemente leen datos
+ (por ejemplo, en un diálogo estilo-asistente abarcando muchos
ciclos petición/respuesta).
+ Esto es más fácil de implementar de lo que suena,
especialmente si usas las funcionalidades de
+ Hibernate:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Versionado Automático</emphasis>
- Hibernate puede llevar un control automático de
+ concurrencia optimista por ti, puede detectar
automáticamente si una modificación concurrente
+ ha ocurrido durante el tiempo de pensar del usuario.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Objetos Separados</emphasis> - Si decides
usar el ya discutido patrón
+ de
<emphasis>sesión-por-petición</emphasis>, todas las
instancias cargadas estarán
+ en estado separado durante el tiempo de pensar del usuario.
Hibernate te permite
+ volver a unir los objetos y hacer persistentes las
modificaciones. El patrón se
+ llama
<emphasis>sesión-por-petición-con-objetos-separados</emphasis>.
Se usa
+ versionado automático para aislar las modificaciones
concurrentes.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Sesión Larga</emphasis> - La
<literal>Session</literal> de Hibernate puede ser
+ desconectada de la conexión JDBC subyacente
después que se haya sido comprometida la
+ transacción de base de datos, y reconectada cuando
ocurra una nueva petición del cliente.
+ Este patrón es conocido como
<emphasis>sesión-por-transacción-de-aplicación</emphasis>
+ y hace la re-unión innecesaria. Para aislar las
modificaciones concurrentes se usa el
+ versionado automático.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Tanto
<emphasis>sesión-por-petición-con-objetos-separados</emphasis>
como
+
<emphasis>sesión-por-transacción-de-aplicación</emphasis>,
ambas tienen
+ ventajas y desventajas, las discutimos más adelante en este
capítulo en el contexto
+ del control optimista de concurrencia.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-basics-identity">
+ <title>Considerando la identidad del objeto</title>
+
+ <para>
+ Una aplicación puede acceder concurrentemente a el mismo
estado persistente en dos
+ <literal>Session</literal>s diferentes. Sin embargo, una
instancia de una clase
+ persistente nunca se comparte entre dos instancias de
<literal>Session</literal>.
+ Por lo tanto existen dos nociones diferentes de identidad:
+ </para>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>Identidad de Base de Datos</term>
+ <listitem>
+ <para>
+ <literal>foo.getId().equals( bar.getId()
)</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Identidad JVM</term>
+ <listitem>
+ <para>
+ <literal>foo==bar</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ Entonces para objetos unidos a una <literal>Session</literal>
<emphasis>en particular</emphasis>
+ (es decir en el ámbito de una
<literal>Session</literal>) las dos nociones son equivalentes, y
+ la identidad JVM para la identidad de base de datos está
garantizada por Hibernate. Sin embargo,
+ mientras la aplicación acceda concurrentemente al
"mismo" (identidad persistente) objeto de negocio
+ en dos sesiones diferentes, las dos instancias serán realmente
"diferentes" (identidad JVM).
+ Los conflictos se resuelven (con versionado automático) en
tiempo de limpieza (flush) usando un
+ enfoque optimista.
+ </para>
+
+ <para>
+ Este enfoque deja que Hibernate y la base de datos se preocupen sobre la
concurrencia. Además
+ provee la mejor escalabilidad, ya que garantizando la identidad un
unidades de trabajo monohebra
+ no se necesitan bloqueos caros u otros medios de
sincronización. La aplicación nunca necesita
+ sincronizar sobre ningún objeto de negocio, siempre que se
apegue a una sola hebra por
+ <literal>Session</literal>. Dentro de una
<literal>Session</literal> la aplicación puede usar
+ con seguridad <literal>==</literal> para comparar objetos.
+ </para>
+
+ <para>
+ Sin embargo, una aplicación que usa
<literal>==</literal> fuera de una <literal>Session</literal>,
+ podría ver resultados inesperados. Esto podría
ocurrir incluso en sitios algo inesperados,
+ por ejemplo, si pones dos instancias separadas dentro del mismo
<literal>Set</literal>.
+ Ambas podrían tener la misma identidad de base de datos (es
decir, representar la misma fila),
+ pero la identidad JVM, por definición, no está
garantizada para las instancias en estado separado.
+ El desarrollador tiene que sobrescribir los métodos
<literal>equals()</literal> y
+ <literal>hashCode()</literal> en las clases persistentes e
implementar su propia noción de igualdad
+ de objetos. Hay una advertencia: Nunca uses el identificador de base de
datos para implementar
+ la igualdad, usa una clave de negocio, una combinación de
atributos únicos, usualmente inmutables.
+ El identificador de base de datos cambiará si un objeto
transitorio es hecho persistente.
+ Si la instancia transitoria (usualmente junta a instancias separadas) es
mantenida en un
+ <literal>Set</literal>, cambiar el código hash
rompe el contrato del <literal>Set</literal>.
+ Los atributos para las claves de negocio no tienen que ser tan estables
como las claves primarias
+ de base de datos, sólo tienes que garantizar estabilidad en
tanto los objetos estén en el mismo
+ <literal>Set</literal>. Mira el sitio web de Hibernate para
una discusión más cuidadosa de este
+ tema. Nota también que éste no es un tema de
Hibernate, sino simplemente cómo la identidad y la igualdad
+ de los objetos Java tiene que ser implementada.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-basics-issues">
+ <title>Temas comunes</title>
+
+ <para>
+ Nunca uses los antipatrones
<emphasis>sesión-por-sesión-de-usuario</emphasis> o
+
<emphasis>sesión-por-aplicación</emphasis> (por
supuesto, hay raras excepciones a esta
+ regla). Nota que algunis de los siguientes temas podrían
también aparecer con los patrones
+ recomendados. Asegúrate que entiendes las implicaciones antes
de tomar una decisión de
+ diseño:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ Una <literal>Session</literal> no es segura entre
hebras. Las cosas que se suponen
+ que funcionan concurrentemente, como peticiones HTTP, beans de
sesión, o workers de
+ Swing, provocarán condiciones de competencia si una
instancia de <literal>Session</literal>
+ fuese compartida. Si guardas tu
<literal>Session</literal> de Hibernate en tu
+ <literal>HttpSession</literal> (discutido
más adelante), debes considerar sincronizar
+ el acceso a tu sesión HTTP. De otro modo, un usuario
que hace click lo suficientemente
+ rápido puede llegar a usar la misma
<literal>Session</literal> en dos hebras ejecutándose
+ concurrentemente.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Una excepción lanzada por Hibernate significa que
tienes que deshacer (rollback) tu
+ transacción de base de datos y cerrar la
<literal>Session</literal> inmediatamente
+ (discutido en más detalle luego). Si tu
<literal>Session</literal> está ligada a la
+ aplicación, tienes que parar la aplicación.
Deshacer (rollback) la transacción de base
+ de datos no pone a tus objetos de vuelta al estado en que estaban
al comienzo de la
+ transacción. Esto significa que el estado de la base
de datos y los objetos de negocio
+ quedan fuera de sincronía. Usualmente esto no es un
problema, pues las excepciones no
+ son recuperables y tienes que volver a comenzar
después del rollback de todos modos.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ La <literal>Session</literal> pone en
caché todo objeto que esté en estado persistente
+ (vigilado y chequeado por estado sucio por Hibernate). Esto
significa que crece sin
+ fin hasta que obtienes una OutOfMemoryException, si la mantienes
abierta por un largo
+ tiempo o simplemente cargas demasiados datos. Una
solución para esto es llamar a
+ <literal>clear()</literal> y
<literal>evict()</literal> para gestionar el caché de la
+ <literal>Session</literal>, pero probalemente debas
considerar un procedimiento almacenado
+ si necesitas operaciones de datos masivas. Se muestran algunas
soluciones en
+ <xref linkend="batch"/>. Mantener una
<literal>Session</literal> abierta por la duración
+ de una sesión de usuario significa también
una alta probabilidad de datos añejos.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="transactions-demarcation">
+ <title>Demarcación de la transacción de base de
datos</title>
+
+ <para>
+ Los límites de las transacciones de base de datos (o sistema) son
siempre necesarios. Ninguna comunicación
+ con la base de datos puede darse fuera de una transacción de base
de datos (esto parece confundir muchos
+ desarrolladores acostumbrados al modo auto-commit). Siempre usa
límites de transacción claros, incluso
+ para las operaciones de sólo lectura. Dependiendo del nivel de
aislamiento y las capacidades de base de
+ datos, esto podría o no ser requerido, pero no hay un merma si
siempre demarcas explícitamente
+ las transacciones.
+ </para>
+
+ <para>
+ Una aplicación Hibernate puede ejecutarse en entornos no manejados
(es decir, como independiente,
+ Web simple, o aplicaciones Swing) y entornos manejados J2EE. En un entorno no
manejado, Hibernate es
+ usualmente responsable de su propio pool de conexiones de base de datos. El
desarrollador de aplicaciones
+ tiene que establecer manualmente los límites de
transacción, en otras palabras, hacer begin, commit, o
+ rollback las transacciones de base de datos por sí mismo. Un
entorno manejado usualmente provee transacciones
+ gestionadas por contenedor, con el ensamble de transacción
definido declarativamente en descriptores de
+ despliegue de beans de sesión EJB, por ejemplo. La
demarcación programática de transacciones no es más
+ necesario, incluso limpiar (flush) la <literal>Session</literal>
es hecho automáticamente.
+ </para>
+
+ <para>
+ Sin embargo, frecuentemente es deseable mantener portable tu capa de
persistencia. Hibernate ofrece
+ una API de envoltura llamada <literal>Transaction</literal> que
se traduce al sistema de transacciones
+ nativo de tu entorno de despliegue. Esta API es realmente opcional, pero
recomendamos fuertemente su uso
+ salvo que estés en un bean de sesión CMT.
+ </para>
+
+ <para>
+ Usualmente, finalizar una <literal>Session</literal> implica
cuatro fases distintas:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ limpiar (flush) la sesión
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ comprometer la transacción
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ cerrar la sesión
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ manejar excepciones
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Limpiar la sesión ha sido discutido anteriormente, tendremos ahora
una mirada más de cerca
+ a la demarcación de transacciones y manejo de excepciones en
sendos entornos manejado y no manejados.
+ </para>
+
+
+ <sect2 id="transactions-demarcation-nonmanaged">
+ <title>Entorno no manejado</title>
+
+ <para>
+ Si una capa de persistencia Hibernate se ejecuta en un entorno no
manejado, las conexiones
+ de base de datos son manejadas usualmente por el mecanismo de pooling de
Hibernate. El idioma
+ manejo de sesión/transacción se ve así:
+ </para>
+
+ <programlisting><![CDATA[//Non-managed environment idiom
+Session sess = factory.openSession();
+Transaction tx = null;
+try {
+ tx = sess.beginTransaction();
+
+ // do some work
+ ...
+
+ tx.commit();
+}
+catch (RuntimeException e) {
+ if (tx != null) tx.rollback();
+ throw e; // or display error message
+}
+finally {
+ sess.close();
+}]]></programlisting>
+
+ <para>
+ No tienes que limpiar con <literal>flush()</literal> la
<literal>Session</literal> explícitamente -
+ la llamada a <literal>commit()</literal>
automáticamente dispara la sincronización.
+ </para>
+
+ <para>
+ Una llamada a <literal>close()</literal> marca el fin de una
sesión. La principal implicación
+ de <literal>close()</literal> es que la conexión
JDBC será abandonada por la sesión.
+ </para>
+
+ <para>
+ Este código Java es portable y se ejecuta tanto en entornos no
manejados como en entornos JTA.
+ </para>
+
+ <para>
+ Muy probablemente nunca veas este idioma en código de negocio
en una aplicación normal;
+ las excepciones fatales (sistema) deben siempre ser capturadas en la
"cima". En otras palabras,
+ el código que ejecuta las llamadas de Hibernate (en la capa de
persistencia) y el código que
+ maneja <literal>RuntimeException</literal> (y usualmente
sólo puede limpiar y salir) están en
+ capas diferentes. Esto puede ser un desafío de
diseñarlo tú mismo y debes usar los servicios
+ de contenedor J2EE/EJB en cuanto estuviesen disponibles. El manejo de
excepciones se dicute
+ más adelante en este capítulo.
+ </para>
+
+ <para>
+ Nota que debes seleccionar
<literal>org.hibernate.transaction.JDBCTransactionFactory</literal>
+ (que es el por defecto).
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-demarcation-jta">
+ <title>Usando JTA</title>
+
+ <para>
+ Si tu capa de persistencia se ejecuta en un servidor de aplicaciones (por
ejemplo, detrás
+ de beans de sesión EJB), cada conexión de
datasource obtenida por Hibernate será parte
+ automáticamente de la transacción JTA global.
Hibernate ofrece dos estrategias para esta
+ integración.
+ </para>
+
+ <para>
+ Si usas transacciones gestionadas-por-bean (BMT) Hibernate le
dirá al servidor de aplicaciones
+ que comience y finalice una transacción BMT si usas la API de
<literal>Transaction</literal>.
+ De modo que, el código de gestión de la
transacción es idéntico al de un entorno no manejado.
+ </para>
+
+ <programlisting><![CDATA[// BMT idiom
+Session sess = factory.openSession();
+Transaction tx = null;
+try {
+ tx = sess.beginTransaction();
+
+ // do some work
+ ...
+
+ tx.commit();
+}
+catch (RuntimeException e) {
+ if (tx != null) tx.rollback();
+ throw e; // or display error message
+}
+finally {
+ sess.close();
+}]]></programlisting>
+
+ <para>
+ Con CMT, la demarcación de la transacción se hace
en descriptores de despliegue de beans de sesión,
+ no programáticamente. Si no quieres limpiar (flush) y cerrar
manualmente la <literal>Session</literal>
+ por ti mismo, solamente establece
<literal>hibernate.transaction.flush_before_completion</literal> a
+ <literal>true</literal>,
<literal>hibernate.connection.release_mode</literal> a
+ <literal>after_statement</literal> o
<literal>auto</literal> y
+ <literal>hibernate.transaction.auto_close_session</literal> a
<literal>true</literal>. Hibernate
+ limpiará y cerrará entonces
automáticamente la <literal>Session</literal> para ti. Lo
único que resta
+ es deshacer (rollback) la transacción cuando ocurra una
excepción. Afortunadamente, en un bean CMT,
+ incluso esto ocurre automáticamente, ya que una
<literal>RuntimeException</literal> no manejada
+ disparada por un método de un bean de sesión le
dice al contenedor que ponga a deshacer la transacción
+ global. <emphasis>Esto significa que, en CMT, no necesitas usar en
absoluto la API de
+ <literal>Transaction</literal> de
Hibernate.</emphasis>
+ </para>
+
+ <para>
+ Nota que debes elegir
<literal>org.hibernate.transaction.JTATransactionFactory</literal> en un
+ bean de sesión BMT, y
<literal>org.hibernate.transaction.CMTTransactionFactory</literal> en un
+ bean de sesión CMT, cuando configures la fábrica de
transacciones de Hibernate. Recuerda además
+ establecer
<literal>org.hibernate.transaction.manager_lookup_class</literal>.
+ </para>
+
+ <para>
+ Si trabajas en un entorno CMT, y usas limpieza (flushing) y cierre
automáticos de la sesión,
+ podrías querer también usar la misma
sesión en diferentes partes de tu código. Típicamente,
+ en un entorno no manejado, usarías una variable
<literal>ThreadLocal</literal> para tener la sesión,
+ pero una sola petición de EJB puede ejecutarse en diferentes
hebras (por ejemplo, un bean de sesión
+ llamando a otro bean de sesión). Si no quieres molestarte en
pasar tu <literal>Session</literal>
+ por alrededor, la <literal>SessionFactory</literal> provee el
método
+ <literal>getCurrentSession()</literal>, que devuelve una
sesión que está pegada al contexto de
+ transacción JTA. ¡Esta es la forma más
fácil de integrar Hibernate en una aplicación!
+ La sesión "actual" siempre tiene habilitados
limpieza, cierre y liberación de conexión automáticos
+ (sin importar la configuración de las propiedades anteriores).
Nuestra idioma de gestión de
+ sesión/transacción se reduce a:
+ </para>
+
+ <programlisting><![CDATA[// CMT idiom
+Session sess = factory.getCurrentSession();
+
+// do some work
+...
+
+]]></programlisting>
+
+ <para>
+ En otras palabras, todo lo que tienes que hacer en un entorno manejado,
es llamar a
+ <literal>SessionFactory.getCurrentSession()</literal>, hacer
tu trabajo de acceso a datos,
+ y dejar el resto al contenedor. Los límites de
transacción se establecen declarativamente
+ en los descriptores de despliegue de tu bean de sesión. El
ciclo de vida de la sesión es
+ manejado completamente por Hibernate.
+ </para>
+
+ <para>
+ Existe una advertencia al uso del modo de liberación de
conexión <literal>after_statement</literal>.
+ Debido a una limitación tonta de la especificación
de JTA, no es posible para Hibernate
+ limpiar automáticamente ningún
<literal>ScrollableResults</literal> no cerrado ni
+ instancias de <literal>Iterator</literal> devueltas por
<literal>scroll()</literal> o
+ <literal>iterate()</literal>.
<emphasis>Debes</emphasis> liberar el cursor de base de datos
+ subyacente llamando a
<literal>ScrollableResults.close()</literal> o
+ <literal>Hibernate.close(Iterator)</literal>
explícitamente desde un bloque <literal>finally</literal>.
+ (Por supuesto, la mayoría de las aplicaciones pueden evitarlo
fácilmente no usando en absoluto ningún
+ <literal>scroll()</literal> o
<literal>iterate()</literal> desde el código CMT.)
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-demarcation-exceptions">
+ <title>Manejo de excepciones</title>
+
+ <para>
+ Si la <literal>Session</literal> lanza una
excepción (incluyendo cualquier
+ <literal>SQLException</literal>), debes inmediatamente
deshacer (rollback) la
+ transacción de base de datos, llamar a
<literal>Session.close()</literal> y
+ descartar la instancia de <literal>Session</literal>. Ciertos
métodos de
+ <literal>Session</literal>
<emphasis>no</emphasis> dejarán la sesión en un
+ estado consistente. Ninguna excepción lanzada por Hibernate
puede ser tratada
+ como recuperable. Asegúrate que la
<literal>Session</literal> sea cerrada llamando
+ a <literal>close()</literal> en un bloque
<literal>finally</literal>.
+ </para>
+
+ <para>
+ La <literal>HibernateException</literal>, que envuelve la
mayoría de los errores que
+ pueden ocurrir en la capa de persistencia de Hibernate, en una
excepción no chequeada
+ (no lo era en versiones anteriores de Hibernate). En nuestra
opinión, no debemos forzar
+ al desarrollador de aplicaciones a capturar una excepción
irrecuperable en una capa baja.
+ En la mayoría de los sistemas, las excepciones no chequeadas y
fatales son manejadas
+ en uno de los primeros cuadros de la pila de llamadas a
métodos (es decir, en las capas
+ más altas) y se presenta un mensaje de error al usuario de la
aplicación (o se toma alguna
+ otra acción apropiada). Nota que Hibernate podría
también lanzar otras excepciones no chequeadas
+ que no sean una <literal>HibernateException</literal>. Una
vez más, no son recuperables y debe
+ tomarse una acción apropiada.
+ </para>
+
+ <para>
+ Hibernate envuelve <literal>SQLException</literal>s lanzadas
mientras se interactúa con la base
+ de datos en una <literal>JDBCException</literal>. De hecho,
Hibernate intentará convertir la excepción
+ en una subclase de <literal>JDBCException</literal>
más significativa. La <literal>SQLException</literal>
+ está siempre disponible vía
<literal>JDBCException.getCause()</literal>. Hibernate convierte la
+ <literal>SQLException</literal> en una subclase de
<literal>JDBCException</literal> apropiada usando
+ el <literal>SQLExceptionConverter</literal> adjunto a la
<literal>SessionFactory</literal>. Por defecto,
+ el <literal>SQLExceptionConverter</literal> está
definido para el dialecto configurado; sin embargo,
+ es también posible enchufar una implementación
personalizada (ver los javadocs de la clase
+ <literal>SQLExceptionConverterFactory</literal> para los
detalles). Los subtipos estándar de
+ <literal>JDBCException</literal> son:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>JDBCConnectionException</literal> - indica
un error con la comunicación JDBC subyacente.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>SQLGrammarException</literal> - indica un
problema de gramática o sintáxis con el
+ SQL publicado.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ConstraintViolationException</literal> -
indica alguna forma de violación de restricción
+ de integridad.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockAcquisitionException</literal> - indica
un error adquiriendo un nivel de bloqueo
+ necesario para realizar una operación solicitada.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>GenericJDBCException</literal> - una
excepción genérica que no cayó en ninguna de las
+ otras categorías.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="transactions-optimistic">
+ <title>Control optimista de concurrencia</title>
+
+ <para>
+ El único enfoque que es consistente con alta concurrencia y alta
escalabilidad es el control
+ optimista de concurrencia con versionamiento. El chuequeo de
versión usa números de versión,
+ o timestamps, para detectar actualizaciones en conflicto (y para prevenir
actualizaciones perdidas).
+ Hibernate provee para tres enfoques posibles de escribir código de
aplicación que use concurrencia
+ optimista. Los casos de uso que hemos mostrado están en el
contexto de transacciones de aplicación
+ largas pero el chequeo de versiones tiene además el beneficio de
prevenir actualizaciones perdidas
+ en transacciones de base de datos solas.
+ </para>
+
+ <sect2 id="transactions-optimistic-manual">
+ <title>Chequeo de versiones de aplicación</title>
+
+ <para>
+ En una implementación sin mucha ayuda de Hibernate, cada
interacción con la base de datos ocurre en una
+ nueva <literal>Session</literal> y el desarrollador es
responsable de recargar todas las intancias
+ persistentes desde la base de datos antes de manipularlas. Este enfoque
fuerza a la aplicación a
+ realizar su propio chequeo de versiones para asegurar el aislamiento de
transacciones de base de datos.
+ Es el enfoque más similar a los EJBs de entidad.
+ </para>
+
+ <programlisting><![CDATA[// foo is an instance loaded by a previous
Session
+session = factory.openSession();
+Transaction t = session.beginTransaction();
+int oldVersion = foo.getVersion();
+session.load( foo, foo.getKey() ); // load the current state
+if ( oldVersion != foo.getVersion() ) throw new StaleObjectStateException();
+foo.setProperty("bar");
+t.commit();
+session.close();]]></programlisting>
+
+ <para>
+ La propiedad <literal>version</literal> se mapea usando
<literal><version></literal>,
+ e Hibernate la incrementará automáticamente durante
la limpieza si la entidad está sucia.
+ </para>
+
+ <para>
+ Por supuesto, si estás operando un entorno de
baja-concurrencia-de-datos y no requieres
+ chequeo de versiones, puedes usar este enfoque y simplemente saltar el
chequeo de versiones.
+ En ese caso, <emphasis>el último compromiso (commit)
gana</emphasis> será la estrategia por
+ defecto para tus transacciones de aplicación largas. Ten en
mente que esto podría confundir
+ a los usuarios de la aplicación, pues podrían
experimentar actualizaciones perdidas sin
+ mensajes de error ni chance de fusionar los cambios conflictivos.
+ </para>
+
+ <para>
+ Claramente, el chequeo manual de versiones es factible solamente en
circunstancias muy triviales,
+ y no es práctico para la mayoría de aplicaciones.
Frecuentemente, no sólo intancias solas, sino grafos
+ completos de objetos modificados tienen que ser chequeados. Hibernate
ofrece chequeo de versiones
+ automático con el paradigma de diseño de
<literal>Session</literal> larga o de instancias separadas.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-optimistic-longsession">
+ <title>Sesión larga y versionado
automático</title>
+
+ <para>
+ Una sola instancia de <literal>Session</literal> y sus
instancias persistentes
+ son usadas para toda la transacción de aplicación.
Hibernate chequea las versiones
+ de instancia en el momento de limpieza (flush), lanzando una
excepción si se detecta
+ una modificación concurrente. Concierne al desarrollador
capturar y manejar esta excepción
+ (las opciones comunes son la oportunidad del usuario de fusionar los
cambios, o recomenzar el
+ proceso de negocio sin datos añejos).
+ </para>
+
+ <para>
+ La <literal>Session</literal> se desconecta de cualquier
conexión JDBC subyacente
+ al esperar por una interacción del usuario. Este enfoque es el
más eficiente en términos
+ de acceso a base de datos. La aplicación no necesita tratar
por sí misma con el chequeo de
+ versiones, ni re-uniendo instancias separadas, ni tiene que recargar
instancias en cada
+ transacción de base de datos.
+ </para>
+
+ <programlisting><![CDATA[// foo is an instance loaded earlier by the
Session
+session.reconnect(); // Obtain a new JDBC connection
+Transaction t = session.beginTransaction();
+foo.setProperty("bar");
+t.commit(); // End database transaction, flushing the change and checking the version
+session.disconnect(); // Return JDBC connection ]]></programlisting>
+
+ <para>
+ El objeto <literal>foo</literal> todavía conoce en
qué <literal>Session</literal> fue cargado.
+ <literal>Session.reconnect()</literal> obtiene una nueva
conexión (o puedes proveer una) y
+ reasume la sesión. El método
<literal>Session.disconnect()</literal> desconectará la
sesión
+ de la conexión JDBC y la devolverá la
conexión al pool (a menos que hayas provisto la conexión).
+ Después de la reconexión, para forzar un chequeo de
versión en datos que no estés actualizando,
+ puedes llamar a <literal>Session.lock()</literal> con
<literal>LockMode.READ</literal> sobre
+ cualquier objeto que pudiese haber sido actualizado por otra
transacción. No necesitas bloquear
+ ningún dato que <emphasis>sí
estés</emphasis> actualizando.
+ </para>
+
+ <para>
+ Si las llamadas explícitas a
<literal>disconnect()</literal> y <literal>reconnect()</literal>
+ son muy onerosas, puedes usar en cambio
<literal>hibernate.connection.release_mode</literal>.
+ </para>
+
+ <para>
+ Este patrón es problemático si la
<literal>Session</literal> es demasiado grande para ser almacenada
+ durante el tiempo de pensar del usuario, por ejemplo, una
<literal>HttpSession</literal> debe
+ mantenerse tan pequeña como sea posible. Ya que la
<literal>Session</literal> es también el caché
+ (obligatorio) de primer nivel y contiene todos los objetos cargados,
podemos probablemente cargar
+ esta estrategia sólo para unos pocos ciclos
petición/respuesta. Esto está de hecho recomendado, ya que
+ la <literal>Session</literal> tendrá pronto
también datos añejos.
+ </para>
+
+ <para>
+ Nota también que debes mantener la
<literal>Session</literal> desconectada próxima a la capa
+ de persistencia. En otras palabras, usa una sesión de EJB con
estado para tener la
+ <literal>Session</literal> y no transferirla a la capa web
para almacenarla en la
+ <literal>HttpSession</literal> (ni incluso serializarla a una
capa separada).
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-optimistic-detached">
+ <title>Objetos separados y versionado
automático</title>
+
+ <para>
+ Cada interacción con el almacén persistente ocurre
en una nueva <literal>Session</literal>.
+ Sin embargo, las mismas instancias persistentes son reusadas para cada
interacción con la base de
+ datos. La aplicación manipula el estado de las instancias
separadas originalmente cargadas en otra
+ <literal>Session</literal> y luego las readjunta usando
<literal>Session.update()</literal>,
+ <literal>Session.saveOrUpdate()</literal>, o
<literal>Session.merge()</literal>.
+ </para>
+
+ <programlisting><![CDATA[// foo is an instance loaded by a previous
Session
+foo.setProperty("bar");
+session = factory.openSession();
+Transaction t = session.beginTransaction();
+session.saveOrUpdate(foo); // Use merge() if "foo" might have been loaded
already
+t.commit();
+session.close();]]></programlisting>
+
+ <para>
+ De nuevo, Hibernate chequeará las versiones de instancia
durante la limpieza (flush),
+ lanzando una excepción si ocurrieron actualizaciones en
conflicto.
+ </para>
+
+ <para>
+ Puedes también llamar a <literal>lock()</literal>
en vez de <literal>update()</literal>
+ y usar <literal>LockMode.READ</literal> (realizando un
chequeo de versión, puenteando
+ todos los cachés) si estás seguro que el objeto no
ha sido modificado.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-optimistic-customizing">
+ <title>Personalizando el versionado
automático</title>
+
+ <para>
+ Puedes deshabilitar el incremento de versión
automático de Hibernate para propiedades en particular
+ y colecciones estableciendo el atributo de mapeo
<literal>optimistic-lock</literal> a
+ <literal>false</literal>. Hibernate entonces no
incrementará ya más las versiones si la propiedad está
+ sucia.
+ </para>
+
+ <para>
+ Los esquemas de base de datos heredados son frecuentemente
estáticos y no pueden ser modificados.
+ U otras aplicaciones podrían también acceder la
misma base de datos y no saber cómo manejar los números
+ de versión ni incluso timestamps. En ambos casos, el
versionado no puede confiarse a una columna en
+ particular en una tabla. Para forzar un chequeo de versiones sin un mapeo
de propiedad de versión o
+ timestamp, con una comparación del estado de todos los campos
en una fila, activa
+ <literal>optimistic-lock="all"</literal> en el
mapeo de <literal><class></literal>.
+ Nota que esto conceptualmente funciona solamente si Hibernate puede
comparar el estado viejo y nuevo,
+ es decir, si usas una sola <literal>Session</literal> larga y
no
+ sesión-por-petición-con-instancias-separadas.
+ </para>
+
+ <para>
+ A veces las modificaciones concurrentes pueden permitirse, en cuanto los
cambios que hayan sido
+ hechos no se traslapen. Si estableces
<literal>optimistic-lock="dirty"</literal> al mapear la
+ <literal><class></literal>, Hibernate
sólo comparará los campos sucios durante la limpieza.
+ </para>
+
+ <para>
+ En ambos casos, con columnas de versión/timestamp dedicadas o
con comparación de campos
+ completa/sucios, Hibernate usa una sola sentencia
<literal>UPDATE</literal>
+ (con una cláusula <literal>WHERE</literal>
apropiada) por entidad para ejecutar el chequeo
+ de versiones y actualizar la información. Si usas persistencia
transitiva para la re-unión
+ en cascada de entidades asociadas, Hibernate podría ejecutar
actualizaciones innecesarias.
+ Esto usualmente no es un problema, pero podrían ejecutarse
disparadores (triggers)
+ <emphasis>on update</emphasis> en la base de datos incluso
cuando no se haya hecho ningún cambio
+ a las instancias separadas. Puedes personalizar este comportamiento
estableciendo
+ <literal>select-before-update="true"</literal> en
el mapeo de <literal><class></literal>,
+ forzando a Hibernate a <literal>SELECT</literal> la instancia
para asegurar que las actualizaciones
+ realmente ocurran, antes de actualizar la fila.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="transactions-locking">
+ <title>Bloqueo pesimista</title>
+
+ <para>
+ No se pretende que los usuarios gasten mucho tiempo preocupándose
de las estrategias de bloqueo.
+ Usualmente es suficiente con especificar un nivel de aislamiento para las
conexiones JDBC y entonces
+ simplemente dejar que la base de datos haga todo el trabajo. Sin embargo, los
usuarios avanzados pueden
+ a veces obtener bloqueos exclusivos pesimistas, o reobtener bloqueos al
comienzo de una nueva
+ transacción.
+ </para>
+
+ <para>
+ ¡Hibernate siempre usará el mecanismo de bloqueo de la
base de datos, nunca bloqueo
+ de objetos en memoria!
+ </para>
+
+ <para>
+ La clase <literal>LockMode</literal> define los diferentes
niveles de bloqueo que pueden ser adquiridos
+ por Hibernate. Un bloqueo se obtiene por los siguientes mecanismos:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>LockMode.WRITE</literal> se adquiere
automáticamente cuando Hibernate actualiza o
+ inserta una fila.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.UPGRADE</literal> puede ser adquirido
bajo petición explícita del usuario
+ usando <literal>SELECT ... FOR UPDATE</literal> en base
de datos que soporten esa sintáxis.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.UPGRADE_NOWAIT</literal> puede ser
adquirido bajo petición explícita del usuario
+ usando un <literal>SELECT ... FOR UPDATE NOWAIT</literal>
bajo Oracle.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.READ</literal> es adquirido
automáticamente cuando Hibernate lee datos
+ bajo los niveles de aislamiento Repeatable Read o Serializable. Puede
ser readquirido por
+ pedido explícito del usuario.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.NONE</literal> representa la ausencia de un
bloqueo. Todos los objetos se pasan
+ a este modo de bloqueo al final de una
<literal>Transaction</literal>. Los objetos asociados con una
+ sesión vía una llamada a
<literal>update()</literal> o <literal>saveOrUpdate()</literal>
también
+ comienzan en este modo de bloqueo.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ La "petición explícita del usuario" se
expresa en una de las siguientes formas:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Una llamada a <literal>Session.load()</literal>,
especificando un <literal>LockMode</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Una llamada a <literal>Session.lock()</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Una llamada a <literal>Query.setLockMode()</literal>.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Si se llama a <literal>Session.load()</literal> con
<literal>UPGRADE</literal> o
+ <literal>UPGRADE_NOWAIT</literal>, y el objeto pedido no ha sido
aún cargado por la sesión, el objeto es
+ cargado usando <literal>SELECT ... FOR UPDATE</literal>. Si se
llama a <literal>load()</literal> para
+ un objeto que ya esté cargado con un bloqueo menos restrictivo que
el pedido, Hibernate llama a
+ <literal>lock()</literal> para ese objeto.
+ </para>
+
+ <para>
+ <literal>Session.lock()</literal> realiza un chequeo de
número de versión si el modo de bloqueo especificado
+ es <literal>READ</literal>,
<literal>UPGRADE</literal> o <literal>UPGRADE_NOWAIT</literal>.
(En el caso de
+ <literal>UPGRADE</literal> o
<literal>UPGRADE_NOWAIT</literal>, se usa
+ <literal>SELECT ... FOR UPDATE</literal>.)
+ </para>
+
+ <para>
+ Si la base de datos no soporta el modo de bloqueo solicitado, Hibernate
usará un modo alternativo
+ apropiado (en vez de lanzar una excepción). Esto asegura que las
aplicaciones serán portables.
+ </para>
+
+ </sect1>
+
+</chapter>
+
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/content/tutorial.xml (from
rev 12794, core/trunk/documentation/manual/es-ES/src/main/docbook/modules/tutorial.xml)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/content/tutorial.xml
(rev 0)
+++ core/trunk/documentation/manual/es-ES/src/main/docbook/content/tutorial.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,1270 @@
+<chapter id="tutorial">
+ <title>Introducción a Hibernate</title>
+
+ <sect1 id="tutorial-intro">
+ <title>Prefacio</title>
+
+ <para>
+ Este capítulo es un tutorial introductorio de Hibernate.
Comenzamos con
+ una aplicación simple de línea de comandos usando un
base de datos
+ en-memoria y desarrollándola en fácil para entender los
pasos.
+ </para>
+
+ <para>
+ Este tutorial está concebido para usuarios nuevos de Hibernate
pero
+ requiere conocimiento en Java y SQL. Está basado en un tutorial
de
+ Michael Gloegl. Las bibliotecas de terceros que mencionamos son para JDK 1.4
+ y 5.0. Podrías necesitar otras para JDK 1.3.
+ </para>
+
+ </sect1>
+
+ <sect1 id="tutorial-firstapp">
+ <title>Parte 1 - La primera Aplicación Hibernate</title>
+
+ <para>
+ Primero, crearemos una aplicación simple de Hibenate basada en
consola.
+ Usamos usamos una base de datos en-memoria (HSQL DB), de modo que no
necesitamos
+ instalar ningún servidor de base de datos.
+ </para>
+
+ <para>
+ Asumamos que necesitamos una aplicación pequeña de base
de datos que
+ pueda almacenar eventos que queremos atender, e información acerca
de los
+ hostales de estos eventos.
+ </para>
+
+ <para>
+ La primera cosa que hacemos, es armar nuestro directorio de desarrollo y
poner
+ en él todas las bibliotecas Java que necesitamos. Descarga la
distribució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ínimo de bibliotecas requeridas para
Hibernate (observa que
+ también hemos copiado hibernate3.jar, el fichero principal). Ver
el fichero
+ <literal>README.txt</literal> en el directorio
<literal>lib/</literal> de la distribución
+ de Hibernate para más informació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ándar de JavaBean
+ para métodos getter y setter de propiedad, así como
visibilidad privada
+ para los campos. Esto es un diseño recomendado, aunque no
requerido. Hibernate
+ también puede acceder a los campos directamente; el beneficio
de los métodos
+ de acceso es la robustez para la refactorización.
+ </para>
+
+ <para>
+ La propiedad <literal>id</literal> tiene un valor
único de identificador para
+ un evento en particular. Todas las clase de entidad persistentes (
también hay
+ clases dependientes menos importantes) necesitarán una
propiedad identificadora
+ similar si queremos usar el conjunto completo de funcionalidades de
Hibernate. De hecho,
+ la mayorí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ón. Sin embargo, usualmente no manipulamos la
identidad de un objeto, por
+ lo tanto el método setter debe ser privado. Sólo
Hibernate asignará
+ identificadores cuando un objeto sea salvado. Puedes ver que Hibernate
puede acceder a
+ métodos de acceso públicos, privados y protegidos,
tanto como directamente a
+ campos (públicos, privados y protegidos). La
elección está en ti,
+ y puedes ajustarla a tu diseño de aplicación.
+ </para>
+
+ <para>
+ El constructor sin argumentos es un requerimiento para todas las clases
persistentes.
+ Hibernate tiene que crear objetos para ti, usando reflección
Java. El constructor
+ puede ser privado, sin embargo, la visibilidad de paquete es requerida
para la generación
+ de proxies en tiempo de ejecución y la recuperación
de datos sin
+ instrumentación del bytecode.
+ </para>
+
+ <para>
+ Coloca este fichero de có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ó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ómo cargar y almacenar objetos de
la
+ clase persistente. Aquí es donde el fichero de mapeo de
Hibernate
+ entra en juego. El fichero de mapeo le dice a Hibernate a qué
tabla en
+ la base de datos tiene que acceder, y qué columnas en esta
tabla debe usar.
+ </para>
+
+ <para>
+ La estructura bá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ón de los elementos y atributos XML de mapeo en
tu
+ editor o IDE. Debes también abrir el fichero DTD en tu editor
de
+ texto. Es la forma más fácil para tener un panorama
de todos
+ los elementos y atributos y ver los valores por defectos, así
como
+ algunos comentarios. Nota que Hibernate no cargará el fichero
DTD de
+ la web, sino que primero buscará en el classpath de la
aplicación.
+ El fichero DTD está incluído en
<literal>hibernate3.jar</literal>
+ así como también en el directorio
<literal>src/</literal> de la
+ distribución de Hibernate.
+ </para>
+
+ <para>
+ Omitiremos la declaración de DTD en futuros ejemplos para
acortar
+ el có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ía haber má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ó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 único a la clave
primaria
+ de la tabla. Además, como no queremos cuidar del manejo de
este identificador,
+ configuramos la estrategia de generació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ón de la propiedad
+ identificadora, <literal>name="id"</literal>
declara el nombre de la
+ propiedad Java. Hibernate usará los métodos getter
y setter
+ para acceder a la propiedad. El attributo de columna dice a Hibernate
cuál
+ columna de la tabla <literal>EVENTS</literal> usamos para
esta clave primaria.
+ El elemento anidado <literal>generator</literal> especifica
la estrategia de
+ generación de identificadores, en este caso usamos
<literal>increment</literal>,
+ que es un método muy simple de incremento de número
en-memoria
+ útil mayormente para testeo (y tutoriales). Hibernate
también
+ soporta identificadores generados por base de datos, globalmente
únicos,
+ así como también asignados por
aplicación (o cualquier
+ estrategia para la que hayas escrito una extensión).
+ </para>
+
+ <para>
+ Finalmente incluí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áles métodos
+ getter y setter usar.
+ </para>
+
+ <para>
+ ¿Por qué 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ía de las bases de datos, así que mejor la
mapeamos
+ a un nombre diferente.
+ </para>
+
+ <para>
+ La pró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ías esperar, tipos de
datos Java.
+ Tampoco son tipos de base de datos SQL. Estos tipos son los llamados
así
+ <emphasis>Tipos de mapeo de Hibernate</emphasis>,
convertidores que pueden
+ traducir de tipos Java a SQL y vice versa. De nuevo, Hibernate
intentará
+ determinar la conversió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ón automática (usando reflecció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á
+ a una columna <literal>date</literal>,
<literal>timestamp</literal> o
+ <literal>time</literal>. Declaramos que queremos preservar la
informació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óximo al fichero de có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ó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ón principal de Hibernate.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-configuration">
+ <title>Configuració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ó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íz del directorio de
+ desarrollo. Allí es donde HSQL DB almacenará sus
ficheros de datos.
+ </para>
+
+ <para>
+ Hibernate es la capa en tu aplicación que se conecta a esta
base de datos,
+ de modo que necesita información de conexión. Las
conexiones se hacen
+ a través de un pool de conexiones JDBC, que tambén
tenemos que configurar.
+ La distribución de Hibernate contiene muchas herramientas de
pooling de conexiones
+ JDBC de có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ón.
+ </para>
+
+ <para>
+ Para la configuración de Hibernate, podemos usar un fichero
+ <literal>hibernate.properties</literal> simple, un fichero
<literal>hibernate.cfg.xml</literal>
+ ligeramente más sofisticado, o incluso una
configuración completamente
+ programática. La mayoría de los usuarios prefieren
el fichero de
+ configuració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ón XML usa un DTD diferente.
+ Configuramos la <literal>SessionFactory</literal> de
Hibernate, una
+ fábrica global responsable de una base de datos en
particular.
+ Si tienes varias bases de datos, usa varias configuraciones
+ <literal><session-factory></literal> ,
usualmente en varios
+ ficheros de configuración (para un arranque más
fácil).
+ </para>
+
+ <para>
+ Los primeros cuatro elementos <literal>property</literal>
contienen la configuración
+ necesaria para la conexión JDBC. El elemento de dialecto
<literal>property</literal>
+ especifica la variante de SQL en particular que genera Hibernate. La
opción
+ <literal>hbm2ddl.auto</literal> activa la
generación automática de esquemas
+ de base de datos, directamente en la base de datos. Esto, por supuesto,
puede desactivarse
+ (quitando la opció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ódigo fuente, de
modo que
+ termine ubicado en la raiíz del classpath. Hibernate busca
automáticamente
+ un fichero llamado <literal>hibernate.cfg.xml</literal> en la
raí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ás tener Ant
instalado.
+ Obténlo de <ulink
url="http://ant.apache.org/bindownload.cgi">Página
+ de descarga de Ant</ulink>. No se cubrirá
aquí cómo instalar Ant.
+ Por favor refiérete al <ulink
url="http://ant.apache.org/manual/index.html">
+ Manual de Ant</ulink>. Después que hayas instalado Ant,
podemos comenzar a
+ crear el buildfile. Será llamado
<literal>build.xml</literal> y colocado
+ directamente en el directorio de desarrollo.
+ </para>
+
+ <note>
+ <title>Reparar Ant</title>
+ <para>
+ Observa que la distribución de Ant está 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ó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ón bá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á a Ant que agregue todos los ficheros en el
directorio lib que terminen con
+ <literal>.jar</literal> al classpath usado para la
compilación. También copiará
+ todos los ficheros que no sean código Java al directorio
objetivo, por ejemplo,
+ ficheros de configuració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ón de
algún
+ código de infraestructura. Tenemos que arrancar Hibernate.
Este
+ arranque incluye construir un objeto
<literal>SessionFactory</literal> global
+ y almacenarlo en algún sitio de fácil acceso en el
código
+ de aplicació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í llamado patrón
<emphasis>Sesión de Hebra Local
+ (ThreadLocal Session)</emphasis> es útil
aquí; mantenemos la unidad
+ de trabajo actual asociada a la hebra actual. Echemos una mirada a la
implementació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 ólo produce la
<literal>SessionFactory</literal> global en
+ su inicializador static (llamado sólo una vez por la JVM al
cargar la clase),
+ sino que también tiene una variable
<literal>ThreadLocal</literal> para
+ tener la <literal>Session</literal> para la hebra actual. No
importa cuándo
+ llames a <literal>HibernateUtil.currentSession()</literal>,
siempre devolverá
+ 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úrate de entender el concepto Java de una variable local a
una hebra antes
+ de usar esta ayuda. Una clase
<literal>HibernateUtil</literal> más potente puede
+ encontrarse en <literal>CaveatEmptor</literal>,
http://caveatemptor.hibernate.org/,
+ así como en el libro "Java Persistence with
Hibernate". Observa que esta clase no es necesaria
+ si despliegas Hibernate en un servidor de aplicaciones J2EE: una
<literal>Session</literal>
+ será automáticamente ligada a la
transacción JTA actual, y puedes
+ buscar la <literal>SessionFactory</literal> a
través de JNDI. Si usas JBoss AS,
+ Hibernate puede ser desplegado como un servicio de sistema manejado y
automáticamente
+ ligará 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én debe compilar sin problemas. Finalmente
necesitamos configurar
+ un sistema de logging (registro). Hibernate usa commons logging y te deja
la elección
+ entre Log4J y logging de JDK 1.4. La mayoría de los
desarrolladores prefieren
+ Log4J: copia <literal>log4j.properties</literal> de la
distribución de Hibernate
+ (está 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ón de
+ ejemplo y cambia los ajustes si te gusta tener una salida más
verborrágica.
+ Por defecto, sólo se muestra el mensaje de arranque de
Hibernate en la salida.
+ </para>
+
+ <para>
+ La infraestructura del tutorial está completa, y estamos
listos para hacer
+ algú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é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í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ó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ía sorprenderte
+ que tengamos una API adicional,
<literal>Transaction</literal>. Esto implica que una unidad
+ de trabajo puede ser "más larga" que una sola
transacción de base de datos;
+ imagina una unidad de trabajo que se abarca varios ciclos
petición/respuesta HTTP
+ (por ejemplo, un diálogo asistente) en una
aplicación web. Separar las
+ transacciones de base de datos de "las unidades de trabajo de la
aplicación desde
+ el punto de vista del usuario" es uno de los conceptos
básicos de diseño de
+ Hibernate. Llamamos una unidad de trabajo larga
<emphasis>Transacción de
+ Aplicación</emphasis>, usualmente encapsulando varias
transacciones de base de
+ datos má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é es lo que hacen
<literal>Transaction.begin()</literal> y
<literal>commit()</literal>?
+ ¿Dónde está 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ón de base de datos por ti mismo (por ejemplo,
llamando a
+ <literal>session.connection.commit()</literal>),
ligarías el código a un entorno
+ de despliegue particular, en este JDBC directo no manejado. Estableciendo
la fábrica
+ de <literal>Transaction</literal> en tu
configuración de Hibernate puedes desplegar
+ tu capa de persistencia en cualquier sitio. Echa una mirada al <xref
linkend="transactions"/>
+ para más información sobre manejo y
demarcación de transacciones.
+ Hemos saltado tambié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ó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ínea de
+ comandos al llamar al objetivo:
+ </para>
+
+ <programlisting><![CDATA[C:\hibernateTutorial\>ant run
-Daction=store]]></programlisting>
+
+ <para>
+ Debes ver, después de la compilación, a Hibernate
arrancando y, dependiendo
+ de tu configuración mucha salida de registro (log). Al final
encontrarás
+ la siguiente lí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á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éramos listar acontecimientos almacenados
también,
+ así que agregamos una opción al mé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én un nuevo mé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í 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á el SQL
apropiado, lo enviará
+ a la base de datosy poblará los objetos
<literal>Event</literal> con datos.
+ Puedes, por supuesto, crear consultas má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á
+ vacío. La razon de esto es la opción
<literal>hbm2ddl.auto</literal>
+ en la configuración de Hibernate: Hibernate
recreará la base de datos
+ en cada ejecución. Deshabilítala quitando la
opción, y verás
+ resultados en tu listado después que llames a la
acción <literal>store</literal>
+ unas cuantas veces. La generación y exportación de
esquema es ú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ó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ón de
Hibernate:
+ </para>
+
+ <programlisting><![CDATA[ <mapping
resource="Event.hbm.xml"/>
+ <mapping resource="Person.hbm.xml"/>
+]]></programlisting>
+
+ <para>
+ Crearemos ahora una asociación entre estas dos entidades.
Obviamente,
+ las personas pueden participar en eventos, y los eventos tienen
participantes.
+ Las cuestiones de diseño con que tenemos que tratar son:
direccionalidad,
+ multiplicidad y comportamiento de colección.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-unidirset">
+ <title>Una asociación unidireccional basada en
Set</title>
+
+ <para>
+ Agregaremos una colecció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ícita, llamando a
<literal>aPerson.getEvents()</literal>.
+ Usamos una colección Java, un
<literal>Set</literal>, porque la colección no
+ contendrá elementos duplicados y el ordenamiento no nos es
relevante.
+ </para>
+
+ <para>
+ Hasta ahora hemos diseñado asociaciones unidireccionales
multivaluadas, implementadas con un
+ <literal>Set</literal>. Escribamos el có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ón, piensa sobre el otro lado.
Claramente, podemos
+ mantener esto solamente unidireccional. O podemos crear otra
colección en el
+ <literal>Event</literal>, si queremos ser capaces de
navegarlos bidireccionalmente;
+ por ejemplo, <literal>anEvent.getParticipants()</literal>.
Esta es una elección
+ de diseño que recae en ti, pero lo que está claro
de esta discusión
+ es la multiplicidad de la asociación: "multi"
valuada a ambos lados, llamamos a esto
+ una asociació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ón, siendo el
más común
+ un <literal><set></literal>. Para una
asociación muchos-a-muchos (o relación
+ de entidad <emphasis>n:m</emphasis>), se necesita una tabla
de asociació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ón, para el lado de la persona, se define con el
elemento
+ <literal><key></literal>. El nombre de columna
para el lado del evento se define con el atributo
+ <literal>column</literal> del
<literal><many-to-many></literal>. También tienes que
decirle
+ a Hibernate la clase de los objetos en tu colección (correcto:
la clase del otro lado de la
+ colecció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ón</title>
+
+ <para>
+ Traigamos alguna gente y eventos juntos en un nuevo mé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és de cargar una <literal>Person</literal> y
un <literal>Event</literal>, simplemente
+ modifica la colección usando sus métodos normales.
Como puedes ver, no hay una llamada
+ explícita a <literal>update()</literal> o
<literal>save()</literal>. Hibernate detecta
+ automáticamente que la colección ha sido modificada
y necesita ser salvada. Esto
+ es llamado <emphasis>chequeo sucio automótico (automatic
dirty checking)</emphasis>, y
+ también puedes intentarlo modificando el nombre de la
propiedad de fecha de cualquiera de tus
+ objetos. Mientras esté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ás. El proceso de sincronización
del estado de memoria con la base
+ de datos, usualmente sólo al final de una unidad de trabajo,
+ es llamado <emphasis>limpieza (flushing)</emphasis>.
+ </para>
+
+ <para>
+ Podrí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á en estado
+ persistente (si antes era persistente llamamos a este estado
<emphasis>separado (detached)
+ </emphasis>). En código (no muy realista), esto se
verí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ías
+ decir que la liga a una nueva unidad de trabajo, de modo que cualquier
modificación que
+ le hagas mientras esté separado puede ser salvada a base de
datos.
+ </para>
+
+ <para>
+ Bueno, esto no es muy usado en nuestra situación actual, pero
es un concepto
+ importante que puedes diseñar en tu propia
aplicación. Por ahora, completa
+ este ejercicio agregando una nueva acción al método
main de
+ <literal>EventManager</literal> y llámala desde la
línea de comandos.
+ Si necesitas los identificadores de una persona o evento, el
método
+ <literal>save()</literal> los devuelve.
+ </para>
+
+ <para>
+ Esto fue un ejemplo de una asociación entre dos clases
igualmente importantes, dos entidades.
+ Como se ha mencionado anteriormente, hay otras clases y tipos en un
modelo típico,
+ usualmente "menos importantes". Algunos ya los
habrá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ólo pueden encontrarse en el JDK (de
hecho, en una aplicación
+ Hibernate todas las clases del JDK son consideradas tipos de valor), sino
que además puedes
+ escribir por ti mismo clases dependientes, por ejemplo,
<literal>Address</literal> o
+ <literal>MonetaryAmount</literal>.
+ </para>
+
+ <para>
+ También puedes diseñar una colección de
tipos de valor. Esto es conceptualmente
+ muy diferente de una colección de referencias a otras
entidades, pero se ve casi lo mismo en
+ Java.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-valuecollections">
+ <title>Colección de valores</title>
+
+ <para>
+ Agregamos una colecció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ó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ón no contiene referencias a otra
entidad, sino una colección
+ de elementos de tipo <literal>String</literal> (el nombre en
minúsculas te dice que es un
+ tipo/conversor de mapeo de Hibernate). Una vez más, el
atributo <literal>table</literal> del
+ elemento <literal>set</literal> determina el nombre de la
tabla para la colección. El elemento
+ <literal>key</literal> define el nombre de la columna clave
foránea en la tabla de colección.
+ El atributo <literal>column</literal> en el elemento
<literal>element</literal> define el nombre de
+ columna donde realmente será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ón es
de hecho una clave
+ compuesta, usando ambas columnas. Esto implica también que no
pueden haber
+ direcciones de email duplicadas por persona, que es exactamente la
semántica
+ que necesitamos para un conjunto en Java.
+ </para>
+
+ <para>
+ Puedes ahora intentar y agregar elementos a esta colección, al
igual que
+ hicimos antes enlazando personas y eventos. Es el mismo código
en Java.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-bidirectional">
+ <title>Asociaciones bidireccionales</title>
+
+ <para>
+ A continuacion vamos a mapear una asociación bidireccional,
haciendo que la
+ asociación entre persona y evento funcione desde ambos lados
en Java. Por supuesto,
+ el esquema de base de datos no cambia; todavía necesitamos
multiplicidad muchos-a-muchos.
+ Una base de datos relacional es más flexible que un lenguaje
de programación
+ de red, así que no necesita nada parecido a una
dirección de navegación;
+ los datos pueden ser vistos y recuperados en cualquier forma posible.
+ </para>
+
+ <para>
+ Primero agrega una colecció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ón tambié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í la
+ adición más importante es el atributo
<literal>inverse="true"</literal> en el
+ elemento <literal>set</literal> del mapeo de
colecció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ón sobre el enlace entre
las dos. Esto será mucho
+ más fácil de entender una vez que veas
có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ántica
normal de Java. ¿Có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ó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ó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é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étodos get y set para esta colección
son ahora protegidos. Esto le
+ permite a clases en el mismo paquete y a subclases acceder aún
a los métodos, pero
+ previene a cualquier otro de ensuciarse con la colección
directamente (bueno, casi).
+ Probablemente debas hacer lo mismo con la colección al otro
lado.
+ </para>
+
+ <para>
+ Y ¿qué del atributo de mapeo
<literal>inverse</literal>? Para ti, y para Java, un enlace
+ bidireccional es simplemente cuestión de establecer
correctamente las referencias a ambos
+ lados. Hibernate, sin embargo, no tiene suficiente información
para arreglar correctamente
+ sentencias <literal>INSERT</literal> y
<literal>UPDATE</literal> de SQL (para evitar violación
+ de restricciones), y necesita alguna ayuda para manejar asociaciones
bidireccionales apropiadamente.
+ El hacer un lado de la asociació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ó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ón
+ uno-a-muchos debe ser el lado-de-muchos. En una asociació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ó los fundamentos de escribir una simple
aplicación independiente
+ de Hibernate.
+ </para>
+
+ <para>
+ Si ya te sientes confidente con Hibernate, continúa navegando a
través de la
+ tabla de contenidos de la documentación de referencia para los
temas que encuentres
+ interesantes. Los más consultados son procesamiento transaccional
(<xref linkend="transactions"/>),
+ rendimiento de recuperació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ás
(especializados) tutoriales.
+ </para>
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/content/xml.xml (from rev
12794, core/trunk/documentation/manual/es-ES/src/main/docbook/modules/xml.xml)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/content/xml.xml
(rev 0)
+++ core/trunk/documentation/manual/es-ES/src/main/docbook/content/xml.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,282 @@
+<chapter id="xml">
+ <title>Mapeo XML</title>
+
+ <para><emphasis>
+ Nota que esta es una funcionalidad experimental en Hibernate 3.0 y está
+ bajo un desarrollo extremadamente activo.
+ </emphasis></para>
+
+ <sect1 id="xml-intro" revision="1">
+ <title>Trabajando con datos XML</title>
+
+ <para>
+ Hibernate te permite trabajar con datos XML persistentes en casi la misma
forma
+ que trabajas con POJOs persistentes. Un árbol XML analizado (parsed) puede
ser
+ pensado como sólo otra forma de representar los datos relacionales a nivel de
objetos,
+ en vez de POJOs.
+ </para>
+
+ <para>
+ Hibernate soporta dom4j como API para manipular árboles XML. Puedes escribir
+ consultas que traigan árboles dom4j de la base de datos y tener cualquier
modificación
+ que hagas al árbol sincronizada automáticamente a la base de datos. Puedes
incluso tomar
+ un documento XML, analizarlo usando dom4j, y escribirlo a la base de datos
con cualquiera
+ de las operaciones básicas de Hibernate: <literal>persist(),
saveOrUpdate(), merge(),
+ delete(), replicate()</literal> (la fusión no está aún soportada).
+ </para>
+
+ <para>
+ Esta funcionalidad tiene muchas aplicaciones incluyendo la
importación/exportación de datos,
+ externalización de datos de entidad vía JMS o SOAP y reportes basados en
XSLT.
+ </para>
+
+ <para>
+ Un solo mapeo puede ser usado para mapear simultáneamente las propiedades de
una clase y los nodos de un
+ documento XML a la base de datos, o, si no hay ninguna clase a mapear, puede
ser usado para mapear sólo
+ el XML.
+ </para>
+
+ <sect2 id="xml-intro-mapping">
+ <title>Especificando los mapeos de XML y de clase juntos</title>
+
+ <para>
+ He aquí un ejemplo de mapear un POJO y XML simultáneamente:
+ </para>
+
+ <programlisting><![CDATA[<class name="Account"
+ table="ACCOUNTS"
+ node="account">
+
+ <id name="accountId"
+ column="ACCOUNT_ID"
+ node="@id"/>
+
+ <many-to-one name="customer"
+ column="CUSTOMER_ID"
+ node="customer/@id"
+ embed-xml="false"/>
+
+ <property name="balance"
+ column="BALANCE"
+ node="balance"/>
+
+ ...
+
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="xml-onlyxml">
+ <title>Especificando sólo un mapeo XML</title>
+
+ <para>
+ He aquí un ejemplo donde no hay ninguna clase POJO:
+ </para>
+
+ <programlisting><![CDATA[<class entity-name="Account"
+ table="ACCOUNTS"
+ node="account">
+
+ <id name="id"
+ column="ACCOUNT_ID"
+ node="@id"
+ type="string"/>
+
+ <many-to-one name="customerId"
+ column="CUSTOMER_ID"
+ node="customer/@id"
+ embed-xml="false"
+ entity-name="Customer"/>
+
+ <property name="balance"
+ column="BALANCE"
+ node="balance"
+ type="big_decimal"/>
+
+ ...
+
+</class>]]></programlisting>
+
+ <para>
+ Este mapeo te permite acceder a los datos como un árbol dom4j, o como un
grafo de pares nombre/valor de
+ propiedad (<literal>Map</literal>s de Java). Los nombres de
propiedades son construcciones puramente
+ lógicas a las que se puede hacer referencia en consultas HQL.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="xml-mapping" revision="1">
+ <title>Mapeo de metadatos XML</title>
+
+ <para>
+ Muchos elementos de mapeo de Hibernate aceptan el atributo
<literal>node</literal>. Esto te permite espcificar
+ el nombre de un atributo o elemento XML que contenga los datos de la
propiedad o entidad. El formato del
+ atributo <literal>node</literal> debe ser uno de los siguientes:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>"element-name"</literal> - mapea
al elemento XML mencionado</para>
+ </listitem>
+ <listitem>
+ <para><literal>"@attribute-name"</literal> -
mapea al atributo XML mencionado</para>
+ </listitem>
+ <listitem>
+ <para><literal>"."</literal> - mapea al elemento
padre</para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>"element-name/@attribute-name"</literal> -
+ mapea al atributo mencionado del elemento mencionado
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Para las colecciones y asociaciones monovaluadas, existe un atributo
adicional <literal>embed-xml</literal>.
+ Si <literal>embed-xml="true"</literal>, que es el valor
por defecto, el árbol XML para la entidad
+ asociada (o colección de tipo de valor) será embebida directamente en el
árbol XML para la entidad que
+ posee la asociación. En otro caso, si
<literal>embed-xml="false"</literal>, sólo el valor identificador
+ referenciado aparecerá en el XML para asociaciones de punto único y para las
colecciones simplemente
+ no aparecerá en absoluto.
+ </para>
+
+ <para>
+ ¡Debes ser cuidadoso de no dejar
<literal>embed-xml="true"</literal> para demasiadas asociaciones,
+ ya que XML no trata bien la circularidad!
+ </para>
+
+ <programlisting><![CDATA[<class name="Customer"
+ table="CUSTOMER"
+ node="customer">
+
+ <id name="id"
+ column="CUST_ID"
+ node="@id"/>
+
+ <map name="accounts"
+ node="."
+ embed-xml="true">
+ <key column="CUSTOMER_ID"
+ not-null="true"/>
+ <map-key column="SHORT_DESC"
+ node="@short-desc"
+ type="string"/>
+ <one-to-many entity-name="Account"
+ embed-xml="false"
+ node="account"/>
+ </map>
+
+ <component name="name"
+ node="name">
+ <property name="firstName"
+ node="first-name"/>
+ <property name="initial"
+ node="initial"/>
+ <property name="lastName"
+ node="last-name"/>
+ </component>
+
+ ...
+
+</class>]]></programlisting>
+
+ <para>
+ en este caso, hemos decidido embeber la colección de ids de cuenta, pero no
los datos reales de cuenta.
+ La siguiente consulta HQL:
+ </para>
+
+ <programlisting><![CDATA[from Customer c left join fetch c.accounts
where c.lastName like :lastName]]></programlisting>
+
+ <para>
+ devolvería conjuntos de datos como estos:
+ </para>
+
+ <programlisting><![CDATA[<customer id="123456789">
+ <account short-desc="Savings">987632567</account>
+ <account short-desc="Credit Card">985612323</account>
+ <name>
+ <first-name>Gavin</first-name>
+ <initial>A</initial>
+ <last-name>King</last-name>
+ </name>
+ ...
+</customer>]]></programlisting>
+
+ <para>
+ Si estableces <literal>embed-xml="true"</literal> en el
mapeo <literal><one-to-many></literal>, los datos
+ podrían verse así:
+ </para>
+
+ <programlisting><![CDATA[<customer id="123456789">
+ <account id="987632567" short-desc="Savings">
+ <customer id="123456789"/>
+ <balance>100.29</balance>
+ </account>
+ <account id="985612323" short-desc="Credit Card">
+ <customer id="123456789"/>
+ <balance>-2370.34</balance>
+ </account>
+ <name>
+ <first-name>Gavin</first-name>
+ <initial>A</initial>
+ <last-name>King</last-name>
+ </name>
+ ...
+</customer>]]></programlisting>
+
+ </sect1>
+
+
+ <sect1 id="xml-manipulation" revision="1">
+ <title>Manipulando datos XML</title>
+
+ <para>
+ Vamos a releer y actualizar documentos XML en la aplicación. Hacemos esto
obteniendo una sesión dom4j:
+ </para>
+
+ <programlisting><![CDATA[Document doc = ....;
+
+Session session = factory.openSession();
+Session dom4jSession = session.getSession(EntityMode.DOM4J);
+Transaction tx = session.beginTransaction();
+
+List results = dom4jSession
+ .createQuery("from Customer c left join fetch c.accounts where c.lastName like
:lastName")
+ .list();
+for ( int i=0; i<results.size(); i++ ) {
+ //add the customer data to the XML document
+ Element customer = (Element) results.get(i);
+ doc.add(customer);
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ <programlisting><![CDATA[Session session = factory.openSession();
+Session dom4jSession = session.getSession(EntityMode.DOM4J);
+Transaction tx = session.beginTransaction();
+
+Element cust = (Element) dom4jSession.get("Customer", customerId);
+for ( int i=0; i<results.size(); i++ ) {
+ Element customer = (Element) results.get(i);
+ //change the customer name in the XML and database
+ Element name = customer.element("name");
+ name.element("first-name").setText(firstName);
+ name.element("initial").setText(initial);
+ name.element("last-name").setText(lastName);
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ Es extremadamente útil combinar esta funcionalidad con la operación
<literal>replicate()</literal>
+ de Hibernate para implementar la importación/exportación de datos basada en
XML.
+ </para>
+
+ </sect1>
+
+</chapter>
+
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/images/AuthorWork.png (from
rev 14074, core/trunk/documentation/manual/fr-FR/src/main/docbook/images/AuthorWork.png)
===================================================================
(Binary files differ)
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/images/AuthorWork.zargo
(from rev 14074,
core/trunk/documentation/manual/fr-FR/src/main/docbook/images/AuthorWork.zargo)
===================================================================
(Binary files differ)
Copied:
core/trunk/documentation/manual/es-ES/src/main/docbook/images/CustomerOrderProduct.png
(from rev 14074,
core/trunk/documentation/manual/fr-FR/src/main/docbook/images/CustomerOrderProduct.png)
===================================================================
(Binary files differ)
Copied:
core/trunk/documentation/manual/es-ES/src/main/docbook/images/CustomerOrderProduct.zargo
(from rev 14074,
core/trunk/documentation/manual/fr-FR/src/main/docbook/images/CustomerOrderProduct.zargo)
===================================================================
(Binary files differ)
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/images/EmployerEmployee.png
(from rev 14074,
core/trunk/documentation/manual/fr-FR/src/main/docbook/images/EmployerEmployee.png)
===================================================================
(Binary files differ)
Copied:
core/trunk/documentation/manual/es-ES/src/main/docbook/images/EmployerEmployee.zargo (from
rev 14074,
core/trunk/documentation/manual/fr-FR/src/main/docbook/images/EmployerEmployee.zargo)
===================================================================
(Binary files differ)
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/images/full_cream.png (from
rev 14074, core/trunk/documentation/manual/fr-FR/src/main/docbook/images/full_cream.png)
===================================================================
(Binary files differ)
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/images/full_cream.svg (from
rev 14074, core/trunk/documentation/manual/fr-FR/src/main/docbook/images/full_cream.svg)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/images/full_cream.svg
(rev 0)
+++
core/trunk/documentation/manual/es-ES/src/main/docbook/images/full_cream.svg 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,429 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
+[
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+
xmlns="http://www.w3.org/2000/svg"
+
xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="354.331"
+ height="336.614"
+ id="svg1">
+ <defs
+ id="defs3">
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop128" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop129" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient130"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ <radialGradient
+ cx="0.5"
+ cy="0.5"
+ fx="0.5"
+ fy="0.5"
+ r="0.5"
+ id="radialGradient131"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ </defs>
+ <g
+ transform="matrix(0.823795,0,0,0.823795,0.120302,5.25349)"
+ style="font-size:12;"
+ id="g659">
+ <rect
+ width="212.257"
+ height="57.2441"
+ x="17.9576"
+ y="100.132"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect137" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ transform="matrix(0.743454,0,0,0.482981,6.46949,52.2178)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect132" />
+ </g>
+ <rect
+ width="325.86"
+ height="63.6537"
+ x="17.4083"
+ y="15.194"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect136" />
+ <rect
+ width="325.86"
+ height="63.6537"
+ x="13.6713"
+ y="12.4966"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect126" />
+ <g
+ transform="matrix(1.14345,0,0,0.729078,-1.67818,105.325)"
+ style="font-size:12;"
+ id="g164">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect138" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect133" />
+ </g>
+ <text
+ x="170.824753"
+ y="58.402939"
+ transform="scale(0.823795,0.823795)"
+
style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text183">
+ <tspan
+ x="170.824997"
+ y="58.402901"
+ id="tspan360">
+Application</tspan>
+ </text>
+ <text
+ x="178.076340"
+ y="364.281433"
+ transform="scale(0.823795,0.823795)"
+
style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text197">
+ <tspan
+ x="178.076004"
+ y="364.281006"
+ id="tspan421">
+Database</tspan>
+ </text>
+ <text
+ x="68.605331"
+ y="138.524582"
+ transform="scale(0.823795,0.823795)"
+
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text216">
+ <tspan
+ x="68.605301"
+ y="138.524994"
+ id="tspan384">
+SessionFactory</tspan>
+ </text>
+ <rect
+ width="67.0014"
+ height="101.35"
+ x="196.927"
+ y="89.2389"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect387" />
+ <rect
+ width="67.0014"
+ height="101.35"
+ x="194.633"
+ y="86.4389"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect388" />
+ <text
+ x="249.108841"
+ y="173.885559"
+ transform="scale(0.823795,0.823795)"
+
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text389">
+ <tspan
+ x="249.108994"
+ y="173.886002"
+ id="tspan392">
+Session</tspan>
+ </text>
+ <rect
+ width="73.0355"
+ height="101.35"
+ x="270.995"
+ y="90.0018"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect395" />
+ <rect
+ width="73.0355"
+ height="101.35"
+ x="267.869"
+ y="87.2018"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect396" />
+ <text
+ x="328.593658"
+ y="174.715622"
+ transform="scale(0.823795,0.823795)"
+
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text397">
+ <tspan
+ x="328.593994"
+ y="174.716003"
+ id="tspan563">
+Transaction</tspan>
+ </text>
+ <g
+ transform="matrix(0.29544,0,0,0.397877,9.70533,103.96)"
+ style="font-size:12;"
+ id="g565">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect566" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect567" />
+ </g>
+ <text
+ x="25.592752"
+ y="204.497803"
+ transform="scale(0.823795,0.823795)"
+
style="font-size:10;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text568">
+ <tspan
+ x="25.592800"
+ y="204.498001"
+ id="tspan662">
+TransactionFactory</tspan>
+ </text>
+ <g
+ transform="matrix(0.298082,0,0,0.397877,99.6898,103.96)"
+ style="font-size:12;"
+ id="g573">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect574" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect575" />
+ </g>
+ <text
+ x="134.030670"
+ y="205.532791"
+ transform="scale(0.823795,0.823795)"
+
style="font-size:10;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text576">
+ <tspan
+ x="134.031006"
+ y="205.533005"
+ id="tspan664">
+ConnectionProvider</tspan>
+ </text>
+ <g
+ transform="matrix(1.14345,0,0,0.729078,-1.67818,38.9539)"
+ style="font-size:12;"
+ id="g587">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect588" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect589" />
+ </g>
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="25.6196"
+ y="206.028"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect594" />
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="24.4229"
+ y="204.135"
+ style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect595" />
+ <text
+ x="85.575645"
+ y="282.300354"
+ transform="scale(0.823795,0.823795)"
+
style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text596">
+ <tspan
+ x="85.575600"
+ y="282.299988"
+ id="tspan607">
+JNDI</tspan>
+ </text>
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="236.937"
+ y="206.791"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect610" />
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="235.741"
+ y="204.898"
+ style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect611" />
+ <text
+ x="342.093201"
+ y="283.226410"
+ transform="scale(0.823795,0.823795)"
+
style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text612">
+ <tspan
+ x="342.092987"
+ y="283.226013"
+ id="tspan621">
+JTA</tspan>
+ </text>
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="130.134"
+ y="206.791"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect616" />
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="128.937"
+ y="204.898"
+ style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect617" />
+ <text
+ x="212.445343"
+ y="283.226410"
+ transform="scale(0.823795,0.823795)"
+
style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text618">
+ <tspan
+ x="212.445007"
+ y="283.226013"
+ id="tspan623">
+JDBC</tspan>
+ </text>
+ <g
+ transform="matrix(0.823795,0,0,0.823795,0.120302,6.19341)"
+ style="font-size:12;"
+ id="g637">
+ <g
+ transform="matrix(0.499515,0,0,0.415467,-0.237339,5.61339)"
+ id="g167">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect134" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect135" />
+ </g>
+ <text
+ x="33.749969"
+ y="50.589706"
+
style="font-size:11;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text188">
+ <tspan
+ x="33.750000"
+ y="50.589699"
+ id="tspan635">
+Transient Objects</tspan>
+ </text>
+ </g>
+ <g
+ transform="matrix(0.823795,0,0,0.823795,0.120302,5.25349)"
+ style="font-size:12;"
+ id="g644">
+ <g
+ transform="matrix(0.297486,0,0,0.516482,230.251,36.9178)"
+ id="g364">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect365" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect366" />
+ </g>
+ <text
+ x="277.123230"
+ y="85.155571"
+
style="font-size:11;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text367">
+ <tspan
+ x="277.122986"
+ y="85.155602"
+ id="tspan631">
+Persistent</tspan>
+ <tspan
+ x="277.122986"
+ y="96.155602"
+ id="tspan633">
+Objects</tspan>
+ </text>
+ </g>
+</svg>
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/images/hibernate_logo_a.png
(from rev 14074,
core/trunk/documentation/manual/fr-FR/src/main/docbook/images/hibernate_logo_a.png)
===================================================================
(Binary files differ)
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/images/lite.png (from rev
14074, core/trunk/documentation/manual/fr-FR/src/main/docbook/images/lite.png)
===================================================================
(Binary files differ)
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/images/lite.svg (from rev
14074, core/trunk/documentation/manual/fr-FR/src/main/docbook/images/lite.svg)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/images/lite.svg
(rev 0)
+++ core/trunk/documentation/manual/es-ES/src/main/docbook/images/lite.svg 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,334 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
+[
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+
xmlns="http://www.w3.org/2000/svg"
+
xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="318.898"
+ height="248.031"
+ id="svg1">
+ <defs
+ id="defs3">
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop128" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop129" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient130"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ <radialGradient
+ cx="0.5"
+ cy="0.5"
+ fx="0.5"
+ fy="0.5"
+ r="0.5"
+ id="radialGradient131"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ </defs>
+ <rect
+ width="291.837"
+ height="57.0074"
+ x="17.3169"
+ y="18.646"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect136" />
+ <rect
+ width="291.837"
+ height="57.0074"
+ x="13.9703"
+ y="16.2302"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect126" />
+ <g
+ transform="matrix(0.326107,0,0,0.765831,9.59261,8.98517)"
+ style="font-size:12;"
+ id="g161">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect137" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect132" />
+ </g>
+ <g
+ transform="matrix(1.02406,0,0,0.652953,0.223384,39.9254)"
+ style="font-size:12;"
+ id="g164">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect138" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect133" />
+ </g>
+ <g
+ transform="matrix(0.449834,0,0,0.338463,-3.15909,9.73319)"
+ style="font-size:12;"
+ id="g167">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect134" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect135" />
+ </g>
+ <text
+ x="302.277679"
+ y="65.943230"
+ transform="scale(0.73778,0.73778)"
+
style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text183">
+ <tspan
+ x="302.277954"
+ y="65.943184"
+ id="tspan360">
+Application</tspan>
+ </text>
+ <text
+ x="36.235924"
+ y="63.796055"
+ transform="scale(0.73778,0.73778)"
+
style="font-size:14;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text188">
+ <tspan
+ x="36.235950"
+ y="63.796051"
+ id="tspan427">
+Transient Objects</tspan>
+ </text>
+ <text
+ x="180.416245"
+ y="290.543701"
+ transform="scale(0.73778,0.73778)"
+
style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text197">
+ <tspan
+ x="180.415939"
+ y="290.543549"
+ id="tspan421">
+Database</tspan>
+ </text>
+ <text
+ x="25.037701"
+ y="179.154755"
+ transform="scale(0.73778,0.73778)"
+
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text216">
+ <tspan
+ x="25.037655"
+ y="179.154648"
+ id="tspan384">
+SessionFactory</tspan>
+ </text>
+ <g
+ transform="matrix(0.252763,0,0,0.765831,109.104,8.98517)"
+ style="font-size:12;"
+ id="g386">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect387" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect388" />
+ </g>
+ <g
+ transform="matrix(0.297394,0,0,0.572692,101.502,21.6359)"
+ style="font-size:12;"
+ id="g364">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect365" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect366" />
+ </g>
+ <text
+ x="202.746506"
+ y="102.992203"
+ transform="scale(0.73778,0.73778)"
+
style="font-size:14;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text367">
+ <tspan
+ x="202.746948"
+ y="102.992249"
+ id="tspan423">
+Persistent</tspan>
+ <tspan
+ x="202.746948"
+ y="116.992355"
+ id="tspan425">
+Objects</tspan>
+ </text>
+ <text
+ x="174.458496"
+ y="180.080795"
+ transform="scale(0.73778,0.73778)"
+
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text389">
+ <tspan
+ x="174.458618"
+ y="180.080338"
+ id="tspan392">
+Session</tspan>
+ </text>
+ <g
+ transform="matrix(0.127369,0,0,0.765831,188.675,8.98517)"
+ style="font-size:12;"
+ id="g394">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect395" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect396" />
+ </g>
+ <text
+ x="260.413269"
+ y="179.154739"
+ transform="scale(0.73778,0.73778)"
+
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text397">
+ <tspan
+ x="260.412964"
+ y="179.154343"
+ id="tspan400">
+JDBC</tspan>
+ </text>
+ <g
+ transform="matrix(0.127369,0,0,0.765831,229.156,8.98517)"
+ style="font-size:12;"
+ id="g405">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect406" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect407" />
+ </g>
+ <text
+ x="320.606903"
+ y="179.154739"
+ transform="scale(0.73778,0.73778)"
+
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text408">
+ <tspan
+ x="320.606964"
+ y="179.154343"
+ id="tspan417">
+JNDI</tspan>
+ </text>
+ <g
+ transform="matrix(0.127369,0,0,0.765831,269.281,8.98517)"
+ style="font-size:12;"
+ id="g411">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect412" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect413" />
+ </g>
+ <text
+ x="377.096313"
+ y="179.154739"
+ transform="scale(0.73778,0.73778)"
+
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text414">
+ <tspan
+ x="377.096008"
+ y="179.154999"
+ id="tspan145">
+JTA</tspan>
+ </text>
+</svg>
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/images/overview.png (from
rev 14074, core/trunk/documentation/manual/fr-FR/src/main/docbook/images/overview.png)
===================================================================
(Binary files differ)
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/images/overview.svg (from
rev 14074, core/trunk/documentation/manual/fr-FR/src/main/docbook/images/overview.svg)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/images/overview.svg
(rev 0)
+++ core/trunk/documentation/manual/es-ES/src/main/docbook/images/overview.svg 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,250 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
+[
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+
xmlns="http://www.w3.org/2000/svg"
+
xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="248.031"
+ height="248.031"
+ id="svg1">
+ <defs
+ id="defs3">
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop128" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop129" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient130"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ <radialGradient
+ cx="0.5"
+ cy="0.5"
+ fx="0.5"
+ fy="0.5"
+ r="0.5"
+ id="radialGradient131"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ </defs>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,4.36019,-3.02123)"
+ style="font-size:12;"
+ id="g158">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="17.3527"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect136" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="15.3883"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect126" />
+ </g>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,4.36019,3.04452)"
+ style="font-size:12;"
+ id="g161">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect137" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect132" />
+ </g>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,4.36019,8.0993)"
+ style="font-size:12;"
+ id="g164">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect138" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect133" />
+ </g>
+ <g
+ transform="matrix(0.771934,0,0,0.543505,2.59104,21.1103)"
+ style="font-size:12;"
+ id="g167">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect134" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect135" />
+ </g>
+ <text
+ x="105.392174"
+ y="56.568123"
+ transform="scale(0.771934,0.771934)"
+
style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text183">
+ <tspan
+ x="105.392273"
+ y="56.568146"
+ id="tspan186">
+Application</tspan>
+ </text>
+ <text
+ x="81.820183"
+ y="103.149330"
+ transform="scale(0.771934,0.771934)"
+
style="font-size:20;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text188">
+ <tspan
+ x="81.820213"
+ y="103.149727"
+ id="tspan206">
+Persistent Objects</tspan>
+ </text>
+ <text
+ x="111.548180"
+ y="278.927887"
+ transform="scale(0.771934,0.771934)"
+
style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text197">
+ <tspan
+ x="111.547874"
+ y="278.927551"
+ id="tspan200">
+Database</tspan>
+ </text>
+ <text
+ x="94.436180"
+ y="153.805740"
+ transform="scale(0.771934,0.771934)"
+
style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text216">
+ <tspan
+ x="94.436180"
+ y="153.805740"
+ id="tspan221">
+HIBERNATE</tspan>
+ </text>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,2.59083,1.02261)"
+ style="font-size:12;"
+ id="g254">
+ <g
+ transform="translate(4.58374,2.61928)"
+ id="g176">
+ <g
+ transform="matrix(0.571429,0,0,0.67347,-10.6174,117.093)"
+ id="g170">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect171" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect172" />
+ </g>
+ <g
+ transform="matrix(0.571429,0,0,0.67347,138.682,117.093)"
+ id="g173">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect174" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect175" />
+ </g>
+ </g>
+ <text
+ x="47.259438"
+ y="182.367538"
+ style="font-weight:bold;stroke-width:1pt;font-family:Courier;"
+ id="text191">
+ <tspan
+ x="47.259399"
+ y="182.367996"
+ id="tspan212">
+hibernate.</tspan>
+ <tspan
+ x="47.259399"
+ y="194.367996"
+ id="tspan214">
+properties</tspan>
+ </text>
+ <text
+ x="198.523010"
+ y="188.260941"
+ style="font-weight:normal;stroke-width:1pt;font-family:helvetica;"
+ id="text194">
+ <tspan
+ id="tspan195">
+XML Mapping</tspan>
+ </text>
+ </g>
+</svg>
Copied: core/trunk/documentation/manual/es-ES/src/main/docbook/legal_notice.xml (from rev
14073, core/trunk/documentation/manual/en-US/src/main/docbook/legal_notice.xml)
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/legal_notice.xml
(rev 0)
+++ core/trunk/documentation/manual/es-ES/src/main/docbook/legal_notice.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,52 @@
+<?xml version='1.0'?>
+<!DOCTYPE legalnotice PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
+
+<!--
+ ~ Copyright (c) 2007, Red Hat Middleware, LLC. All rights reserved.
+ ~
+ ~ This copyrighted material is made available to anyone wishing to use, modify,
+ ~ copy, or redistribute it subject to the terms and conditions of the GNU
+ ~ Lesser General Public License, v. 2.1. This program is distributed in the
+ ~ hope that it will be useful, but WITHOUT A WARRANTY; without even the implied
+ ~ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details. You should have received a
+ ~ copy of the GNU Lesser General Public License, v.2.1 along with this
+ ~ distribution; if not, write to the Free Software Foundation, Inc.,
+ ~ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ ~
+ ~ Red Hat Author(s): Steve Ebersole
+ -->
+<legalnotice id="Legal_Notice">
+ <title>Legal Notice</title>
+ <para>
+ <address>
+ <street>1801 Varsity Drive</street>
+ <city>Raleigh</city>,
<state>NC</state><postcode>27606-2072</postcode><country>USA</country>
+ <phone>Phone: +1 919 754 3700</phone>
+ <phone>Phone: 888 733 4281</phone>
+ <fax>Fax: +1 919 754 3701</fax>
+ <pob>PO Box 13588</pob><city>Research Triangle
Park</city>,
<state>NC</state><postcode>27709</postcode><country>USA</country>
+ </address>
+ </para>
+ <para>
+ Copyright <trademark class="copyright"></trademark> 2007 by
Red Hat, Inc. This material may be distributed only subject to the terms and conditions
set forth in the Open Publication License, V1.0 or later (the latest version is presently
available at <ulink
url="http://www.opencontent.org/openpub/">http://www.opencon...>).
+ </para>
+ <para>
+ Distribution of substantively modified versions of this document is prohibited
without the explicit permission of the copyright holder.
+ </para>
+ <para>
+ Distribution of the work or derivative of the work in any standard (paper) book
form for commercial purposes is prohibited unless prior permission is obtained from the
copyright holder.
+ </para>
+ <para>
+ Red Hat and the Red Hat "Shadow Man" logo are registered trademarks of
Red Hat, Inc. in the United States and other countries.
+ </para>
+ <para>
+ All other trademarks referenced herein are the property of their respective owners.
+ </para>
+ <para>
+ The GPG fingerprint of the security(a)redhat.com key is:
+ </para>
+ <para>
+ CA 20 86 86 2B D6 9D FC 65 F6 EC C4 21 91 80 CD DB 42 A6 0E
+ </para>
+</legalnotice>
\ No newline at end of file
Added: core/trunk/documentation/manual/es-ES/src/main/docbook/legal_notice2.xml
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/legal_notice2.xml
(rev 0)
+++ core/trunk/documentation/manual/es-ES/src/main/docbook/legal_notice2.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,16 @@
+<?xml version='1.0'?>
+<!DOCTYPE legalnotice PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
+
+<legalnotice id="Legal_Notice">
+ <title>Translation-specific Legal Notice</title>
+ <para>
+ Advertencia! Esta es una versión traducida del inglés de
+ la documentacién de referencia de Hibernate. La versión
+ traducida puede no estar actualizada! Sin embargo, las diferencias
+ deberían ser sólo menores. Consulta la
documentación
+ de referencia en inglés si estás perdiendo
información
+ o encuentras algún error de traducción. Si quieres
colaborar con
+ una traducción en particular, contáctanos en la lista de
correo
+ de desarrolladores de Hibernate.
+ </para>
+</legalnotice>
\ No newline at end of file
Deleted: core/trunk/documentation/manual/es-ES/src/main/docbook/master.xml
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/master.xml 2007-10-09 18:28:36
UTC (rev 14074)
+++ core/trunk/documentation/manual/es-ES/src/main/docbook/master.xml 2007-10-09 18:45:36
UTC (rev 14075)
@@ -1,203 +0,0 @@
-<?xml version='1.0' encoding="iso-8859-1"?>
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3CR3//EN"
- "../support/docbook-dtd/docbookx.dtd"
-[
-<!ENTITY quickstart SYSTEM "modules/quickstart.xml">
-<!ENTITY tutorial SYSTEM "modules/tutorial.xml">
-<!ENTITY architecture SYSTEM "modules/architecture.xml">
-<!ENTITY configuration SYSTEM "modules/configuration.xml">
-<!ENTITY persistent-classes SYSTEM "modules/persistent_classes.xml">
-<!ENTITY basic-mapping SYSTEM "modules/basic_mapping.xml">
-<!ENTITY collection-mapping SYSTEM "modules/collection_mapping.xml">
-<!ENTITY association-mapping SYSTEM
"modules/association_mapping.xml">
-<!ENTITY component-mapping SYSTEM "modules/component_mapping.xml">
-<!ENTITY inheritance-mapping SYSTEM
"modules/inheritance_mapping.xml">
-<!ENTITY session-api SYSTEM "modules/session_api.xml">
-<!ENTITY transactions SYSTEM "modules/transactions.xml">
-<!ENTITY events SYSTEM "modules/events.xml">
-<!ENTITY batch SYSTEM "modules/batch.xml">
-<!ENTITY query-hql SYSTEM "modules/query_hql.xml">
-<!ENTITY query-criteria SYSTEM "modules/query_criteria.xml">
-<!ENTITY query-sql SYSTEM "modules/query_sql.xml">
-<!ENTITY filters SYSTEM "modules/filters.xml">
-<!ENTITY xml SYSTEM "modules/xml.xml">
-<!ENTITY performance SYSTEM "modules/performance.xml">
-<!ENTITY toolset-guide SYSTEM "modules/toolset_guide.xml">
-<!ENTITY example-parentchild SYSTEM
"modules/example_parentchild.xml">
-<!ENTITY example-weblog SYSTEM "modules/example_weblog.xml">
-<!ENTITY example-mappings SYSTEM "modules/example_mappings.xml">
-<!ENTITY best-practices SYSTEM "modules/best_practices.xml">
-]>
-
-<book lang="es">
-
- <bookinfo>
- <title>HIBERNATE - Persistencia Relacional para Java
Idiomático</title>
- <subtitle>Documentación de Referencia de
Hibernate</subtitle>
- <releaseinfo>3.0.5</releaseinfo>
- </bookinfo>
-
- <toc/>
-
- <preface id="preface" revision="2">
- <title>Prefacio</title>
-
- <para>
- Advertencia! Esta es una versión traducida del inglés
de
- la documentacién de referencia de Hibernate. La versión
- traducida puede no estar actualizada! Sin embargo, las diferencias
- deberían ser sólo menores. Consulta la
documentación
- de referencia en inglés si estás perdiendo
información
- o encuentras algún error de traducción. Si quieres
colaborar con
- una traducción en particular, contáctanos en la lista
de correo
- de desarrolladores de Hibernate.
- </para>
-
- <para>
- Traductor(es): Bernardo Antonio Buffa Colomé
&lt;kreimer(a)bbs.frc.utn.edu.ar&gt;
-<!--,
- Antonio López Gota &lt;antoniogota(a)gmail.com&gt; -->
- </para>
-
- <para>
- Trabajar con software orientado a objetos y una base de datos relacional
puede ser
- incómodo y consumir tiempo en los entornos de empresa de hoy.
Hibernate es una
- herramienta de mapeo objeto/relacional para entornos Java. El
término mapeo
- objeto/relacional (MOR) hace referencia a la técnica de mapear
una
- representación de datos desde un modelo de objetos a un modelo de
datos relacional
- con un esquema basado en SQL.
- </para>
-
- <para>
- Hibernate no sólo se encarga de mapear de clases Java a tablas de
base de datos
- (y de tipos de datos de Java a tipos de datos SQL), sino que
también provee
- facilidades de consulta y recuperación de datos y puede reducir
significativamente
- el tiempo de desarrollo que de otra forma se gasta en el manejo de los datos
en SQL y JDBC.
- </para>
-
- <para>
- La meta de Hibernate es relevar al desarrollador del 95 por ciento de las
tareas comunes
- relacionadas a la programación de la persistencia de los datos.
- Hibernate puede no ser la mejor solución para aplicaciones que
usan solamente
- procedimientos almacenados para implementar la lógica de negocio
en la base de
- datos, es mas útil con modelos de dominio orientados a objetos y
lógica de
- negocio en middle-tier basada en Java. Sin embargo, Hibernate ciertamente
puede ayudarte
- a quitar o encapsular código SQL específico de vendedor
y ayudará
- con la tarea común de traducción de resultados desde
una representación
- tabular a un grafo de objetos.
- </para>
-
- <para>
- Si eres nuevo en Hibernate y lo del Mapeo Objeto/Relacional o incluso en
Java,
- sigue por favor estos pasos:
- </para>
-
- <orderedlist>
- <listitem>
- <para>
- Lee <xref linkend="quickstart"/> para un tutorial de
30 minutos, usando Tomcat.
- </para>
- </listitem>
- <listitem>
- <para>
- Lee <xref linkend="architecture"/> para entender los
entornos en los que
- puede ser usado Hibernate.
- </para>
- </listitem>
- <listitem>
- <para>
- Dale una mirada al directorio <literal>eg/</literal> en
la distribución
- de Hibernate, contiene una aplicación independiente
simple.
- Copia tu driver JDBC al directorio
<literal>lib/</literal> y edita
- <literal>etc/hibernate.properties</literal>,
especificando los valores
- correctos para tu base de datos. Desde línea de comandos
en el
- directorio de la distribución, tipea <literal>ant
eg</literal>
- (usando Ant), o bajo Windows, tipea <literal>build
eg</literal>.
- </para>
- </listitem>
- <listitem>
- <para>
- Usa esta documentación de referencia como tu fuente de
información
- primaria. Ten en consideración leer <emphasis>Java
Persistence with Hibernate</emphasis>
- (
http://www.manning.com/bauer2) si necesitas mas ayuda con el
diseño
- de aplicaciones o si prefieres un tutorial paso a paso.
- Visita también
http://caveatemptor.hibernate.org y
descarga la aplicación
- de ejemplo para Java Persistence with Hibernate.
- </para>
- </listitem>
- <listitem>
- <para>
- Los FAQs son respondidos en el sitio web de Hibernate.
- </para>
- </listitem>
- <listitem>
- <para>
- En el sitio web de Hibernate hay enlaces a demos
de terceros, ejemplos
- y tutoriales.
- </para>
- </listitem>
- <listitem>
- <para>
- El Area de Comunidad en el sitio web de Hibernate es una buena
fuente
- de patrones de diseño y varias soluciones de
integración
- (Tomcat, JBoss, Struts, EJB, etc.).
- </para>
- </listitem>
- </orderedlist>
-
- <para>
- Si tienes preguntas, usa el foro de usuarios enlazado en el sitio web de
Hibernate.
- También proveemos un sistema de seguimiento JIRA para reportes de
defectos y
- peticiones de nuevas características.
- Si estas interesado en el desarrollo de Hibernate, únete a la
lista de correo
- de desarrolladores. Si estas interesado en traducir esta
documentación a tu
- lenguaje, contáctanos en la lista de correo de desarrolladores.
- </para>
-
- <para>
- A través de JBoss Inc. (see
http://www.hibernate.org/SupportTraining/) hay
- disponibilidad de soporte comercial de desarrollo, soporte de
producción y
- entrenamiento en Hibernate.
- Hibernate es un proyecto de la suite de productos de código
abierto
- JBoss Professional.
- </para>
-
- </preface>
-
- &quickstart;
- &tutorial;
- &architecture;
-
- &configuration;
-
- &persistent-classes;
-
- &basic-mapping;
- &collection-mapping;
- &association-mapping;
- &component-mapping;
- &inheritance-mapping;
-
- &session-api;
- &transactions;
- &events;
- &batch;
-
- &query-hql;
- &query-criteria;
- &query-sql;
- &filters;
- &xml;
-
- &performance;
-
- &toolset-guide;
-
- &example-parentchild;
- &example-weblog;
- &example-mappings;
-
- &best-practices;
-
-</book>
-
Added: core/trunk/documentation/manual/es-ES/src/main/docbook/translators.xml
===================================================================
--- core/trunk/documentation/manual/es-ES/src/main/docbook/translators.xml
(rev 0)
+++ core/trunk/documentation/manual/es-ES/src/main/docbook/translators.xml 2007-10-09
18:45:36 UTC (rev 14075)
@@ -0,0 +1,10 @@
+<?xml version='1.0'?>
+
+<!DOCTYPE authorgroup PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
+
+<authorgroup id="AuthorGroup">
+ <othercredit class="translator">
+ <othername><![CDATA[Bernardo Antonio Buffa
Colomé]]></othername>
+ <email>kreimer(a)bbs.frc.utn.edu.ar</email>
+ </othercredit>
+</authorgroup>
\ No newline at end of file