Está en la página 1de 198

HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.

html

HIBERNATE - Persistencia relacional para Java Idiomático

Documentación de Referencia de Hibernate


3.3.1

Traductor: Gonzalo Díaz

Copyright © 2004 Red Hat Middleware, LLC.

Prefacio
1. Introducción a Hibernate
1.1. Prefacio
1.2. Parte 1 - La Primera Aplicación Hibernate
1.2.1. La primera clase
1.2.2. El archivo de mapeo
1.2.3. Configuración de Hibernate
1.2.4. Construyendo con Ant
1.2.5. Comienzo y ayudantes
1.2.6. Cargar y almacenar objetos
1.3. Parte 2 - Mapear asociaciones
1.3.1. Mapear la clase Person
1.3.2. Una asociación unidireccional basada en un Set
1.3.3. Trabajar con la asociación
1.3.4. Colección de valores
1.3.5. Asociaciones bidireccionales
1.3.6. Trabajar con vínculos bidireccionales
1.4. Parte 3 - La aplicación de web "Event Manager"
1.4.1. Escribir el servlet básico
1.4.2. Procesamiento y presentación
1.4.3. Despliegue (deploy) y test
1.5. Sumario
2. Arquitectura
2.1. Generalidades
2.2. Estados de una instancia
2.3. Integración con JMX
2.4. Soporte de JCA
2.5. Sesiones contextuales
3. Configuración
3.1. Configuración programática
3.2. Obtener una SessionFactory
3.3. Conexiones JDBC
3.4. Propiedades optativas de configuración
3.4.1. Dialectos de SQL
3.4.2. Captura (fetching) por Outer Join
3.4.3. Streams Binarios
3.4.4. Caché de 2do nivel y caché de consultas
3.4.5. Sustituciones en el lenguaje de consultas.
3.4.6. Estadísticas de Hibernate
3.5. Logueo (logging, bitácora)
3.6. Implementando una NamingStrategy
3.7. Archivo de configuración XML
3.8. Integración con los Servidores de Aplicación J2EE
3.8.1. Configuración de una estrategia transaccional
3.8.2. SessionFactory ligada a JNDI
3.8.3. Manejo del contexto actual de la sesión con JTA
3.8.4. Despliegue de JMX
4. Clases Persistentes
4.1. Un simple ejemplo de POJO

1 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

4.1.1. Implemente un constructor sin argumentos


4.1.2. Porvea una propiedad identificadora (optativo)
4.1.3. Prefiera clases que no sean finales (optativo)
4.1.4. Declare métodos de acceso y "mutadores" (accessors, mutators) para los campos persistentes.
4.2. Implementar herencia
4.3. Implementar equals() y hashCode()
4.4. Modelos dinámicos
4.5. T-uplizadores
4.6. Extensiones
5. Mapeo O/R básico
5.1. Declaración del mapeo
5.1.1. El Doctype o "tipo de documento XML"
5.1.1.1. EntityResolver
5.1.2. hibernate-mapping
5.1.3. class
5.1.4. id
5.1.4.1. Generator
5.1.4.2. El algoritmo hi/lo
5.1.4.3. El argoritmo UUID
5.1.4.4. Columnas de identidad y secuencias
5.1.4.5. Identificadores asignados
5.1.4.6. Claves primarias assignadas por triggers
5.1.5. Generadores de identificador mejorados.
5.1.6. Optimización de los generadores de identificador
5.1.7. composite-id
5.1.8. discriminator
5.1.9. version (optativo)
5.1.10. timestamp (optativo)
5.1.11. property
5.1.12. many-to-one
5.1.13. one-to-one
5.1.14. natural-id
5.1.15. component, dynamic-component
5.1.16. properties
5.1.17. subclass
5.1.18. joined-subclass
5.1.19. union-subclass
5.1.20. join
5.1.21. key
5.1.22. elementos column y formula
5.1.23. import
5.1.24. any
5.2. Tipos de Hibernate
5.2.1. Entidades y "value types"
5.2.2. "Value types" básicos
5.2.3. "Value types" hechos a medida
5.3. Mapear una misma clase más de una vez
5.4. Identificadores de SQL entrecomillados
5.5. Alternativas de meta-datos
5.5.1. Usar marcadores de XDoclet
5.5.2. Usar anotaciones de JDK 5.0
5.6. Propiedades generadas
5.7. Objetos auxiliares de base de datos
6. Mapeo de Colecciones
6.1. Colecciones persistentes
6.2. Mapeo de colecciones
6.2.1. Claves foráneas de las colecciones
6.2.2. Elementos de la colección
6.2.3. Colecciones indexadas
6.2.4. Colecciones de valores y asociaciones de-muchos-a-muchos
6.2.5. Asociaciones de-uno-a-muchos
6.3. Mapeos de colección avanzados
6.3.1. Colecciones ordenadas

2 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

6.3.2. Asociaciones bidireccionales


6.3.3. Asociaciones bidireccionales con colecciones indexadas
6.3.4. Asociaciones ternarias
6.3.5. Usar una <idbag>
6.4. Ejemplos de colecciones
7. Mapeo de asociaciones
7.1. Introducción
7.2. Asociaciones unidireccionales
7.2.1. de-muchos-a-uno
7.2.2. de-uno-a-uno
7.2.3. de-uno-a-muchos
7.3. Asociaciones unidireccionales con tablas de unión
7.3.1. de-uno-a-muchos
7.3.2. de-muchos-a-uno
7.3.3. de-uno-a-uno
7.3.4. de-muchos-a-muchos
7.4. Asociaciones bidireccionales
7.4.1. de-uno-a-muchos / de-muchos-a-uno
7.4.2. de-uno-a-uno
7.5. Asociaciones bidireccionales con tablas de unión
7.5.1. de-uno-a-muchos / de-muchos-a-uno
7.5.2. de-uno-a-uno
7.5.3. de-muchos-a-muchos
7.6. Mapeos de asociacoones más complejas
8. Mapeo de componentes
8.1. Objetos dependientes
8.2. Colecciones de objetos dependientes
8.3. Comonentes usados como índices de un Map
8.4. Componentes usados como identificadores compuestos
8.5. Componentes dinámicos
9. Mapeo de herencia
9.1. Las tres estrategias
9.1.1. Una tabla por jerarquía de clases
9.1.2. Una tabla por subclase
9.1.3. Una tabla por subclase, usando un discriminador
9.1.4. Mezclar "una tabla por jerarquía de clases" con "una tabla por subclase"
9.1.5. Una tabla por cada clase concreta
9.1.6. Una tabla por cada clase concreta, usando polimorfismo implícito
9.1.7. Mezclar polimorfismo implícito con otras estrategias de mapeo de herencia
9.2. Limitaciones
10. Trabajar con objetos
10.1. Estados de un objeto de Hibernate
10.2. Hacer que los objetos se vuelvan persistentes
10.3. Cargar un objeto
10.4. Consultas
10.4.1. Ejecutar consultas
10.4.1.1. Iterar resultados
10.4.1.2. Consultas que devuelven T-uplas
10.4.1.3. Resultados escalares
10.4.1.4. Parámetros vinculados
10.4.1.5. Paginación
10.4.1.6. Iteración navegable
10.4.1.7. Externalizar consultas nombradas
10.4.2. Filtrar colecciones
10.4.3. Consultas "Criteria"
10.4.4. Consultas en SQL nativo
10.5. Modificar objetos persistentes
10.6. Modificar objetos desprnendidos
10.7. Detección automática de estado
10.8. Borrar objetos persistentes
10.9. Replicar un objeto entre dos repositorios de datos distintos
10.10. "Flush" de la sesión
10.11. Persistencia transitiva

3 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

10.12. Usar metadatos


11. Transacciones y concurrencia
11.1. La sesión y el alcance (scope) de las transacciones
11.1.1. Unidad de trabajo
11.1.2. Conversaciones largas
11.1.3.Considerar la identidad de los objetos
11.1.4. Problemas comunes
11.2. Demarcación de las transacciones de base de datos
11.2.1. Entornos no administrados
11.2.2. Usar JTA
11.2.3. Manejo de excepciones
11.2.4. Expiración de transacciones
11.3. Control optimista de concurrencia
11.3.1. Chequeo de versión hecho por la aplicación
11.3.2. Sesión extendida y versionado automático
11.3.3. Objetos desprendidos y versionado automático
11.3.4. Crear un método a medida para el versionado automático
11.4. "Lock" pesimista
11.5. Modos de liberación de conecciones
12. Interceptores y eventos
12.1. Interceptores
12.2. Sistema de eventos
12.3. Seguridad declarativa de Hibernate
13. Procesamiento en lotes
13.1. Inserciones en lotes
13.2. Actualizaciones en lotes
13.3. La interfaz StatelessSession
13.4. Operaciones del tipo "Lenguaje de Modificacion de Datos" (DML-style)
14. HQL: El lenguaje de consultas de Hibernate
14.1. Relevancia de mayúsculas y minúsculas
14.2. La cláusula "from"
14.3. Asociaciones y "joins"
14.4. Formas de la sintaxis de los "joins"
14.5. Referirse a la propiedad identificadora
14.6. La cláusula "select"
14.7. Funciones agregadas
14.8. Consultas polimórficas
14.9. La cláusua "where"
14.10. Expresiones
14.11. La cláusula "order by"
14.12. La cláusula "group by"
14.13. Subconsultas
14.14. Ejemplos de HQL
14.15. Actualizaciones y borrados en masa
14.16. Consejos y trucos
14.17. Componentes
14.18. Sintaxis del "Constructor de Valor de Fila" (row value constructor)
15. Consultas "Criteria"
15.1. Crear una instancia de Criteria
15.2. Acotar el resultado
15.3. Ordenar el resultado
15.4. Asociaciones
15.5. Captura dinámica de asociaciones
15.6. Consultas "Example"
15.7. Proyecciones, agregado y agrupamiento
15.8. Consultas y subconsultas desprendidas
15.9. Consultas por identificador natural
16. SQL nativo
16.1. Usar un SQLQuery
16.1.1. Consultas escalares
16.1.2. Consultas con entidades
16.1.3. Manipular colecciones y asociaciones
16.1.4. Devolver múltiples entidades

4 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

16.1.4.1. Alias y referencias a propiedades


16.1.5. Devolver entidades no administradas
16.1.6. Manejar herencia
16.1.7. Parámetros
16.2. Consultas SQL nombradas
16.2.1. Usar return-property para especificar nombres de columna/alias explícitamente
16.2.2. Usar procedimientos almacenados (stored procedures) para efectuar consultas
16.2.2.1. Reglas y limitaciones en el uso de procedimientos almacenados
16.3. SQL a medida para crear, almacenar y borrar
16.4. SQL a medida para cargar
17. Filtrar datos
17.1. Filtros de Hibernate
18. Mapeo XML
18.1. Trabajar con datos XML
18.1.1. Especificar el mapeo XML y el mapeo de la clase al mismo tiempo
18.1.2. Especificar sólo un mapeo XML
18.2. Metadatos del mapeo XML
18.3. Manipulación de los datos XML
19. Mejorar la performance
19.1. Estrategias de captura (fetch)
19.1.1. Trabajar con asociaciones haraganas
19.1.2. Ajustar las estrategias de captura
19.1.3. Proxies de las asociaciones de un solo extremo
19.1.4. Inicializar colecciones y proxies
19.1.5. Usar la captura por lotes
19.1.6. Usar la captura mediante subselects
19.1.7. Usar la captura de propiedades haragana
19.2. El caché de 2do nivel
19.2.1. Mepeos de caché.
19.2.2. Estrategia de sólo lectura
19.2.3. Estrategia de lecto/escritura
19.2.4. Estrategia de lecto/escritura no estricta
19.2.5. Estrategia transaccional
19.2.6. Compatibilidad entre el proveedor de caché y la estrategia de concurrencia
19.3. Admninistrar los cachés
19.4. El caché de consultas (query cache)
19.5. Comprender la performance de las colecciones
19.5.1. Taxonomía
19.5.2. Las lists, maps, idbags y sets son las colecciones más eficientes de actualizar
19.5.3. Las bags y lists son las colecciones inversas más eficientes
19.5.4. Borrado en una pasada
19.6. Monitorear la performance
19.6.1. Monitorear una SessionFactory
19.6.2. Mediciones
20. Guía de las herramientas (Toolset)
20.1. Generación automática del esquema de base de datos
20.1.1. Retocar el esquema de base de datos
20.1.2. Ejecutar la herramienta
20.1.3. Propiedades
20.1.4. Usar Ant
20.1.5. Actualizaciones incrementales del esquema de base de datos
20.1.6. Utilizar Ant para las actualizaciones incrementales del esquema de base de datos
20.1.7. Validación del esquema de base de datos
20.1.8. Usar Ant para la validación del esquema de base de datos
21. Ejemplo: Padre/Hijo
21.1. Nota sobre las colecciones
21.2. de-uno-a-muchos bidireccional
21.3. Ciclo de vida de las propagaciones en cascada
21.4. Propagaciones en cascada y unsaved-value
21.5. Conclusión
22. Ejemplo: La aplicación Weblog
22.1. Clases persistentes
22.2. Mapeos de Hibernate

5 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

22.3. Código Hibernate


23. Ejemplo: Mapeos varios
23.1. Empleador/Empleado
23.2. Autor/Obra
23.3. Cliente/Orden/Producto
23.4. Ejemplos misceláneos de asociación
23.4.1. Asociación de-uno-a-uno "con tipo"
23.4.2. Ejemplo de clave compuesta
23.4.3. de-muchos-a-muchos con atributo compartido de clave compuesta
23.4.4. Discriminación basada en el contenido
23.4.5. Asociaciones en claves alternativas
24. Prácticas recomendadas

Prefacio
Trabajar con software orientado a objetos y bases de datos relacionales, puede ser embarazoso y demandar mucho tiempo,
en los entornos corporativos actuales. Hibernate es una herramienta de mapeo objeto/relacional para ambientes Java. El
término "mapeo objeto/relacional" (ORM por sus siglas en inglés) se refiere a esta técnica de "mapear" la representación
de los datos desde un modelo de objetos hacia un modelo de datos relacional, con un esquema de base de datos basado en
SQL.

Hibernate no sólo se hace cargo del mapeo de clases Java a las tablas de una base de datos (y de los tipos Java a los tipos
de la base de datos), sino que también provee utilidades para consulta y captura de datos, y puede reducir
considerablemente el tiempo que, de otra manera, habría que invertir con el manejo manual de datos mediante SQL y
JDBC.

La meta de Hibernate es aliviar al programador del 95% de las tareas más comunes relacionadas con persistencia.
Probablemente, Hibernate no sea la mejor solución para aplicaciones data-céntricas que tengan casi toda su lógica de
negocios en procedimientos almacenados (stored procedures) en la base de datos; es más útil con modelos orientados a
objetos cuya lógica de negocio reside en la capa intermedia. Sin embargo, Hibernate puede ayudarlo a encapsular o
eliminar código SQL que sea específico de un proveedor de BD, y ayudará en la tarea usual de traducir desde una
representación tabular a un gráfico de objetos.

Si usted es nuevo en Hibernate y en lo que respecta al Mapeo objeto/relacional, o incluso nuevo en Java, por favor siga los
siguientes pasos:

Lea el siguiente instructivo: Capítulo 1, Introducción a Hibernate, el cual es una especie de manual con
instrucciones paso a paso. El código fuente del instructivo está incluido en la distribución descargable, en el
directorio doc/reference/tutorial/

Lea Capítulo 2, Arquitectura para entender en qué entornos Hibernate puede ser usado.

Échele un vistazo al directorio eg/ en la distribución de Hibernate. Contiene una simple aplicación autosuficiente.
Copie su driver de JDBC al directorio lib/ y edite etc/hibernate.properties, especificando valores correctos
para su base de datos. Desde la consola, situado en el directorio de distribución, tipee ant eg (usando Ant), o desde
Windows, tipee build eg.

Use esta documentación de referencia como su fuente primaria de información. Considere leer Java Persistence
with Hibernate (http://www.manning.com/bauer2) si necesita más ayuda con el diseño de aplicaciones o si prefiere
un instructivo paso a paso. También visite http://caveatemptor.hibernate.org y descargue la aplicación de ejemplo
para Persistencia de Java con Hibernate.

Las preguntas frecuentes (FAQ, por sus siglas en inglés), son contestadas en el sitio de web de Hibernate.

En el sitio de web de Hibernate hay vínculos a demostraciones de terceros, ejemplos e instructivos.

El Área Comunitaria del sitio de web de Hibernate es un buen recurso acerca de patrones de diseño y varias
soluciones de integración (Tomcat, JBoss AS, Struts, EJB, etc).

Si tiene preguntas, utilice el foro en el sitio de Hibernate. También proveemos un sistema JIRA de seguimiento de
problemas, para reportes de defectos (bugs) y pedidos de mejoras. Si a usted le interesa la programación de Hibernate,
únase a la lista de correo de programación de Hibernate. Si le interesa traducir este documento, póngase en contacto con
nosotros en la lista de correo de programación.

6 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

A través de JBoss Inc, hay disponible soporte para desarrollo comercial y de producción, y entrenamiento para Hibernate.
(véase http://www.hibernate.org/SupportTraining/). Hibernate es un componente vital del paquete de productos conocido
como "Sistema Empresarial JBoss de Middleware" (JEMS, por sus siglas en inglés).

Capítulo 1. Introducción a Hibernate

1.1. Prefacio
Este capítulo es una introducción a Hibernate, con el tono de un instructivo, destinado a usuarios nuevos de Hibernate.
Empezamos con una simple aplicación que usa una base de datos residente en memoria. Construimos la aplicación de a
pasos pequeños, fáciles de comprender. Este instructivo se basa en otro anterior, escrito por Michael Gloegl. Todo el
código está contenido en el directorio tutorials/web del código fuente del proyecto.

Importante

Este instructivo sobreentiende que el usuario ya tiene conocimiento de Java y de SQL. Si cualquiera de
ambos es nuevo para usted, o no se maneja bien ellos, le recomendamos que comience con una buena
introducción a estas tecnologías, antes de adentrarse en Hibernate. A la larga, le ahorrará tiempo y
esfuerzo.

Nota

Hay otra aplicación a modo de ejemplo/instructivo en el directorio del código fuente /tutorials/eg.
Dicho ejemplo se basa en línea de comandos (consola), y como tal no depende de un contenedor de servlets
para poder ser ejecutado. El montaje (setup) básico es el mismo que para las instrucciones a continuación.

1.2. Parte 1 - La primera aplicación Hibernate


Supongamos que tenemos una pequeña aplicación de base de datos que puede almacenar eventos a los que queremos
asistir, e información acerca del anfitrión (o anfitriones) de dichos eventos. Vamos a usar una base de datos residente en
memoria, llamada HSQLDB, para evitar describir la instalación y configuración de cualquier base de datos en particular.
Siéntase en libertad de usar cualquier base de datos con la que esté familiarizado.

Lo primero que tenemos que hacer es montar nuestro entorno de desarrollo, y, específicamente, instalar todas las
dependencias que Hibernate necesita, así como otras bibliotecas (libraries). Hibernate se construye usando Maven, el cual,
entre otras cosas, provee manejo de dependencias. Más aún: provee un manejo transitivo de dependencias, lo cual
simplemente significa que para usar Hibernate podemos definir nuestras dependencias dentro de él: Hibernate mismo
define las dependencias que necesita, las cuales se convierten en "transitivas".

.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.

...

<dependencies>
<dependency>
<groupId>${groupId}</groupId>

<artifactId>hibernate-core</artifactId>
</dependency>

<!-- Como ésta es una aplicación de web, también necesitamos una dependencia a la API de se
<dependency>
<groupId>javax.servlet</groupId>

<artifactId>servlet-api</artifactId>
</dependency>
</dependencies>

</project>

7 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Nota

Básicamente, aquí estamos describiendo el archivo /tutorials/web/pom.xml. Vea el sitio de Maven para
más información.

Consejo

Aunque no es estrictamente necesario, muchos entornos visuales de progrmación (IDEs) ya cuentan con
integración con Maven para leer estos archivos POM, y automáticamente generar el proyecto por usted (lo
cual puede ahorrar mucho tiempo y esfuerzo).

Luego creamos una clase que representa el evento que queremos almacenar en la base de datos.

1.2.1. La primera clase

Nuestra primera clase persistente es un simple JavaBean con algunas propiedades.

package org.hibernate.tutorial.domain;

import java.util.Date;

public class Event {


private Long id;

private String title;


private Date date;

public Event() {}

public Long getId() {


return id;
}

private void setId(Long id) {


this.id = id;
}

public Date getDate() {


return date;
}

public void setDate(Date date) {


this.date = date;
}

public String getTitle() {


return title;
}

public void setTitle(String title) {


this.title = title;
}
}

Se puede ver que esta clase usa la convención estándar de JavBeans para nombrar a sus métodos "setter" y "getter"
(escritura y lectura de propiedades, respectivamente). Este diseño es el recomendado - pero no es obligatorio. Hibernate
puede aceder a los campos directamente; la ventaja de los métodos de acceso es proveer mayor solidez a la hora de refinar
("refactoring") el código. El constructor sin argumentos sí es obligatorio, para poder instanciar el objeto mediante
reflexión.

La propiedad identificadora o id contiene un identificador único para un evento en particular. Todas las clases de entidad
persistentes (las hay menos importantes también) necesitarán dicho id, si queremos usar a pleno las capacides de
Hibernate. De hecho, la mayoría de las aplicaciones (especialmente aplicaciones de web), ya necesitan distinguir objetos
por id, así que esta característica debería considerarse una ventaja, más que una limitación. De todos modos, usualmente
no manipulamos directamente la identidad de un objeto, así que el método "setter" del id debería ser privado. Sólo
Hibernate asigna ids, cuando el objeto es grabado. Hibernate puede acceder a métodos en cualquier nivel de acceso
(protected, public, private, etc) directamente. La opción de qué nivel de acceso utilizar es suya, según el diseño de su
aplicación.

8 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

El constructor sin argumentos es obligatorio para todas las clases persistentes; Hibernate tiene que crear los objetos para
usted utilizando Java Reflection. El constructor puede ser privado; sin embargo, para poder generar "proxies" en tiempo de
ejecución, y para la captura de datos sin la construcción de bytecode, se requiere al menos el nivel de acceso "package" o
por defecto.

Ponga este archivo de código fuente Java en un directorio llamado src en el directorio de desarrollo, y dentro del paquete
correspondiente. El directorio debería verse así:

.
+lib
<bibliotecas de Hibernate y de terceros>
+src
+events
Event.java

En el paso siguiente, le vamos a informar a Hibernate acerca de esta clase persistente.

1.2.2. El archivo de mapeo

Hibernate necesita saber cómo cargar y almacenar objetos de la clase persistente. Aquí es donde entra en juego el archivo
de mapeo. Éste le dice a Hibernate a qué tabla en qué base de datos tiene que acceder, y qué columnas de dicha tabla
tiene que utilizar.

La estructura básica del archivo de mapeo se ve así:

<?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>

Nótese que la DTD de Hibernate es bastante sofisticada. La puede usar para autocompletar elementos de mapeo y
atributos de XML en su indetfaz gráfica (IDE). También puede abrir la el archivo de la DTD en su procesador de texto - es
la forma más fácil de tener una visión general de todos los elementos, y de ver los valores por defecto, junto con algunos
comentatios. Sepa que Hibernate no buscará la DTD en la red, sino en el classpath. La DTD está incluida en el archivo,
hibernate3.jar, así como en el directorio src/ de la distribución de Hibernate.

En los ejemplos sucesivos, omitiremos la declaración de la DTD, por brevedad. Por supuesto, ésta no es optativa.

Entre las dos tags hibernate-mapping, incluya un elemento class. Todas las clases de entidad persistente (de nuevo: hay
clases dependientes, como veremos luego, que no son entidades de primer nivel) necesitan dicho mapeo, a una tabla en la
base de datos SQL.

<hibernate-mapping>

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

</class>

</hibernate-mapping>

Hasta el momento, le hemos dicho a Hibernate cómo persistir y cargar un objeto de la clase Event en la tabla EVENTS,
cada instancia representando un registro de la tabla. Ahora continuamos con el mapeo del identificador único a la clave
primaria de la tabla. Por añadidura, como no queremos preocuparnos por manipular dicho identificador, configuramos una
"estrategia de generación de identificador" de Hibernate para que use una clave primaria "sustituta" (surrogate key).

<hibernate-mapping>

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

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


<generator class="native"/>
</id>
</class>

9 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

</hibernate-mapping>

El elemento id es la declaracuión de la propiedad indentificadora. El atributo name="id" declara el nombre de la


propiedad Java - Hibernate usará los métodos getter y setter para acceder a ella. El atributo "column" le dice a Hibernate
qué columna de la tabla EVENTS usamos como clave primaria. El elemento anidado generator especifica la estrategia para
la generación de identificador. En este caso, usamos native, la cual elige la mejor estrategia dependiendo de la BD y
dialecto configurados. Hibernate soporta tanto identificadores generados por la base de datos, como globalmente únicos, o
asignados por la aplicación (o generados por cualquier estrategia para la cual usted haya escrito una extensión).

Finalmente, incluimos declaraciones para las propiedades persistentes en el archivo de mapeo. Por defecto, ninguna de las
propiedades de la clase se considera persistente.

<hibernate-mapping>

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


<id name="id" column="EVENT_ID">
<generator class="native"/>
</id>
<property name="date" type="timestamp" column="EVENT_DATE"/>
<property name="title"/>
</class>

</hibernate-mapping>

Igual que con el elemento id, el atributo name del elemento property le dice a Hibernate qué métodos getter y setter usar.
Así que, en este caso, Hibernate buscará getDate()/setDate(), así como getTitle()/setTitle().

¿Por qué el mapeo de la propiedad date incluye el atributo column, pero el title no? A falta del atributo column,
Hibernate por defecto usa el nombre de la propiedad como nombre de columna. Esto funciona para title. Pero date es
una palabra reservada en la mayoría de las base de datoss, así que mejor la mapeamos con un nombre diferente.

Otra cosa interesante, es que el mapeo title también carece del atributo type. Los tipos que usamos en los archivos de
mapeo no son, como es de esperarse, tipos Java. Tampoco son tipos SQL. A estos tipos se los llama Tipos de mapeo de
Hibernate, conversores que podemos traducir de tipos Java a SQL y viceversa. De nuevo, Hibernate tratará de determinar
la conversión adecuada e incluso el tipo mismo, si el atributo type no se especifica en el mapeo. En algunos casos, esta
detección automática (que usa Java Reflection) puede no generar el valor por defecto que usted esperaba o necesita. Ése
es el caso con la propiedad date. Hibernate no puede saber si la propiedad, que es del tipo java.util.Date, debería ser
mapeada a una columna timestamp, o a una columna time. En este caso, mantengamos la información completa (de día y
hora) mapeando la propiedad al conversor timestamp.

Este achivo de mapeo debería ser grabado como Event.hbm.xml, justo en el mismo directorio que el archivo Java de la
clase Event. El nombre de los archivos de mapeo puede ser arbitrario, pero los sufijos hbm.xml son una convención en la
comunidad de programadores de Hibernate. Ahora la estructura de directorios debería verse así:

.
+lib
<bibliotecase de Hibernate y de terceros>
+src
+events
Event.java
Event.hbm.xml

Continuamos con la configuración principal de Hibernate

1.2.3. Configuración de Hibernate

Ahora tenemos ubicados una clase persistente y su archivo de mapeo. Es el momento de configurar Hibernate mismo.
Antes de hacerlo, necesitamos una base de datos. HSQLBD es una base de datos basada en Java; puede ser descargada del
sitio de web de HSQL DB (http://hsqldb.org/). En ralidad, usted sólo necesita hsqldb.jar de dicha descarga. Coloque
este archivo en el directorio lib/ del directorio de desarrollo.

Cree un directorio llamado data en la raíz del directorio de desarrollo - es ahí en donde HSQL DB almacenará sus
archivos de datos. Ahora, haga arrancar la base de datos ejecutando: java -classpath ../lib/hsqldb.jar
org.hsqldb.Server en este directorio de datos. Usted podrá ver que arranca y está ligada a un socket TPC/IP. Ahí es
donde nuestra aplicacíón se conectará luego. Si quiere comenzar con una base de datos nueva en el transcurso de este
instructivo, cierre la base de datos HSQL DB pulsando CTRL + C en la ventana, borre todos los archivos en el

10 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

subdirectorio data/, y arranque HSQL DB nuevamente.

Hibernate es la capa de su aplicación que se conecta con esta base de datos, así que necesita información de conexión. Las
conexiones se hacen mediante un pool de conexiones JDBC, el cual también tenemos que configurar. La distribución de
Hibernate contiene varias herramientas de código abierto (open source) que generan pool de conexiones , pero para este
instructivo usaremos el pool que ya viene incorporado en Hibernate. Dése cuenta de que, si quiere usar un pool de
conexiones JDBC de mayor calidad (para aplicaciones ya instaladas en producción) hecho por terceros, deberá copiar las
bibliotecas que hagan falta en el classpath, y usar propiedades de conexión diferentes.

Para la configuración de Hibernate, podemos usar un simple archvo hibernate.properties, un archivo


hibernate.cfg.xml un tanto más sofisticado, o incluso una configuración totalmente programática. La mayoría prefiera
el archivo de configuración XML.

<?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>

<!-- datos de conexión de la BD -->


<property name="connection.driver_class">org.hsqldb.jdbcDriver</property>

<property name="connection.url">jdbc:hsqldb:hsql://localhost</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>

<!-- pool de conexiones JDBC (usamos el que ya viene incorporado) -->

<property name="connection.pool_size">1</property>

<!-- dialecto SQL -->


<property name="dialect">org.hibernate.dialect.HSQLDialect</property>

<!-- habilita el manejo automátio de contexto de sesión por parte de Hibernate -->

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

<!-- inhabilita el caché de 2do nivel -->


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

<!-- imprime todo el SQL ejecutado en la salida estándar -->


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

<!-- borra y recrea la BD en cada arranque -->


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

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

</session-factory>

</hibernate-configuration>

Fíjese en que este archivo de configuración XML usa una DTD distinta. Configuramos la SessionFactory de Hibernate -
una fabrica global responsable de una base de datos en particular. Si tiene varias base de datos, use varias configuraciones
de <session-factory> por lo común en otros tantos archivos de configuración (para un arranque más fácil).

Los primeros 4 elementos property contienen la configuración necesaria para la conexíón JDBC. La propiedad "dialect"
especifica la variante de SQL en particular que Hibernate genera. El manejo automático de sesiones por contextos de
persistencia será muy útil, como pronto veremos. La opción hbm2ddl.auto activa la generación automática de esquemas -
directamente en la BD. Esto por supuesto puede ser desactivado (quitando esta opción) o ser redirigido a un archivo, con
la ayuda de la tarea de Ant SchemaExport. Fnalmente, agregamos el o los archivo de mapeo para las clases persistentes a
la configuración.

Copie este archivo en el directorio de fuentes (src), de manera que quede en la raíz del classpath. Hibernate busca
automáticamente un arhivo llamado hibernate.cfg.xml en la raíz del classpath, al arrancar.

1.2.4. Construir con Ant

11 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Ahora vamos a construir (build) esta aplicación instructiva usando Ant. Deberá tener Ant ya instalado - obténgalo de la
Página de descarga de Ant. Aquí no vamos a discutir cómo instalar Ant. Por favor refiérase al Manual de Ant. Después de
haber instalado Ant, podemos crear el archivo de construcción de Ant (build file), que se llamará build.xml, y estará
situado directamente en el directorio de desarrollo.

Un archivo de construcción Ant básico se ve así:

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

Esto le dice a Ant que agregue todos los archivos del el directorio lib que terminen en .jar al classpath que usemos para la
compilación. También copiará todos los archivos fuente no-Java al directorio de destino (target), por ejemplo, los archivos
de configuración y mapeo.. Si ejecuta Ant ahora, debería obtener la siguiente salida:

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

1.2.5. Arranque y ayudantes

Es hora de cargar y grabar algunos objetos Event,pero primero debemos completar la instalación (setup) con algo de
código "infraestructural". Tenemos que hacer arrancar Hibernate. Dicho arranque incluye construir un objeto
SessionFactory global, y almacenarlo en algún lugar de fácil acceso para el código de la aplicación. Una
SessionFactory puede abrir nuevos objetos Session. Cada objeto Session representa una "unidad de trabajo" de un
solo Thread. El código de SessionFactory es thread-safe, y es instanciado sólo una vez.

Vamos a crear una clase de ayuda llamada HibernateUtil, que se encargará del arranque y hará que el acceso a
SessionFactory sea más conveniente. Veamos cómo implementarla:

package util;

import org.hibernate.*;

12 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

import org.hibernate.cfg.*;

public class HibernateUtil {

private static final SessionFactory sessionFactory;

static {
try {
// Cree la SessionFactory para hibernate.cfg.xml
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
// Asegúrese de loguear la excepción, dado que puede ser "tragada"
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}

public static SessionFactory getSessionFactory() {


return sessionFactory;
}

Esta clase no sólo produce la SessionFactory global, en un inicializador estático (invocado una sola vez por la JVM
cuando la clase se carga), sino que oculta el hecho de que se emplea un singleton estático. Podría haber estado buscando la
SessionFactory en el JNDI de un servidor de aplicaciones, , por ejemplo,.

Si usted le da un nombre a la SessionFactory en su archivo de configuración, Hibernate en realidad intentará vincularla a


JNDI tras haber sido creada. Para omitir este código completamente, usted también podría usar "despliegue JMX" y dejar
que el un contenedor habilitado para JMX instancie y construya un HibernateService en la JNDI. Estas opciones
avanzadas se discuten en la documentación de referencia de Hibernate.

Coloque HibernateUtil.java en el directorio de código fuente, en un paquete al lado de events:

.
+lib
<Hibernate y las bibliotecas de terceros>

+src
+events
Event.java
Event.hbm.xml
+util
HibernateUtil.java
hibernate.cfg.xml
+data
build.xml

Esto debería poder compilarse sin problemas. Finalmente, necesitamos configurar un sistema de logueo (bitácora, logging)
- Hibernate usa commons logging y le deja a usted la opción entre log4j y el logging específico de Java. La mayoría de los
programadores prefiere Log4j. Copie log4j.properties de la distribución de Hibernate (está en el directorio etc/) a su
directorio src, al lado de hibernate.cfg.xml. Dele un vistazo a la configuración de ejemplo, y cambie los valores si
desea una salida más locuaz. Por defecto, sólo los mensajes de arranque de Hibernate se muestran en la salida estándar.

La parte infraestructural del instructivo ha finalizado. Ahora estamos listos para efectuar verdadero trabajo con Hibernate.

1.2.6. Cargar y almacenar objetos

Finalmente podemos usar Hibernate para cargar y grabar objetos . Escribimos una clase EventManager con un método
main().

package events;
import org.hibernate.Session;

import java.util.Date;

import util.HibernateUtil;

public class EventManager {

public static void main(String[] args) {

13 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

EventManager mgr = new EventManager();

if (args[0].equals("store")) {
mgr.createAndStoreEvent("My Event", new Date());
}

HibernateUtil.getSessionFactory().close();
}

private void createAndStoreEvent(String title, Date theDate) {

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

session.beginTransaction();

Event theEvent = new Event();


theEvent.setTitle(title);
theEvent.setDate(theDate);

session.save(theEvent);

session.getTransaction().commit();
}

Creamos un objeto Event, y se los pasamos a Hibernate. Ahora Hibernate se encarga del SQL, y ejecuta INSERTs en la
base de datos. Echémosle un vistazo a la sesión, y al código de manejo de transacciones antes de ejecutarlo.

Usa sesión (Session) es una unidad de trabajo. Por ahora mantendremos todo simple y asumiremos una correspondencia
uno-a-uno entre una sesión de Hibernate y una transacción de BD. Para "escudar" nuestro codigo respecto del sistema
subyacente de transacciones (en este caso, sólo JDBC, pero podría haber sido JTA), usamos la API para transacciones que
está disponible en la clase Session.

¿Qué hace sessionFactory.getCurrentSession()? Primero, a este método se lo puede llamar desde dondequiera, y
cuantas veces se desee, una vez que obtenemos una SessionFactory. (fácilmente, gracias a la HibernateUtil). El código
getCurrentSession() siempre devuelve la unidad "actual" de trabajo. ¿Recuerda que configuramos una opción con
valor "thread" en hibernate.cfg.xml? Debido a esto, la unidad actual de trabajo está ligada al thread de Java que se esté
ejecutando en ese momento en su aplicación. De todos modos, ésta no es toda la historia: también hay que considerar el
alcance (scope), cuándo una unidad de trabajo empieza y cuándo termina.

Una sesión comienza cuando se la necesita por primera vez, cuando se hace la primera llamada a getCurrentSession().
Entonces, es ligada al thread actual por Hibernate. Cuando la transacción termina, Hibernate desliga la sesión del thread, y
la cierra por usted. Si usted llama getCurrentSession() de nuevo, obtiene una nueva sesión y comienza una nueva
unidad de trabajo. El modo de programación "ligado a threads" (thread-bound), es la forma más difundida de usar
Hibernate, dado que permite una distribución en capas muy flexible: el código de delimitación de transacciones puede
separarse del código de acceso a datos, como veremos más adelante.

En relación al alcance de la unidad de trabajo: Una sesión ¿debería usarse para ejecutar una sola operación de base de
datos, o varias? El ejemplo precedente usa una sesión para una operación. Esto es simple casualidad, el ejemplo no es lo
suficientemente complejo como para demostrar ningún otro enfoque. El alcance de una sesión de Hibernate es flexible,
pero nunca se debe designar una aplicación de maneera que utilice una sesión para cada operación de base de datos. Así
que, incluso si usted lo ve en algunos pocos de los ejemplos siguientes, considere la práctica de "una sesión por operación"
como algo a evitar (un "anti-pattern"). Una aplicación real (de web) se analiza más adelante en este instructivo.

Échele un vistazo al capítulo Capítulo 11, Transacciones y Concurrencia acerca del manejo de transacciones y su
delimitación. También hemos salteado cualquier manejo de errores en el ejemplo precedente.

Para ejecutar esta primera rutina, tenemos que agregar una "target" invocable al archivo de construcción de Ant.

<target name="run" depends="compile">

<java fork="true" classname="events.EventManager" classpathref="libraries">


<classpath path="${targetdir}"/>
<arg value="${action}"/>
</java>

</target>

El valor del argumento "action" se asigna en la línea de comandos cuando esta target se invoca.

14 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

C:\hibernateTutorial\>ant run -Daction=store

Después de la compilación, usted debería ver que que Hibernate arracna, y, dependiendo de su configuración, un montón
de salida de logueo. Al final encontrará la siguiente línea:

[java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)

Ése es el código INSERT ejecutado por Hibernate. Los signos de interrogación representan parámetros JDBC ligados. Para
ver los valores de dichos parámetros, o para reducir la locuacidad del archivo de log, revise su archivo
log4j.properties.

Ahora, querríamos tanbién listar los eventos almacenados, así que agregamos una opción el el método principal:

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());
}
}

También agregamos un nuevo método listEvents() method:

private List listEvents() {

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

session.beginTransaction();

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

session.getTransaction().commit();

return result;
}

Lo que hicimos aquí, es usar el lenguaje de consultas de Hibernate (HQL, por sus siglas en inglés) para cargar todos los
objetos Event que existen en la base de datos. Hibernate generará el código SQL que haga falta, lo enviará la base de
datos, y poblará los objetos Event con los datos que sean devueltos. Se pueden crear consultas SQL mucho más complejas
con HQL, por supuesto.

Ahora, para ejecutar y chequear todo esto, siga estos pasos:

Ejecute ant run -Daction=store para almacenar algo en la base de datos y, por supuesto, para previamente
generar el esquema de base de datos mediante hbm2ddl.

Ahora inhabilite hbm2ddl (convirtiendo la propiedad en un comentario) en el archivo hibernate.cfg.xml. Por lo


general, sólo se la deja habilitada cuando se efectúa un "unit testing continuo", pero en este caso, dejarla habilitada
borraría todo lo que usted haya almacenado hasta ese momento. (el valor de hbm2ddl="create" se traduce como
"haga un DROP de todas las tablas del esquema, y recree todas las tablas cuando la SessionFactory sea construida")

Si usted ahora invocara Ant con -Daction=list, debería ver los eventos que haya almacenado hasta ese moento. Por
supuesto, puede también invocar la acción store un par de veces más.

Nota: A esta altura, la mayoría de los usuarios de Hibernate experimenta problemas, y aparecen seguido mensajes del tipo
Table not found . De todos modos, si usted sigue los pasos que acabamos de describir cuidadosamente, no tendrá este
problema, ya que hbm2ddl crea el esquema de base de datos la primera vez, y las veces subsiguientes en que la aplicación
recomienza utilizan dicho esquema. Si usted en algún momento cambia algo del mapeo o del esquema, debe rehabilitar
hbm2dll nuevamente para recrear la BD.

1.3. Parte 2 - Mapear asociaciones


Hemos mapeado una clase persistente a una tabla. Ahora, partiendo de esta base, agreguemos algunas asociaciones de

15 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

clase. Primero, agregaremos algunas personas a nuestra aplicación, y almacenaremos una lista de eventos en los cuales
participan.

1.3.1. Mapear la clase Person

El primer bosquejo de la clase Person es simple:

package events;

public class Person {

private Long id;


private int age;
private String firstname;
private String lastname;

public Person() {}

// métodos "getter" y "setter" de acceso, y setter privado para 'id'

Cree un nuevo archivo de mapeo llamado Person.hbm.xml (y no olvide la referencia a la DTD en la parte superior):

<hibernate-mapping>

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

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


<generator class="native"/>
</id>
<property name="age"/>
<property name="firstname"/>
<property name="lastname"/>

</class>

</hibernate-mapping>

Por último, agreguemos el nuevo archivo de mapeo a la configuración de Hibernate.

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

Y ahora crearemos 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 las que tenemos que lidiar son: multiplicidad, direccionalidad, y
comportamiento de las colecciones.

1.3.2. Una asociación unidireccional basada en un Set

Agregaremos una colección de eventos a la clase Person. De este modo podremos navegar cómodamente los eventos para
una determinada persona sin tener que ejecutar una consulta explícita, simplemente llamando aPerson.getEvents().
Usamos una colección de Java, un Set, porque esta colección no contendrá elementos duplicados, y el orden no es
relevante para nosotros.

Necesitamos una asociación unidireccional y de varios valores, que pueda ser implementada con un Set. Escribamos el
código para esto en las clases de Java, y luego mapeémoslas.

public class Person {

private Set events = new HashSet();

public Set getEvents() {


return events;
}

public void setEvents(Set events) {


this.events = events;
}

16 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Antes de mapear esta asociación, piense en el otro lado (los eventos). Claramente, podemos simplemente mantener esta
asociación unidireccional. O si no, podríamos crear otra colección en la clase Event, si deseamos navegar en forma
bidireccional, es decir, anEvent.getParticipants(). Esto no es estrictamente necesario, desde un punto de vista
funcional, siempre se puede ejecutar una consulta explícita para obtener los participamntes de un determinado evento.
Esta es una opción de diseño que le dejamos a usted; pero lo que sacamos en limpio de esta discusión, es la multiplicidad
de la asociación: hay "muchos valores" desde ambos lados. A esto se lo llama una asociación "de-muchos-a-muchos"
(many-to-many). Por esto, usamos el mapeo many-to-many de Hibernate.

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


<id name="id" column="PERSON_ID">
<generator class="native"/>
</id>

<property name="age"/>
<property name="firstname"/>
<property name="lastname"/>

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


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

</class>

Hibernate soporta todo tipo de mapeos de coleccíón, siendo el "set" el más común. Para una asociación "many-to-many",
o relación entre entidades, se necesita una tabla de asociación. Cada registro de esta tabla simboliza un vínculo entre una
persona y un evento. El nombre de esta tabla se configura con el atributo table del elemento set. La columna
indentificadora por el lado de las personas, se define con el elemento <key>, el nombre de la columna por el lado de los
eventos, con el atributo column del <many-to-many>. Usted también debe decirle a Hibernate la clase de objetos de su
colección.

El esquema de base de datos para este esquema es, entonces:

_____________ __________________
| | | | _____________
| EVENTS | | PERSON_EVENT | | |
|_____________| |__________________| | PERSON |
| | | | |_____________|
| *EVENT_ID | <--> | *EVENT_ID | | |
| EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID |
| TITLE | |__________________| | AGE |
|_____________| | FIRSTNAME |
| LASTNAME |
|_____________|

1.3.3. Trabajar con asociaciones

Juntemos a alguna gente con sus eventos, en un nuevo método de EventManager:

private void addPersonToEvent(Long personId, Long eventId) {

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


session.beginTransaction();

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


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

aPerson.getEvents().add(anEvent);

session.getTransaction().commit();
}

Después de cargar una Person y un Event, simplemente modifique la colección usando los métodos normales de las
colecciones. Como puede usted ver, no hay llamados explícitos a update() o a save(), Hibernate detecta
automáticamente que la colección ha sido modificada y necesita ser actualizada. Esto se llama "dirty checking
automático", y usted puede experimentar con ello cmabiando la propiedad "date" en cualquiera de sus objetos. Siempre y
cuando se mantengan en un estado persistente (es decir, ligados a una sesión de Hibernate, por haber sido cargados o

17 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

grabados en una unidad de trabajo), Hibernate monitoreará cualquier cambio y ejecutará SQL entre bambalinas. El
proceso de sincronizar el estado de lo que está en memoria con la base de datos, generalmente al final de la unidad de
trabajo, se denomina "nivelar, desagotar" la sesión (en inglés, flush). En nuestro código, la unidad de trabajo termina con
un "commit" (o "rollback") de la transacción de base de datos, tal como se define por la opción de configuración thread
para la clase CurrentSessionContext.

Se podría, por supuesto, cargar personas y eventos en distintas unidades de trabajo. O modificar un objeto fuera de una
sesión, cuando no está en estado persistente (si lo estuvo anteriormente, este estado se llama "desprendido", en inglés
detached). Incluso una colección se puede modificar estando en este estado desprendido.

private void addPersonToEvent(Long personId, Long eventId) {

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


session.beginTransaction();

Person aPerson = (Person) session


.createQuery("select p from Person p left join fetch p.events where p.id = :pid")
.setParameter("pid", personId)
.uniqueResult(); // Eager fetch the collection so we can use it detached

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

session.getTransaction().commit();

// fin de la primera unidad de trabajo

aPerson.getEvents().add(anEvent); // aPerson (and its collection) is detached

// comienzo de la segunda unidad de trabajo

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


session2.beginTransaction();

session2.update(aPerson); // reasociación de aPerson, que estaba desprendida

session2.getTransaction().commit();
}

El llamado a update convierte de nuevo en persistente un objeto que estaba desprendido. Se podría decir que lo liga a una
nueva unidad de trabajo, de manera que cualquier modificación que se le hubiera hecho mientras estaba desprendido
pueda ser grabada en la base de datos. Esto incluye cualquier modificación que se le hubiera hecho a la colección de ese
objeto entidad.

Bueno, esto no es muy útil en la situación actual, pero es un concepto importante, que usted puede aplicar al diseño de su
propia aplicación. Por ahora, simplemente complete este ejercicio agregando una nueva acción al método main() de
EventManager, e invóquelo desde la línea de comandos. Si necesita los identificadores de una persona y de un evento, el
método save() los devuelve (tal vez usted deba modificar alguno de los métodos previos, para que devuelvan dicho
identificador).

else if (args[0].equals("addpersontoevent")) {
Long eventId = mgr.createAndStoreEvent("My Event", new Date());
Long personId = mgr.createAndStorePerson("Foo", "Bar");
mgr.addPersonToEvent(personId, eventId);
System.out.println("Added person " + personId + " to event " + eventId);
}

Éste fue un ejemplo de una asociación entre dos clases igualmente importantes, dos "entidades". Como se mencionó
anteriormente, hay otras clases y tipos "menos importantes" en un modelo típico. Algunos, usted ya los ha visto, como los
int o String. A esas clases las llamamos value types, y sus instancias dependen de una entidad en particular. Las
instancias de estos tipos no tienen su propia identidad, ni se pueden compartir entre entidades (dos personas no pueden,
por ejemplo, hacer referencia al mismo objeto firstname o primer nombre, incluso si son tocayos). Por supuesto, estos
value types pueden ser encontrados no solamente en la JDK. De hecho, en una aplicación Hibernate todas las clases JDK
son consideradas "value types", pero usted puede escribir sus propias clases dependientes, para representar una dirección
o una cantidad de dinero, por ejemplo.

También se puede diseñar una colección de "value types". Esto es conceptualmente bien distinto de una colección de
referencias a otras entidades, pero en el código Java ambas se ven casi igual.

1.3.4. Colección de valores

18 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Agreguemos una colección de objetos "value type" a la entidad Person. Queremos almacenar direcciones de correo
electrónico (email), de manera que el tipo que usaremos es String, y la colección, de nuevo un Set.

private Set emailAddresses = new HashSet();

public Set getEmailAddresses() {


return emailAddresses;
}

public void setEmailAddresses(Set emailAddresses) {


this.emailAddresses = emailAddresses;
}

El mapeo de este Set:

<set name="emailAddresses" table="PERSON_EMAIL_ADDR">

<key column="PERSON_ID"/>
<element type="string" column="EMAIL_ADDR"/>
</set>

La diferencia, comparada con el mapeo anterior, es la parte element, la cual le dice a Hibernate que la colección no
contiene referencias a otra entidad, sino a una colección de elementos de tipo string (al estar con minúscula nos damos
cuenta de que es un tipo/conversor de Hibernate). Une vez más, el atributo table del set determina el nombre de la tabla
para la colección. El elemento key define el nombre de columna para la clave foránea, El atributo column de "element"
define en dónde estas cadenas van a ser almacenadas.

Echémosle un vistazo al nuevo esquema de DB

_____________ __________________
| | | | _____________
| 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 |
|_____________|

Se puede ver que la clave primaria de la tabla de la colección es, en realidad, una clave compuesta, que usa ambas
columnas. Esto también implica que no puede haber direcciones de email duplicadas para una misma persona, lo cual es
precisamente la semántica de un conjunto o "Set" en Java.

Ahora podemos intentar agregar elementos a esta colección, igual que como hicimos antes al vincular personas y eventos.
El código Java es el mismo:

private void addEmailToPerson(Long personId, String emailAddress) {

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


session.beginTransaction();

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

// el getEmailAddresses() podría disparar una carga "haragana" de la colección


aPerson.getEmailAddresses().add(emailAddress);

session.getTransaction().commit();
}

Esta vez, no utilizamos una consulta con fetch para inicializar la colección. De ahí que la invocacíón de su método "getter"
disparará un SELECT adicional para inicializarla, de manera que podamos agregarle un elemento. Monitoree el log de
SQL e intente optimizar esto con un "eager fetch" (captura ansiosa).

1.3.5. Asociaciones bidireccionales

Acto seguido, vamos a mapear una asociación bidireccional, es decir, vamos a hacer que la asociación funcione desde

19 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

ambos lados en Java. Desde luego, el esquema de base de datos no cambia, todavía tenemos una multiplicidad de-muchos-
a-muchos (many-to-many). Una base de datos relacional es más flexible que un lenguaje de programación, no necesita
cosas como una "dirección de navegación", los datos pueden ser adquiridos de cualquier manera posible.

Primero, agregue una colección de participantes al código de la clase Event.

private Set participants = new HashSet();

public Set getParticipants() {


return participants;
}

public void setParticipants(Set participants) {


this.participants = participants;
}

Ahora, mapee también este lado de la asociación, en Event.hbm.xml.

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


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

Como puede ver, éstos son mapeos set normales en ambos documentos de mapeo. Fíjese en que los nombres de columna
en ambos documentos han sido alternados. La adición más importante es el atributo inverse="true" en el elemento set
del mapeo de la colección de Event.

Lo que esto significa, es que Hibernate debería rferirse al otro lado (el lado "Person"), cuando necesite obtener
información sobre el vínculo entre los dos. Esto será más fácil de entender una vez que veamos cómo se crea el vínculo
bidireccional entre las dos entidades.

1.3.6. Trabajar con vínculos bidireccionales

Primero que nada, tenga en cuenta que Hibernate no afecta la semántica normal de Java, ¿Cómo habiamos creado un
vínculo entre personas y eventos en el ejemplo unidireccional? Agregando una instancia de Event a la colección de
referencias contenida en una instancia de Person. Así que, obviamente, si queremos que este vínculo funcione también en
sentido inverso, tenemos que hacer lo mismo del otro lado: agregar una referencia a Person a la colección en Event. Esto
de "establecer el vínculo de los dos lados" es absolutamente necesario, y usted jamás debe olvidarse de hacerlo.

Muchos programadores trabajan "defensivamente", y crean métodos facilitadores que asignan correctamente los vínculos
a ambos lados. Por ejemplo, en la clase Person:

protected Set getEvents() {


return events;
}

protected void setEvents(Set events) {


this.events = events;
}

public void addToEvent(Event event) {


this.getEvents().add(event);
event.getParticipants().add(this);
}

public void removeFromEvent(Event event) {


this.getEvents().remove(event);
event.getParticipants().remove(this);
}

Fíjese en que los métodos "get" y "et" de la colección ahora son "protected" - esto les permite a las clases en el mismo
paquete, y a las subclases, acceder a los métodos, pero les impide a todos los demás inmiscuirse con estas colecciones
directamente (bueno... casi). Usted debería, probablemente, hacer lo mismo con las colecciones del otro lado.

Y ¿qué hay del atributo de mapeo inverse? Para usted (y para Java), un vínculo bidrecciolal es simplemente cuestión de
asignar correctamente las referencias en ambos lados .Hibernate, sin embargo, no tiene información suficiente como para
organizar adecuadamente sus llamados a comandos SQL INSERT y UPDATE. Marcar uno de los lados de la asociación
como inverse, básicamente le dice a Hibernate que lo ignore, que lo considere como un espejo de lo que ocurre en el otro

20 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

lado. Eso es todo lo que se necesita para que Hibernate resuelva todos los problemas que derivan de transformar un
modelo de navegación bidireccional en un esquema de base de datos. Las reglas que usted denbe recordar son son simples:
Toda asociación bidireccional necesita que uno de sus lados sea "inverse". En una asociación de-uno-a-muchos
(one-to-many) tiene que ser el lado "many". En una asociación de-muchos-a-muchos (many to many), elija cualquier lado,
da lo mismo.

1.4. Parte 3 - La aplicación de web "EventManager"


Convirtamos la siguiente discusión en una pequeña aplicación de web.

Una aplicación de web Hibernate utiliza sesiones y transacciones, lo mismo que la aplicacióm autosuficiente que vimos
anteriormente. Sin embargo, es conveniente usar algunos patrones de programación ("patterns"). Escribamos un servlet
EventManagerServlet que pueda listar los eventos alamacenados en la base de datos, y provea un formulario HTML
para ingresar nuevos eventos.

1.4.1. Escribir el servlet básico

Cree una nueva clase en su directorio de código fuente, en el paquete events:

package events;

// Imports

public class EventManagerServlet extends HttpServlet {

// Servlet code
}

Este servlet maneja requests HTTP del tipo GET solamente, así que el método que implementamos es doGet():

protected void doGet(HttpServletRequest request,


HttpServletResponse response)
throws ServletException, IOException {

SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy");

try {
// comienzo nuestra unidad de trabajo
HibernateUtil.getSessionFactory().getCurrentSession().beginTransaction();

// procesamiento de la solicitud (request) y presentación de la página...

// fin de la unidad de trabajo


HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().commit();

} catch (Exception ex) {


HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().rollback();
throw new ServletException(ex);
}

El "pattern" o patrón de programacíón que estamos aplicando aquí se llama session-per-request, (una sesión por cada
"solicitud" o request HTTP). Cuando una nueva request apela al servlet, se abre una nueva sesión de Hibernate mediante
el primer llamado a getCurrentSession() de la SessionFactory. Entonces, ss inicia una transacción de base de datos
(todo el código de acceso a datos ocurre dentro de la transacción, no importa si para lectura o escritura; en las aplicaciones
no usamos auto-commit).

Nunca cree una nueva sesión de Hibernate para cada operación de base de datos. Use una sesión de Hibernate a lo largo
de toda la request, es decir, que tenga un "alcance" de toda la sesión. Use getCurrentSession(), de manera que quede
automáticamente ligada al Thread actual.

A continuación, las acciones posibles de la request son procesadas, y es generada la respuesta HTML. Enseguida nos
dedicaremos a esa parte.

Finalmente, la "unidad de trabajo" finaliza, cuando el procesamiento y presentación hayan sido completados. Si ocurrió
algún problema durante este procesamiento o presentación, se lanzará una excepción y la se ejecutará un "rollback" de la

21 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

transacción. Esto cubre el "pattern" session-per-request. En lugar de escribir código para demarcar la transacción en
cada request, usted debería crea un Servlet Filter. Refiérase al sitio de web de Hibernate y a la Wiki para más información
acerca de este "pattern", llamado Open Session in View u OSIV, algo así como "apertura de una sesión por cada vista".
Este patrón se necesitará en cuanto usted considere presentar sus "vistas" usando JSP en lugar de servlets.

1.4.2. Procesamiento y presentación

Implementemos el procesamiento de la solicitud y la presentación de la página.

// escribe el header encabezado HTML

PrintWriter out = response.getWriter();


out.println("<html><head><title>Event Manager</title></head><body>");

// maneja las acciones

if ( "store".equals(request.getParameter("action")) ) {

String eventTitle = request.getParameter("eventTitle");


String eventDate = request.getParameter("eventDate");

if ( "".equals(eventTitle) || "".equals(eventDate) ) {
out.println("<b><i>Please enter event title and date.</i></b>");
} else {
createAndStoreEvent(eventTitle, dateFormatter.parse(eventDate));
out.println("<b><i>Added event.</i></b>");
}
}

// imprime la página
printEventForm(out);
listEvents(out, dateFormatter);

// escribe el pie HTML


out.println("</body></html>");
out.flush();
out.close();

Concedido, este tipo de escritura de código, mezclando Java y HTML, sería inadmisible en aplicaciones más complejas,
tenga en cuenta que sólo estamos ilustrando conceptos básicos de Hibernate en este instructivo. El código imprime un
encabezado y un pie de página en HTML. Dentro de la página propiamente dicha, se imprime un formulario (form) para el
ingreso de eventos. El primer método es trivial, y sólo produce HTML:

private void printEventForm(PrintWriter out) {


out.println("<h2>Add new event:</h2>");
out.println("<form>");
out.println("Title: <input name='eventTitle' length='50'/><br/>");
out.println("Date (e.g. 24.12.2009): <input name='eventDate' length='10'/><br/>");
out.println("<input type='submit' name='action' value='store'/>");
out.println("</form>");
}

El método listEvents() usa la sesión de Hibernate ligada al Thread actual para ejecutar una consulta:

private void listEvents(PrintWriter out, SimpleDateFormat dateFormatter) {

List result = HibernateUtil.getSessionFactory().getCurrentSession().createCriteria(Event.class)


if (result.size() > 0) {
out.println("<h2>Events in database:</h2>");
out.println("<table border='1'>");
out.println("<tr>");
out.println("<th>Event title</th>");
out.println("<th>Event date</th>");
out.println("</tr>");
for (Iterator it = result.iterator(); it.hasNext();) {
Event event = (Event) it.next();
out.println("<tr>");
out.println("<td>" + event.getTitle() + "</td>");
out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>");
out.println("</tr>");
}
out.println("</table>");
}

22 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Finalmente, la acción store es despachada al método createAndStoreEvent(), el cual usa la sesión del Thread actual.

protected void createAndStoreEvent(String title, Date theDate) {


Event theEvent = new Event();
theEvent.setTitle(title);
theEvent.setDate(theDate);

HibernateUtil.getSessionFactory().getCurrentSession().save(theEvent);
}

Eso es todo. El servlet está completo. Una request al servlet será procesada en una única sesión y transacción. Tal como
ocurrió en el ejemplo anterior, Hibernate puede ligar estos objetos al Thread actual de ejecución. Esto le da a usted la
libertad de acceder a la SessionFactory de cualquier manera que guste. Normalmente, usted usaría un diseño más
sofisticado, y movería el código de acceso a datos (DAO, por sus siglas en inglés) a otra "capa". Refiérase a la Wiki de
Hibernate para más ejemplos.

1.4.3. Despliegue (deploy) y test

Para desplegar (deploy) esta aplicación, tiene que crear un archivo ".war". Agregue la siguiente target de Ant a su archivo
build.xml:.

<target name="war" depends="compile">


<war destfile="hibernate-tutorial.war" webxml="web.xml">

<lib dir="${librarydir}">
<exclude name="jsdk*.jar"/>
</lib>

<classes dir="${targetdir}"/>
</war>
</target>

Esta target crea un archivo llamado hibernate-tutorial.war en su directorio de proyecto. Empaqueta todas las
bibliotecas y el descriptor web.xml, el cual se espera que esté en el directorio base de su proyecto.

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


<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4

<servlet>
<servlet-name>Event Manager</servlet-name>
<servlet-class>events.EventManagerServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>Event Manager</servlet-name>
<url-pattern>/eventmanager</url-pattern>
</servlet-mapping>

</web-app>

Antes de compilar y desplegar, como ésta es una aplicación de web, note que se necesita una biblioteca adicional :
jsdk.jar. Es el kit de herramientas o "developer kit" para los servlets de Java. Si aún no tiene esta biblioteca, descárguela
del sitio de web de Sun y colóquela en su directorio de bibliotecas (lib). De todos modos, se la usa para compilar
solamente, no se incluye en los paquetes WAR.

Para construir (build) y desplegar (deploy), invoque el comando ant war situado en el directorio de su proyecto, y luego
copie el archivo hibernate-tutorial.war en el directorio webapp de Tomcat. Si no tiene Tomcat instalado, descárguelo
y siga las instrucciones. No es necesario que cambie nada en la configuración de Tomcat para que este ejemplo ande.

Una vez desplegada y con Tomcat andando, accceda a la aplicación en http://localhost:8080/hibernate-tutorial


/eventmanager. Asegúrese de monitorear el log de Tomcar para comprobar si Hibernate se inicializa cuando ocurre la
primera solicitud (request) a su servlet (tiene que ejecutarse el inicializado estático en HibernateUtil), y también
asegúrese de obtener una mensaje detallado si ocurre algún error.

23 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

1.5. Sumario
Este instructivo cubrió lo básico para escribir una simple aplicación Hibernate autosuficiente, y una pequeña aplicación de
Web.

Si ya va tomando confianza con Hibernate, continúe hojeando la tabla de contenidos en la documentación de referencia,
buscando temas que le interesen. Los más solicitados son el procesamiento de transacciones (Capítulo 11, Transacciones y
Concurrencia), la performance de las capturas de datos o "fetch performance" (Capítulo 19, Mejorar la performance), el
uso de las API (interfaces de programación) (Capítulo 10, Trabajar con objetos) y las características de las consultas
(Sección 10.4, “Consultar”).

No olvide chequear el sitio de web de Hibernate en busca de más instructivos especializados.

Capítulo 2. Arquitectura

2.1. Generalidades
Una vista a vuelo de pájaro de la arquitectura de Hibernate

Este diagrama muestra a Hibernate usando datos de configuración y base de datos para proveerle servicios de persistencia
(y objetos persistentes) a la aplicación.

Nos gustaría mostrar una vista más detallada de la arquitectura en tiempo de ejecución. Lamentablemente, Hibernate es
tan flexible, que soporta muchas estrategias. Vamos a mostrar los dos extremos: la arquitectura "liviana" fuerza a la
aplicación a proveer sus propias conexiones JDBC y a gerenciar sus propias tranascciones. Esta arquitectura usa un
subconjunto mínimo de las APIs de Hibernate.

24 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

La arquitectura "con todas las luces" abstrae la aplicación, alejándola de las capas subyacentes de JDBC/JTA, y deja que
Hibernate se encargue de los detalles.

He aquí algunas definiciones de los objetos en los diagramas:

SessionFactory (org.hibernate.SessionFactory)

Un caché thread-safe e inmutable de mapeos, compilados para una base de datos en particular. Una fábrica (factory)
de sesiones, y cliente de ConnectionProvider. Puede contener un caché optativo (llamado "de 2do nivel") que es
reusable entre transacciones, a nivel de proceso o de cluster.

Session (org.hibernate.Session)

Un objeto de thread simple y de corta visa, que representa una conversación entre la aplicación y el repositorio
persistente. Envuelve a una fábrica de conexiones JDBC por transacción. Contiene un caché obligatorio (llamado
"de primer nivel") de objetos persistentes, que se usa al navegar el árbol de objetos, o cuando se buscan los objetos
por identificador.

Objetos y colecciones persistentes

Objetos de un solo thread y corta vida, que contienen "estado" persistente y cumplen una función de negocio.
Pueden ser JavaBeans comunes, o puros y simples objetos de Java (POJOs por sus siglas en inglés), la única
característica notable que tienen, es que están asociados con una sesión. En cuanto la sesión se cierra, serán
desprendidos, y quedarán listos para ser usados en cualquier capa de la aplicación (por ejemplo, directamente como
objetos de transmisión de datos o "DTOs", desde y hacia la capa de presentación).

25 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Objetos y colecciones transitorios y desprendidos

Instancias de clases persistenteses que, por el momento, no están asociadas con una sesión. Pudieron haber sido
instanciadas por la aplicacíón, y aún no haber sido asociadas con una sesión, o bien haber sido instanciadas por una
sesión que en ese momento esté cerrada.

Transacción (org.hibernate.Transaction)

(Optativo) un objeto de un solo thread y corta vida, usado por la aplicacíon para especificar unidades atómicas de
trabajo. Abstrae a la aplicación de la transacción JDBC, JTA o CORBA subyacente. Una sesíón puede extenderse a
lo largo de varias transacciones en algunos casos. ¡Pero, sea como sea, el código para demarcar transacciones (ya
sea utilizando APIs subyancentes o la interfaz Transaction) nunca es optativo!

ConnectionProvider (org.hibernate.connection.ConnectionProvider)

(Optativo) Una fábrica y repositorio (pool) de conexiones JDBC. Abstrae la aplicación de la fuente de datos
(Datasource) o del gerente de driver (DriverManager) subyacentes. No está expuesto directamente a la aplicación,
pero puede ser implementado o extendido por el programador.

TransactionFactory (org.hibernate.TransactionFactory)

(Optativo) Una fábrica de instancias de Transaction. No está expuesta directamente a la aplicación, pero puede ser
implementada o extendida por el programador.

Interfaces de extensión

Hibernate ofrece varias interfaces de extensión optativoes, se pueden implementar para personalizar el
comportamiento de la capa de persistencia. Vea la documentación de la API para más detalles.

En la arquitectura "liviana", la aplicación se saltea las APIs de Transaction/TransactionFactory y/o


ConnectionProvider APIs para dialogar con JTA o JDBC directamente.

2.2. Estados de una instancia


Una instancia de una clase persistente puede estar en uno de tres estados diferentes, los cuales se definen con respecto a
un contexto de persistencia: la sesión.

transitorio (transient)

La instancia no está asociada con ningún "contexto de persistencia" (sesión), ni nunca lo ha estado. Carece de
"identidad persistente", es decir, de clave primaria.

persistente (persistent)

La instancia está al momento asociada con un contexto de persistencia. Tiene identidad persistente (valor de clave
primaria) y, tal vez, un valor correspondiente en la base de datos. Para un contexto de persistencia determinado,
Hibernate garantiza que la identidad persistente equivale a la "identidad Java" (ubicación en memoria del objeto).

desprendida (detached)

La instancia estuvo alguna vez asociada con un contexto de persistencia, pero dicho contexto está cerrado, o la
instancia ha sido serializada a otro proceso. Tiene una identidad persistente y, tal vez, el correspondiente registro en
la base de datos. Hibernate no ofrece ninguna garantía acerca de la relación entre identida persistente e identidad
Java.

2.3. Integración con JMX


JMX es el estándar de J2EE para la administración de componentes Java. Hibernate puede ser administrado via un servicio
JMX estándar. Nosotros proveemos una implementación de MBean en la distribución:
org.hibernate.jmx.HibernateService.

Para un ejemplo sobre cómo desplegar Hibernate como un servicio JMX en el servidor de aplicaciones JBoss, por favor
vea la guía del usuario de JBoss. En el servidor de applicaciones JBoss, también se obtienen los siguientes beneficios si se

26 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

despliega (deploy) usando JMX:

Manejo de sesiones: El ciclo de vida de una sesión de Hibernate puede ser automáticamente ligado al alcance
(scope) de una transacción JTA. Esto significa que usted ya no necesita abrir y cerrar manualmente las sesiones, de
esto se encarga el interceptor de JBoss. Tampoco debe preocuparse ya por demarcar la transacción en su código (a
menos que quiera escribir usted mismo una capa de persistencia portátil, use la API Transaction de Hibernate para
eso). Puede llamar a HibernateContext para acceder a una sesión.

Despliegue del Archivo de Hibernate (HAR por sus siglas en inglés): Normalmente se despliega el servicio JMX de
Hibernate usando el "descriptor de despliegue" (deployment descriptor), en un archivo EAR o SAR, el cual soporta
las opciones de configuración de una SessionFactory de Hibernate. De todos modos, para esto aún se debe
nombrar a todos los archivos de mapeo en el descriptor de despliegue. En cambio, si decide usar el despliegue HAR,
JBoss detecta automáticamente todos los archivos de mapeo en su archivo HAR.

Consulte la guía del usuario del servidor de aplicaciones JBoss para mayor información acerca de estas opciones.

Otra característica disponible en forma de servicio JMX son las estadísticas de Hibernate en tiempo de ejecución. Vea la
Sección 3.4.6, “Estadísticas de Hibernate”

2.4. Soporte de JCA


Hibernate también puede ser configurado como un conector JCA. Por favor, vea el sitio de web para más detalles, pero
note que el soporte Hibernate de JCA se considera todavía experimental.

2.5. Sesiones contextuales


La mayoría de las aplicaciones que usan Hibernate necesitan alguna forma de sesión "contextual", donde una sesión
determinada esté en efecto a lo largo del "scope" o alacance de un contexto determinado. De todos modos, entre las
distintas aplicaciones, la definición de lo que constituye un "contexto" suele diferir, así como la definición de qué
constituye el contexto "actual". Las aplicaciones que usaban Hibernate antes de la versión a 3.0, tendían a utilizar o bien
versiones "caseras" de sesión contextual (basadas en ThreadLocal) o bien utilizaban frameworks de terceras partes (como
Spring o Pico), los cuales proveían sesiones contextuales basadas en proxies e intercepción.

Empezando con la versión 3.0.1, Hibernate incorporó el método SessionFactory.getCurrentSession(). Inicialmente,


este método asumía el uso de transacciones JTA, donde la transacción JTA definía tando el alcance como el contexto de la
sesión actual.
El equipo de Hibernate mantiene que, dada la madurez de las numerosas implementaciones autosuficientes de JTA que
existen hoy día, la mayoría (si no todas) las aplicaciones deberían estar usando JTA para su manejo de transacciones (estén
o no desplegadas en un contenedor J2EE). Basándose en eso, las sesiones contextuales JTA son todo lo que usted debería
necesitar usar.

Sin embargo, a partir de la versión 3.1, el proceso que ocurre por detrás de SessionFactory.getCurrentSession() es
configurable. Con este fin, una nueva interfaz de extensión (org.hibernate.context.CurrentSessionContext) y un
nuevo parámetro de configuración (hibernate.current_session_context_class) han sido agregados, para permitir
"enchufar" implementaciones nuevas para definir el alcance y contexto de las sesiones.

Vea los Javadocs acerca de la interfaz org.hibernate.context.CurrentSessionContext, para una descripción


detallada del contrato que ésta implica. Define un solo método, currentSession(), por el cual la implementación es
responsable de rastrear la sesión contextual actual. Hibernate ya viene con 3 implementaciones incuidas de esta interfaz.

org.hibernate.context.JTASessionContext - las sesiones actuales son localizadas y su alcance definido por


una transacción JTA. El procesamiento aquí es exactamente el mismo que en el antiguo enfoque sólo-JTA. Vea los
Javadocs para más detalles.

org.hibernate.context.ThreadLocalSessionContext - las sesiones actuales son localizadas por thread de


ejecución. De nuevo, vea los Javadocs para más detalles.

org.hibernate.context.ManagedSessionContext - las sesiones actuales son localizadas por thread de ejecución.


Sin embargo, usted es responsable por ligar y desligar las instancias de sesión, mediante métodos estáticos en esta
clase. Nunca abre, cierra o le aplica flush a una sesión.

Las dos primeras implementaciones proveen un modelo de programación del tipo "una sesión - una transacción de base de

27 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

datos", también llamado session-per-request. El comienzo y fin de una sesión de Hibernate está definido por la duración
de la transacción de base de datos. Si usted usa demarcación programática de transacciones, en simple JSE sin JTA, se le
aconseja que use la API de Transaction de Hibernate, para ocultar el sistema de transacciones subyacente. Si el código
se está ejecutando en un contenedor EJB que soporte CMT, los límites de la transacción son definidos declarativamente, y
usted no necesita ninguna transacción ni demarcación de operaciones en el código propiamente dicho. Refiérase al
Capítulo 11, Transacciones y Concurrencia para más información y ejemplos de código.

El parámetro de configuración hibernate.current_session_context_class define cuál implementación de


org.hibernate.context.CurrentSessionContext debe ser usada. Note que, por compatibilidad con versiones
anteriores, si este parámetro de configuración no se asigna, sino que se configura
org.hibernate.transaction.TransactionManagerLookup en su lugar, Hibernate usará el
org.hibernate.context.JTASessionContext. Típicamente, el valor de este parámetro simplemente nombra la clase de
implementación a usar; para las tres implementaciones de fábrica existen los respectivos nombres cortos: "jta", "thread" y
"managed".

Capítulo 3. Configuración
Como Hibernate está diseñado para operar en varios entornos diferentes, hay un gran número de parámetros de
configuración. Afortunadamente, la mayoría tiene valores por defecto razonables, e Hibernate es distribuido con un
archivo hibernate.properties de ejemplo en el directorio etc/, que muestra varias de las opciones. Simplemente
coloque este ejemplo en su classpath y modifíquelo a medida.

3.1. Configuración programática


Una instancia de org.hibernate.cfg.Configuration representa un conjunto completo de mapeos desde los tipos Java
de una aplicación, hacia una base de datos SQL. org.hibernate.cfg.Configuration se usa para construir una
org.hibernate.SessionFactory inmutable. Los mapeos son compilados a partir de los varios archivos de mapeo XML.

Se puede ontener una instancia de org.hibernate.cfg.Configuration instanciándola directamente, y especificando los


archivos de mapeo XML. Si los archivos de mapeo están en el classpath, utilice addResource():

Configuration cfg = new Configuration()


.addResource("Item.hbm.xml")
.addResource("Bid.hbm.xml");

Una alternativa (a veces preferible), es especificar la clase mapeada, y dejar que Hibernate encuentre el documento de
mapeo él solo:

Configuration cfg = new Configuration()


.addClass(org.hibernate.auction.Item.class)
.addClass(org.hibernate.auction.Bid.class);

Entonces Hibernate buscará archivos de mapeo llamados /org/hibernate/auction/Item.hbm.xml y /org/hibernate


/auction/Bid.hbm.xml en el calsspath. Este enfoque elimina la necesidad de "hardcodear" los nombres de archivo.

org.hibernate.cfg.Configuration también permite especificar propiedades de configuración:

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");

Ésta no es la única manera de pasarle propiedades de configuración a Hibernate. Entre las muchas otras opciones, están
éstas:

Pasarle una instancia de java.util.Properties a Configuration.setProperties().

Colocar un archivo llamado hibernate.properties en un directorio raíz del classpath.

Configurar propiedades "de sistema" (System) usando java -Dproperty=value.

28 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Incluir elementos <property> en hibernate.cfg.xml (se discute más adelante).

hibernate.properties es el enfoque más fácil, si lo que se busca es empezar rápido.

La idea de org.hibernate.cfg.Configuration es ser un objeto que viva durante el "tiempo de arranque" (startup), para
ser descartado luego, una vez que la SessionFactory haya sido creada.

3.2. Obtener una SessionFactory


Una vez que todos los mapeos hayan sido revisados por org.hibernate.cfg.Configuration, la aplicación debe obtener
una fábrica o "SessionFactory" para las instancias de org.hibernate.Session. Esta fábrica será compartida por todos los
threads que accedan a la aplicacíón.

SessionFactory sessions = cfg.buildSessionFactory();

Hibernate sí le permite a la aplicación instanciar más de una org.hibernate.SessionFactory. Esto es útil si se está
usando más de una base de datos.

3.3. Conexiones JDBC


Usualmente, usted querrá que la org.hibernate.SessionFactory cree y administre el "fondo común" (pool) de sesiones
por usted. Si se adopta este enfoque, abrir una org.hibernate.Session es tan simple como escribir:

Session session = sessions.openSession(); // abre una nueva sesión

No bien se haga algo que requiera acceso a la base de datos, una nueva conexión JDBC será obtenida del "pool".

Para que esto funcione, necesitamos pasarle algunas propiedades de conexión JDBC a Hibernate. Todos los nombres y
semántica de las propiedades Hibernate están definidos en la clase org.hibernate.cfg.Environment. Vamos a describir
los valores más importantes para configurar la conexión JDBC.

Hibernate obtendrá las conexiones (y las administrará en un "pool") usando un java.sql.DriverManager si usted
configura las siguientes propiedades:

Table 3.1. Hibernate JDBC Properties

Nombre de la propiedad Propósito


hibernate.connection.driver_class clase del driver JDBC
hibernate.connection.url URL de JDBC
hibernate.connection.username usuario de la base de datos
hibernate.connection.password clave del usuario de la base de datos
hibernate.connection.pool_size número máximo de conexiones en el "pool"

El algoritmo que ya viene incluido en Hibernate para el "pooling" de conexiones es, sin embargo, bastante rudimentario.
Fue concebido más que nada para ayudarlo a dar los primeros pasos, no para ser usado en un sistema de producción, ni
siquiera para un test de performance. Ustde debería usar un "pool" de algún tercero para asegurar mayor redimiento y
estabilidad. Simplemente reemplace la propiedad hibernate.connection.pool_size con propiedades específicas de la
herramienta de "pooling" de su elección. Esto desactivará el "pooling" interno de Hibernate. Por ejemplo, usted podría
elegir C3P0.

C3P0 es una biblioteca para pool de conexiones distribuida junto con Hibernate en el direactorio lib. Hibernate usará su
org.hibernate.connection.C3P0ConnectionProvider para efectuar el "pooling" de conexiones, si usted les asigna
valores a las propiedades que empiezan con hibernate.c3p0.*. Si prefiere usar Proxool, refiérase a las propiedades
empaquetadas en hibernate.properties, o al sitio de web de Hibernate para mayor información.

He aquí un ejemplo de un hibernate.properties para C3P0:

hibernate.connection.driver_class = org.postgresql.Driver

29 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

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

Para usar "pooling" dentro de un servidor de aplicaciones, se debería configurar Hibernate de manera tal, que siempre
obtenga las conexiones desde una fuente de datos javax.sql.Datasource registrada usando JNDI. Se necesitarán por lo
menos las siguientes propiedades:

Table 3.2. Propiedades para fuente de datos de Hibernate

Nombre de la propiedad Propósito


hibernate.connection.datasource nombre JNDI de la fuente de datos
hibernate.jndi.url URL del proveedor de JNDI (optativo)
hibernate.jndi.class clase del InitialContextFactory de JNDI (optativo)
hibernate.connection.username usuario de la base de datos (optativo)
hibernate.connection.password clave del usuario de base de datos (optativo)

He aquí un ejemplo de un archivo hibernate.properties para configurar la fuente de datos JNDI en un servidor de
aplicaciones:

hibernate.connection.datasource = java:/comp/env/jdbc/test
hibernate.transaction.factory_class = org.hibernate.transaction.JTATransactionFactory
hibernate.transaction.manager_lookup_class = org.hibernate.transaction.JBossTransactionManagerLooku
hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect

Las conexiones JDBC que se obtengan de una fuente de datos JNDI participarán automáticamente de las transacciones
"manejadas por el contenedor" del servidor de aplicaciones.

También se pueden agregar propiedades de conexión arbitrarias, afijando "hibernate.connection" al nombre de la


propiedad de conexión. Por ejemplo, usted puede especificar una propiedad de conexión charSet (juego de caracteres),
usando "hibernate.connection.charSet".

Usted también puede definir su propia estrategia de plugin para obtener conexiones JDBC, implementando la interfaz
org.hibernate.connection.ConnectionProvider, y especificando su propia implementación a medida en la propiedad
hibernate.connection.provider_class.

3.4. Propiedades optativas de configuración


Hay varias otras propiedades que controlan el comportamiento de Hibernate en tiempo de ejecución. Todas son optativas,
y tienen valores por defecto razonables.

Advertencia: algunas de estas propiedades existen "a nivel de sistema" solamente. A las propiedades a nivel de sistema
sólo se les puede asignar valores via java -Dproperty=value o hibernate.properties. No se les puede asignar valores
usando las técnicas descritas anteriormente.

Tabla 3.3. Porpiedades de Configuration de Hibernate

Nombre de la propiedad Propósito


El nombre de clase de un org.hibernate.dialect.Dialect que le permita a
Hibernate generar SQL optimizado para una BD relacional en particular.
Tabla 3.4. Propiedades Hibernate de JDBC y Conexión
hibernate.dialect por ejemplo classname.completo.del.dialecto
Nombre de la propiedad Propósito
En laUn
mayoría de los casos,
valor distinto Hibernateelserá
de 0 determina capazdedelaelegir
tamaño la implementación
captura o "fetch" JDBC de
(llama
hibernate.jdbc.fetch_size dialecto correcta, basándose en los metadatos devueltos por el driver JDBC.
a Statement.setFetchSize() ).

30 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Nombre de la propiedad Propósito


Un valor distinto de 0 habilita el uso de actualizacionesn en lotes (batch
updates) de JDBC2 por parte de Hibernate.
hibernate.jdbc.batch_size
valores posibles se recomienda entre 5 y 30

Asígnele true a esta propiedad, si su driver JDBC devuelve conteos de fila al


ejecutar executeBatch() (normalmente, es seguro hacerlo). Entonces,
Hibernate usará "batched DML" (lenguaje de creación de datos en lotes) para
hibernate.jdbc.batch_versioned_data datos a los que se les haya asignado automátiacmente número de versión. El
valor por defecto es false.

valores posibles true | false

Selecciona un org.hibernate.jdbc.Batcher hecho a medida. La mayoría de


las aplicaciones no necesita configurar esta propiedad.
hibernate.jdbc.factory_class
por ejemplo classname.of.BatcherFactory

Habilita el uso de resultados JDBC2 navegables (scrollable resultsets) por parte


de Hibernate. Esta propiedad sólo es necesaria cuando se emplean conexiones
JDBC provistas por el usuario. De otro modo, Hibernate usa los metadatos de
hibernate.jdbc.use_scrollable_resultset la conexión.

valores posibles true | false

Indica que se usarán streams al leer o escribir código binario, o tipos


serializables desde o hacia JDBC. *propiedad a nivel de sistema*
hibernate.jdbc.use_streams_for_binary
valores posibles true | false

Habilita el uso del PreparedStatement.getGeneratedKeys() de JDBC3


para capturar claves generadas en forma nativa luego de insertar. Requiere que
a Requires JDBC3+ driver y a JRE1.4+, le sea asignado "false" si el driver que
usted está usando tiene problemas con los generadores de identificadores. Por
hibernate.jdbc.use_get_generated_keys defecto, intenta determinar las capacidades del driver usando los metadatos de
conexión.

valores posibles true|false

El nombre de clase de un
org.hibernate.connection.ConnectionProvider hecho a medida que le
hibernate.connection.provider_class provea conexiones JDBC a Hibernate.

por ejemplo classname.of.ConnectionProvider

Determina el nivel de aislamiento de las transacciones JDBC. Revise


java.sql.Connection para averiguar qué valores tiene sentido asignar aquí,
pero note que la mayoría de las BD no soportan todos los niveles de
hibernate.connection.isolation aislamiento (isloation levels), y algunas definen niveles no estándar adicionales.

por ejemplo 1, 2, 4, 8

Habilita la autocomisión (autocommit) para las conexiones JDBC en pool (no


se recomienda).
hibernate.connection.autocommit
valors posibles true | false

31 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Nombre de la propiedad Propósito


Especifica cuándo Hibernate debería liberar conexiones. Por defecto, una
conexión JDBC es retenida hasta que la sesión es cerrada explícitamente o
desconectada. Para fuentes de datos JTA en un servidor de aplicaciones, se
debería usar after_statement para liberar conexiones agresivamente luego
de cada llamado a JDBC. Para conexiones que no sean JTA, a menudo tiene
sentido liberar las conexiones al final de cada transacción, usando
after_transaction. auto elegirá after_statement para las estrategias JTA
y CMT (manejadas por el contenedor), y after_transaction para las
estrategias transaccionales JDBC.
hibernate.connection.release_mode
valores posibles auto (el valor por defecto) | on_close | after_transaction
| after_statement

Note que este valor sólo afecta sesiones devueltas por


SessionFactory.openSession. Para las que se hayan obtenido usando
SessionFactory.getCurrentSession, lo que importa para determinar el
modo de liberación de conexiones, es la implementación que se haya
configurado de CurrentSessionContext. Vea la Sección 2.5, “Sesiones
Contextuales”

hibernate.connection. Le pasa el valor de propiedad JDBC <propertyName> a


<nombreDeLaPropiedad> DriverManager.getConnection().

hibernate.jndi. Le pasa la propiedad <nombreDeLaPropiedad> a la


<nombreDeLaPropiedad> InitialContextFactory de JNDI.

Tabla 3.5. Propiedades del caché de Hibernate

Nombre de la propiedad Propósito


El nombre de clase de un CacheProvider hecha a medida.
hibernate.cache.provider_class
por ejemplo classname.of.CacheProvider

Optimiza el caché de 2do nivel para minimizar escrituras, al costo de


realizar lecturas más frecuentemente. Esta configuración es más útil
con cachés en "cluster", y en Hibernate3 se habilita por defecto con
hibernate.cache.use_minimal_puts cachés en cluster.

valores posibles true|false

Habilita el caché de consultas. Cada consulta (query) debe ser


individualmente configurada como "cacheable".
hibernate.cache.use_query_cache
valores posibles true|false

Puede ser usado para inhabilitar completamente el caché de 2do nivel,


el cual está habilitado por defecto para toda clase que especifique un
hibernate.cache.use_second_level_cache mapeo <cache>

valores posibles true|false

El nombre de una interfaz QueryCache personalizada, que por defecto


es StandardQueryCache, la cual ya viene de fábrica.
hibernate.cache.query_cache_factory
por ejemplo classname.of.QueryCache

32 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Nombre de la propiedad Propósito


Un prefijo a usar con los nombres de región de caché de 2do nivel.
hibernate.cache.region_prefix
por ejemplo prefix

Fuerza a Hibernate a almacenar datos en el caché de 2do nivel en un


formato más legible.
hibernate.cache.use_structured_entries
valores posibles true|false

Tabla 3.6. Propiedades para la configuración de transaciones en Hibernate

Nombre de la propiedad Propósito


El nombre de clase de la TransactionFactory a usar con
Hibernate (por defecto, JDBCTransactionFactory).
hibernate.transaction.factory_class
por ejemplo classname.of.TransactionFactory

El nombre JNDI usado por JTATransactionFactory para


obtener una UserTransaction JTA del servidor de
jta.UserTransaction aplicaciones.

por ejemplo jndi/composite/name

El nombre de clase de un TransactionManagerLookup.


Obligatorio cuando se utiliza caché a nivel de la JVM, o se usa
hibernate.transaction.manager_lookup_class el generador hilo en un entorno JTA.

por ejemplo classname.of.TransactionManagerLookup

Si está habilitado, a la sesión le será automátiamente hecho un


"flush" antes de la fase de compleción de la transacción. Es
preferible usar el manejo de transacciones que ya viene
hibernate.transaction.flush_before_completion incorporado, y el manejo automático de contexto de sesiones;
ver laSección 2.5, “Sesiones contextuales”.

valores posibles true | false

Si está habilitado, la sesión será automáticamente cerrada


durante la fase de compleción de la transacción. Es preferible
usar el manejo de transacciones que ya viene incorporado, y el
hibernate.transaction.auto_close_session manejo automático de contexto de sesiones; ver la
Sección 2.5, “Sesiones contextuales”.

valores posibles true | false

Tabla 3.7. Propiedades Misceláneas

Nombre de la propiedad Propósito


Provee una estrategia personalizada para determinar el alcance
(scope) respecto de cuál es la sesión "actual". Véase la Sección 2.5,
3.4.1. Dialectos de SQL “Sesiones Contextuales” para más información sobre cuáles son las
hibernate.current_session_context_class opciones que ya vienen incorporadas.
Siempre se debería configurar la propiedad hibernate.dialect al valor correcto. de la subclase de
org.hibernate.dialect.Dialect que correspondavalores posibles
a su BD. jta | thread
Si se especifica | managed
un dialecto, | la clase
Hibernate usará valores por
personalizada
defecto adecuados para otras de las propiedades listadas anteriormente, ahorrándole el esfuerzo de especificarlas

33 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

manualmente.

Tabla 3.8. Dialectos SQL de Hibernate (hibernate.dialect)

Base de datos relacional Dialecto


DB2 org.hibernate.dialect.DB2Dialect

DB2 AS/400 org.hibernate.dialect.DB2400Dialect

DB2 OS390 org.hibernate.dialect.DB2390Dialect

PostgreSQL org.hibernate.dialect.PostgreSQLDialect

MySQL org.hibernate.dialect.MySQLDialect

MySQL with InnoDB org.hibernate.dialect.MySQLInnoDBDialect

MySQL with MyISAM org.hibernate.dialect.MySQLMyISAMDialect

Oracle (any version) org.hibernate.dialect.OracleDialect

Oracle 9i/10g org.hibernate.dialect.Oracle9Dialect

Sybase org.hibernate.dialect.SybaseDialect

Sybase Anywhere org.hibernate.dialect.SybaseAnywhereDialect

Microsoft SQL Server org.hibernate.dialect.SQLServerDialect

SAP DB org.hibernate.dialect.SAPDBDialect

Informix org.hibernate.dialect.InformixDialect

HypersonicSQL org.hibernate.dialect.HSQLDialect

Ingres org.hibernate.dialect.IngresDialect

Progress org.hibernate.dialect.ProgressDialect

Mckoi SQL org.hibernate.dialect.MckoiDialect

Interbase org.hibernate.dialect.InterbaseDialect

Pointbase org.hibernate.dialect.PointbaseDialect

FrontBase org.hibernate.dialect.FrontbaseDialect

Firebird org.hibernate.dialect.FirebirdDialect

3.4.2. Captura (fetch) por Outer Join

Si su base de datos soporta outer joins del estilo ANSI, Oracle o Sybase, la captura con outer joins a menudo aumentará la
performance, limitando la cantidad de viajes de ida y vuelta a la BD (probablemente a costa de un mayor trabajo
efectuado por la base de datos misma). La captura por outer joins permite que todo un árbol (graph) de ojetos conectados
por asociaciones de-uno-a-muchos, de-muchos-a-uno, de-muchos-a-muchos, y de-uno-a-uno sea capturado de una sola
vez, con un solo comando SQL SELECT.

La captura mediante outer joins puede ser inhabilitada globalmente asignándole el valor 0. a la propiedad
hibernate.max_fetch_depth. Al asignar un valor de 1 o superior, se permiten las capturas (fetch) con outer joins para
asociaciones que hayan sido mapeadas como de-una-a-una o de-muchas-a-una con fetch="join".

Vea Sección 19.1, “Estrategias de captura (fetch)” para más información.

3.4.3. Streams binarios

Oracle limita el tamaño de los arrays de tipo byte que pueden ser pasados desde y hacia el driver JDBC. Si usted desea
usar instancias grandes de tipos binary o serializable, debería habilitar hibernate.jdbc.use_streams_for_binary.
Ésta es una propiedad configurable a nivel de sistema solamente.

34 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

3.4.4. Caché de 2do nivel y caché de consultas.

Las propiedades con prefijo hibernate.cache le permiten usar un sistema de caché de 2do nivel con alcance (scope) de
proceso o de cluster. Vea Sección 19.2, “El caché de 2do nivel” para más detalles.

3.4.5. Sustituciones en el lenguaje de consultas.

Se pueden definir nuevos símbolos para las consultas de Hibernate usando hibernate.query.substitutions. Por
ejemplo:

hibernate.query.substitutions true=1, false=0

causrá que los símbolos true y false sean traducidos como valores enteros literates en el SQL que se genere.

hibernate.query.substitutions toLowercase=LOWER

permitiría renombrar la propiedad LOWER (pasar a minúsculas) de SQL

3.4.6. Estadísticas de Hibernate

Si se habilita hibernate.generate_statistics, Hibernate expondrá un buen número de mediciones que son útiles al
ajustar (tuning) el rendimiento de un sistema en marcha, a través de SessionFactory.getStatistics(). Hibernate
incluso puede ser configurado para exponer dichas estadísticas via JMX. Lea el Javadoc de las interfaces en
org.hibernate.stats para más información.

3.5. Logueo (logging, bitácora)


Hibernate utiliza la librería llamada "Fachada simple de logueo para Java" (Simple Logging Facade for Java o SLF4J por
sus siglas en inglés), para loguear varios eventos de sistema. SLF4J puede dirigir la salida de su actividad de logueo a
varios "frameworks" de logueo: NOP, Simple, log4j 1.2, JDK 1.4, JCL o logback), dependiendo de la vinculación elegida.
Para configurar el logueo adecuadamente, necesitará slf4j-api.jar en su classpath, así como el archivo de jar de su
preferencia (en el caso de log4j, slf4j-log4j12.jar). Vea la documentación de SLF4J para más detalles. Para usar log4j
también necesitará colocar un archivo log4j.properties en su classpath. Un archivo de propiedades a modo de ejemplo
viene distribuido con Hibernate en el directorio src/.

Le recomendamos fuertemente que se familiarice con los mensajes de log de Hibernate. Se ha invertido mucho trabajo en
lograr que el logueo en Hibernate sea lo más detallado posible, sin volverlo ilegible. Es una herramienta esencial para la
detección y resolución de problemas. Las categorías más interesantes son las siguientes:

Table 3.9. Hibernate Log Categories

Category (categoría) Función


Loguea todo el "lenguaje de modificación de datos" (DML) de SQL a medida que éste
org.hibernate.SQL
es ejecutado.
org.hibernate.type Loguea todos los parámetros JDBC
Loguea todos los comandos SQL de definición de datos (DDL) que hayan sido
org.hibernate.tool.hbm2ddl
ejecutados
Loguea el estado de todas las entidades (con un máximo de 20) asociadas con una
org.hibernate.pretty
sesión al momento de aplicarle "flush".
org.hibernate.cache Loguea toda la actividad del caché de 2do nivel
org.hibernate.transaction Loguea toda actividad relacionada con transacciones
org.hibernate.jdbc Loguea toda adquisición de recursos JDBC
org.hibernate.hql.ast.AST Loguea todo AST de HQL y SQL durante la revisión (parsing) de las consultas

35 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Category (categoría) Función


org.hibernate.secure Loguea todas las autorizaciones para requests JAAS
Loguea todo (un montón de información, pero muy útil a para detectar y solucionar
org.hibernate
problemas).

Al desarrollar aplicaciones con Hibernate, se debería trabajar casi siempre con debug habilitado para
org.hibernate.SQL, o, alternativamente, habilitar la propiedad hibernate.show_sql enabled.

3.6. Implementar una NamingStrategy


La interfaz org.hibernate.cfg.NamingStrategy permite especificar un estándar de nombrado o "NamingStrategy" para
los objetos de base de datos y elementos del esquema.

Usted puede proveer reglas para generar automáticamente los identificadores a partir de identificadores Java, o para
procesar los nombres de tabla y columna "lógicos" dados en el archivo de mapeo, y convertirlos en nombres de tabla y
columna "físicos". Esta característica reduce la locuacidad de los documentos de mapeo, eliminando el "ruido" provocado
por la repetición de prefijos TBL_, por ejemplo. La estrategia que Hibernate usa por defecto es bastante parca.

Se puede especificar una estrategia diferente, llamando a Configuration.setNamingStrategy() antes de agregar


mapeos.

SessionFactory sf = new Configuration()


.setNamingStrategy(ImprovedNamingStrategy.INSTANCE)
.addFile("Item.hbm.xml")
.addFile("Bid.hbm.xml")
.buildSessionFactory();

org.hibernate.cfg.ImprovedNamingStrategy es una estrategia que ya viene de incorporada, y puede ser un punto de


partida útil para algunas aplicaciones.

3.7. Archivo de configuración XML


Un enfoque alternativo de configuración, es especificar la configuración completa en un archivo llamado
hibernate.cfg.xml. Este archivo puede usarse en reemplazo del archivo hibernate.properties, o, si ambos están
presentes, para suplantar propiedades de éste.

Se espera que el archivo de configuración XML esté por defecto en la raíz de su CLASSPATH. He aquí un ejemplo:

<?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>

<!-- una instancia de SessionFactory listada como /jndi/name -->


<session-factory
name="java:hibernate/SessionFactory">

<!-- propiedades -->


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

<!-- archivos de mapeo -->


<mapping resource="org/hibernate/auction/Item.hbm.xml"/>
<mapping resource="org/hibernate/auction/Bid.hbm.xml"/>

<!-- configuración del caché -->

36 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

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

Como se puede ver, la ventaja de este enfoque es externalizar la configuración de los nombres de los acrhivos de mapeo.
hibernate.cfg.xml también es más conveniente para ajustar los valores del caché. Note que usar hibernate.cfg.xml o
hibernate.properties es indistinto, excepto por los beneficios mencionados de usar una sintaxis XML.

Con la configuración XML, hacer arrancar a Hibernate es tan simple como:

SessionFactory sf = new Configuration().configure().buildSessionFactory();

Se puede elegir un archivo distinto de configuración, usando:

SessionFactory sf = new Configuration()


.configure("catdb.cfg.xml")
.buildSessionFactory();

3.8. Integración con los Servidores de Aplicación J2EE


Hibernate tiene los siguientes puntos de integración con la infraestructura J2EE

Fuentes de datos manejadas por el contenedor: Hibernate puede usar conexiones JDBC manejadas por el
contenedor (container-mamaged), provistas a través de JNDI. Usualmente, un TransactionManager compatible
con JTA y un ResourceManager se hacen cargo del manejo de transacciones (CMT), especialmente transcciones
distribuidas a lo largo de varias fuentes de datos. Por supuesto, usted también puede demarcar los límites de sus
transacciones programáticamente (BMT) o usar la API de transacciones de Hibernate (Transaction) para mantener
su código portable.

Enlace JNDI automático: Hibernate puede ligar su SessionFactory a JNDI luego del arranque.

Enlace de sesión JTA: La sesión de Hibernate puede ser automáticamente ligada al alcance o "scope" de las
transacciones JTA. Simplemente obtenga la SessionFactory de JNID (haciendo un "lookup"), y de ahí obtenga la
sesión actual. Deje que Hibernate se haga cargo de aplicarle "flush" y de cerrar la sesión cuando la transacción JTA
se haya terminado. La demarcación de transacciones es o bien declarativa (CMT) o bien programática
(BMT/UserTransaction).

Despliegue JMX: Si usted cuenta con un servidor de aplicaciones habilitado para JMX, (por ejemplo, JBoss), puede
elegir desplegar Hibernate como un "Managed MBean" Esto le ahorra el códgo de arranque de una línea que
construye la SessionFactory a partir de Configuration. El container hará arrancar su HibernateService, e,
idealmente, también se hará cargo de las dependencias de servicios (la fuente de datos tiene que estar disponible
antes de que Hibernate arranque, etc).

Dependiendo del entorno, usted puede tener que asignar el valor "true" a la opción de configuración
hibernate.connection.aggressive_release, si su servidor de aplicaciones produce excepciones de "contención de
conexiones" ("connection containment" exceptions).

3.8.1. Configuración de una estrategia transaccional

La sesión de Hibernate es independiente de cualquier sistema de demarcación de transacciones presente en la arquitectura


de su sistema. Si usted deja que Hibernate use JDBC directamente a través de un "pool" de conexiones, usted puede
comenzar y terminar sus transacciones llamando a la API de JDBC. Si usted ejecuta su aplicación en un servidor de
aplicaciones J2EE, es posible que prefiera usar transaccioes manejadas por bean (bean-managed) e invocar la API de JTA
y UserTransaction según haga falta.

Para mantener su código portable entre estos dos entornos (y otros), le recomedamos emplear la API Transaction de
Hibernate, la cual envuelve y oculta el sistema subyacente. Va a tener que especificar una clase "fábrica" de instancias de
Transaction, configurando la propiedad de Hibernate hibernate.transaction.factory_class.

37 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Éstas son las tres opciones estándar, que ya vienen incluidas "de fábrica":

org.hibernate.transaction.JDBCTransactionFactory

delega a las transacciones JDBC de la base de datos (es el valor por defecto)

org.hibernate.transaction.JTATransactionFactory

delega a la transacción manejada por el contenedor (container-managed) si hay una tranascción en proceso en este
contexto (por ejemplo, un método de un Session EJB), en caso contrario, crea una nueva transción, y se utilizan
transacciones manejadas por bean.

org.hibernate.transaction.CMTTransactionFactory

delega a transacciones JTA manejadas por el contenedor

Usted también puede definir sus propias estrategias transaccionales (por un servicio de transacciones CORBA, por
ejemplo).

Algunas características de Hibernate (por ejemplo, caché de 2do nivel, sesiones contextuales con JTA, etc) requieren aceso
al TransactionManager de JTA en un entorno administrado. En un servidor de aplicaciones, usted tiene que especificar
cómo Hibernate obtendrá una referencia al TransactionManager, dado que en J2EE no hay un mecanismo estándar y
único.

Tabla 3.10. TransactionManagers de JTA

Fábrica de transacciones Servidor de aplicaciones


org.hibernate.transaction.JBossTransactionManagerLookup JBoss
org.hibernate.transaction.WeblogicTransactionManagerLookup Weblogic
org.hibernate.transaction.WebSphereTransactionManagerLookup WebSphere
org.hibernate.transaction.WebSphereExtendedJTATransactionLookup WebSphere 6
org.hibernate.transaction.OrionTransactionManagerLookup Orion
org.hibernate.transaction.ResinTransactionManagerLookup Resin
org.hibernate.transaction.JOTMTransactionManagerLookup JOTM
org.hibernate.transaction.JOnASTransactionManagerLookup JOnAS
org.hibernate.transaction.JRun4TransactionManagerLookup JRun4
org.hibernate.transaction.BESTransactionManagerLookup Borland ES

3.8.2. SessionFactory ligada a JNDI

Una fábrica de sesiones (SessionFactory) ligada a JNDI puede simplificar la búsqueda y obtención (lookup) de nuevas
sesiones. Note que esto no se relaciona con las Datasource, también ligadas a JNDI, ¡ambas simplemente comparten el
mismo repositorio de registro (registry)!

Si usted desea mantener la fábrica de sesiones ligada a un "espacio de nombre" o "namespace" JNDI, especifique un
nombre (por ejemplo, java:hibernate/SessionFactory) usando la propiedad hibernate.session_factory_name. Si
esta propiedad es omitida, la fábrica de sesiones no quedará ligada a JNDI. (Esto es especialmente útil en entornos que por
defecto tienen una JNDI de sólo lectura, como por ejemplo Tomcat).

Cuando de liga una fábrica de sesiones a JNDI, Hibernate empleará los valores de hibernate.jndi.url y
hibernate.jndi.class para instanciar un contexto inicial. Si no son especificadas, se usa el InitialContext por
defecto.

Hibernate colocará la fábrica de sesiones (SessionFactory) automáticamente en JNDI en cuanto se invoque


cfg.buildSessionFactory(). Esto significa que este llamado estará presente, por lo menos, en algún código de arranque
(o clase utilitaria) de su aplicación, a menos que usted usa el despliegue JMX, con HibernateService (lo cual se discute
luego).

38 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Si usted usa una fábrica de sesiones JNDI, un EJB o cualquier otra clase puede obtenerla usando el JNDI lookup.

Le recomendamos que, en un entorno administrado, use JNDI para obtener sus fábricas de sesiones, y en cualquier otro
entorno, un singleton estático. Para escudar su aplicación de estos detalles, también le recomendamos que oculte el código
que efectivamente realiza el "lookup" dentro de una clase utilitaria, (como por ejemplo,
HibernateUtil.getSessionFactory()). Note que dicha clase es también una buena manera de hacer arrancar
Hibernate (capítulo 1).

3.8.3. Manejo del contexto actual de la sesión con JTA

La manera más fácil de manejar sesiones y transacciones es el manejo automático que Hibernate hace de la "sesión
actual". Vea la discusión en Sesiones actuales. Usando el contexto de sesión "jta", si no hay ninguna sesión de Hibernate
asociada con la transacción JTA actual, se creará una y se la asociará con la transacción la primera vez que
sessionFactory.getCurrentSession() sea invocada. A las sesiones que hayan sido creadas de esta manera, les será
automáticamente efectuado el "flush" antes de que la transacción finalice, serán cerradas después de que la transacción
finalice, y las conexiones JDBC serán agresivamente liberadas después de cada comando. Esto permite que las sesiones
sean manejadas por el ciclo de vida de la transacción JTA al cual están asociadas, manteniendo al código libre de ese tipo
de preocupaciones administrativas. Su código puede utilizar o bien JTA programáticamente a través de UserTransaction,
o (lo que más recomendamos para producir código portable) usar la API de Transaction de Hibernate para establecer los
límites de la transacción. Si su aplicación se está ejecutando en un contenedor de EJB, se prefiere utilizar demarcación
declarativa de transacciones con CMT (siglas en inglés de "transacciones manejadas por el contenedor").

3.8.4. Despliegue de JMX

La línea cfg.buildSessionFactory() aín tiene que ser ejecutada en algún lado para meter una fábrica de sesiones en
JNDI. Esto se puede lograr o bien con un bloque inicializador estático en una clase utilitaria (como el que está en
HibernateUtil) o bien desplegando Hibernate como un "servicio administrado" (managed service),

Hibernate es distribuido con org.hibernate.jmx.HibernateService para el despliegue en servidores de aplicaciones


con capacidad JMX, como JBoss. El despliegue y configuración son específicos de la marca del servidor. He aquí un
jboss-service.xml de ejemplo para for JBoss 4.0.x:

<?xml version="1.0"?>
<server>

<mbean code="org.hibernate.jmx.HibernateService"
name="jboss.jca:service=HibernateFactory,name=HibernateFactory">

<!-- Servicios requeridos -->


<depends>jboss.jca:service=RARDeployer</depends>
<depends>jboss.jca:service=LocalTxCM,name=HsqlDS</depends>

<!-- Ligar el servicio Hibernate a JNDI -->

<attribute name="JndiName">java:/hibernate/SessionFactory</attribute>

<!-- fuente de datos -->


<attribute name="Datasource">java:HsqlDS</attribute>
<attribute name="Dialect">org.hibernate.dialect.HSQLDialect</attribute>

<!-- integración de transacciones -->


<attribute name="TransactionStrategy">org.hibernate.transaction.JTATransactionFactory</attribut
<attribute name="TransactionManagerLookupStrategy">org.hibernate.transaction.JBossTransactionMa

<attribute name="FlushBeforeCompletionEnabled">true</attribute>
<attribute name="AutoCloseSessionEnabled">true</attribute>

<!-- opciones de captura -->


<attribute name="MaximumFetchDepth">5</attribute>

<!-- caché de 2do nivel -->


<attribute name="SecondLevelCacheEnabled">true</attribute>
<attribute name="CacheProviderClass">org.hibernate.cache.EhCacheProvider</attribute>
<attribute name="QueryCacheEnabled">true</attribute>

<!-- Logueo -->


<attribute name="ShowSqlEnabled">true</attribute>

<!-- archivos de mapeo -->

39 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

<attribute name="MapResources">auction/Item.hbm.xml,auction/Category.hbm.xml</attribute>

</mbean>

</server>

Este archivo es desplegado en un directorio llamado META-INF, y empaquetado en un archivo JAR con la extensión .sar
(service archive o "archivo de servicio). Sus EJB (usualmente session beans) pueden ser conservados en su propio archivo
JAR, pero este archivo JAR de EJB se puede incluir en el archivo principal de servicio para obtener una única desplegable
"en caliente" (hot deployment). Consulte la documentación del servidor de aplicaciones JBoss para más información
acerca del servicio JMX y el despliegue de EJB.

Capítulo 4. Clases Persistentes


Las clases persistentes son clases de una aplicación que implementen las entidades de un problema de negocios (por
ejemplo, "Cliente" y "Orden" en una aplicación de e-commerce). No todas las instancias de una clase persistente se
considera que estén en un "estado persistente". Una instancia pude, en cambio, ser transitoria (transient) o desprendida
(detached).

Hibernate trabaja mejor si estas clases siguen un formato muy simple, conocido como el modelo de programación de
"objeto Java liso y llano" o POJO (Plain Old Java Object) por sus siglas en inglés. Sin embargo, ninguna de estas reglas es
estrictamente obligatoria. Mas aún, Hibernate3 presupone muy poco acerca de la naturaleza de sus objetos persistentes.
Un modelo de dominio (domain model) se puede expresar de otras maneras: mediante árboles de instancias de Map, por
ejemplo.

4.1. Un simple ejemplo de POJO


La mayoría de las aplicaciones Java requieren una clase persistente que represente felinos:

package eg;
import java.util.Set;
import java.util.Date;

public class Cat {


private Long id; // identificador

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) {

40 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

this.color = color;
}

void setSex(char sex) {


this.sex=sex;
}
public char getSex() {
return sex;
}

//litter=en inglés, camada


void setLitterId(int id) {
this.litterId = id;
}
public int getLitterId() {
return litterId;
}

void setMother(Cat mother) {


this.mother = mother;
}
public Cat getMother() {
return mother;
}

//kittens=gatitos
void setKittens(Set kittens) {
this.kittens = kittens;
}
public Set getKittens() {
return kittens;
}

// addKitten no es requerido por Hibernate


public void addKitten(Cat kitten) {
kitten.setMother(this);
kitten.setLitterId( kittens.size() );
kittens.add(kitten);
}
}

Aquí hay 4 reglas principales a seguir:

4.1.1. Implemente un constructor sin argumentos

Cat tiene un constuctor sin argumentos. Todas las clases persistentes deben tener un constuctor por defecto (el cual puede
no ser público) de manera qu Hibernate pueda instanciarlas usando Constructor.newInstance(). Recomendamos
fuertemente un constructor por defecto, que tenga al menos visibilidad package para la generación del "proxy" en
Hibernate.

4.1.2. Provea una propiedad indentificadora (optativo)

Cat tiene una propiedad llamada id. Esta propiedad corresponde a la columna de clave primaria de la base de datos. La
propiedad puede llamarse como sea, y su tipo puede ser cualquiera de los tipos de dato primitivos, cualquier tipo
"envoltorio" (wrapper), java.lang.String, o java.util.Date. Si su base de datos anticuada tiene claves compuestas,
usted puede usar inclusive una clase definida por el usuario con propiedades de los tipos mencionados (vea la sección
sobre claves compuestas más adelante).

La propiedad indentificadora es estrictamente optativa. La puede omitir e Hibernate aún podrá seguirle la pista al objeto,
internamente. De todos modos, no le recomendamos que haga esto.

De hecho, algunas de las funcoionalidades están disponibles sólo para aquellas clases que sí tienen identificador.

Recuperación (reattachment) transitiva para objetos desprendidos (update o merge en cascada)-vea Sección 10.11,
“Persistencia Transitiva”
Session.saveOrUpdate()

Session.merge()

Le recomendamos que declare propiedades indentificador nombradas de una manera consistente, en sus clases

41 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

persistentes. Mas aún, le recomendamos que use un tipo anulable (es decir, no primitivo).

4.1.3. Prefiera clases que no sean finales (optativo)

Una característica central de Hibernate, los representantes o proxies, depende de que la clase persistente no sea final, o de
que sea la implementación de una interfaz con todos sus métodos públicos.

Se puede persistir clases finales que no implementen una interfaz con Hibernate, pero usted no será capaz de usar proxies
para la captura por asociaciones perezosas (lazy association fetching), lo cual limitará sus opciones de ajuste de
performance.

También se debería evitar declarar métodos public final en las clases no finales. Si se quiere usar clases con métodos
públicos finales, se debe inhabilitar el "proxying" explícitamente, especificando lazy="false".

4.1.4. Declare métodos de acceso y "mutadores" (accessors, mutators) para los campos persistentes.

Cat declara métodos de acceso para todos sus campos persistentes. Muchos otras herramientas de ORM persisten
directamente sus variables de instancia. Nosotros creemos que es mejor proveer un nivel de aislamiento entre el esquema
relacional y la estructura interna de datos de la clase. Por defecto, Hibernate persiste propiedades del tipo JavaBean, y
reconoce nombres de método de la forma getAlgo, isAlgo y setAlgo. Si es necesario, usted puede revertir esto, y
permitir el acceso directo, para propiedades específicas,

No se necesita que las propiedaes sean declaradas como públicas. Hibernate puede persistir una propiedad con un par
get/set que tenga acceso por defecto (package) o privado.

4.2. Implementar herencia


Una subclase también debe cumplir con la primera y la segunda regla. Hereda su identificador de la superclase Cat.

package eg;

public class DomesticCat extends Cat {


private String name;

public String getName() {


return name;
}
protected void setName(String name) {
this.name=name;
}
}

4.3. Implementar equals() y hashCode()


Usted debe reemplazar (override) los métodos equals() y hashCode() si:

planea poner instacias de clases persistentes en un Set (lo cual es la manera recomendada de representar cualquier
asociación con un lado "muchos"), y

planea usar recuperación (reattachment) de instancias desprendidas

Hibernate garantiza la equivalencia de la identidad persistente (el registro de base de datos) y la identidad de Java, sólo
dentro del alcance de una sesión en particular. Así que, tan pronto como mezclamos instancias que hayan sido obtenidas
de distintas sesiones, debemos implementar equals() y hashCode() si deseamos tener una semántca con sentido para los
Sets.

La manera más obvia es implementar equals()/hashCode() comparando el valor identificador de ambos objetos. Si el
valor es el mismo, ambos deben ser el mismo registro de la base de datos, siendo por lo tanto iguales (si ambos son
agregados al Set., sólo tendremos un elemento en el Set.). ¡Desafortunadamente, no podemos utilizar ese enfoque con los
identificadores generados! Hibernate sólo asignará identificadores los objetos que hayan sido creados, y una instancia que
haya sido recientemente creada ¡aún no tiene ningún identificador! Más aún, si una de estas instancias, sin grabar, está
dentro de un Set., grabarla le va a asignar un identificador, y sus valores de equals() y hashCode() (que estarían

42 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

basados en el identificador) cambiarían, rompiendo el contrato del Set. Vea el sitio de web de Hibernate para una
discusión completa de este problema. Note que esto no es un problema de Hibernate, sino parte de la semántica normal de
Java en lo que respecta a la igualdad e identidad de los objetos.

Nosotro recomendamos implementar equals() y hashCode() utilizando una igualdad "por clave de negocio". Esto quiere
decir, que estos métodos comparen solamente propiedades que formen la clave de negocio, una clave que identificaría a
nuestro objeto en el mundo real (una clave candidata natural).

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;
}

Note que la clave de negocio no necesita ser tan sólida como una clave primaria de la base de datos. (vea Sección 11.1.3,
“Considerar la identidad de un objeto”). Propiedades que sean únicas o inmutables, usualmente son buenas candidatas a
"clave de negocio".

4.4. Modelos dinámicos


Advierta que las siguientes características se consideran experimentales, y pueden cambiar en el futuro cercano

Las entidades persistentes no tienen que ser representadas, en tiempo de ejecución, necesariamente, com clases POJO u
objetos JavaBean. Hibernate también soporta modelos dinámicos (usando Maps de Maps en tiempo de ejecución), y la
representación de entidades como árboles DOM4J. Con este enfoque, no se escriben clases, simplemente archivos de
mapeo.

Por defecto, Hibernate trabaja en el modo POJO normal. Se puede configurar un modo de representación de entidad por
defecto para una fábrica de sesiones en particular, usando la opción de configuración default_entity_mode (vea
Tabla 3.3, “Propiedades de Configuración de Hibernate”.

Los ejemplos siguientes demuestran la representación usando mapas. Primero, en el archicvo de mapeo, se debe declara
un entity-name en lugar de (o además de) el nombre de la clase:

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

43 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

</class>

</hibernate-mapping>

Note que, incluso si las asociaciones son declaradas usando nombres de clases de destino, el tipo de destino de una
asociación también puede ser una entidad dinámica en lugar de un POJO.

Después de configurar el modo de entidad por defecto a dynamic-map para esta SessionFactory, podemos trabajar con
"mapas de mapas" en tiempo de ejecución.

Session s = openSession();
Transaction tx = s.beginTransaction();
Session s = openSession();

// Crear un cliente
Map david = new HashMap();
david.put("name", "David");

// Crear una organización


Map foobar = new HashMap();
foobar.put("name", "Foobar Inc.");

// Vincular a ambos
david.put("organization", foobar);

// Grabar a ambos
s.save("Customer", david);
s.save("Organization", foobar);

tx.commit();
s.close();

La ventaja de un mapeo dinámico es acelerar el tiempo de entrega, para prototipos, sin la necesidad de implementar clases
de entidades. De todos modos, se pierde el chequeo de tipos en tiempo de compilación, y esto muy probablemente causará
varias excepciones en tiempo de ejecución. Gracias al mapeo de Hibernate, el esquema de base de datos puede ser
fácilmente normalizado y saneado, permitiendo agregar una implementación apropiada de modelo de dominio encima, más
adelante.

Los modos de representación de entidad también pueden ser configurados por sesión:

Session dynamicSession = pojoSession.getSession(EntityMode.MAP);

// Crea un cliente
Map david = new HashMap();
david.put("name", "David");
dynamicSession.save("Customer", david);
...
dynamicSession.flush();
dynamicSession.close()
...
// Continúa con la sesión "POJO"

Dese cuenta por favor de que el llamado a getSession() usando un EntityMode determinado, es cosa de la API de
Session API, no de SessionFactory. De esta manera, la nueva sesión comparte la misma conexión JDBC subyacente, y
otra información de contexto. Esto vuelve innecesario invocar flush() y close() en la sesión secundaria, y le deja el
manejo de transacciones y conexiones a unidad de trabajo primaria.

Se puede encontrar más información sobre las capacidades de representación con XML en Capítulo 18, Mapeo XML.

4.5. T-uplizadores
org.hibernate.tuple.Tuplizer, y sus sub-interfaces, son los encargados de manejar una representación en particular
de un fragmento de datos, dado el org.hibernate.EntityMode de dicha representación. Si un determinado fragmento de
datos se concibe como una estructura de datos, entonces un t-uplizador es lo que sabe cómo crear dicha estructura. Por
ejemplo, para el modo de entidad "POJO", el t-uplizador correspondiente sabe cómo crear el POJO mediante su
constructor, y cómo acceder a las propiedades de POJO usando los métodos de acceso. Hay dos t-uplizadores de alto
nivel, representados por las interfaces org.hibernate.tuple.entity.EntityTuplizer y

44 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

org.hibernate.tuple.component.ComponentTuplizer. Los EntityTuplizers see encargan de manejar los


"contratos" recién descritos para las entidades, mientras que los ComponentTuplizers hacen lo propio para los
componentes.

El usuario puede, asimismo, insertar sus propios t-uplizadores. Tal vez usted desee que par el modo de entidad
"dynamic-map" se utilice una implementación de java.util.Map que no sea java.util.HashMap, o tal vez usted
necesita que se utilice una estrategia de generación de proxies distinta de la que viene de fábrica. Las definiciones de
t-uplizer van adjuntas a la entidad o mapeo de componentes que están destinadas a manejar. Volviendo al ejemplo de la
entidad "cliente" (customer).

<hibernate-mapping>
<class entity-name="Customer">
<!--
Reemplaza el t-uplizer de modo de entidad dynamic-map
por el de entity-mode
-->

<tuplizer entity-mode="dynamic-map" class="CustomMapTuplizerImpl"/>

<id name="id" type="long" column="ID">


<generator class="sequence"/>
</id>

<!-- otras propiedades-->

...
</class>
</hibernate-mapping>

public class CustomMapTuplizerImpl extends org.hibernate.tuple.entity.DynamicMapEntityTuplizer {

// suplanta al método buildInstantiator() para "enchufar" nuestro mapeo hecho a medida ...
protected final Instantiator buildInstantiator(org.hibernate.mapping.PersistentClass mappingInf
return new CustomMapInstantiator( mappingInfo );
}

//suplanta a generateMap() para devolver nuestro map hecho a medida


private static final class CustomMapInstantiator extends org.hibernate.tuple.DynamicMapInstanti
protected final Map generateMap() {
return new CustomMap();
}
}
}

4.6. Extensiones
A HACER: documentar el framework de extensiones de usuario y paquetes proxy.

Capítulo 5. Mapeo O/R básico

5.1. Declaración del mapeo


Los mapeos objeto/relacionales generalmente se definen en un documento XML. El documento de mapeo ha sido
diseñado para ser legible y editable a mano. El lenguaje de mapeo es "Javacéntrico", lo cual significa que los mapeos se
construyen en torno a declaraciones de clases persistentes, no de tablas.

Note que, aunque muchos usuarios de Hibernate eligen escribir el XML a mano, existe un buen número de herramientas
para generar el documento de mapeo, como: XDoclet, Middlegen y AndroMDA.

Comencemos con un mapeo de ejemplo:

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC


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

45 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

<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">
<!-- acá podría ir un mapeo para Perro -->
</class>

</hibernate-mapping>

(N.del T): "eg" son las siglas de "exempli gratia", una locución latina que en inglés hace las veces de "por ejemplo". A lo
largo de esta documentación, "eg" se usa como el paquete por defecto para los ejemplos de código.
Ahora discutiremos el contenido del documente de mapeo. Sólo describiremos los elementos y atributos del documento
que son usados por Hibernate en tiempo de ejecución. El documento de mapeo también contiene algunos atributos
optativos adicionales, y elementos que afectan los esquemas de BD exportados por herramientas de exportación de
esquemas (por ejemplo, el atributo not-null).

5.1.1. El Doctype o "tipo de documento XML"

Todos los mapeos XML deberían declarar el doctype que se muestra. La DTD real puede ser encontrada en la URL
mencionada, en el directorio hibernate-x.x.x/src/org/hibernate o en hibernate3.jar. Hibernate siempre buscará
la DTD primero en el classpath. Si experimenta problemas al buscar la DTD debido a su conexión de Internet, compare su
declaración de DTD contra el contenido de su classpath.

5.1.1.1. EntityResolver

Como se mencionó anteriormente, Hibernate primero intentará resolver la DTD en su classpath. La manera en que lo hace,
es registrando una implementación personalizada de org.xml.sax.EntityResolver con el SAXReader que usa para leer
los archivos xml. Este EntityResolver perosnalizado reconoce dos espacios de nombre de systemId diferentes.

un "espacio de nombre" (namespace) de Hibernate es reconocido siempre que el resolver encuentra un systemId qie
comience con http://hibernate.sourceforge.net/; el resolver intenta resolver estas entidades mediante el
classloader que haya cargado las clases de Hibernate.

un espacia de nombre de usuario se reconoce siempre que el resolver encuentra un systemId que use un protocolo
de URL classpath://; el resolver intentará resolver esas entidades a través de (1) el classloader del contexto de
thread actual, y (2), el classloader que haya cargado las clases de Hibernate.

Un ejemplo de utilización de un espacio de nombre (namespace) hecho a medida:

46 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" [
<!ENTITY types SYSTEM "classpath://your/domain/types.xml">

]>

<hibernate-mapping package="your.domain">
<class name="MyEntity">
<id name="id" type="my-custom-id-type">
...
</id>
<class>

&types;
</hibernate-mapping>

En donde types.xml es un recurso en el paquete your.domain y contiene una definición de tipo a medida typedef.

5.1.2. hibernate-mapping

Este elemento tiene varios atributos optativos. Los atributos schema y catalog especifican que las tablas a las que este
mapeo se refiere pertenecen al esquema o catálogo indicado. Si se especifican, los nombres de tabla serán calificados con
el esquema y/o catálogo dados. Si no están presentes, los nombres de tabla no serán calificados. El atributo default-
cascade especifica qué estilo de propagación en cascada debería asumirse para las propiedades y colecciones que no
especifiquen un atributo cascade. El atributo auto-import nos permite, por defecto, usar un nombre de clase no
calificado en el lenguaje de consultas.

<hibernate-mapping
schema="schemaName" (1)
catalog="catalogName" (2)
default-cascade="cascade_style" (3)
default-access="field|property|ClassName" (4)
default-lazy="true|false" (5)

auto-import="true|false" (6)
package="package.name" (7)
/>

(1)
schema (optativo): El nombre de un esquema de BD

(2)
catalog (optativo): El nombre de un catálogo de BD

(3)
default-cascade (optativo - por defecto, none): El estilo por defecto de propagación en cascada

(4)
default-access (optativo - por defecto, property): La estrategia que Hibernate debería usar para acceder a
todas las propiedades. Puede ser una implementacíón personalizada de PropertyAccessor.

(5)
default-lazy (optativo - por defecto, true): El valor por defecto para los atributos lazy no especificados de
clases y colecciones.

(6)
auto-import (optativo - por defecto true): Especifica si se puede usar nombres de clase no calificados (de clases
en el mapeo) el en lenguaje de consultas.

(7)
package (optativo): Especifica un prefijo de paquete a ser asumido, para las clases no calificadas en el documento
de mapeo.

47 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Si usted tiene dos clases persistentes cuyo nombre (no calificado) es el mismo, debería configurar auto-import="false".
Hibernate lanzará una excepción si usted le intenta asignar dos clases al mismo nombre "imported".

Note que el elemento hibernate-mapping le permite anidar los mapeos de varias clases persistentes, como se muestra
anteriormente. Sin embargo, mapear sólo una clase persistente (o jerarquía de clases) por archivo de mapeo es una
costumbre más establecida (y lo que algunas herramientas esperan). Por ejemplo, Cat.hbm.xml, Dog.hbm.xml, o, si se usa
herencia, Animal.hbm.xml.

5.1.3. class

Se puede declarar una clase persistente usando el elemento class.

<class
name="ClassName" (1)
table="tableName" (2)

discriminator-value="discriminator_value" (3)
mutable="true|false" (4)
schema="owner" (5)
catalog="catalog" (6)
proxy="ProxyInterface" (7)

dynamic-update="true|false" (8)
dynamic-insert="true|false" (9)
select-before-update="true|false" (10)
polymorphism="implicit|explicit" (11)
where="arbitrary sql where condition" (12)

persister="PersisterClass" (13)
batch-size="N" (14)
optimistic-lock="none|version|dirty|all" (15)
lazy="true|false" (16)
entity-name="EntityName" (17)

check="arbitrary sql check condition" (18)


rowid="rowid" (19)
subselect="SQL expression" (20)
abstract="true|false" (21)
node="element-name"
/>

(1)
name (optativo): El nombre (totalmente calificado) de la clase (o interfaz) persistente de Java. Si este atributo está
ausente, se asume que no se trata de una entidad POJO.

(2)
table (optativo - por defecto el nombre no calificado de la clase): El nombre de su tabla en la base de datos

(3)
discriminator-value (optativo - por defecto, el nombre de la clase): Un valor que distingue entre subclases
individuales, usado para comportamiento polimórfico. null y not null también son valores aceptables.

(4)
mutable (optativo, por defecto, true): Especifica si las instancias de esta clase son o no mutables.

(5)
schema (optativo): Suplanta al nombre de esquema especificado por el elemento <hibernate-mapping> raíz.

(6)
catalog (optativo): Suplanta al nombre de catálogo especificado por el elemento <hibernate-mapping> raíz.

(7)
proxy (optativo): Especifica una interfaz a utilizar para los proxies de inicialización perezosa. Se puede especificar
el nombre de la clase misma.

48 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

(8)
dynamic-update (optativo, por defecto false): Especifica que los comandos SQL UPDATE deberían ser generados
en tiempo de ejecución, y contener sólo aquéllas comlumnas cuyos valores hayan cambiado.

(9)
dynamic-insert (optativo, por defecto, false): Especifica que se deberían generar comandos SQL INSERT en
tiempo de ejecución, conteniendo sólo las columnas cuyos valores son no nulos.

(10)
select-before-update (optativo, por defecto false): Especifica que Hibernate nunca debería ejecutar un
UPDATE SQL a menos que esté seguro de que en realidad se está modificando algún objeto. En algunos casos (en
realidad, sólo cuando un objeto transitorio ha sido asociado con una nueva sesión usando update()), esto significa
que Hibernate ejecutará un comando SQLSELECT adicional, para determinar si un en realidad un UPDATE es
necesario.

(11)
polymorphism (optativo, por defecto, implicit): Determina si se usa polimorfismo explícito o implícito.

(12)
where (optativo) especifica una condición SQL WHERE arbitraria a ser usada cuando se seleccionen objetos de esta
clase.

(13)
persister (optativo): Especifica un ClassPersister hecho a medida.

(14)
batch-size (optativo, por defecto, 1) especifica un "tamaño de lote" para capturar instancias de esta clase por
identificador.

(15)
optimistic-lock (optativo, por defecto, version): Determina la estrategia de "locking" optimista.

(16)
lazy (optativo): La captura haragana puede ser completamente inhabilitada espefificando lazy="false".

(17)
entity-name (optativo, por defecto, el nombre de la clase): Hibernate3 permite que una clase sea mapeada
muchas veces (potencialmente, a distintas tablas), y permite mapeos de entidades qie estén representados por
Maps a nivel de Java, En estos casos, se debería proveer un nombre arbitrario explícito para la entidad. Vea
Sección 4.4, “Modelos dinámicos” y Capítulo 18, Mapeo XML for more information.

(18)
check (optativo): Una expresión SQL arbitraria usada para generar una constraint tipo check multifila a usar
durante la generación automática del esquema.

(19)
rowid (optativo): Hibernate puede usar los vulgarmente llamados ROWIDs, en aquellas DB que lo soporten. Por
ejemplo, en Oracle, Hibernate puede usar la columna adicional rowid para actualizar más rapidamente, si usted
configura esta opción a rowid. Un ROWID es un detalle de implementación, y representa la ubicación física de
una t-upla alamacenada.

(20)
subselect (optativo): Mapea una entidad inmutable y de sólo-lectura a un subselect de la base de datos. Es útil si
se quiere tener una vista en lugar de una tabla, pero no se cuenta con una vista en la base de datos. Vea más
adelante para más información.

(21)
abstract (optativo): Usado para marcar superclases abstractas en jerarquías de <union-subclass>.

49 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Es perfectamente aceptable que la clase persistente nombrada sea une interfaz. En ese caso, se debe declarar la clase
implementadora usando el elemento <subclass>. Se puede persistir cualquier clase anidada (inner class) estática. Se
debería especificar el nombre de este tipo de clases usando la nomenclatura estándar, es decir com.Mi$ClaseInterna.

Las clases inmutables (mutable="false") no pueden ser actualizadas o borradas por la aplicación. Esto le permite a
Hibernate realizar algunas optimizaciones menores.

El atributo optativo proxy permite la inicialización perezosa de instancias persistentes de la clase. Inicialmente, Hibernate
devolverá proxies CGLIB, que implementan la interfaz mencionada. El objeto persistente propiamente dicjo será cargado
cuando se invoque un método del proxy. Vea "Inicializar colecciones y proxies" más adelante.

Polimorfismo implícito significa que cualquier consulta que nombre a una superclas o interfaz devolverá instancias de la
clase misma. Polimorfismo explícito significa que instancias de esta clase sólo serán devueltas por consulta que nombren a
esta clase, y que las consultas que nombren a esta clase devolverán sólo instancias de subclases que hayan sido mapeados
como <subclass> o <joined-subclass>. Para la mayoría de los casos, el valor por defecto polymorphism="implicit",
es lo más apropiado. El polimorfismo explícito es útil cuando hay dos clases diferentes mapeadas a la misma tabla (lo cual
permite tener una clase "de peso ligero" que contenga un subconjunto de las columnas de la tabla).

El atributo persister le permite personalizar la estrategia de persistencia usada para la clase. Por ejemplo, usted puede
especificar su propia subclase de org.hibernate.persister.EntityPersister, o hasta puede proveer una
implementación completamente nueva de la interfaz org.hibernate.persister.ClassPersister que persista, por
ejemplo, valiéndose de una llamada a un procedimiento almacenado de base de datos, o usando serialización a archivos
planos, o LDAP. Vea org.hibernate.test.CustomPersister para un ejemplo simple (de "persistencia" a una
Hashtable).

Note que las asignaciones de valores dynamic-update y dynamic-insert no son heredadas por la subclases, así que
pueden ser especificadas en los elementos <subclass> o <joined-subclass>. Estos valores pueden incrementar la
performance en algunos casos, pero reducirla en otros. Úselos con cuidado.

El uso de select-before-update normalmente bajará la performance. Es muy útil para evitar que un "update trigger" de
la base de datos sea invocado innecesariamente si usted está revinculando todo un árbol de instancias desprendidas a una
sesión.

Si usted habilita dynamic-update, tendrá las siguientes opciones de "locking" optimista

version verifica las columnas de versión/timestamp

all verifica todas las columnas

dirty verifica las columnas que hayan cambiado, permitiendo algunas modificaciones concurrentes

none no usa "locking" optimista

Recomendamos muy fuertemente que se utilicen las columnas de versión/timestamp para locking optimista con Hibernate.
Ésta es la estrategia óptima en lo que a performace se refiere, y es la única estrategia que maneja correctamente las
modificaciones que se hagan a instancias desprendidas (es decir, cuando se use Session.merge() ).

Para un mapeo Hibernate, no hay diferencia entre una vista y una tabla de la base de datos. Como es de esperarse, esto es
transparente a nivel de base de datos (note que algunas base de datos no soportan vistas correctamente, especialmente
actualizaciones de éstas). A veces usted quiere usar una vista, pero no puede crear una en la base de datos (por ejemplo,
con un esquema heredado anticuado). En este caso, usted puede mapear una entidad inmutable y de sólo-lectura a una
expresión "subselect" de SQL.

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

50 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Declara las tablas contra las cuales se ha de sincronizar esta entidad, asegurándose de que el auto-flush ocurra
correctamente. y que las consultas efectuadas contra la entidad derivada no devuelvan datos vencidos. En el mapeo,
<subselect> está disponible como atributo y como elemento anidado.

5.1.4. id

Las clases mapeadas deben declarar la columna de clave primaria de la tabla en la base de datos. La mayoría de las clases
también tendrán una propiedad del estilo JabaBeans conteniendo el didentificador único de una instancia determinada. El
elemento <id> define el mapeo de esa propiedad a la columna de clave primaria.

<id
name="propertyName" (1)
type="typename" (2)
column="column_name" (3)
unsaved-value="null|any|none|undefined|id_value" (4)
access="field|property|ClassName"> (5)

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

<generator class="generatorClass"/>
</id>

(1)
name (optativo): El nombre de la propiedad indentificadora.

(2)
type (optativo): Un nombre que indica el tipo de Hibernate

(3)
column (optativo, por defecto, el nombre de la propiedad): El nombre de la columna de la clave primaria

(4)
unsaved-value (optativo, por defecto, un valor "adecuado"): Una propiedad identficadora que indica que una
instancia ha sido recientemente instanciada (no grabada), distinguiéndola de las instancias desprendidas que fueron
grabadas o cargadas en una sesión previa.

(5)
access (optativo, por defecto, property): La estrategia que Hibernate debería usar para acceder al valor de la
propiedad.

Si falta el atributo name , se asume que la clase no posee propiedad indentificadora.

El atributo unsaved-value casi nunca se usa en Hibernate3.

Hay una declaración alternativa de <composite-id>, para permitur el acceso a datos de formato anticuado, con clave
compuesta. Desaconsejamos su uso en cualquier otro caso.

5.1.4.1. Generator

El elemento opcional hijo <generator> nombra a la clase de Java que se usa para generar los identificadores únicos de la
clase persistente. Si hace falta algún parámetro para inicializar la instancia del generador, se pasa usando el elemento
<param>.

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

Todos los generadores implementan la interfaz org.hibernate.id.IdentifierGenerator. Es una interfaz muy simple;

51 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

algunas aplicaciones podrían elegir proveer su propia implementación a medida. De todos modos, Hibernate provee una
variedad de implementaciones que ya vienen incluidas. Los siguientes son los apodos mnemónicos para estos generadores
ya incluidos:

increment

genera identificadores de tipo long, short o int que son únicos solamente cuando ningún otro proceso está
insertando datos en la misma tabla. No usar en un cluster.

identity

soporta "columnas de identidad" (identity) en DB2, MySQL, MS SQL Server, Sybase e HypersonicSQL. El
identificador que se devuelve es de tipo long, short o int.

sequence

usa una secuencia (sequence) en DB2, PostgreSQL, Oracle, SAP DB, McKoi o un generador en Interbase. El
identificador que se devuelve es de tipo long, short o int

hilo

usa un algoritmo hi/lo para generar identificadores de los tipos long, short o int eficientemente, dada una tabla y
una columna (por defecto, hibernate_unique_key y next_hi respectivamente) como fuente de los valores "hi". El
algorithmo hi/lo genera identificadores que son únicos sólo para una base de datos en particular.

seqhilo

usa un algoritmo hi/lo para generar identificadores de los tipos long, short o int eficientemente, dada una
secuencia (sequence) nombrada de base de datos.

uuid

usa un algoritmo UUID de 128 bits para generar identificadores de tipo string, únicos para toda una red (se usa la
dirección de IP). El UUID es codificado como una cadena de 32 dígitos hexadecimales.

guid

usa una cadena GUID generada por la base de datos, en MS SQL Server y MySQL.

native

elije identity, sequence o hilo, dependiendo de las capacidades de la base de datos subyancente.

assigned

deja que sea la aplicación la que asigne el identificador al objeto antes de que save() sea llamado. Ésta es la
estrategia por defecto si no se especifica un elemento <generator>.

select

obtiene una clave primaria asignada por un trigger de la base de datos, seleccionando la fila por alguna clave única y
obteniendo el valor de clave primaria.

foreign

usa el identificador de algún otro objeto asociado. Normalmente se usa en conjunción con una asocoación
<one-to-one> por clave primaria.

sequence-identity

una generación de secuencias especializada que utiliza una sequencia de base de datos para la generación del valor
en sí, pero lo combina con el método de JDBC3 getGeneratedKeys para devolver el valor final, como parte del
comando INSERT. Esta estrategia sólo es soportada, que sepamos, por los drivers de Oracle 10g diseñados para
JDK1.4. Note que los comentarios en estos comandos están inhabilitados, debido a un error en los drivers de Oracle.

52 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

5.1.4.2. El algoritmo hi/lo

Los generadores hilo y seqhilo proveen dos implementaciones alternativas del algoritmo hi/lo, un enfoque muy común
para generar identificadores. La primera implementación requiere una tabla de base de datos especial que almacene el
siguiente valor "hi" disponible. La segunda, usa una secuencia al estilo de Oracle (si esto se soporta).

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

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

Desafortunadamente, usted no puede usar hilo cuando le provee su propia conexión (Connection) a Hibernate. Cuando
Hibernate use una fuente de datos de un servidor de aplicaciones para obtener conexiones inscriptas en JTA, usted deberá
configurar adecuadamente la hibernate.transaction.manager_lookup_class.

5.1.4.3. El algoritmo UUID

El UUID (siglas en inglés de "identificador universal único) contiene: la dirección de IP, el tiempo de comienzo de la JVM
(al cuarto de segundo), la hora del sistema, y un valor contador que es único a lo ancho de la JVM. Desde el código Java
no se puede obtener la dirección MAC o direcciones de memoria, así que esto es lo mejor que se puede lograr, sin usar
JNI.

5.1.4.4. Columnas de indentidad y secuencias

Con las BD que soportan columnas de identidad (DB2, MySQL, Sybase, MS SQL), se puede usar la generación de claves
identity. Con las que soportan secuencias (DB2, Oracle, PostgreSQL, Interbase, McKoi, SAP DB) se puede usar el valor
sequence. Ambas estrategias requieren el uso de 2 comandos SQL para insertar un objeto nuevo.

<id name="id" type="long" column="person_id">

<generator class="sequence">
<param name="sequence">person_id_sequence</param>
</generator>

</id>

<id name="id" type="long" column="person_id" unsaved-value="0">

<generator class="identity"/>

</id>

La estrategia native produce un desarrollo más portátil entre distintas plataformas (cross-platform), ya que elige entre
identity, sequence e hilo, dependiendo de las capadidades de la DB subyacente.

5.1.4.5. Identificadores asignados

Si desea que sea la aplicación la que asigne los identificadores (en lugar de que Hibernate los genere), usted puede usar el
"generador" assigned. Este generador especial usa el valor de identificador ya asignado a la propiedad indentificadora del
objeto. Este generador se usa cuando la clave primaria es una clave natural en lugar de una clave sustituta (surrogate key).
Éste es el comportamiento por defecto, si no se especifica el elemento <generator>.

53 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Elegir el generador assigned, hace que Hibernate use unsaved-value="undefined", forzándolo a determinar si una
instancia es transitoria o desprendida, a menos que haya una propiedad "version" o "timestamp", o usted defina
Interceptor.isUnsaved().

5.1.4.6. Claves primarias asignadas por triggers

Para esquemas de DB anticuados/heredados solamente (Hibernate no genera DDL con triggers).

<id name="id" type="long" column="person_id">

<generator class="select">
<param name="key">socialSecurityNumber</param>
</generator>

</id>

(N del T): el número de seguridad social o SSN es un número único asignado por el Estado a cada persona en EE.UU.

En el ejemplo precedente, hay un valor de propiedad único llamado socialSecurityNumber, definido por la clase, como
clave natural, y una clave primaria en la tabla llamada person_id, cuyo valor es generado por un trigger que seleciona
dicho número.

5.1.5. Generadores de identificador mejorados

A partir de la versión 3.2.3, hay 2 nuevos generadores que representan un replanteo de 2 aspectos diferentes de la
generación de identificadores. El primer aspecto, es la portabilida de base de datos, el segundo es la optimización (no tener
que consultar a la base de datos cad vez que se requiera un valor de identificador nuevo). Estos dos nuevos generadores (a
partir de la versión 3.3.x) han sido concebidos con el objetivo de reemplazar a algunos de los generadores nombrados
anteriormente De todos modos, siguen incluyéndose en los lanzamientos actuales, y se puede hacer referencia a ellos via
FQN.

El primero de estos nuevos generadores es el org.hibernate.id.enhanced.SequenceStyleGenerator, el cual ha sido


concebido como reemplazo del generador sequence, y como un generador con una portabilidad superior a native
(porque native (generalmente) elije entre identity y sequence, las cuales tienen en general semánticas distintas, lo cual
puede causar sutiles problemas aplicaciones que consideren portabilidad).
org.hibernate.id.enhanced.SequenceStyleGenerator, sin embargo, consigue la portabilidad de una manera
diferente. Elije entre usar una tabla o una secuencia en la base de datos para almacenar sus valores incrementados,
dependiendo de las capacidades del dialecto que esté siendo usado. La diferencia entre esto y native, es que el
almacenamiento basado en tablas y el basado en secuencias tienen exactamente la misma semántica (de hecho, las
secuencias son exactamente lo que Hibernate trata de emular con sus generadores badados en tablas). Este generador tiene
varios parámetros de configuración:

sequence_name (optativo, por defecto, hibernate_sequence): El nombre de la secuencia (o tabla) a ser usada.

initial_value (optativo, por defecto, 1): El valor inicial a ser devuelto por la tabla/secuencia. En términos de la
creación de la secuencia, esto es análogo a la cláusula típica "STARTS WITH".

increment_size (optativo, por defecto, 1): El incremento usdo en llamadas ulteriores a la tabla/secuencia. En
términos de la creación de la secuencia, esto es análogo a la cláusula típica "INCREMENT BY".

force_table_use (optativo, por defecto, false): Deberíamos forzar el uso de una tabla como estructura de
respaldo, incluso si el dialecto soporta secuencias?

value_column (optativo, por defecto, next_val): ¡Sólo relevante para estructuras de tabla! El nombre de la
columna en la tabla usada para almacenar el valor.

optimizer (optativo, por defecto, none): Vea Sección 5.1.6, “Optimización de los generadores de identificador”

El segundo de estos nuevos generadores es org.hibernate.id.enhanced.TableGenerator, el cual ha sido


primariamente concebido en reemplazo del generador table, (aunque, en realidad, funciona mucho más como un
org.hibernate.id.MultipleHiLoPerTableGenerator). Secundariamente, ha sido concebido como una
reimplementación de org.hibernate.id.MultipleHiLoPerTableGenerator que utiliza la noción de optimizadores
"enchufables" (pluggable). En esencia, este generador define una tabla que es capaz de contener múltiples valores de

54 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

incremento distintos, usando múltiples registros con claves diferentes. Este generador tiene varios parámetros de
configuración:

table_name (optativo, por defecto, hibernate_sequences): El nombre de la tabla a usar.

value_column_name (optativo, por defecto, next_val): El nombre de la columna en la tabla, que será usada para
contener el valor.

segment_column_name (optativo, por defecto, sequence_name): El nombre de la columna en la tabla que setá
usada para contener la "clave de segmento". Este es el valor que identifica de manera distintiva qué valor de
incremento usar.

segment_value (optativo, por defecto, default): El valor de "clave de segmento" del cual queremos extraer
valores de incremento para este generador.

segment_value_length (optativo, por defecto, 255): Usado para la generaciónde esquemas; el tamaño del campo
de esta "clave de segmento".

initial_value (optativo, por defecto, 1): El valor inicial a ser devuelto por la tabla.

increment_size (optativo, por defecto, 1): El valor por el cual las llamdas subsiguientes a la tabla deberían diferir.

optimizer (optativo, por defecto, ): Vea Sección 5.1.6, “Optimización de los generadores de identficadores”

5.1.6. Optimización de los generadores de identficador

Para los identificadores que almacenan valores en la base de datos, es ineficiente acudir a la base de datos cada una de las
veces en que se necesita generar un nuevo valor de identificador. En lugar de ello, lo ideal es agrupar un buen número de
ellos en la memoria, y solamente acudir a la base de datos cuando se ese grupo de valores se haya agotado. Ésta es la
función de los "optimizadores enchufables". Por el momento sólo los dos generadoeres mejorados (Sección 5.1.5,
“Generadores mejorados de identificadores ”) soportan esta noción.

none (éste es, generalmente, el valor por defecto si no se especificó ningún optimizador): Esto indica que no se
efectúe ninguna optimización, y que se acuda a la base de datos para todos y cada uno de los pedidos.

hilo: aplica un algoritmo hi/lo en torno a los valores devueltos por la base de datos. Se espera que los valores de
base de datos para este optmizador sean secuenciales. Los valores devueltos desde la estructura de base de datos
para este optmizador indican el "número de grupo"; el increment_size (tamaño del incremento) se multiplica por
el valor en memoria para definir un grupo "hi value".

pooled: como fue discutido para hilo, estos optimizadores intentan minimizar el número de viajes a la base de
datos. Aquí, sin embargo, simplemente almacenamos en valor inicial para el "próximo grupo" en la estructura de
base de datos, más que un valor secuencial en combinación con un algoritmo de agrupamiento en memoria.Aquí,
increment_size se refiere a los valores que vienen de la base de datos.

5.1.7. composite-id

<composite-id
name="propertyName"
class="ClassName"
mapped="true|false"
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>

En una tabla con clave compuesta, se puede mapear varias de sus propiedades como identificadores. El elemento
<composite-id> acepta mapeos de propiedades <key-property> y mapeos como elementos hijos.

<composite-id>
<key-property name="medicareNumber"/>

55 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

<key-property name="dependent"/>
</composite-id>

Su clase persistente debe reemplazar equals() y hashCode() para implementar igualdad entre identificadores
compuestos. También debe implementar Serializable.

Desafortunadamente, este abordaje de los identificadores significa que el objeto persistente es su propio identificador. No
hay otro "puntero" al objeto que no sea el objeto mismo. Se debe instanciar el objeto persistente mismo, y poblarle sus
propiedades identificadores antes de poder cargarlo en estado persistente asociado con una clave primaria. A este enfoque
lo llamamos "identificador compuesto incrustado (embedded)", y lo desaconsejamos para cualquier aplicación seria.

El segundo abordaje es lo que llamamos "identificador compuesto mapeado", en donde las propiedades identificador
usadas dentro del <composite-id> son duplicadas tanto en la clase persistente como en una clase identificadora separada.

<composite-id class="MedicareId" mapped="true">


<key-property name="medicareNumber"/>
<key-property name="dependent"/>
</composite-id>

(N.del.T): "Medicare" es el nombre en inglés del seguro estatal de salud en EE.UU. y otros países.
En este ejemplo, tanto la clase del identificador compuesto, MedicareId, como la clase de la entidad misma tienen
propiedades llamadas medicareNumber y dependent. La clase identificadora debe reemplazar equals() y hashCode() e
implementar Serializable. La desventaja de este abordaje es obvia: duplicación de código.

Los siguientes atributos son usados para especificar un identificador compuesto mapeado:

mapped (optativo, por defecto, false): indica que se usa un identificador mapeado compuesto, y que los mapeos
contenidos de propiedades se refieren tanto a la clase entidad como a la clase del identificador compuesto.

class (optativo, pero requerido para un identificador compuesto mapeado): La clase usada como identificador
compuesto.

Vamos a describir una tercera manera, aún más conveniente de enfocar el tema de los modificadores compuestos, en
donde el identificador compuesto es implementado como una clase componente en Sección 8.4, “Componentes como
identificadores compuestos”. Los atributos descritos a continuación se aplican sólo a este enfoque alternativo:

name (optativo, pero obligatorio para este abordaje): Una propiedad de tipo "componente" que contiene el
identificador compuesto (ver capítulo 9).

access (optativo, por defecto, property): La estrategia que Hibernate debería usar para acceder al valor de las
propiedades.

class (optativo, por defecto, el tipo de propiedad determinado por reflexión): La clase componente usada como
identificador compuesto (ver la sección siguiente).

Este tercer abordaje, un compoennte identificador, es el que recomendamos para casi todas las aplicaciones.

5.1.8. discriminator (discriminador)

El elemento <discriminator> se requiere para la persistencia polimórfica que use la estrategia de mapeo de "una tabla
por cada jerarquía de clases" y declara una columna como el "discriminador" de la tabla. La columna "discriminador"
contiene un valor rótulo, que le dice a la capa de persistencia qué clase debe instanciar para cada registro en particular.
Sólo un número restringido de tipos puede ser usado: string, character, integer, byte, short, boolean, yes_no,
true_false.

<discriminator
column="discriminator_column" (1)
type="discriminator_type" (2)
force="true|false" (3)
insert="true|false" (4)
formula="arbitrary sql expression" (5)

/>

56 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

(1)
column (optativo, por defecto, class) el nombre de la columna del discriminador

(2)
type (optativo, por defecto, string) un nombre que indica el tipo de Hibernate

(3)
force (optativo, por defecto, false) "fuerza" a Hibernate a especificar valores permitidos de discriminador
cuando captura todas las instancias de la clase raíz.

(4)
insert (optativo, por defecto, true) asígnele false si su columna de discriminador tambíén forma parte de un
identificador compuesto mapeado (le dice a Hibernate que no incluya a esta columna en el INSERT).

(5)
formula (optativo) una expresión SQL arbitraria que se ejecuta cuando un tipo tiene que ser evaluado. Permite
discriminación basada en contenido.

Los verdaderos valores de la columna del discriminador son especificados por el atributo discriminator-value de los
elementos <class> y <subclass>.

El atributo force solamente es útil si la tabla contiene valores de discriminador "adicionales" que no están mapeados a
una clase persistente. Éste no es casi nunca el caso.

Usando el atributo formula, se puede declarar una expresión SQL arbitraria que puede ser usada para evaluar el tipo de
una fila.

<discriminator
formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end"
type="integer"/>

5.1.9. version (optativo)

El elemento <version> element es optativo e indica que la tabla contiene datos con versión. Esto es particularmente útil si
se planea usar transacciones largas (véase más abajo).

<version
column="version_column" (1)
name="propertyName" (2)

type="typename" (3)
access="field|property|ClassName" (4)
unsaved-value="null|negative|undefined" (5)
generated="never|always" (6)
insert="true|false" (7)

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

(1)
column (optativo, por defecto, el nombre de la propiedad): El nombre de la columna que contiene el número de
versión.

(2)
name: El nombre de una propiedad de la clase persistente.

(3)
type (optativo, por defecto, integer): El tipo del número de versión.

(4)
Los números
accessde(optativo,
versión pueden ser deproperty
por defecto, los tipos de Hibernate
): La que, integer
long
estrategia , short
Hibernate , timestamp
debería o calendar
usar para obtener . de la
el valor
propiedad.

57 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Una propiedad de versión o timestamp nunca debería ser nula para una instancia desprendida, de manera que Hibernate
pueda identificar cualquier instancia con una versión o timestamp nulos como transitoria, sin importar qué otras estrategias
de unsaved-value se hayan usado. Declarar una propiedad versión o timestamp como anulable es un forma fácil de
evitar problemas con la revinculación transitiva en Hibernate, ¡especialmente útil para quienes usen identificadores
asignados o claves compuestas!

5.1.10. timestamp (optativo)

El elemento optativo <timestamp> indica que la tabla contiene datos marcados con fecha y hora. Esto apunta a ser una
alternativa a asignar números de versión. Las timestamps son, por naturaleza, una implementación menos segura de
"locking" optimista. De todos modos, la aplicación podría usar timestamps de otras formas.

<timestamp
column="timestamp_column" (1)
name="propertyName" (2)
access="field|property|ClassName" (3)
unsaved-value="null|undefined" (4)
source="vm|db" (5)

generated="never|always" (6)
node="element-name|@attribute-name|element/@attribute|."
/>

(1)
column (optativo, por defecto, the property name): el nombre de una columna que contiene la timestamp.

(2)
name: El nombre de una propiedad estilo JavaBeans del tipo Date o Timestamp de la clase persistente.

(3)
access (optativo, por defecto, property): La estrategia que Hibernate debería usar para acceder al valor de la
propiedad.

(4)
unsaved-value (optativo, por defecto, null): Una propiedad de versión, que indica que una instancia ha sido
recientemente instanciada (no grabada, "transitoria"), distinguiéndola de una instancia desprendida, que haya sido
cargada o grabada por una sesión previa (undefined especifica que debería usarse el valor de la propiedad
indentificadora).

(5)
source (optativo, por defecto, vm): ¿De dónde debería Hibernate obtener el valor de la timestamp? ¿De la base de
datos, o de la JVM actual? Las timestamps originadas en la base de datos son costosas, porque Hibernate debe
consultar la base de datos para determinar el "próximo" valor, pero serán más seguras en entornos de cluster. Note
también, que no se sabe si todos los dialectos soportan esta obtención de timestamps de la base de datos, mientras
que otros pueden ser inseguros de usar en "locking", dada su falta de precisión (Oracle 8, por ejemplo).

(6)
generated (optativo, por defecto, never): Especifica que esta propiedad timestamp en realidad es generada por la
base de datos. Vea la discusión de propiedades generadas.

Note que <timestamp> equivale a <version type="timestamp">. Y <timestamp source="db"> equivale a <version
type="dbtimestamp">

5.1.11. property

El elemento <property> declara una propiedad pesistente de la clase, estilo JavaBeans.

<property
name="propertyName" (1)
column="column_name" (2)

type="typename" (3)

58 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

update="true|false" (4)
insert="true|false" (4)
formula="arbitrary SQL expression" (5)
access="field|property|ClassName" (6)

lazy="true|false" (7)
unique="true|false" (8)
not-null="true|false" (9)
optimistic-lock="true|false" (10)
generated="never|insert|always" (11)

node="element-name|@attribute-name|element/@attribute|."
index="index_name"
unique_key="unique_key_id"
length="L"
precision="P"
scale="S"
/>

(1)
name: el nombre de la propiedad, com una letra minúscula inicial.

(2)
column (optativo, por defecto, the property name): el nombre de la columna de la tabla en la base de datos
mapeada. También puede especificarse con elemento(s) <column> anidados.

(3)
type (optativo): un nombre que indica el tipo de Hibernate.

(4)
update, insert (optativo, por defecto, true) : indica que las columnas mapeadas deben ser incluidas en los
comandos SQL UPDATE y/o INSERT. Asignarles false a las dos permite una propiedad puramente "derivada", cuyo
valor es inicializado solamente por alguna otra propieadad que se mapea a la(s) misma(s) columna(s), o por un
trigger, u otra aplicación.

(5)
formula (optativo): una expresión SQL que define el valor de una propiedad computada. Las propiedades
computadas no tienen un mapeo de columna propio.

(6)
access (optativo, por defecto, property): La estrategia que Hibernate debería usar para acceder al valor de esta
propiedad.

(7)
lazy (optativo, por defecto, false): Especifica que esta propiedad debería ser obtenida de manera haragana,
cuando se acceda por primera vez a la variable de instancia (requiere implementación bytecode en tiempo de
ejecución).

(8)
unique (optativo): Habilita la generación del lenguaje de definición de datos (DDL) para una constraint única para
las columnas. También permite que esto sea apuntado por una property-ref.

(9)
not-null (optativo): Habilita la generación del lenguaje de definición de datos (DDL) para una constraint de
nulabilidad para las columnas.

(10)
optimistic-lock (optativo, por defecto, true): Especifica si las actualizaciones al valor de esta propiedad
requieren o no un "lock" optimista. En otras palabras, determina si pueden ocurrir incrementos de versión cuando
la propiedad está "sucia".

(11)
generated (optativo, por defecto, never): Especifica que el valor de esta propiedad es en realidad generado por la

59 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

base de datos. Vea la discusión de propiedades generadas.

typename puede ser:

El nombre de un tipo básico de Hibernate (por ejemplo integer, string, character, date, timestamp,
float, binary, serializable, object, blob).

El nombre de una clase de Java con un tipo básico por defecto (por ejemplo int, float, char,
java.lang.String, java.util.Date, java.lang.Integer, java.sql.Clob).

El nombre de una clase de Java serializable.

El nombre de un tipo de Java hecho a medida (por ejemplo, com.mipaquete.MiTipoPersonalizado).

Si no se especifica un tipo, Hibernate usará reflexión en la propiedad nombrada, para adivinar el tipo Hibernate correcto.
Hibernate intentará interpretar el nombre de la clase devuelta por el método "getter" de la propiedad, usando las reglas 2, 3
y 4, en ese orden. De todos modos, esto no siempre es suficiente. En algunos casos, aún se necesita el atributo type (por
ejemplo, para distinguir entre Hibernate.DATE e Hibernate.TIMESTAMP, o para especificar un tipo hecho a medida).

El atributo access le permite controlar cómo Hibernate accederá a la propiedad en tiempo de ejecución. Por defecto,
Hibernate invocará al par get/set. Si usted especifica access="field", Hibernate salteará el par get/set y accederá al
campo directamente, usando reflexión. Usted puede especificar su propia estrategia de acceso, nombrando una clase que
implemente la interfaz org.hibernate.property.PropertyAccessor.

Las propiedades derivadas son una característica especialmente poderosa. Estas propiedades son de sólo lectura, por
definición. Son computadas en el momento en que se cargan. Se declara dicha computación como un comando SQL, que
se traduce en una subconsulta SELECT en el código SQL que carga la instancia.

<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 )"/>

Note que usted se puede referir a la tabla misma de la entidad, evitando usar el alias para una columna en particular (en el
ejemplo, customerId). También note que puede usar elementos <formula> anidados si no le gusta especificar el valor
como un atributo.

5.1.12. many-to-one

Una asociación común con otra clase persistente se declara usando un elemento many-to-one element. El modelo
relacional es una asociación de-muchos-a-uno: la clave foránea en una tabla se refiera a la(s) columna(s) clave de la tabla
de destino.

<many-to-one
name="propertyName" (1)
column="column_name" (2)

class="ClassName" (3)
cascade="cascade_style" (4)
fetch="join|select" (5)
update="true|false" (6)
insert="true|false" (6)

property-ref="propertyNameFromAssociatedClass" (7)
access="field|property|ClassName" (8)
unique="true|false" (9)
not-null="true|false" (10)
optimistic-lock="true|false" (11)

lazy="proxy|no-proxy|false" (12)
not-found="ignore|exception" (13)
entity-name="EntityName" (14)
formula="arbitrary SQL expression" (15)
node="element-name|@attribute-name|element/@attribute|."
embed-xml="true|false"
index="index_name"

60 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

unique_key="unique_key_id"
foreign-key="foreign_key_name"
/>

(1)
name: El nombre de la propiedad.

(2)
column (optativo): El nombre de la columna de clave foránea. También puede ser especificada por elementos
<column> anidados.

(3)
class (optativo, por defecto, el tipo de la propiedad, determinado por reflexión): El nombre de la clase asociada

(4)
cascade (optativo): Especifica qué operaciones serán propagadas en cascada desde el objeto padre hacia los
objetos asociados.

(5)
fetch (optativo, por defecto, select): Elije entre la captura (fetching) por outer-join y la captura por selección
secuencial.

(6)
update, insert (optativo, por defecto, true) especifica que las columnas mapeadas deben incluirse en el los
comandos SQL UPDATE y/o INSERT. Asignarles false permite una propiedad puramente "derivada", cuyo valor es
inicializado desde alguna otra propiedad, o desde un trigger, u otra aplicación.

(7)
property-ref: (optativo) El nombre de una propiedad de una clase asociada que está ligada a esta clave foránea.
Si no se especifica, se usa la clave primaria de la clase asociada.

(8)
access (optativo, por defecto, property): La estrategia que Hibernate debería usar para acceder al valor de la
propiedad.

(9)
unique (optativo): Habilita la generación de lenguaje de definición de datos (DDL) para la creación de una
constraint única para la columna de clave foránea. También permite que ésta sea el blanco referido por una
property-ref. Esto hace que la "multiplicidad" de la asociación sea, efectivamente, de-uno-a-uno.

(10)
not-null (optativo): Habilita la generación de lenguaje de definición de datos (DDL) para la creación de una
constraint de nulabilidad para las columnas de la clave foránea.

(11)
optimistic-lock (optativo, por defecto, true): Especifica si las actualizaciones de esta propiedad requieren o no
la adquisición de un "lock" optimista. En otras palabras, determina si debería ocurrir un incremento de versión
cuando esta propiedad esté "sucia".

(12)
lazy (optativo, por defecto, proxy): Las asociaciones de extremo único están "proxied" por defecto. lazy="no-
proxy" especifica que el valor de la propiedad debe ser capturado en forma haragana, cuando se acceda a la
variable de instancia por primera vez (requiere instrumentación bytecode en tiempo de ejecución). lazy="false"
especifica que la asociación siempre será capturada de manera ansiosa ("eager" fetching).

(13)
not-found (optativo, por defecto, exception): Especifica cómo serán manejadas las claves foráneas que se
refieran a filas ausentes. ignore tratará dicha fila ausente como si fuera una asociación nula.

61 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

(14)
entity-name (optativo): El nombre de entidad de la clase asociada.

(15)
formula (optativo): una expresión SQL que define el valor de una clave foránea computada.

Asignarle cualquier valor que tenga sentido al atributo cascade que no sea none propagará ciertas operaciones al objeto
asociado. Los valores que tienen sentido son las operaciones básicas de Hibernate, persist, merge, delete,
save-update, evict, replicate, lock, refresh, así como los valores especiales delete-orphan y all, y
combinaciones de nombres de operacion separados por comas, por ejemploc ascade="persist,merge,evict" o
cascade="all,delete-orphan". Vea la Sección 10.11, “Persistencia transitiva” para una explicación completa. Note
que las asociaciones a un solo valor (de-muchos-a-uno y de-uno-a-uno) no soportan el borrado de huérfanos.

Una declaración many-to-one típica se ve tan simple como esto:

<many-to-one name="product" class="Product" column="PRODUCT_ID"/>

El atributo property-ref debe ser usado solamente para datos heredados/anticuados en donde la clave foránea apunte a
una clave única de la tabla asociada que no es la clave primaria. Éste es un modelo relacional feo. Por ejemplo, suponga
que la clase Product class tiene un número de serie único, que no es la clave primaria. (El atributo unique controla en
Hibernate la generación de DDL con la herramienta SchemaExport).

<property name="serialNumber" unique="true" type="string" column="SERIAL_NUMBER"/>

Entonces, el mapeo para OrderItem podría usar:

<many-to-one name="product" property-ref="serialNumber" column="PRODUCT_SERIAL_NUMBER"/>

De todos modos, esto se desaconseja.

Si la clave única referida comprende múltiples propiedades de la entidad asociada, usted debería mapear las propiedades
referidas dentro de un elemento llamado <properties>.

Si la clave única referida es la propiedad de un componente, se puede especificar un "path de propiedades":

<many-to-one name="owner" property-ref="identity.ssn" column="OWNER_SSN"/>

5.1.13. one-to-one

Una asociación de-uno-a-uno a otra clase persistente se declara usando el elemento one-to-one.

<one-to-one
name="propertyName" (1)

class="ClassName" (2)
cascade="cascade_style" (3)
constrained="true|false" (4)
fetch="join|select" (5)
property-ref="propertyNameFromAssociatedClass" (6)

access="field|property|ClassName" (7)
formula="any SQL expression" (8)
lazy="proxy|no-proxy|false" (9)
entity-name="EntityName" (10)
node="element-name|@attribute-name|element/@attribute|."
embed-xml="true|false"
foreign-key="foreign_key_name"
/>

(1)
name: El nombre de la propiedad.

62 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

(2)
class (optativo, por defecto, el tipo de la propiedad, determinado por reflexión): El nombre de la clase asociada.

(3)
cascade (optativo) especifica qué operaciones deben ser propagadas en cascada desde el objeto padre hacia el
objeto asociado.

(4)
constrained (optativo) especifica que una constraint de clave foránea a la clave primaria de la tabla mapeada
apunta a la tabla de la clase asociada. Esta opción afecta el orden en que save() y delete() son propagadas en
cascada, y determina si la asociación puede generar "proxies" (también es usada por la herramienta de exportación
de esquema de base de datos).

(5)
fetch (optativo, por defecto, select): Elige entre captura por outer-join y captura por selección secuencial.

(6)
property-ref: (optativo) El nombre de una propiedad de la clase asociada que está asociada a la clave primaria
de esta clase. Si no se especifica, se usa la clave primaria de la clase asociada.

(7)
access (optativo, por defecto, property): La estrategia que Hibernate debe usar para acceder al valor de la
propiedad.

(8)
formula (optativo): Casi todas las asociaciones de-uno-a-uno se mapean a la clave primaria de la entidad a la que
pertenecen, En el raro caso de que no sea así, se puede especificar alguna otra columna o expresión con la cual
asociarla usando una fórmula SQL. (vea org.hibernate.test.onetooneformula por un ejemplo).

(9)
lazy (optativo, por defecto, proxy): Por defecto, las asociaciones de extremo único usan proxies. lazy="no-
proxy" especififica que la propuedad debe ser capturada en forma haragana cuando se accede a la variable de
instancia por primera vez (requiere instrumentación bytecode de tiempo de ejecución). lazy="false" especifica
que á asociación será siempre capturada de forma "ansiosa" (eager fetching). ¡Note que si se usa
constrained="false", usar proxies es imposible e Hibernate siempre capturará en forma ansiosa!

(10)
entity-name (optativo): El nombre de entidad de la clase asociada.

Hay dos variantes de asociación de-uno-a-uno:

asociaciones por clave primaria.

asociaciones por clave foránea única.

Las asociaciones por clave primaria no necesitan una columna extra en la tabla; si dos filas están relacionadas por esta
asociación, entonces las dos filas en las respectivas tablas comparten el mismo valor de clave primaria. ¡Así que si usted
quiere que dos objetos estén relacionados por la misma asociación de clave primaria, tiene que asegurarse de que se les
asigne el mismo valor de identificador!

Para una asociación por clave primaria, agréguele los siguientes mapeos a Employee y Person, respectivamente.

<one-to-one name="person" class="Person"/>

<one-to-one name="employee" class="Employee" constrained="true"/>

Ahora, debemos asegurarnos de que las claves primarias de las filas relacionadas en las tablas PERSON y EMPLOYEE
son iguales. Usamos una estrategia especial de Hibernate para asegurarnos de que las claves primarias de filas relacionadas
sean iguales: un a estrategia de generación de identificador llamada foreign:

63 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

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

Entonces, a una instancia recientemente creada de Person se le asigna el mismo valor de clave primaria que a la instancia
de Employee referida por la propiedad employee.

Alternativamente, una clave foránea que apunte a una constraint única de Employee a Person, puede expresarse como:

<many-to-one name="person" class="Person" column="PERSON_ID" unique="true"/>

Y esta asociación puede ser convertida en bidireccional agregando lo siguiente al mapeo de Person:

<one-to-one name="employee" class="Employee" property-ref="person"/>

5.1.14. natural-id

<natural-id mutable="true|false"/>

<property ... />


<many-to-one ... />
......
</natural-id>

Aunque recomendamos el uso de clave sustitutas como claves primarias, aún se deberían identificar claves naturales para
todas las entidades. Una clave natural es una propiedad o combinación de propiedades que sea única y no nula. Si también
es inmutable, mejor todavía. Mapee las propiedades de la clave natural dentro del elemento <natural-id>, e Hibernate
generará las constraints necesarias de unicidad y nulabilidad, y su mapeo quedará más auto-documentado.

Recomentamos fuertemente que implemente equals() y hashCode() para comparar las propiedades de la clave natural
de la entidad.

Este mapeo no debería usarse con entidades para las cuales la clave primaria es la clave natural.

mutable (optativo, por defecto, false): Por defecto, propiedades identificador naturales que asume son inmutables
(constantes).

5.1.15. component, dynamic-component

El elemento <component> mapea propiedades de un objeto hijo a columnas de la tabla de la clase padre. Los componenes
a su vez pueden declarar sus propias propiedades, componentes o colecciones. Vea "Componentes" a continuación.

<component
name="propertyName" (1)
class="className" (2)

insert="true|false" (3)
update="true|false" (4)
access="field|property|ClassName" (5)
lazy="true|false" (6)
optimistic-lock="true|false" (7)

unique="true|false" (8)
node="element-name|."
>

<property ...../>
<many-to-one .... />
........
</component>

64 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

(1)
name: El nombre de la propiedad

(2)
class (optativo, por defecto, el tipo de la propiedad, determinado por reflexión): El nombre de la clase
componente (hija).

(3)
insert: ¿Aparecen las columnas mapeadas en los SQL INSERTs?

(4)
update: ¿Aparence las columnas mapeadas en los SQL UPDATEs?

(5)
access (optativo, por defecto, property): La estrategia que Hibernate debería usar para acceder al valor de la
propiedad.

(6)
lazy (optativo, por defecto, false): Especifica que este componente debería ser capturado en forma haragana
(lazy fetching) cuando se acceda a la variable de instancia por primera vez (requiere implementación bytecode en
tiempo de ejecución).

(7)
optimistic-lock (optativo, por defecto, true): Especifica si las actualizaciones de este componente requieren o
no la adquisición de un "lock" optimista. En otras palabras, determina si debería haber un incremento de versión
cuando la propiedad esté "sucia"

(8)
unique (optativo, por defecto, false): Especifica que existe una constraint de unicidad sobre todas las columnas
mapeadas del componente.

Las propiedades <property> hijas mapean propiedades de la clase hija a columnas de la tabla.

El elemento <component> acepta un subelemento <parent> que mapea una propiedad del componente como una
referencia que apunta de vuelta a la entidad contenedora.

El elemento <dynamic-component> permite que un Map sea mapeado como componente, en donde el nombre de la
propiedad se refiere a claves del mapa, véase Sección 8.5, “Componentes dinámicos”.

5.1.16. properties

El elemento <properties> permite la definición de un agrupamiento lógico, con un nombre, de algunas propiedades de
una clase. La utilidad más importante de este tipo de construcción, es que permite que una combinación de propiedades
sea el blanco de un property-ref. También es una manera conveniente de definir una constraint de unicidad.

<properties
name="logicalName" (1)
insert="true|false" (2)
update="true|false" (3)
optimistic-lock="true|false" (4)
unique="true|false" (5)

>

<property ...../>
<many-to-one .... />
........
</properties>

(1)
name: El nombre lógico del agrupamiento ( no es un nombre de propiedad real).

65 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

(2)
insert: ¿Aparecen las propiedades mapeadas en los comandos INSERT?

(3)
update: ¿Aparecen las propiedades mapeadas en los comandos UPDATE?

(4)
optimistic-lock (optativo, por defecto, true): Especifica si las actualizaciones de esta propiedad requieren o no
la adquisición de un "lock" optimista. En otras palabras, determina si debería ocurrir un incremento de versión
cuando esta propiedad esté "sucia".

(5)
unique (optativo, por defecto, false): Especifica que existe un constraint de unicidad sobre todas las columnas
mapeadas del componente.

Por ejemplo, si tenemos el siguiente mapeo de <properties>:

<class name="Person">

<id name="personNumber"/>
...
<properties name="name" unique="true" update="false">
<property name="firstName"/>
<property name="initial"/>
<property name="lastName"/>
</properties>

</class>

Y entonces podemos tener una asociación de datos al estilo anticuado, que se refiera a esta clave única de la tabla Person
table, en lugar de a la clave primaria.

<many-to-one name="person" class="Person" property-ref="name">

<column name="firstName"/>
<column name="initial"/>
<column name="lastName"/>

</many-to-one>

Pero no recomendamos esto, excepto en el contexto de un mapeo a datos anticuados/heredados.

5.1.17. subclass

Finalmente, la persistencia polimórfica requiere la declaracíón de cada subclase de la clase persistente raíz. Para la
estrategia de mapeo una-tabla-por-clase, se usa la declaración <subclass>.

<subclass
name="ClassName" (1)

discriminator-value="discriminator_value" (2)
proxy="ProxyInterface" (3)
lazy="true|false" (4)
dynamic-update="true|false"
dynamic-insert="true|false"
entity-name="EntityName"
node="element-name"
extends="SuperclassName">

<property .... />

.....
</subclass>

(1)
Cada subclase
name: Eldebería
nombredeclara sus propias
(enteramente propiedades
calificado) persistentes y subclases. Se asume que las propiedades <version>
de la subclase.
e <id> serán heredadas de la clase raíz. Cada subclase en la jerarquía debe definir un valor único de discriminator-

66 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

value. Si no es especificado ninguno, se usa el nomre enteramente calificado de la clase de Java.

Para información sobre el mapeo de herencias, vea Capítulo 9, Mapeo de Herencia.

5.1.18. joined-subclass

Alternativamente, cada subclase puede ser mapeada a su propia tabla (la estrategia de mapeo "una-table-por-subclase"). El
estado heredado se captura haciendo un "join" con la tabla de la superclase. Usamos el elemento <joined-subclass>.

<joined-subclass
name="ClassName" (1)
table="tablename" (2)
proxy="ProxyInterface" (3)
lazy="true|false" (4)
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>

(1)
name: El nombre enteramente calificado de la subclase.

(2)
table: El nombre de la tabla de la subclase.

(3)
proxy (optativo): Especifica una clase o interfaz a usar para la inicilización haragana de proxies.

(4)
lazy (optativo, por defecto, true): Asignar lazy="false" inhabilita el uso de captura haragana (lazy fetching).

Para esta estrategia de mapeo no se requiere ninguna columna discriminadora. Sin embargo, cada subclase debe declarar
una columna de tabla que contenga al identificador de objeto, usando el elemento <key>. El mapeo del comienzo del
capítulo sería rescrito así:

<?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"/>

67 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

<property name="name" type="string"/>


</joined-subclass>

</class>

<class name="eg.Dog">
<!-- acá podría ir el mapeo de Dog -->
</class>

</hibernate-mapping>

Para información sobre los mapeos de herencias, vea Capítulo 9, Mapeo de Herencia.

5.1.19. union-subclass

Una tercera opción es mapear a tablas sólo las clases concretas de la jerarquía de herencias. (la estrategia de "una-tabla-
por-clase-concreta"), en la cual una tabla define todo el estado persustente de la clase, incluido el estado persistente. En
Hibernate, no es absolutamente necesario mapear dichas jerarquías de herencia. Se puede, simplemente, mapear cada
clase con una declaracíón separada de <class>. De todos modos, si desea usar asociaciones polimórficas (por ejemplo,
una asociación a la superclase de su jeraquía), es necesario usar un mapeo con <union-subclass>.

<union-subclass
name="ClassName" (1)

table="tablename" (2)
proxy="ProxyInterface" (3)
lazy="true|false" (4)
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>

(1)
name: El nombre, enteramente calificado, de la subclase.

(2)
table: El nombre de la tabla de la subclase.

(3)
proxy (optativo): Especifica una clase o interfaz a usar para la inicilización haragana de proxies.

(4)
lazy (optativo, por defecto, true): Especificar lazy="false" inhabilita el uso de captura haragana (lazy
fetching).

Para esta estrategia de mapeo no se requiere una columna discriminadora.

Para información sobre los mapeos de herencias, vea Capítulo 9, Mapeo de Herencia.

5.1.20. join

Usando el elemento <join>, es posible mapear propiedades de una clase a varias tablas, cuando hay una relacion
de-uno-a-uno entre dichas tablas.

<join

68 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

table="tablename" (1)
schema="owner" (2)
catalog="catalog" (3)
fetch="join|select" (4)
inverse="true|false" (5)

optional="true|false"> (6)

<key ... />

<property ... />


...
</join>

(1)
table: El nombre de la tabla asociada.

(2)
schema (optativo): Suplanta al nombre del esquema de base de datos especificado en el elemento raíz
<hibernate-mapping>.

(3)
catalog (optativo): Suplanta al nombre del catálogo de base de datos especificado en el elemento raíz
<hibernate-mapping>.

(4)
fetch (optativo, por defecto, join): Si se le asigna el valor pr defecto join, Hibernate usará un INNER JOIN para
capturar una asociación definida por una clase en su superclase, y utilizará un OUTER JOIN para una asociación
definida por una subclase. Si se le asigna el valor select, entonces Hibernate usará una asociación secuencial
definida en una subclase, la cual sólo será emitida cuando ocurra que una fila represente una instancia de la
subclase. Los INNER JOINS aún serán usados para capturar asociaciones definidas por la clase y sus superclases.

(5)
inverse (optativo, por defecto, false): Si se habilita, Hibernate no intentará insertar o acualizar las propiedades
definidas por esta asociación.

(6)
optional (optativo, por defecto, false): Si se habilita, Hibernate insertará una nueva fila sólo si las propiedades
definidas por esta asociación son no nulas, y siempre usará un OUTER JOIN para capturar las propiedades.

Por ejemplo, la información de la dirección de una perosna puede ser mapeada en una tabla separada, al tiempo que se
preserva la semántica para todas las propiedades.

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

...

Esta característica es a menudo sólo útil para modelos de datos anticuados. Recomendamos que haya menos tablas que
clases, y un modelo de dominio bien detallado.
N.del T.: se les suele llamar "detallados" (en inglés "fine-grained") a los modelos con clases que contiengan otras clases
como miembro, por ejemplo, una clase Dirección dentro de una clase Persona, en lugar de incluir los campos de la
direccíón (calle, número, etc) directamente en Persona.
Aunque los campos de Dirección probablemente se terminen persistiendo en la misma tabla que los de Persona, se
considera que una representación jerárquica que use las clases Persona y Dirección es más "detallada" que la simple
tabla PERSONA subyacente

69 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

De todos modos, "join-table" es útil para alternar entre distintas estrategias de mapeo en una misma jerarquía, como se
explica más adelante.

5.1.21. key

Hemos visto surgir el elemento <key> varias veces ya. Aparece cada vez que el mapeo de un elemento padre define una
asociación a una nueva tabla, que haga referencia a la clave primaria de la tabla original.

<key
column="columnname" (1)

on-delete="noaction|cascade" (2)
property-ref="propertyName" (3)
not-null="true|false" (4)
update="true|false" (5)
unique="true|false" (6)

/>

(1)
column (optativo): El nombre de la columna de clave foránea. Esto también puede ser especficado por elementos
<column> anidados.

(2)
on-delete (optativo, por defecto, noaction): Especifica si la constraint de clave foránea tiene habilidtado el
borrado en cascada a niver de la base de datos.

(3)
property-ref (optativo): Especifica que la clave foránea se refiere a columnas que no son la clave primaria de la
tabla original. (provisto para datos anticuados/heredados solamente).

(4)
not-null (optativo): Especifica que las columnas de la clave foránea no son anulables (lo cual se sobreentiende
cuando la clave foránea es también parte de la clave primaria)

(5)
update (optativo): Especifica que la clave foránea nunca debería ser actualizada (lo cual se sobreentiende cuando
la clave foránea es también parte de la clave primaria)

(6)
unique (optativo): Especifica que la clave foránea debería tener una constraint de unicidad (lo cual se
sobreentiende cuando la clave foránea es también la clave primaria).

Recomendamos que, para sistemas en los cuales la performance de delete sea importante, toda las claves sean definidas
con on-delete="cascade", e Hibernate generará una constraint ON CASCADE DELETE a nivel de la base de datos, en
lugar de varios comandos DELETE individuales. Tenga presente que esta característica saltea la estrategia usual de locking
optimista para datos versionados.

Los atributos not-null y update son útiles cuando se mapea una asociación de-uno-a-muchos unidireccional. Si la mapea
a una clave foránea no anulable, hay que declarar la columa clave usando <key not-null="true">.

5.1.22. elementos column y formula

Cualquier elemento de mapeo que acepte un atributo column aceptará, alternativamente, un subelemento <column>. De la
misma manera, el elemento <formula> es una alternativa al atributo formula.

<column
name="column_name"
length="N"
precision="N"
scale="N"
not-null="true|false"

70 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

unique="true|false"
unique-key="multicolumn_unique_key_name"
index="index_name"
sql-type="sql_type_name"
check="SQL expression"
default="SQL expression"/>

<formula>SQL expression</formula>

Los atributos column y formula pueden incluso ser combinados dentro de la mismo mapeo de propiedad o asociación,
para expresar, por ejemplo, codiciones de asociación exóticas.

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

5.1.23. import

Supongamos que su aplicación tiene dos clases persistenes con el mismo nombre, y usted no quiere especificar un nombre
de clase enteramente calificado (con el paquete) en las consutas de Hibernate. Las clases pueden ser "importadas"
explícitamente en lugar de tener que confiar en el auto-import="true". Usted puede incluso importar clases e interfaces
que no estén mapeadas explícitamente.

<import class="java.lang.Object" rename="Universe"/>

<import
class="ClassName" (1)
rename="ShortName" (2)
/>

(1)
class: The fully qualified class name of of any Java class.

(2)
rename (optativo, por defecto, the unqualified class name): A name that may be used in the query language.

5.1.24. any

Hay un tipo más de mapeo de propiedad: El elemento de mapeo <any> define una asociación polimórfica a clases de
múltiples tablas. Este tipo de mapeo siempre requiere más de una columna. La primera columna contiene el tipo de la
entidad asociada. Es imposible especificar una constraint de clave foránea para este tipo de asociaciones. Esto se debería
usar sólo en casos muy especiales (por ejemplo, logs de auditoría, datos de sesión de usuario, etc).

El atributo meta-type le permite a la aplicación especificar un tipo hecho a medida, que mapeará valores de columna en
la base de datos a clases persistentes, las cuales tendrán propiedades indentificadoras del tipo identificado por id-type. Se
debe especificar el mapeo desde los valores valores del meta-tipo a los nombres de las clases.

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

<any
name="propertyName" (1)

id-type="idtypename" (2)
meta-type="metatypename" (3)
cascade="cascade_style" (4)
access="field|property|ClassName" (5)

71 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

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

>
<meta-value ... />
<meta-value ... />
.....
<column .... />
<column .... />
.....
</any>

(1)
name: el nombre de la propiedad.

(2)
id-type: el tipo del identificador.

(3)
meta-type (optativo, por defecto, string): Cualquier tipo que sea permisible para mapear un discriminador.

(4)
cascade (optativo, por defecto none): el estilo de propagación en cascada

(5)
access (optativo, por defecto, property): La estrategia que Hibernate debería usar para acceder al valor de la
propiedad.

(6)
optimistic-lock (optativo, por defecto, true): Especifica si las actualizaciones de esta propiedad requieren o no
la adquisición de un "lock" optimista. En otras palabras, determina si debería ocurrir un incremento de versión
cuando esta propiedad esté "sucia".

5.2. Tipos de Hibernate

5.2.1. Entidades y "value types"

Para comprender el comportamiento de varios objetos (a nivel del lenguaje Java) con respecto al servicio de persistencia,
necesitamos clasificarlos en 2 grupos:

Una entidad (en inglés, "entity") existe independientemente de que cualquier otro objeto tenga o no referencias a ella.
Contraste esto con el modelo de Java normal, en donde un objeto sufre "garbage colection" en cuanto dejan de referirse a
él. Las entidades deben ser grabadas y borradas explícitamente (excepto cuando los borrados o grabaciones en cascada se
transmiten de padre a hijos). Éste es un tipo distinto de manejo de objetos de datos (ODMG, por sus siglas en inglés), que
consiste en la persistencia de objetos basada en la capacidad de acceder a ellos, lo cual corresponde más íntimamente con
cómo los objetos de una aplicación son usados en sistemas grandes. Las entidades soportan referencias compartidas y
circulares, y pueden ser versionadas.

El estado persistente de una entidad consiste en referencias a otras entidades, y a instancias de lo que en Hibernate se
denomina "value types" (tipos "valor"), Los "value types" son los tipos primitivos, las colecciones (el componente
colección en sí, no lo que está dentro de ellas), y ciertos objetos inmutables. Los "value types" no pueden ser grabados ni
versionados independientemente, sino que son persistidos junto con la entidad que los contiene. No tienen "identidad"
independiente, y por lo tanto no pueden ser compartidos entre dos o más entidades, ni entre colecciones.

Hasta ahora hemos usado el término "clase persistente" para referirnos a entidades. Lo seguiremos haciendo, pero,
estrictamente hablando, sin embargo, un "componente" (en inglés, component) también es una clase, definida por el
usuario, que es persistente. La diferencia es que estos "componentes" tienen la semántica de un "value type", no la de una
entidad. Un "value type" puede ser, entonces, un tipo primitivo de la JDK, o una String, o un tipo definido por el usuario.
Una clase definida por el usuario puede ser, a criterio del programador de la aplicación, un "value type" o una entidad. En
un modelo de dominio, ser una entidad en general implica que varios otros objetos compartirán referencias a ella, mientras
que los "value types" simplemente forman parte de una única clase, vía composición o agrupación (en inglés,
"composition" y "aggregation").

72 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Retomaremos estos conceptos todo a lo largo de la documentación.

El desafío es mapear el sistema de tipos de Java (junto con la definición del desarrollador de las entidades y "value types")
a un sistema del tipo SQL/BD. El puente entre ambos sistemas es provisto por Hibernate. Para las entidades proveemos
<class>, <subclass> y así sucesivamente. Para los "value types" usamos <property>, <component>, etc., normalmente
con un atributo type. El valor de este atributo es uno de los tipos para mapeo de Hibernate. Hibernate trae varios mapeos
(para los tipos estándar de la JDK) ya incluidos. Pero usted puede escribir sus propios tipos para mapeo e implementar sus
propias estrategias de conversión, como veremos más adelante.

Todos los tipos que vienen ya incluidos en Hibernate, excepto las colecciones, soportan la semántica de nulo.

5.2.2. "Value types" básicos

Los tipos de mapeo básicos ya incluidos pueden ser someramente clasificados en:

integer, long, short, float, double, character, byte, boolean, yes_no, true_false

Mapeos de los respectivos tipos primitivos o "clases envoltorio" a valores de columna SQL (que dependen de la
marca de la BD): boolean, yes_no y true_false son todas codificaciones alternativas de un boolean o un
java.lang.Boolean de Java.

string

Un mapeo de tipo de java.lang.String a VARCHAR (o el VARCHAR2 de Oracle).

date, time, timestamp

Mapeos de tipo de java.util.Date y sus subclases a tipos SQL DATE, TIME y TIMESTAMP (o equivalentes).

calendar, calendar_date

Mapeos de tipo de java.util.Calendar a tipos SQL TIMESTAMP y DATE (o equivalente).

big_decimal, big_integer

Mapeos de tipo de java.math.BigDecimal y java.math.BigInteger a NUMERIC (e el NUMBER de Oracle).

locale, timezone, currency

Mapeos de tipo de java.util.Locale, java.util.TimeZone y java.util.Currency a VARCHAR (o el VARCHAR2


de Oracle). Las instancias de Locale y Currency son mapeadas a sus códigos ISO. Las instancias de TimeZone son
mapeadas a su ID.

class

Un mapeo de tipo java.lang.Class a VARCHAR (o la VARCHAR2 de Oracle). Una Class es mapeada con su nombre
enteramente calificado.

binary

Mapea arrays de bytes a un tipo SQL binario apropiado.

text

Mapea cadenas largas de Java los tipos CLOB o TEXT de SQL.

serializable

Mapea tipos serializables de Java a un tipo SQL binario apropiado. También se puede indicar el tipo de Hibernate
serializable con el nombre de una clase o interfaz serializable de Java, que no sea por defecto un tipo básico.

clob, blob

Mapeos de tipo para las clases JDBC java.sql.Clob y java.sql.Blob. Estos tipos pueden ser inconvenientes
para algunas aplicaciones, dado que los objetos clob y blob no pueden ser reusados fuera de una transacción. (Más

73 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

aún, el soporte de drivers es esporádico e inconsistente).

imm_date, imm_time, imm_timestamp, imm_calendar, imm_calendar_date, imm_serializable, imm_binary

Mapeos de tipo para lo que normalmente se considera "tipos mutables de Java", en los que Hibernate adopta ciertas
optimizaciones que son sólo apropiadas para tipos inmutables de Java, y la aplicación trata al objeto como
inmutable. Por ejemplo: para una instancia mapeada como imm_timestamp, no se debería invocar Date.setTime().
Para cambiar el valor de la propiedad (y hacer que ese valor cambiado sea persistido), la aplicación tiene que
asignarle un nuevo objeto (no idéntico) a la propiedad.

Los identificadores únicos de las entidades y colecciones pueden ser de cualquier tipo básico excepto binary, blob y
clob. (Los identiicadores compuestos también están permitidos, ver más adelante).

Los "value types" básicos tienen constantes Type definidas en org.hibernate.Hibernate. Por ejemplo,
Hibernate.STRING representa el tipo string.

5.2.3. "Value types" hechos a medida

Para los programadores, es relativamente fácil crear sus propios "value types". Por ejemplo, usted podría querer persistir
propiedades del tipo java.lang.BigInteger a columnas VARCHAR. Hibernate no trae un tipo ya incluido para esto. Pero
los tipos a medida no solamente sirven para mapear una propiedad de Java (o un elemento colección) a una sola columna
de una tabla. Por ejemplo, usted puede tener una propiedad Java con métodos getNombre()/setNombre() de tipo
java.lang.String que sea persistida varias columnas, como PRIMER_NOMBRE, INICIAL_DEL_SEGUNDO, APELLIDO.

Para crear un tipo a medida, implemente org.hibernate.UserType o org.hibernate.CompositeUserType y declare


propiedades usando el nombre enteramente calificado del tipo. Revise org.hibernate.test.DoubleStringType para
comprobar el tipo de cosas que es posible hacer.

<property name="twoStrings" type="org.hibernate.test.DoubleStringType">

<column name="first_string"/>
<column name="second_string"/>

</property>

Note el uso de los elementos <column> para mapear una sola propiedad a múltiples columnas.

Las interfaces CompositeUserType, EnhancedUserType, UserCollectionType, y UserVersionType poveen soporte


para usuarios más especializados.

Incluso se le pueden proveer parámetros a un UserType en el archivo de mapeo. Para lograr esto, su UserType debe
implementar la interfaz org.hibernate.usertype.ParameterizedType. Para proveerle parámetros a su tipo a medida,
usted puede usar el elemento <type> en sus archivos de mapeo.

<property name="priority">

<type name="com.mycompany.usertypes.DefaultValueIntegerType">
<param name="default">0</param>
</type>

</property>

Ahora el UserType puede aceptar valrores para el parámetro llamado default con el objeto Properties que le fue
pasado.

Si se usa un objeto UserType muy a menudo, sería útil definirle un nombre corto. Esto se puede hacer usando el elemento
<typedef>. Los "typedefs" le asignan un nombre a un tipo a medida, y pueden contener también una lista de parámetros
por defecto si el tipo es parametrizado.

<typedef class="com.mycompany.usertypes.DefaultValueIntegerType" name="default_zero">

<param name="default">0</param>

</typedef>

<property name="priority" type="default_zero"/>

74 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

También es posible sustituir (override) los parámetros provistos en un typedef, caso por caso, usando parámetros de tipo
en el mapeo de propiedades.

Aunque la rica variedad de tipos ya incluidos en Hibernate hace que sólo en contadas ocasiones realmente se necesite usar
un tipo a medida, se considera aconsejable crear tipos a medida para clases (no entidades) que ocurran frecuentemente en
su aplicación. Por ejemplo, una clase MonetaryAmount (suma de dinero) sería una buena candidata para un
CompositeUserType, incluso si pudiere ser fácilmente mapeada como componente. Uno de los motivos para esto es
abstracción. Con un tipo a medida como éste, sus documentos de mapeo serán a prueba de posibles cambios en la forma
en que los valores monetarios se representaren en el futuro.

5.3. Mapear una misma clase más de una vez


Es posible proveer más de un mapeo para una clase persistente en particular. En tal caso, se debe proveer un nombre de
entidad para desambigüar entre instancias de las dos entidades mapeadas. (Por defecto, el nombre de la entidad es igual al
nombre de la clase). Hibernate permite especificar el nombre de entidad al trabajar con objetos persistentes, al escribir
consultas, o al mapear asociaciones a dicha entidad.

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

Note cómo ahora las asociaciones son especificadas usando entity-name en lugar de class.

5.4. Identificadores de SQL entrecomillados


Se puede forzar a Hibernate a encerrar el identificador entre comillas, en el SQL generado, encerrando el nombre de tabla
o de columna en signos de acento grave (`), en inglés, "backticks". Hibernate usará el entrecomillado adecuado para el
dialecto SQL correspondiente (lo cual es usualmente comillas, pero para SQL Server son corchetes, y para MySQL, estos
"backticks").

<class name="LineItem" table="`Line Item`">

<id name="id" column="`Item Id`"/><generator class="assigned"/></id>


<property name="itemNumber" column="`Item #`"/>
...
</class>

5.5. Alternativas de metadatos


XML no es para ualquiera, así que hay algunas otras alternativas para definir los metadatos del mapeo O/R en Hibernate.

5.5.1. Usar marcadores de XDoclet

Muchos usuarios e Hibernate prefieren incrustar la información de mapeo directamente en su código fuente, usando las
tags @hibernate.tags de XDoclet. No cubriremos esta estrategia aquí, dado que es estrictamente parte de XDoclet. De
todos modos, incluimos el siguiente ejemplo de la clase Cat con mapeo XDoclet.

package eg;
import java.util.Set;
import java.util.Date;

/**
* @hibernate.class

75 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

* 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;
}

76 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

// 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;
}
}

Vea el sitio de web de Hibernate para más ejemplos de XDoclet con Hibernate.

5.5.2. Usar anotaciones de JDK 5.0

La JDK 5.0 introdujo anotaciones al estilo XDoclet a nivel del lenguaje. Son de tipo comprobado (type-safe) y se verifican
en tiempo de compilación. Este mecanismo es más poderoso que las anotaciones XDoclet, y mejor soportado por las
herramientas gráficas (IDE) como IntelluJ IDEA, muchas de las cuales proveen autocompleción y resaltado de sintaxis
para anotaciones de JDK 5.0. La nueva revisión de los EJB, (JSR-220), usa anotaciones como su principal mecanismo de
metadatos para los Entity Beans. Hibernate3 implementa el EntityManager JSR-220 (la API de persistencia), hay soporte
disponible para el mapeo de metadatos en el paquete de Annotataciones de Hibernate (Hibernate Annotations), como una
descarga separada. Los metadatos que se soportan son tanto Hibernate3 como EJB3 (JSR-220).

El siguiente es un ejemplo de un POJO anotado como Entity Bean EJB.

@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;

// métodos Getter/setter, y de negocio ...


}

Note que el soporte para anotaciones JDK 5.0 (y JSR-220) todavía es un trabajo en curso, incompleto. Por favor remítase
al módulo de Anotaciones de Hobernate para más detales.

5.6. Propiedades generadas


Las propiedades generadas son propiedades cuyos valores son generados por la base de datos. En general, las aplicaciones
Hibernate tienen que refrescar los objetos que contengan cualquier propiedad para la cual la base de datos esté generando
valores. Sin embargo, al marcar propiedades como "generadas" se deja que la aplicación delegue esta responsabilidad en
Hibernate. Esencialmente, cada vez que Hibernate emita un INSERT o UPDATE para una entidad que tenga propiedades
generadas definidas, inmediatamente generará un SELECT para capturar los valores generados.

Por añadidura, las propiedades marcadas como generadas debe ser no insertables y e inmodifocables. Sólo las versiones,
las timestamps, y las propiedades simples pueden ser marcadas como generadas.

77 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

never (el valor por defecto) - significa que el valor de la propiedad dada nunca es generada por la base de datos.

insert - declara que el valor de la propiedad dada es generado al ocurrir un INSERT, pero no es regenerado en los
UPDATEs subsiguientes. Cosas como "fecha de creación" entrarían en esta categoría. Note que, aunque las propiedades
version y timestamp pueden ser marcadas como generadas, esta opción no está disponible para ellas.

always - declara que el valor de esta propiedad es generado tanto al insertar como al modificar.

5.7. Objetos auxiliares de base de datos


Permite la ejecución de comandos CREATE y DROP arbitrarios para objetos de base de datos, en conjunción con las
herramientas de evolución de esquema de base de datos con las que cuenta Hibernate, para poveer la capacidad de definir
un esquema totalmente dentro de los archivos de mapeo de Hibernate. Aunque fue designada específicamente para
eliminar (DROP) cosas como triggers o procedimientos almacenados, en realidad cualquier comando SQL que pueda ser
ejecutado mediante un java.sql.Statement.execute() es válido aquí: ALTERs, INSERTs, etc). Esencialmente, hay
dos maneras de definir objetos de base de datos auxiliares:

La primera es listar explícitamente los comandos CREATE y DROP en el archivo de mapeo:

<hibernate-mapping>

...
<database-object>
<create>CREATE TRIGGER my_trigger ...</create>
<drop>DROP TRIGGER my_trigger</drop>
</database-object>

</hibernate-mapping>

La segunda es proveer una clase a medida, la cual sepa cómo construir los comandos CREATE y DROP. Esta clase a
medida debe implementar la interfaz org.hibernate.mapping.AuxiliaryDatabaseObject.

<hibernate-mapping>
...
<database-object>
<definition class="MyTriggerDefinition"/>
</database-object>

</hibernate-mapping>

Adicionalmente, a estos objetos de base de datos se les puede definir un alcance, de manera que se apliquen sólo cuando
se usen ciertos dialectos determninados.

<hibernate-mapping>
...
<database-object>
<definition class="MyTriggerDefinition"/>
<dialect-scope name="org.hibernate.dialect.Oracle9Dialect"/>
<dialect-scope name="org.hibernate.dialect.OracleDialect"/>

</database-object>
</hibernate-mapping>

Capítulo 6. Mapeo de colecciones

6.1. Colecciones persistentes


Hibernate requiere que los campos con valor de colección sean declarados como el tipo de una interfaz, por ejemplo:

public class Product {


private String serialNumber;
private Set parts = new HashSet();

public Set getParts() { return parts; }


void setParts(Set parts) { this.parts = parts; }

78 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

public String getSerialNumber() { return serialNumber; }


void setSerialNumber(String sn) { serialNumber = sn; }
}

La interfaz puede ser, efectivamente, un java.util.Set, java.util.Collection, java.util.List, java.util.Map,


java.util.SortedSet, java.util.SortedMap o cualquier otra cosa que implemente la interfaz
org.hibernate.usertype.UserCollectionType.)

Dese cuenta de cómo inicializamos la variable de instancia con una instancia de HashSet. Ésta es la mejor manera de
inicializar propiedades con valor de colección, o instancias recientemente inicializadas (no persistentes). Cuando una clase
se vuelve persistente (al llamar persist(), por ejemplo) Hibernate en realidad reemplazará el HashSet con una
implementación de Set propia de Hibernate mismo. Esté atento a errores como éstos:

Cat cat = new DomesticCat();


Cat kitten = new DomesticCat();
....
Set kittens = new HashSet(); //kittens=gatitos
kittens.add(kitten);
cat.setKittens(kittens);
session.persist(cat);
kittens = cat.getKittens(); // Correcto, porque la colección kittens es un Set
(HashSet) cat.getKittens(); // ¡Error!

Las colecciones persistentes que son inyectadas por Hibernate se comportan como: HashMap, HashSet, TreeMap, TreeSet
or ArrayList, dependiendo del tipo de interfaz.

Las instancias de coleccioones tienen el comportamiento usual de los "value tupes". Son automáticamente persistidas
cuando son referidas por un objeto persistente, y aumáticamente borradas cuando son "des-referidas". Si una colección se
pasa de un objeto persistente a otro, sus elementos pueden ser movidos de una tabla a otra. Dos entidades no pueden
compartir una referencia a la misma instancia de una colección. Dado el modelo relacional subyacente, las propiedades
con valor de colección no soportan la semántica de nulo. Hibernate no distingue entre una colección nula y una colección
vacía.

No vale la pena preocuparse mucho por nada de esto. Use las colecciones persistentes del mismo modo en que usaría las
colecciones comunes de Java. Sólo asegúrese de comprender la semántica de las asociaciones bidireccionales (la cual se
discute más adelante).

6.2. Mapeo de colecciones

Consejo

Hay una gran variedad de mapeos que puede ser generada para colecciones, abarcando muchos modelos
relacionales de uso común. Le sugerimos que experimente con la herramienta de generación de esquemas
de DB para tener una idea de cómo las declaraciones de mapeo se traducen en tablas de una base de datos.

El elemento de mapeo de Hibernate que se use para mapear una colección depende del tipo de interfaz. Por ejemplo, un
elemento <set> se usa para mapear propiedades del tipo Set.

<class name="Product">

<id name="serialNumber" column="productSerialNumber"/>


<set name="parts">
<key column="productSerialNumber" not-null="true"/>
<one-to-many class="Part"/>
</set>

</class>

Además de <set>, también están los elementos de mapeo <list>, <map>, <bag>, <array> y <primitive-array> . El
elemento <map> es representativo:

<map
name="propertyName" (1)
table="table_name" (2)
schema="schema_name" (3)
lazy="true|extra|false" (4)

79 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

inverse="true|false" (5)

cascade="all|none|save-update|delete|all-delete-orphan|delet(6)e-orphan"
sort="unsorted|natural|comparatorClass" (7)
order-by="column_name asc|desc" (8)
where="arbitrary sql where condition" (9)
fetch="join|select|subselect" (10)

batch-size="N" (11)
access="field|property|ClassName" (12)
optimistic-lock="true|false" (13)
mutable="true|false" (14)
node="element-name|."
embed-xml="true|false"

>

<key .... />


<map-key .... />
<element .... />
</map>

(1)
name el nombre de la propiedad colección

(2)
table (optativo, por defecto, el nombre de la propiedad) el nombre de la tabla de la colección (no se usa en
asociaciones de-muchos-a-muchos).

(3)
schema (optativo) el nombre de un esquema de base de datos que suplanta al esquema declarado en el elemento
raíz.

(4)
lazy (optativo, por defecto, true) puede usarse para inhabilitar la "captura haragana" (lazy fetching) y especificar
que la asociación es siempre capturada en forma "ansiosa" (eager fetching), o para especificar que la asociación es
"súper-haragana", en donde la mayoría de las operaciones evitan inicializar la colección (prescrito para
colecciones muy grandes).

(5)
inverse (optativo, por defecto, false) marca esta colección como el extremo "inverso" de una asociación
bidireccional.

(6)
cascade (optativo, por defecto, none) les permite a las operaciones propagarse en cascada a las entidades hijas.

(7)
sort (optativo) especifica una colección ordenada con un orden natural, o una clase "comparator" dada.

(8)
order-by (optativo, a partir de JDK1.4 solamente) especifica una columna o columnas de tabla que define el
orden de iteración del Map, Set o bag, junto con un asc o desc opcional (ascendente/descendente).

(9)
where (optativo) especifica una condición SQL WHERE arbitraria, a ser usada ciando se capture o borre la
colección (útil cuando la colección sólo debe contener un subconjunto de los datos disponibles).

(10)
fetch (optativo, por defecto, select) Elije entre la captura por outer-join, select secuencial, y subselect
secuencial.

(11)
6.2.1. batch-size (optativo,
Claves foráneas porcolecciones
de las defecto, 1) especifica el "tamaño de lote" al capturar instancias de esta colección en
forma haragana.

80 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Las instancias de colecciones se distinguen en la BD por la clave foránea que posee a la colección. Esta clave foránea se
denomina columna (o columnas) clave de la colección en la tabla de la colección. La columna clave de la colección es
mapeada por el elemento <key>.

Puede haber constraints de nulabilidad en la columna de la clave foránea. Para la mayoría de las colecciones, esto está
implícito. Para las asociaciones de-uno-a-muchos unidireccionales, la columna de la clave primaria es anulable por
defecto, así que a veces es necesario especificar not-null="true".

<key column="productSerialNumber" not-null="true"/>

la constraint de clave foránea puede usar ON DELETE CASCADE.

<key column="productSerialNumber" on-delete="cascade"/>

Vea el capítulo correspondiente para una definición completa del elemento <key>.

6.2.2. Elementos de las colecciones

Las colecciones pueden contener casi cualquier otro tipo de Hibernate, incluyendo todos los tipos básicos, los tipos a
medida, componentes, y, por supuesto, referencias a otras entidades. Esta es una distinción importante: un objeto en una
colección puede ser manipulado con la semántica de "valor" (que todo su ciclo de vida dependa del dueño de la
colección), o puede ser una referencia a otra entidad, con su propio ciclo de vida. En este último caso, sólo el "vínculo"
entre ambos objetos es lo que se considera como estado almacenado por la colección.

Al tipo contenido se lo refiere como al tipo del elemento de la colección. Los elementos de la colección son mapeados por
<element> o <composite-element>, o, en el caso de referencias a entidades, con <one-to-many> o <many-to-many>.
Los primeros dos, mapean elementos con la semántica de valor. Los otros dos, se usan para mapear asociaciones de
entidades.

6.2.3. Colecciones indexadas

Todos los mapeos de colección, excepto los los que tengan semántica de set o de bag, necesitan una columna índice en la
tabla de la colección (una columna que mapea a un índice de una array), o un índice de List, o una clave de un Map. El
índice de un Map puede ser de cualqueira de los tipos básico, mapeado con <map-key>, puede ser una entidad mapeada
con <map-key-many-to-many>, o puede ser un tipo compuesto, mapeado con <composite-map-key>. El índice de un
array o lista es siempre del tipo integer y se mapea usando el elemento <list-index> element. La columna mapeada
contiene enteros secuenciales (comenzando por cero, por defecto).

<list-index
column="column_name" (1)
base="0|1|..."/>

(1)
column_name (obligatorio): El nombre de la columna que contiene el valor del índice de la colección.

(1)
base (optativo, por defecto, 0): El valor de columna índice correspondiente al primer elemento de la lista o array.

<map-key
column="column_name" (1)
formula="any SQL expression" (2)
type="type_name" (3)

node="@attribute-name"
length="N"/>

(1)
column (optativo): El nombre de la columna que contiene los valores de índice de la colección.

(2)
<map-key-many-to-many
formula (optativo): Una fórmula SQL usada para evaluar la clave del mapa
column="column_name" (1)

81 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

formula="any SQL expression" (2)(3)


class="ClassName"
/>

(1)
column (optativo): El nombre de la columna de clave foránea para los valores de índice de la colección.

(2)
formula (optativo): Una fórmula SQL usada para evaluar la clave foránea de la clave del mapa.

(3)
class (obligatorio): La clase de entidad usada como clave primaria del mapa.

Si su tabla no tiene una columna índice, pero usted aún desea usar una List como el tipo de propiedad, usted debería
mapear la propiedad como una <bag> de Hibernate. Una bag (bolsa) no retiene su orden cuando es obtenida de la base de
datos, pero puede ser opcionalmente ordenada.

6.2.4. Colecciones de valores y asociaciones de-muchos-a-muchos

Cualquier colección de valores o asociación de-muchos-a-muchos requiere una tabla de colección dedicada, con una
columna o columnas de clave foránea, una columna o columnas para el elemento de la colección, y, posiblemente, una
columna o columnas índice.

Para una colección de valores, usamos la tag <element>.

<element
column="column_name" (1)
formula="any SQL expression" (2)
type="typename" (3)

length="L"
precision="P"
scale="S"
not-null="true|false"
unique="true|false"
node="element-name"
/>

(1)
column (optativo): El nombre de la columna que contiene los valores de elementos de la colección.

(2)
formula (optativo): Una fórmula SQL usada para evaluar el elemento.

(3)
type (obligatorio): El tipo de elemento de colección

Una asociación de-muchos-a-muchos (many-to-many) se especifica usando el elemento <many-to-many>.

<many-to-many
column="column_name" (1)
formula="any SQL expression" (2)
class="ClassName" (3)
fetch="select|join" (4)
unique="true|false" (5)

not-found="ignore|exception" (6)
entity-name="EntityName" (7)
property-ref="propertyNameFromAssociatedClass" (8)
node="element-name"
embed-xml="true|false"
/>

82 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

(1)
column (optativo): el nombre de columna del elemento clave foránea.

(2)
formula (optativo): una fórmula SQL usada para evaluar el valor del elemento clave foránea

(3)
class (obligatorio): el nombre de la clase asociada.

(4)
fetch (optativo, por defecto, join): permite capturas para esta asociación mediante un outer-join o un select
secuencial. Éste es un caso especial: para una captura total y "ansiosa" (usando un solo SELECT) de una entidad y
sus relaciones de-muchos-a-muchos con otras entidades, se debería habilitar la captura (fetch) de tipo join no
solamente en la colección misma, sino también en este atributo del elemento anidado <many-to-many>.

(5)
unique (optativo): habilita la generación de lenguaje de definición de datos (DDL) para una constraint de unicidad
en la columna de clave foránea. Esto vuelve efectivamente "de-uno-a-muchos" la multiplicidad de la asociación.

(6)
not-found (optativo, por defecto, exception): Especifica cómo se actuará cuando le falten filas a la clave
foránea. ignore tratará la fila faltante como una asociación nula

(7)
entity-name (optativo): el nombre de entidad de la clase asociada, como una alternativa a class.

(8)
property-ref: (optativo) el nombre de una propiedad, en la clase asociada que está ligada a esta clave foránea. Si
no se especifica, se usa la clave primaria de la clase asociada.

A continuación, algunos ejemplos. Primero, un conjunto de strings.

<set name="names" table="person_names">


<key column="person_id"/>
<element column="person_name" type="string"/>
</set>

Una bag conteniendo enteros, con un orden de iteración determinado por el atributo order-by:

<bag name="sizes" table="item_sizes" order-by="size asc">


<key column="item_id"/>
<element column="size" type="integer"/>
</bag>

Un array de entidades (en este caso, una asociación de-uno-a-muchos):

<array name="addresses" table="PersonAddress" cascade="persist">


<key column="personId"/>
<list-index column="sortOrder"/>
<many-to-many column="addressId" class="Address"/>
</array>

Un mapa de índices string a fechas:

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

Una lista de componentes (se discute más adelante):

83 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

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

6.2.5. Asociaciones de-uno-a-muchos

Una asociación de-uno-a-muchos vincula las tablas de dos clases a través de una clave foránea, sin la intervención de una
"tabla de colección". Este mapeo pierde algo de la semántica de las colecciones normales de Java, porque:

Una instancia de la clase-entidad contenida, no puede pertenecer a más de una instancia de la colección.

Una instancia de la clase-entidad contenida, no puede aparecer en más de un valor de índice de la colección.

Una asociación de Product a Part (producto a parte) requiere la existencia de una clave foránea, y posiblemente una
columna índice en la tabla Part. Una tag <one-to-many> indica que ésta es una asociación de-uno-a-muchos.

<one-to-many
class="ClassName" (1)
not-found="ignore|exception" (2)
entity-name="EntityName" (3)
node="element-name"
embed-xml="true|false"
/>

(1)
class (obligatorio): El nombre de la clase asociada.

(2)
not-found (optativo, por defecto, exception): especifica cómo se manejarán los identificadores en caché que
estén refiriéndose a columnas perdidas. ignore tratará la fila ausente como una asociación nula.

(3)
entity-name (optativo): El nombre de la entidad asociada, como alternativa a class.

Note que el elemento <one-to-many> no necesita declarar ninguna columna. Tampoco es necesatio especificar un nombre
de tabla en ningún lado.

Nota muy importante: Si la columna de clave foránea de una asociación <one-to-many> se declara NOT NULL, se debe
declarar el mapeo de <key> not-null="true" o usar una asociación bidireccional con el mapeo de la colección
marcado como inverse="true". Vea la discusíón sobre asociaciones bidireccionales más adelante en este capítulo.

Este ejemplo muestra un mapa de entidades Part por nombre (donde el nombre de la parte, partName, es una propiedad
persistente de Part). Note el uso de un índice basado en una fórmula.

<map name="parts" cascade="all">


<key column="productId" not-null="true"/>
<map-key formula="partName"/>
<one-to-many class="Part"/>
</map>

6.3. Mapeos de colección avanzados:

6.3.1. Colecciones ordenadas.

Hibernate soporta colecciones que implementen java.util.SortedMap y java.util.SortedSet. Se debe especificar


un comparador en el archivo de mapeo.

84 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

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

Valores permitidos del atributo sort son unsorted, natural y el nombre de una clase que implemente
java.util.Comparator.

Las colecciones ordenadas en realidad se comportan como un java.util.TreeSet o java.util.TreeMap.

Si quiere que la base de datos misma ordene los elementos de la colección, use el atributo order-by del mapeo de los
mapeos de set, bag o map. Esta solución sólo se encuentra disponible bajo la JDK 1.4 o superior (se implementa usando
LinkedHashSet o LinkedHashMap). Esto efectúa el ordenamiento en la consulta SQL, no en memoria.

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

¡Note que el valor del atributo order-by es un ordenamiento SQL, no HQL!

Las asociaciones pueden incluso ser ordenadas por algún criterio arbitrario en tiempo de ejecución, usandp un filtro
(filter()) de colección.

sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();

6.3.2. Asociaciones bidireccionales

Una asociación bidireccional (bidirectional association) permite navegar desde ambos "extremos" de la asociación. Se
soportan dos tipos de asociación bidireccional:

one-to-many

(de-uno-a-muchos) un set o una bag en un extremo, un valor simple en en otro extremo.

many-to-many

(de-muchos-a-muchos) un set o una bag con valores en ambos extremos

Se puede especificar una asociación bidireccional de-muchos-a-muchos simplemente mapeando dos asociaciones a la
misma tabla de la base de datos, y declarando uno de los extremos como inverse (cuál, es a elección, pero no puede ser
una colección indexada).

He aquí un ejemplo de una asociación bidireccional de-muchos-a-muchos; cada categoría puede tener muchos items y
cada ítem puede tener muchas categorías:

<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">

85 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

<id name="id" column="ITEM_ID"/>


...

<!-- lado inverso -->


<bag name="categories" table="CATEGORY_ITEM" inverse="true">
<key column="ITEM_ID"/>
<many-to-many class="Category" column="CATEGORY_ID"/>
</bag>
</class>

Los cambios que se le hacen sólo al extremo inverso de la asociación, no son persistidos. Esto significa que Hibernate
tiene dos representaciones en memoria por cada asociación bidireccional.: un vínculo de A a B, y otro vínculo de B a A.
Esto es más fácil de entender si se piensa en el modelo de objetos Java y cómo se crea una relación de-muchos-a-muchos
en Java.

category.getItems().add(item); // La categoría ahora "sabe" acerca de la relación


item.getCategories().add(category); // El ítem ahora "sabe" acerca de la relación

session.persist(item); // ¡La relación no será grabada!


session.persist(category); // La relación sí será grabada

El lado no-inverso se usa para grabar en la base de datos la representación en memoria.

Se puede definir una asociación bidireccional de-muchos-a-muchos mapeando un a asocíación de-uno-a-muchos a la(s)
misma(s) columna(s) de la tabla que una asociación de-muchos-a-uno, y poniendo inverse="true" en el el extremo
'muchos'.

<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="Child">
<id name="id" column="child_id"/>
....
<many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/>
</class>

Mapear uno de los extremos de la asociación con inverse="true" no afecta las propagaciones en cascada. ¡Son
conceptos ortogonales!

6.3.3. Asociaciones bidireccionales con colecciones indexadas.

Una asociación bidireccional en donde un extremo esté representado como una <list> o un <map>necesita algunas
consideraciones especiales. Si existe una propiedad de la clase hija que mapee a la columna de índice, no hay problema,
podemos continuar usando inverse="true" en el mapeo de la colección.

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

86 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Pero si tal propiedad no existe en la clase hoja, no podemos concebir la asociación como verdaderamete bidireccional (hay
información disponible en un extremo que no lo está en el otro extremo). En este caso, no podemos mapear la colección
con inverse="true". En lugar de eso, podemos usar el siguiente mapeo.

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

</class>

Note que en este mapeo, el extremo con la colección es el responsable de las actualizaciones de la clave foránea. A
HACER: ¿esto resulta realmente en la ejecución de UPDATES innecesarios?

6.3.4. Asociaciones ternarias

Hay tres abordajes posibles para mapear asociaciones ternarias. Uno es usar un Map con la asociación como índice:

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

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

Un segundo abordaje es simplemente remodelar la asociación como una clase de entidad. Éste es el enfoque que se usa
más comúnmente.

Una última alternativa, es usar elementos compuestos, lo cual se discute más adelante.

6.3.5. Usar una <idbag>

Si usted está totalmente convencido de que las claves compuestas son una cosa mala, y de que las entidades debe tener
identidicadores "sintéticos" (claves sustitutas), entonces encontrará un tanto raro que las asociaciones de-muchos-
a-muchos y las colecciones que le hemos mostrado hasta ahora ¡se mapean todas a tablas con claves compuestas! Este
punto es debatible. Una tabla que sea puramente "de asociación" no se beneficia mucho con la existencia de una clave
sustituta (aunque una colección de valores compuestos sí podría). De todos modos, Hibernate provee un mecanismo que
permite mapear asociaciones de-muchos-a-muchos y colecciones de valores a una tabla con clave sustituta.

El elemento <idbag> le permite mapear una List (o Collection) con semántica de bag.

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

87 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Como se puede ver, una <idbag> tiene un generador de id "sintético", ¡igual que una clase de entidad! A cada fila de la
colección se le asigna una clave sustituta diferente. Si embargo, Hibernate no provee ningún mecanismo para descubrir el
valor de la clave sustituta de una fila en particular.

Note que la performance al actualizar una <idbag> ¡es mucho mejor que la de una <bag> común! Hibernate puede
localizar filas individuales eficientemente, y actualizarlas o borraras individualmente, como si fuera una list, un map o un
set.

En la implementación actual, el identificador native como estrategia de generación de identificadores no se soporta para
las <idbag>s.

6.4. Ejemplos de colecciones


Las secciones precedentes son bastante confusas. Así que veamos un ejemplo. Esta clase:

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; }

....
....
}

Tiene una colección de instancias Child. Si cada child (hijo) tiene como mucho un padre, el mapeo más natural es una
asociación de-uno-a-muchos:

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

Esto se mapea a las siguientes definiciones de tabla:

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

If the parent is required, use a bidirectional one-to-many association:

<hibernate-mapping>

<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>

88 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

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

Note la constraint NOT NULL:

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

Alternativamente, si usted insiste absolutamente en que la asociación debe ser unidireccional, debe declarar la constraint
NOT NULL en el mapeo de la <key>.

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

Por otra parte, si un hijo tiene múltiles padres, lo apropiado es una relación de-muchos-a-muchos.

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

Definiciones de tablas:

create table parent ( id bigint not null primary key )

89 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

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_i
alter table childset add constraint childsetfk0 (parent_id) references parent
alter table childset add constraint childsetfk1 (child_id) references child

Para más ejemplos, y una explicación paso a paso del mapeo de una relación padre-hijo, vea Capítulo 21, Ejemplo:
Padre/Hijo.

Son posibles mapeos de asociaciones aún más exóticas. Catalogaremos todas las posibilidades en el capítulo siguiente.

Capítulo 7. Mapeos de asociaciones

7.1. Introducción
Los mapeos de asociaciones son lo más difícil de comprender correctamente. En esta sección revisaremos los casos
canónicos uno por uno, empezando con los mapeos unidireccionales, y siguiendo con los bidireccionales. Usaremos
Person y Address ("persona" y "dirección") en todos los ejemplos.

Clasificaremos las asociaciones según usen o no una tabla de asociación, y según su multiplicidad.

Las claves foráneas anulables no son consideradas una práctica recomendable, en la modelización de datos tradicional, así
que todos nuestros ejemplos usarán claves foráneas no nulas. Esto no es obligatorio, los mapeos aún funcionarían si se
eliminara la constraint de nulabilidad.

7.2. Asociaciones unidireccionales.

7.2.1. de-muchos-a-uno

Una asociación unidireccional de-muchos-a-uno es el tipo más común de asociación unidireccional.

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

create table Person ( personId bigint not null primary key, addressId bigint not null )
create table Address ( addressId bigint not null primary key )

7.2.2. de-uno-a-uno

Una asociación unidireccional de-uno-a-uno por clave foránea es prácitcamente idéntica. La única diferencia es la
constraint de unicidad.

<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">

90 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

<generator class="native"/>
</id>
</class>

create table Person ( personId bigint not null primary key, addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )

Una asociación unidirectional de-uno-a-uno por clave primaria normalmente usa un generador de id especial (note que
hemos revertido la dirección de la asociación en este ejemplo).

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

create table Person ( personId bigint not null primary key )


create table Address ( personId bigint not null primary key )

7.2.3. de-uno-a-muchos

Una asociación unidireccional de-uno-a-muchos por clave foránea es un caso muy inusual, que no recomendamos usar.

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

create table Person ( personId bigint not null primary key )


create table Address ( addressId bigint not null primary key, personId bigint not null )

Pensamos que para este caso es mejor usar una tabla de asociación.

7.3. Asociaciones unidireccionales con tablas de asociación

7.3.1. de-uno-a-muchos

Una asociación unidireccional de-uno-a-muchos por tabla de asociación es mucho más recomendable. Note que al
especificar unique="true", hemos cambiado la multiplicidad de "de-muchos-a-muchos" a "de-uno-a-muchos".

<class name="Person">

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


<generator class="native"/>
</id>

91 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

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

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 )

7.3.2. de-muchos-a-uno

Una asociación A unidireccional de-muchos-a-uno por tabla de asociación es muy común, cuando la asociación es
optativa

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

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 )

7.3.3. de-uno-a-uno

Una asociación unidireccional de-uno-a-uno por tabla de asociación es muy inusual, pero posible.

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

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 )

7.3.4. de-muchos-a-muchos

92 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Finalmente, tenemos una asociación unidireccional de-muchos-a-muchos.

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

create table Person ( personId bigint not null primary key )


create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (pers
create table Address ( addressId bigint not null primary key )

7.4. Asociaciones bidireccionales

7.4.1. de-uno-a-muchos / de-muchos-a-uno

Una asociación bidireccional de-muchos-a-uno es el tipo más común de asociación (la relación padre/hijo estándar).

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

create table Person ( personId bigint not null primary key, addressId bigint not null )
create table Address ( addressId bigint not null primary key )

Si se usa una List (u otra colección idexada) se necesitará asignarle not null a la columna "key" de la clave foránea, y
dejar que Hibernate maneje la asociación desde el extremo "muchos" para mantener el índice de cada elemento
(convirtiendo el otro lado virtualmente en inverso, al especificar update="false" y insert="false"):

<class name="Person">

<id name="id"/>
...
<many-to-one name="address" column="addressId" not-null="true" insert="false" update="false"/>
</class>

<class name="Address">
<id name="id"/>
...
<list name="people">
<key column="addressId" not-null="true"/>
<list-index column="peopleIdx"/>
<one-to-many class="Person"/>
</list>
</class>

93 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Es importante que se defina not-null="true" en el elemento <key> del mapeo de la colección, si la columna de clave
foránea sunyacente es NOT NULL. No debe declararse solamente not-null="true" en un posible elemento <column>
anidado, sino en el elemento <key>.

7.4.2. de-uno-a-uno

Un asociación bidireccional de-uno-a-uno por clave foránea es bastante común

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

create table Person ( personId bigint not null primary key, addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )

Un asociación bidireccional de-uno-a-uno por clave primaria usa el generador de id especial.

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

create table Person ( personId bigint not null primary key )


create table Address ( personId bigint not null primary key )

7.5. Asociaciones bidireccionales por tablas de asociación

7.5.1. de-uno-a-muchos / de-muchos-a-uno

Un asociación bidireccional de-uno-a-muchos por tabla de asociación. Note que el inverse="true" puede ir an
cualquier extremo de la asociación, en la colección, o en el join.

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

94 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

</id>
<join table="PersonAddress" inverse="true" optional="true">
<key column="addressId"/>
<many-to-one name="person" column="personId" not-null="true"/>
</join>
</class>

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 )

7.5.2. de-uno-a-uno

Un asociación bidireccional de-uno-a-uno por tabla de asociación es extremadamente inusual, pero posible.

<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="person" column="personId" not-null="true" unique="true"/>
</join>
</class>

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 )

7.5.3. de-muchos-a-muchos

Finalmente, tenemos una asociación bidireccional de-muchos-a-muchos.

<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>
<set name="people" inverse="true" table="PersonAddress">
<key column="addressId"/>
<many-to-many column="personId" class="Person"/>
</set>
</class>

create table Person ( personId bigint not null primary key )


create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (pers
create table Address ( addressId bigint not null primary key )

7.6. Mapeos de asociaciones más complejas

95 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Asociaciones más complejas son extremadamente raras. Hibernate posibilita manejar asociaciones más complejas usando
fragmentos SQL incrustados en el documento de mapeo. Por ejemplo, si una tabla con información contable histórica
definiere las columnas accountNumber, effectiveEndDate and effectiveStartDate, mapeadas de esta manera:

<properties name="currentAccountKey">
<property name="accountNumber" type="string" not-null="true"/>
<property name="currentAccount" type="boolean">
<formula>case when effectiveEndDate is null then 1 else 0 end</formula>
</property>
</properties>

<property name="effectiveEndDate" type="date"/>

<property name="effectiveStateDate" type="date" not-null="true"/>

entonces podemos mapear una asociación a la instancia actual (la que tiene effectiveEndDate nula) usando:

<many-to-one name="currentAccountInfo" property-ref="currentAccountKey" class="AccountInfo">


<column name="accountNumber"/>
<formula>'1'</formula>
</many-to-one>

En un ejemplo más complejo, imagine que la asociación entre Employee y Organization estuviere mantenida por una
tabla Employment, llena de datos históricos de empleo. Entonces, una asociación al empleador más reciente de un
empleado (el que tuviere la startDate más reciente) podría ser mapeada de esta manera:

<join>
<key column="employeeId"/>
<subselect>
select employeeId, orgId
from Employments
group by orgId
having startDate = max(startDate)
</subselect>
<many-to-one name="mostRecentEmployer" class="Organization" column="orgId"/>
</join>

Uno se puede poner bastante creativo con esta funcionalidad, pero normalmente es más práctico manejar estos casos
unando HQL o consultas Criteria.

Capítulo 8. Mapeos de componentes


La noción de componente (en inglés, component) se reusa a lo largo y a lo ancho de Hibernate en distintos contextos, para
distintos propósitos.

8.1. Objetos dependientes


Un componente es un objeto contenido, que es persistido como "value type", no como referencia a una entidad. El término
"componente" se refiere a la noción orientada a objetos de "composición" (no a "componentes" a nivel de arquitectura).
Por ejemplo, se puede modelar a una persona así:

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() {

96 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

return name;
}
public void setName(Name name) {
this.name = name;
}
......
......
}

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;
}
}

Ahora, Name puede ser persistida como un componente de Person (en inglés, "nombre" y "persona", respectivamente).
Note que Name define métodos getter y setter para sus propiedades persistentes, pero no necesita declarar ninguna interfaz
ni propiedades identiicadoras.

Nuestro mapeo Hibernate se vería así:

<class name="eg.Person" table="person">


<id name="Key" column="pid" type="string">
<generator class="uuid"/>
</id>
<property name="birthday" type="date"/>
<component name="Name" class="eg.Name"> <!-- el atributo 'class" es optativo -->
<property name="initial"/>
<property name="first"/>
<property name="last"/>
</component>
</class>

La tabla PERSON tendría las columnas pid, birthday, initial, first y last.

Como todos los "value types", los componentes no soportan referencias compartidas. Dos personas pueden tener el mismo
nombre, pero los dos objetos Person correspondientes contendrán dos objetos nombre independientes, sólo que con "el
mismo" valor. La semántica de nulo de un componente es ad hoc. Cuando se recargue el objeto contenedor, Hibernate
assumirá que el componente entero es nulo. Esto debería alcanzar para la mayoría de los casos.

Las propiedades de un componente pueden ser de cualquier tipo Hibernate (colecciones, asociaciones de-muchos-a-uno,
otros componentes, etc). Los componentes anidados no deben ser considerados una rareza. Se espera que Hibernate
soporte un modelo de objetos muy jerárquico y detallado en este sentido.

El elemento <component> acepta un subelemento <parent> que se mapee a una propiedad desde la clase del componente
como referencia de vuelta a la entidad contenedora.

<class name="eg.Person" table="person">


<id name="Key" column="pid" type="string">
<generator class="uuid"/>
</id>
<property name="birthday" type="date"/>
<component name="Name" class="eg.Name" unique="true">
<parent name="namedPerson"/> <!-- referencia de vuelta a Person -->
<property name="initial"/>

97 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

<property name="first"/>
<property name="last"/>
</component>
</class>

8.2. Colecciones de objetos dependientes


Hay soporte para colecciones de componentes (por ejemplo, un array de tipos Name). Declare su colección de
componentes reemplazando la tag del elemento <element> con una tag <composite-element>.

<set name="someNames" table="some_names" lazy="true">


<key column="id"/>
<composite-element class="eg.Name"> <!-- el atributo class es obligatorio -->
<property name="initial"/>
<property name="first"/>
<property name="last"/>
</composite-element>
</set>

Nota: si se define un Set de elementos compuestos, es importante implementar equals() y hashCode() correctamente.

Los elementos compuestos pueden contener otros componentes, pero no colecciones. Si su componente compuesto a su
vez contiene componentes, use la tag <nested-composite-element>. Éste es un caso batante exótico: una colección de
componentes que a su vez tengan componentes. A este punto usted debería preguntarse si no sería más apropiada una
asociación de-uno-a-muchos. Trate de remodelar el elemento compuesto como una entidad, pero dése cuenta de que,
aunque el modelo de Java es el mismo, le modelo relacional y la semántica de persistencia son aún ligeramente diferentes.

Por favor, note que un mapeo de elemento compuesto no soporta propiedades anulables si se está usando un <set>.
Hibernate tiene que usar el valor de cada columna para identificar un registro cuando se borra objetos (no hay una clave
primaria separada en la tabla de elementos compuestos), lo cual no es posible con valores nulos. Se deberá o bien usar sólo
valores no nulos en un <composite-element>, o bien elegir una <list>, <map>, <bag> o <idbag>.

Un caso especial de elemento compuesto ese el elemento compuesto que tiene un elemento <many-to-one> anidado. Un
mapeo como éste permite mapear columnas extra de una tabla de asociación de-muchos-a-muchos a la clase del elemento
compuesto. La siguiente es una asociación de-muchos-a-muchos de from Order a Item (de orden a ítem) en donde la
fecha de compra ( purchaseDate), el precio (price) y la cantidad (quantity) son propiedades de la asociación.

<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"/> <!-- el atributo "clase" es optativo -->

</composite-element>
</set>
</class>

Por supuesto, no puede haber una referencia a la compra del otro lado, para proveer navegación bidireccional de la
asociación. Recuerde que los componentes son "value types", y no aceptan referencias compartidas. Una simple compra
(Purchase) puede estar en el set de una Order, pero no puede ser referida por el Item al mismo tiempo.

Incluso son posibles asociaciones ternarias o cuaternarias:

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

98 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

</class>

Los elementos compuestos pueden aparecer en consultas, usando la misma sintaxis que las entidades.

8.3. Componentes como índices de un Map


El elemento <composite-map-key> (clave compuesta de mapa) permite mapear una clase componente como la clave de
un Map. Asegúrese de reemplazar correctamente los métodos hashCode() y equals() en la clase componente.

8.4. Componentes usados como identificadores compuestos


Se puede usar un componente como identificador de una clase entidad. Dicho componente debe satisfacer ciertos
requisitos:

Debe implementar java.io.Serializable.

Debe reimplementar equals() y hashCode(), de una manera consistente con la noción de igualdad para la clave
compuesta en la DB.

Nota: en Hibernate3, el segundo requisito no es "sine qua non", pero cúmplalo de todos modos.

No se puede usar un IdentifierGenerator para generar claves compuestas. En lugar de eso, la aplicacíón debe asignar
sus propios identificadores.

Use la tag <composite-id> (con elementos <key-property> anidados) en lugar de la declaración de <id> usual. Por
ejemplo, la clase OrderLine (línea de una orden) tiene una clave primaria que depende de la clave (compuesta) de Order.

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

Ahora, cualquier clave foránea que se refiera a la tabla ORDERLINE, también será compuesta. Esto se debe declarar en
los mapeos para otras clases. La asociación con OrderLine sería mapeada así:

<many-to-one name="orderLine" class="OrderLine">


<!-- el atributo "class" es optativo, como de costumbre -->

<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>

</many-to-one>

(Note que la tag <column> es una alternativa al atributo column en todos lados.)

Una asociación de-muchos-a-muchos a OrderLine también usa la clave foránea compuesta.

<set name="undeliveredOrderLines">
<key column name="warehouseId"/>

<many-to-many class="OrderLine">
<column name="lineId"/>
<column name="orderId"/>

99 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

<column name="customerId"/>
</many-to-many>

</set>

La colección de OrderLines en Order usaría:

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

<key>
<column name="orderId"/>
<column name="customerId"/>
</key>
<one-to-many class="OrderLine"/>
</set>

(El elemento <one-to-many>, como es usual, no declara ninguna columna).

Si la OrderLine misma posee una colección, también tiene una clave foránea compuesta.

<class name="OrderLine">
....
....
<list name="deliveryAttempts">

<key> <!-- una colección hereda el tipo de clave compuesta -->


<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</key>

<list-index column="attemptId" base="1"/>

<composite-element class="DeliveryAttempt">
...
</composite-element>
</set>
</class>

8.5. Componentes dinámicos


Se puede incluso mapear una propiedad de tipo Map:

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

La semántica de un mapeo <dynamic-component> es idéntica a la de <component>. La ventaja de este tipo de mapeo es


la capacidad de determinar las verdaderas propiedades del bean en tiempo de despliegue (deployment), simplemente
editando el archivo de mapeo. También es posible la manipulación en tiempo de ejecución del documento de mapeo,
usando un parser DOM. Mejor aún, se puede acceder al meta-modelo de tiempo de configuración de Hibernate (y
cambiarlo) usando el objeto Configuration .

Capítulo 9. Mapeo de herencia

9.1. Las tres estrategias


Hibernate soporta tres estrategias básicas de mapeo de herencia:

una tabla por jerarquía de clases

una tabla por subclase

una tabla por clase concreta

100 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Adicionalmente, Hibernate soporta una cuarta clase de polimorfismo, ligeramente diferente:

polimorfismo implícito

Es posible usar estrategias de mapeo diferentes para distintas ramas de la misma jerarquía de herencias, y después hacer
uso del polimorfismo implícito para lograr polimorfismo todo a lo largo de dicha jerarquía. Sin embargo, Hibernate no
soporta mezclar mapeos de <subclass>, and <joined-subclass> y <union-subclass> bajo el mismo elemento
<class> de la clase raíz. Sí es posible mezclar las estrategias de "una tabla por jerarquía" y de "una tabla por subclase"
bajo el mismo elemento <class>, combinando los elementos <subclass> y <join> (véase a continuación).

Es posible definir mapeos de subclass, union-subclass, y joined-subclass en documentos de mapeo separados, justo
debajo de hibernate-mapping. Esto permite extender la jerarquía de clases, simplemente agregando un nuevo archivo de
mapeo. Se debe especificar el atributo extends en el mapeo de la subclase, nombrando una superclase previamente
mapeada. Nota: en el pasado, esta característica hacía que el orden de los documentos de mapeo fuese relevante. Desde
Hibernate 3, el orden no importa cuando se usa la palabra "extends". El orden dentro de cada documento individual, aún
tiene que tener las superclases antes de las subclases.

<hibernate-mapping>

<subclass name="DomesticCat" extends="Cat" discriminator-value="D">


<property name="name" type="string"/>
</subclass>

</hibernate-mapping>

9.1.1. Una tabla por jerarquía de clases

Suponga que tenemos una interfaz Payment (pago), implementada por las clases CreditCardPayment, CashPayment,
ChequePayment (pago por tarjeta de crédito, efectivo y cheque respectivamente). El mapeo de "una tabla por jerarquía" se
vería así:

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

Se requiere exactamente una tabla. Con esta estrategia de mapeo, hay una gran limitación: las columnas declaradas por las
subclases, como CCTYPE, no pueden tener constraints NOT NULL.

9.1.2. Una tabla por subclase

El mapeo de una tabla por sublcase se vería así:

<class name="Payment" table="PAYMENT">

<id name="id" type="long" column="PAYMENT_ID">


<generator class="native"/>
</id>
<property name="amount" column="AMOUNT"/>
...

101 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

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

Se requieren 4 tablas. Las tres tablas de subclases tienen asociaciones por clave primaria a la tabla de la superclase (de
modo que el modelo relacional es en realidad una asociación de-uno-a-uno).

9.1.3. Una tabla por subclase, usando un discriminador

Note que la implementación de Hibernate para "una tabla por subclase" no requiere una columna discriminadora. Otros
mapeadores objeto/relacionales usan una implementación diferente de "una tabla por subclase", que sí requiere una
columna discriminadora en la tabla de la superclase. El enfoque adoptado por Hibernate es mucho más difícil de
implementar, pero probablemente más correcto desde el punto de vista relacional. Si usted desea usar una columna
discriminadora con la estrategia de "una tabla por subclase", puede combinar el uso de <subclass> y <join>, como sigue:

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

La declaración optativa fetch="select" le dice a Hibernate que no capture los datos de la subclase ChequePayment
usando un outer join cuando se consulte a la superclase.

9.1.4. Mezclar "una tabla por jerarquía de clases" con "una tabla por subclase"

Incluso se pueden mezclar las estrategias de "una tabla por jerarquía de clases" con "una tabla por subclase" usando este
abordaje:

<class name="Payment" table="PAYMENT">

102 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

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

Para cualquiera de estas estrategias de mapeo, una asociación polimórfica a la clase raíz Payment se mapea usando
<many-to-one>.

<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>

9.1.5. Una tabla por clase concreta

Con la estrategia de "una tabla por clase concreta" podríamos proceder de dos maneras. La primera es usar <union-
subclass>.

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

Para las subclases, hay tres clases involucradas. Cada tabla define columnas para todas las propiedades de la subclase,
incluso las propiedades heredadas.

La limitación de este abordaje, es que si la propiedad está mapeada en la superclase, el nombre de la columna debe ser el
mismo en todas las tablas de subclases. (Podemos relajar este requisito en versiones futuras de Hibernate). La estrategia de
generador de identidad no está permitida para una herencia que esté usando union-subclass. Lógico, dado que la clave
primaria debe ser compartida por todas las subclases de la jerarquía participantes en la unión.

Si su superclase es abstracta, mapéela con abstract="true". Por supuesto, si no es abstracta, se necesita una tabla
adicional (por defecto sería PAYMENT en el ejemplo anterior) para almacenar instancias de la superclase.

9.1.6. Una tabla por cada clase concreta, usando polimorfismo implícito

Un enfoque alternativo es usar polimorfismo implícito:

<class name="CreditCardPayment" table="CREDIT_PAYMENT">


<id name="id" type="long" column="CREDIT_PAYMENT_ID">
<generator class="native"/>

103 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

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

Nótese que no mencionamos explícitamente en ningún lado la interfaz Payment. También nótese que las propiedades de
Payment están mapeadas en cada una de las subclases. Si se quiere evitar la duplicación, considere usar entidades XML
(por ejemplo, [ <!ENTITY allproperties SYSTEM "allproperties.xml"> ] en la declaración de DOCTYPE y
&allproperties; en el mapeo).

La desventaja de este enfoque, es que Hibernate no generará SQL UNIONs cuando ejecute consultas polimórficas.

Según esta estrategia de mapeo, una asociación polimórfica a Payment sería normalmente mapeada usando <any>.

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

9.1.7. Mezclar polimorfismo implícito con otras estrategias de mapeo de herencia

Hay una cosa más que resaltar, acerca de este mapeo. Como cada una de las subclases está mapeada en su propio
elemento <class> (y dado que Payment sólo es una interfaz), ¡cada una de las subclases podría fácilmente ser parte de
otra jerarquía de clases! (y aún se podrían usar consultas polimórficas contra la interfaz Payment).

<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">

104 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

<key column="PAYMENT_ID"/>
<property name="amount" column="CHEQUE_AMOUNT"/>
...
</joined-subclass>

</class>

De nuevo no necesitamos mencionar la interfaz Payment explícitamente. Hibernate devuelve automáticamente instancias
de, por ejemplo, CreditCardPayment y sus subclases, que también implementan Payment, pero no instancias de
NonelectronicTransaction.

9.2. Limitaciones
Hay ciertas limitaciones del abordaje a la estrategia "una tabla por clase concreta" usando polimorfismo implícito. Y hay
limitaciones un tanto menos restrictivas para los mapeos que usan <union-subclass>.

Una comparación de las limitaciones de ambas estrategias se muestra en la tabla siguiente:

Tabla 9.1. Características de los mapeos de herencia

polimórfica
Estrategia de polimórfica polimórfica polimórfica
de-muchos- load()/get() polimórficos
herencia de-muchos-a-uno de-uno-a-uno de-uno-a-muchos
a-muchos

una tabla por


<de-muchos- <de-uno- <de-uno- <de-muchos-
jerarquía de a-uno> a-uno> a-muchos> a-muchos>
s.get(Payment.class, id)
clase

una tabla por <de-muchos- <de-uno- <de-uno- <de-muchos-


s.get(Payment.class, id)
subclase a-uno> a-uno> a-muchos> a-muchos>

una tabla por


<de-uno-
clase a-muchos> (for
<de-muchos- <de-uno- <de-muchos-
concreta a-uno> a-uno> inverse="true" a-muchos>
s.get(Payment.class, id)
(union- only)
subclass)
una tabla por
clase s.createCriteria(Payment.class).
not <many-
concreta <any> not supported to-any>
Restrictions.idEq(id)
supported ).uniqueResult()
(polimorfismo
implícito)

Capítulo 10. Trabajar con objetos


Hibernate es un dispositivo completo de mapeo objeto/relacional, que no sólo escuda al programador contra los detalles de
gestión del sistema de base de datos subyacente, sino que ofrece manejo del estado de sus objetos. Y ésta es una forma
muy natural de manejar la persistencia en una aplicación de Java, al contrario de los comandos SQL en las capas usuales
de persistencia JDBC/SQL.

En otras palabras, los programadores en Hibernate siempre deberían pensar en términos del estado de sus objetos, y no
necesariamente en términos de qué comandos SQL están siendo ejecutados. De esto se encarga Hibernate, y sólo es
relevante para el programador cuando se trate de ajustar la perfomance del sistema.

10.1. Estados de un objeto de Hibernate


Hibernate define y soporta los siguientes estados de un objeto:

105 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Transitorio (en inglés, transient): un objeto es transitorio cuando ha sido instanciado usando el operador new , y no
está asociado con una sesión de Hibernate. No tiene representación persistente en la base de datos y no se le ha
asignado ningún identificador. Las instancias transitorias serán destruidas por el recolector de basura (garbage
collector), si la aplicación ya no tiene ninguna referencia a ellas. Use la sesión de Hibernate para volver persistente
un objeto (y deje que Hibernate se encargue de los comandos SQL que sean necesarios para dicha transición).

Persistente (en inglés, persistent): una instancia persistente ya tiene una representación en la base de datos, y un
valor de identificador. Aunque recién haya sido cargada o grabada, por definición ya está inscripta en el alcance de
la sesión. Hibernate detectará cualquier cambio que se efectúe sobre un objeto en estado persistente, y sincronizará
su estado con el de la base de datos cuando la unidad de trabajo se complete. Si el objeto persistente es tranformado
en transitorio, el programador no necesita ejecutar comandos UPDATE ni DELETE manualmente.

Desprendido (en inglés, detached): un objeto desprendido es un objeto que era persistente, pero cuya sesión ha sido
cerrada. La referencia al objeto aún es válida, y la instancia desprendida puede incluso ser modificada en este
estado. Una instancia desprendida puede ser reasociada a una nueva sesión en el futuro, volviéndola persistente de
nuevo (junto con todas las modificaciones que haya sufrido). Esta característica es ideal para programar unidades de
trabajo largas, que necesiten darle a la aplicación tiempo para "pensar". Las llamamos transacciones "de la
aplicación", esto es, una unidad del trabajo desde el punto de vista del usuario.

Ahora discutiremos los estados y las transiciones entre ellos (así como los métodos de Hibernate que disparan estas
transiciones) con más detalle.

10.2. Volver persistente un objeto


Las instancias recientemente creadas de una clase son consideradas transitorias por Hibernate. Podemos convertir una
instancia transitoria en persistente asociándola con una sesión:

DomesticCat fritz = new DomesticCat();


fritz.setColor(Color.GINGER);
fritz.setSex('M');
fritz.setName("Fritz");
Long generatedId = (Long) sess.save(fritz);

Si Cat tiene un identificador autogenerado, dicho identificador se genera y se le asigna a cat cuando save() sea llamado.
Si Cat tiene un identificador asignado externamente, o una clave compuesta, dicho identificador debería serle asignado a
cat antes de invocar save(). Se puede usar persist() em lugar de save(), con la semántica definida en el borrador
temprano de EJB3.

persist() convierte una instancia transitoria en persistente. Pero no garantiza que el identificador le vaya a ser
asignado a la instancia inmediatamente: la asignación puede ocurrir al aplicársele "flush" a la sesión. persist()
también garantiza que nos ejecutará un comando INSERT si es llamado fuera de los límites de una transacción. Esto
es útil para conversaciones de largo aliento, con un contexto de sesión/persistencia extendido.

save() sí garantiza la devolución de un identificador, Si hay que ejecutar un INSERT (por ejemplo, porque el
generador es "identity" y no "sequence") el INSERT ocurre inmediatamente, sin importar si se está dentro o fuera de
una transacción. Esto es indeseable en conversaciones largas, con un contexto de sesión/persistencia extendido.

Alternativamente, se puede asignar el identificador usando una versión sustituida (overloaded) de of save().

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) );

Si el objeto que se hace persistente tiene objetos asociados, (por ejemplo, la colección "kittens", gatitos, en el ejemplo
anterior), dichos objetos pueden ser hechos persistentes en cualquier orden, a menos que exista una constraint NOT NULL
en una columna de clave foránea. El riesgo de violar una constraint de clave foránea no existe, pero sí se puede llegar a
violar una constraint NOT NULL si se les aplica save() a los objetos en el orden incorrecto.

Normalmente, usted no debe preocuparse por estos detalles, dado que, muy probablemente, usted terminará usando la
característica de Hibernate llamada persistencia transitiva para grabar los objetos asociados automáticamente. Con ella, ni
siquiera las violaciones de NOT NULL ocurren, Hibernate se hace cargo de todo. La persistencia transitiva se discute más

106 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

adelante en este capítulo.

10.3. Cargar un objeto


El método load() de Session provee una manera de capturar una instancia persistente, si ya se conoce su identificador.
load() acepta un objeto Class, y cargará el estado en una nueva instancia de esa clase, inicializada en el momento, en
estado persistente.

Cat fritz = (Cat) sess.load(Cat.class, generatedId);

// se necesita "envolver" los identificadores de tipos primitivos


long id = 1234;
DomesticCat pk = (DomesticCat) sess.load( DomesticCat.class, new Long(id) );

Alternativamente, se puede cargar estado en una instancia ya existente:

Cat cat = new DomesticCat();


// cargar el estado identificado por la clave primaria en Cat
sess.load( cat, new Long(pkId) );
Set kittens = cat.getKittens();

Dese cuenta de que load() lanzará una excepción irrecuperable si no existe la fila de base de datos correspondiente. Si la
clase es mapeada con un proxy, load() simplemente devuelve un proxy no inicializado, y no hay contacto con la base de
datos hasta que realmente se invoque un método del proxy. Este comportamiento es muy útil si se desea crear una
asociación con un objeto, sin realmente cargarlo desde la base de datos. También permite cargar múltiples instancias en
lotes, si batch-size está definido para la clase.

Cuando no se esté seguro de que exista una fila correspondiente, debería usarse el método get(), el cual consulta la base
de datos inmediatamente y devuelve null si no existe una fila correspondiente.

Cat cat = (Cat) sess.get(Cat.class, id);


if (cat==null) {
cat = new Cat();
sess.save(cat, id);
}
return cat;

Incluso se puede cargar un objeto usando un SQL SELECT ... FOR UPDATE, usando LockMode. Vea la documentación de
la API para más información.

Cat cat = (Cat) sess.get(Cat.class, id, LockMode.UPGRADE);

Note que ni las instancias asociadas ni las colecciones contenidas son seleccionadas FOR UPDATE (con el propósito de ser
modificadas), a menos que se especifiquen los valores lock o all en el parámetro de mapeo "cascade".

Se puede recargar un objeto y todas sus asociaciones an cualquier momento, usndo el método refresh(). Esto es muy útil
cuando se están usando triggers de DB para inicializar algunas de las propiedades del objeto.

sess.save(cat);
sess.flush(); //fuerza el SQL INSERT
sess.refresh(cat); //relee el estado (luego de que el trigger se ejecuta)

Llegados a este punto,usualmente se plantea una cuestión: ¿Cuánto carga Hibernate de la base de datos?¿Cuántos SQL
SELECTs se usan? Esto depende de la estrategia de captura (fetching strategy) y se explica en la Sección 19.1,
“Estrategias de captura (fetch)”.

10.4. Consultas
Si no se conocen los identificadores de los objetos que se está buscando, se necesita una consulta (query). Hibernate
soporta un lenguaje para consultas poderoso pero fácil de usar: HQL (las siglas en inglés de "Hibernate Query Language").
Para la creación programática de consultas, Hibernate soporta dos clases muy sofisticadas, Criteria y Example (a veces
referidos como QBC y QBE, por "query by criteria" y "query by example", respectivamente). También se puede expresar
la consulta en el lenguaje nativo de su base de datos, con soporte opcional de conversión de su resultado en objetos.

107 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

10.4.1. Ejecutar consultas

Las consultas en HQL y en SQL nativo son representadas por una instancia de org.hibernate.Query. Esta interfaz
ofrece métodos para vincular parámetros, manejo de resultsets, y la ejecución de la consulta misma. Siempre se obtiene el
Query a partir la sesión actual.

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();]]

Query mothersWithKittens = (Cat) session.createQuery(


"select mother from Cat as mother left join fetch mother.kittens");
Set uniqueMothers = new HashSet(mothersWithKittens.list());

Una consulta normalmente se ejecuta invocando list(), el resultado de la consulta será cargado completamente en una
colección en memoria. Las entidades de instancia que son devueltas están en estado persistente. El método
uniqueResult() ofrece un atajo si usted ya sabe que la consulta devolverá un solo objeto. Note que las consultas que
hacen uso de "captura ansiosa" (eager fetching), normalmente devuelven duplicados del objeto raíz, con sus colecciones
inicializadas. Estos duplicados se pueden eliminar, simplemente usando un Set.

10.4.1.1. Iterar resultados

Ocasionalmente, es posible que se obtenga una mejor performace si se ejecuta la consulta usando el método iterate().
Esto es normalmente cierto si se espera que las instancias de entidad devueltas por la consulta ya estarán en la sesión, o en
el caché de 2do nivel. Si no están en el caché, iterate() será más lento que list(), y es posible que requiera varios
viajes a la base de datos para una simple consulta: normalmente 1 SELECT inicial que sólo devuelve los identificadores, y
N SELECTs adicionales que inicializan las instancias propiamente dichas.

// captura de ids
Iterator iter = sess.createQuery("from eg.Qux q order by q.likeliness").iterate();
while ( iter.hasNext() ) {
Qux qux = (Qux) iter.next(); // captura de un objeto
// alguna comprobación complicada que no podíamos expresar en la consulta
if ( qux.calculateComplicatedAlgorithm() ) {
// borrar la isntancia actual
iter.remove();
// no necesitamos procesar el resto
break;
}
}

10.4.1.2. Consultas que devuelven T-uplas

Las consultas de Hibernate a veces devuelven t-uplas de objetos, en cuyo caso cada t-upla se devuelve como un array.

Iterator kittensAndMothers = sess.createQuery(


"select kitten, mother from Cat kitten join kitten.mother mother")
.list()
.iterator();

while ( kittensAndMothers.hasNext() ) {
Object[] tuple = (Object[]) kittensAndMothers.next();

108 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Cat kitten = (Cat) tuple[0];


Cat mother = (Cat) tuple[1];
....
}

10.4.1.3. Resultados escalares

Una consulta pueden especificar una propiedad de la clase en la cláusula SELECT. Las funciones agregadas son
consideradas resultados "escalares", y no entidades en estado persistente.

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 = (Object[]) results.next();
Color type = (Color) row[0];
Date oldest = (Date) row[1];
Integer count = (Integer) row[2];
.....
}

10.4.1.4. Parámetros vinculados

En Query se proveen métodos para vincular valores con parámetros nombrados, o parámetros ? al estilo JDBC. Al
contrario que con JDBC, Hibernate numera los parámetros empezando de 0. Los parámetros nombrados son
identificadores con la forma :name en la caden de la consulta. Las ventajas de los parámetros nombrados son:

los parámetros nombrados son independientes del orden en que ocurren en la cadena de la consulta

pueden ocurrir varias veces en la misma consulta

son autodocumentados

//parámetro nombrado (la forma preferida)


Query q = sess.createQuery("from DomesticCat cat where cat.name = :name");
q.setString("name", "Fritz");
Iterator cats = q.iterate();

//parámetro posicional
Query q = sess.createQuery("from DomesticCat cat where cat.name = ?");
q.setString(0, "Izi");
Iterator cats = q.iterate();

//lista de parámetros nombrada


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();

10.4.1.5. Paginación

Si se necesita establecer límtes en el resultset (el máximo número de filas que se quiere capturar / desde qué fila se desea
obtener datos), deberían usarse los siguientes métodos de la interfaz Query:

Query q = sess.createQuery("from DomesticCat cat");


q.setFirstResult(20);
q.setMaxResults(10);
List cats = q.list();

Hibernate sabe cómo traducir este límite al el SQL nativo de su base de datos.

109 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

10.4.1.6. Iteración navegable

Si su driver de JDBC soporta ResultSets navegables, la interfaz Query puede ser usada para obtener un objeto
ScrollableResults, el cual permite una navegación flexible del los resultados de la consulta.

Query q = sess.createQuery("select cat.name, cat from DomesticCat cat " +


"order by cat.name");
ScrollableResults cats = q.scroll();
if ( cats.first() ) {

// encuentra el primer nombre en cada página de una lista alfabética de gatos, por nombre.
firstNamesOfPages = new ArrayList();
do {
String name = cats.getString(0);
firstNamesOfPages.add(name);
}
while ( cats.scroll(PAGE_SIZE) );

// ahora obtiene la primera página de gatos


pageOfCats = new ArrayList();
cats.beforeFirst();
int i=0;
while( ( PAGE_SIZE > i++ ) && cats.next() ) pageOfCats.add( cats.get(1) );

}
cats.close()

Nótese que para esta funcionalidad se requiere una conexión abierta a la base de datos. y un cursor. Use
setMaxResult()/setFirstResult() si necesita funcionalidad de paginación desconectada.

10.4.1.7. Externalizar consultas nombradas

Se puede definir consultas nombradas (named queries) en el documento de mapeo. Recuerde usar una sección CDATA si su
consulta contiene carateres que podrían ser interpretados como caracteres reservados de XML).

<query name="ByNameAndMaximumWeight"><![CDATA[
from eg.DomesticCat as cat
where cat.name = ?
and cat.weight > ?
] ]></query>

La vinculación de parámetros y la ejecución se hacen programáticamente.

Query q = sess.getNamedQuery("ByNameAndMaximumWeight");
q.setString(0, name);
q.setInt(1, minWeight);
List cats = q.list();

Note que el código del programa propiamente dicho es independiente del lenguaje de consultas que se use; se pueden
definir consultas SQL nativas en metadata, o migrar consultas existentes a Hibernate colocándolas en archivos de mapeo.

También note que la declaración dentro de un elemento <hibernate-mapping> requiere un nombre globalmente único
para la consulta, mientras que la declaración de una consulta dentro de un elemento <class> se vuelve única
automáticamente al afijarle el nombre enteramente calificado de la clase, por ejemplo, eg.Cat.PorNombreYPesoMaximo.

10.4.2. Filtrar colecciones

El filtro de una colección es un tipo especial de consulta que puede aplicársele a una colección persistente, o a un array.
La cadena de la consulta puede hacer uso de this, refiriéndos el elemento actual de la colección.

Collection blackKittens = session.createFilter(


pk.getKittens(),
"where this.color = ?")
.setParameter( Color.BLACK, Hibernate.custom(ColorUserType.class) )
.list()
);

110 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

La colección devuelta se considera una bag, y es una copia de a colección dada. La colección original no es modificada (lo
cual es conceptualmente opuesto a la idea de un "filtro", pero consistente en cuanto al resultado que se espera).

Observe que los filtros no requieren una cláusula FROM (aunque pueden tenerla si es necesario). Los filtros no están
limitados a devolver los elementos de colecciones mismos.

Collection blackKittenMates = session.createFilter(


pk.getKittens(),
"select this.mate where this.color = eg.Color.BLACK.intValue")
.list();

Incluso un filtro con su consulta vacía es útil, por ejemplo para cargar un subconjunto de elementos en una colección
enorme:

Collection tenKittens = session.createFilter(


mother.getKittens(), "")
.setFirstResult(0).setMaxResults(10)
.list();

10.4.3. Consultas "Criteria"

HQL es extremadamente poderoso, pero algunos programadores prefieren ir construyendo sus consultas dinámicamente,
usando una API orientada a objetos, en lugar de utilidar cadenas. Hibernate provee una API intuitiva, Criteria, para
estos casos:

Criteria crit = session.createCriteria(Cat.class);


crit.add( Restrictions.eq( "color", eg.Color.BLACK ) );
crit.setMaxResults(10);
List cats = crit.list();

Las APIs de Criteria y su pariente Example se discuten con más detalle en Capítulo 15, Consultas Criteria.

10.4.4. Consultas en SQL nativo

Se puede expresar una consulta en SQL, usando createSQLQuery() y dejar que Hibernate se haga cargo del mapeo del
resultset a objetos. Note que en cualquier momento se puede llamar session.connection() y usar la Connection de
JDBC directamente. Si decide usar la API de Hibernate, debe incluir los alias de SQL entre llaves:

List cats = session.createSQLQuery("SELECT {cat.*} FROM CAT {cat} WHERE ROWNUM<10")


.addEntity("cat", Cat.class)
.list();

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()

Las consultas SQL pueden contener parámetros nombrados o posicionales, al igual que las consultas de Hibernate. Se
puede encontrar más información sobre las consultas SQL nativas en Capítulo 16, SQL Nativo.

10.5. Modificar objetos persistentes


Las instancias persistentes transaccionales (esto es, los objetos cargados, grabados, creados o consultados por las sesión)
pueden ser manipulados por la aplicación, y cualquier cambio a su estado de persistencia será persistido cuando se le
aplique "flush" a la sesión (como se discute más adelante en este capítulo). No hay necesidad de invocar ningún método en
particular (como por ejemplo update(), que sirve para otra cosa) para que las modificaciones se vuelvan persistentes. Así
que la manera más sencilla y directa de actualizar el estado de un objeto es cargarlo con load(), y luego manipularlo
directamente, mientras la sesión esté abierta.

DomesticCat cat = (DomesticCat) sess.load( Cat.class, new Long(69) );


cat.setName("PK");
sess.flush(); // los cambios se detectan y son persistidos automáticamente

111 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

A veces, este modelo de programación es ineficiente, dado que requeriría tanto un SQL SELECT (para cargar el objeto)
como un SQL UPDATE (para persistir su estado modificado) en la misma sesión. Por lo tanto, Hibernate ofrece un abordaje
alternativo, usando instancias desprendidas:

Note que Hibernate no expone una API propia para ejecutar comandos UPDATE o DELETE directamente. Hibernate es un
servicio de manejo de persistencia, y no se lo debe concebir en términos de qué comandos ejecuta. JDBC es una API
perfectamente adecuada para ejecutar comandos SQL, se puede obtener una conexión de JDBC en cualquier momento
invocando session.connection(). Más aún, la noción de "operaciones en masa" entra en conflicto con el mapeo
objeto/relacional para las aplicaciones orientadas al procesamiento de transacciones en línea. Puede ser, sin embargo,
que versiones futuras de Hibernate provean funciones especiales para procesamiento en masa. Véase Capítulo 13,
Procesamiento en lotes para posibles trucos de operación en lote.

10.6. Modificar objetos desprendidos


Muchas aplicaciones necesitan capturar un objeto en una transacción, mandarlo a la capa de interfaz de usuario para su
manipulación, y grabar sus cambios en una nueva transacción. Las aplicaciones que usan este tipo de estrategia en
entornos de alta concurrencia, normalmente usan datos versionados para garantizar aislamiento durante la "larga" unidad
de trabajo.

Hibernate soporta este modelo, proveyendo "reasociación" de entidades desprendidas usando los métodos
Session.update() o Session.merge():

// en la primera sesión
Cat cat = (Cat) firstSession.load(Cat.class, catId);
Cat potentialMate = new Cat();
firstSession.save(potentialMate);

// en una capa más alta de la aplicación


cat.setMate(potentialMate); //mate=pareja

// luego, en una nueva sesión


secondSession.update(cat); // actualiza cat
secondSession.update(mate); // actualiza mate

Si el Cat con identificador catId ya hubiera sido cargado por la segunda sesión (secondSession) cuando la sesión trataba
de reasociarlo, se habría producido una excepción.

Use update() si usted está seguro de que la sesión no contiene una instancia que ya es persistente con el mismo id,
merge() si usted desea consolidar sus modificaciones en cualquier momento, independientemente del estado de esa
instancia en la sesión. En otras palabras, update() es normalmente el primer método que se invocará en una sesión nueva,
garantizando que la reasociación de las instancias desprendidas sea la primera operación que ocurra.

La aplicación debería aplicarles update() a las instancias desprendidas dependientes de la instancia desprendida en
cuestión, si y sólo si desea que su estado también sea actualizado. Esto puede ser automatizado, por supuesto, usando
persistencia transitiva, vea la Sección 10.11, “Persistencia transitiva”.

El método lock() también le permite a la aplicación reasociar un objeto con una sesión nueva. ¡Sin embargo, la instancia
desprendida no debe haber sido modificada!

//simplemente reasocia
sess.lock(fritz, LockMode.NONE);
//primero verifique la versión, luego reasocie
sess.lock(izi, LockMode.READ);
//primero verifique la versión usando SELECT ... FOR UPDATE, luego reasocie
sess.lock(pk, LockMode.UPGRADE);

Note que lock() puede ser usado con varios LockModes, vea la documentacíón de la API y el capítulo sobre manejo de
transacciones para más informacíón. Reasociar no es el único uso que lock() tiene.

Otros modelos para unidades largas de trabajo se discuten en Sección 11.3, “Control de concurrencia optimista”.

10.7. Detección de estado automática


Los usuarios de Hibernate han solicitado un método de uso general, que o bien grabe una instancia transitoria (generando

112 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

un identificador nuevo), o bien actualice/reasocie las instancias desprendidas asociadas con el identificador actual. El
método saveOrUpdate() implementa tal funcionalidad:

// en la primera sesión
Cat cat = (Cat) firstSession.load(Cat.class, catID);

// en una capa más alta de la aplicación


Cat mate = new Cat();
cat.setMate(mate);

// luego, en una nueva sesión


secondSession.saveOrUpdate(cat); // actualice el estado existente (cat tiene un id no nulo)
secondSession.saveOrUpdate(mate); // grabe la instancia nueva (mate tiene un id nulo)

El uso y semántica de saveOrUpdate() les parece confuso a los nuevos usuarios. En primer lugar, a menos que se esté
intentando usar instancias de una sesión en otra sesión nueva, no hay necesidad de usar update(), saveOrUpdate(), ni
merge(). Hay aplicaciones enteras que jamás utilizarán ninguno de estos métodos.

Usualmente, update() o saveOrUpdate() se usan en el siguiente escenario:

la aplicación carga un objeto en la primera sesión

el objeto es pasado a la capa de interfaz de usuario

se le hacen algunas modificaciones al objeto

el objeto baja de nuevo a la capa de lógica de negocios

la aplicación persiste estas modificaciones invocando update() en una segunda sesión

saveOrUpdate() hace lo siguiente

si el objeto ya es persistente en esta sesíón, no hace nada

si ya hay otro objeto asociado con la sesión con el mismo identificador, se produce una excepción

si el objeto no tiene una valor de identificador, se invoca save()

si el identificador del objeto es asignable, y le ha sido recientemente asignado a una instancia recién creadam se
invoca save().

si el objeto es versionado (por una <version> o <timestamp>), y la propiedad versión es el mismo valor asignado a
un objeto recién instanciado, se invoca save()

en cualquier otro caso, se invoca update()

y merge() es muy distinto

si ya hay una instancia persistente con el mismo identificador asociada con la sesión, copia el estado del objeto dado
en la instancia persistente.

si no hay ninguna instancia asociada con la sesión, intenta cargarla de la base de datos, o crear una nueva instancia
persistente.

se devuelve la instancia persistente

la instancia dada (como parámetro) no se vuelve asociada con la sesión, permanece desprendida.

10.8. Borrar objetos persistentes


Session.delete() quitará el estado de un objeto de la base de datos. Por supuesto, su aplicación podría aún contener
una referencia al objeto quitado. Es mejor pensar que delete() está convirtiendo una instancia persistente en transitoria.

sess.delete(cat);

Se puede borrar objetos en cualquier orden que se desee, sin temor de causar violaciones de clave foránea. Pero sí es

113 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

posible violar constraints NOT NULL aplicadas a la columna de clave foránea, por ejemplo si se borra al padre antes que al
hijo.

10.9. Replicar un objeto entre dos repositorios de datos distintos


Ocasionalmente, es útil poder tomar un árbol de instancias persistentes y hacerlas persistentes en otro repositorio de datos
distinto, sin tener que regenerar los valores de los identificadores.

//obtener un cat de la base de datos


Session session1 = factory1.openSession();
Transaction tx1 = session1.beginTransaction();
Cat cat = session1.get(Cat.class, catId);
tx1.commit();
session1.close();

//reconciliar con la segunda base de datos


Session session2 = factory2.openSession();
Transaction tx2 = session2.beginTransaction();
session2.replicate(cat, ReplicationMode.LATEST_VERSION);
tx2.commit();
session2.close();

El ReplicationMode determina cómo el método replicate() lidiará con conflictos que puedan existir con filas
existentes en la base de datos.

ReplicationMode.IGNORE: ignorar el objeto cuando exista una fila de base de datos con el mismo identificador.

ReplicationMode.OVERWRITE: sobrescribir cualquier fila existente en la base de datos que tenga el mismo
identificador.

ReplicationMode.EXCEPTION: lanzar una excepción si ya existe una fila en la base de datos que tenga el mismo
identificador.

ReplicationMode.LATEST_VERSION: sobrescribir la fila si su número de versión es más antiguo que el del objeto.
En caso contrario, ignorar.

Algunos casos de uso para esta característica incluyen: reconciliar datos ingresados en dos instancias distintas de base de
datos, actualizar la información de configuración del sistema durante la actualización de un producto, dar marcha atrás
(rollback) con cambios hechos durante transacciones carentes de integridad transaccional (non-ACID), y muchos más.

10.10. "Flush" de la sesión


De tanto en tanto la sesión ejecutará los comandos SQL que sean necesarios para sincronizar el estado de la conexión
JDBC con el estado de los objetos mantenidos en la memoria. Este proceso llamado el "nivelado" o "desagote" de la sesión
(en inglés, flush), ocurre por defecto en los siguientes puntos:

antes de algunas ejecuciones de consultas

en org.hibernate.Transaction.commit()

en Session.flush()

Los comandos SQL son ejecutados en el siguiente orden

todas las inserciones de entidades, en el mismo orden en que los objetos hayan sido grabados con Session.save()

todas las actualizaciones a entidades

todos los borrados de colecciones

todos los borrados, modificaciones e inserciones de elementos en las colecciones

todas las inserciones de colecciones

todos los borrados de entidades, en el mismo orden en que los objetos hayan sido borrados usando

114 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Session.delete()

(una excepción es que los objetos que usen la generación de identificador native serán insertados al invocar save).

A excepción de cuando se invoque flush() explícitamente, no hay absolutamente ninguna garantía acerca de cuándo los
llamados JDBC serán ejecutados por la sesión, sólo acerca del orden en que serán ejecutados. Sin embargo, Hibernate
garantiza que Query.list(..) nunca devolverá datos rancios.

Es posible cambiar el comportamiento por defecto para que el "flush" ocurra menos frecuentemente. La clase FlushMode
define tres modos diferentes: sólo provocar el "flush" al momento de llamar "commit" (y esto sólo en una transacción de la
API de Hibernate), provocar el "flush" automáticamente usando la rutina explicada, o nunca provocar el "flush" menos
que flush() sea invocado explícitamente. Este último modo es útil para unidades de trabajo largas, en donde la sesión se
mantiene abierta y desconectada por largo tiempo (véase la Sección 11.3.2, “Sesión extendida y versionado automático”).

sess = sf.openSession();
Transaction tx = sess.beginTransaction();
sess.setFlushMode(FlushMode.COMMIT); // permite que las consultas devuelvan datos rancios

Cat izi = (Cat) sess.load(Cat.class, id);


izi.setName(iznizi);

// podría devolver datos rancios


sess.find("from Cat as cat left outer join cat.kittens kitten");

// ¡el cambio a izi no ha tenido "flushing"!


...
tx.commit(); // flush occurs
sess.close();

Durante el "flush", puede ocurrir una excepción (por ejemplo, si una operación de creación de datos viola una constraint).
Como el manejar excepciones involucra algún conocimiento del comportamiento transaccional de Hibernate, lo
discutiremos en el Capítulo 11, Transacciones y concurrencia.

10.11. Persistencia transitiva


Es bastante engorroso grabar, borrar o reasociar objetos individuales, especialmente si se está lidiando con un árbol de
objetos asociados. Un caso común es el de la relación padre/hijo. Considere el siguiente ejemplo:

Si los hijos en una relación padre/hijo fuesen de la índole "value type" (por ejemplo una colección de direcciones, o de
cadenas), su ciclo de vida dependería del padre, y no haría falta hacer nada para que la propagación en cascada se
efectuare de manera adecuada ante cambios de estado. Cuando el padre es grabado, los hijos "value type" son grabados
también; cuando se borra al padre, a los hijos también, etc. Esto inclusive funciona para operaciones como el borrado de
un hijo de una colección: Si un "value type" hijo es borrado de una colección, como los "value types" no pueden tener
referencias compartidas, ya no tiene razón se ser, e Hibernate lo borrará de la base de datos.

Ahora considere el mismo escenario, pero con objetos padre e hijos siendo entidades, no "value types" (por ejemplo,
categorías e items, o gata y gatitos). Las entidades tienen su propio ciclo de vida, soportan referencias compartidas (así que
quitar a una referncia de una colección no significa que pueda ser borrada), y por defecto no hay propagación en cascada
de una entidad a sus entidades asociadas. Hibernate no implementa el el concepto de persistir todo lo que esté al alcance
(persistence by reachability) por defecto.

Para cada operación básica de la sesión de Hibernate, incluyendo: persist(), merge(), saveOrUpdate(), delete(),
lock(), refresh(), evict(), replicate() hay un estilo de propagación en cascada correspondiente. Se se quiere
que una operación sea propagada en cascada en la dirección de una asociación, eso se debe indicar en el documento de
mapeo. Por ejemplo:

<one-to-one name="person" cascade="persist"/>

Los estilos de propagación en cascada pueden ser combinados.

<one-to-one name="person" cascade="persist,delete,lock"/>

Incluso se puede usar cascade="all" para indicar que todas las operaciones deben ser propagadas en dirección de esa
asociación. El valor por defecto, cascade="none", especifica que ninguna operación debe ser propagada.

115 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Un tipo especial de propagación en cascada, delete-orphan, se aplica sólo a las asociaciones de-uno-a-muchos, e indica
que la operación delete() debe ser aplicada a cualquier hijo cuyo padre haya sido borrado.

Recomendaciones:

Normalmente no tiene sentido habilitar la propagación en cascada para asociaciones <many-to-one> o <many-
to-many>.La propagación es usualmente útil para asociaciones <one-to-one> y <one-to-many>.

Si el ciclo del vida del hijo está ligado al del padre, conviértalo en un objeto de ciclo de vida especificando
cascade="all,delete-orphan".

En cualquier otro caso, puede que usted no necesite usar cascade en absoluto. Pero si usted calcula que deberá
trabajar a menudo con el padre y los hijos en la misma transacción, y quiere ahorrarse algo de tecleo, considere usar
cascade="persist,merge,save-update".

Mapear una asociación (ya sea de un solo valor, o de una colección) con cascade="all" marca la asociación como "del
estilo padre/hijo", en donde una grabación/actualización/borrado del padre resulta en una grabación/actualización/borrado
de los hijos.

Más aún, una mera referencia a un hijo desde un padre persistente resultará en la grabación/actualización del hijo. Lo
inverso no se da, sin embargo. Un hijo a quien el padre deje de hacer referencia no es automáticamente borrado, excepto
en el caso de una asociación de-uno-a-muchos mapeada con cascade="delete-orphan". La semántica exacta de la
relación padre/hijo es como sigue:

Si se le pasa un padre (como parámetro) a un persist(), todos sus hijos serán también pasados a persist()

Si se le pasa un padre a un merge(), todos sus hijos serán también pasados a merge()

Si se le pasa un padre a un save(), update() o saveOrUpdate(), todos sus hijos serán también pasados a
saveOrUpdate().

Si un hijo transitorio o desprendido empieza a ser referido por un padre persistente, se le pasa a saveOrUpdate()

si un padre es borrado, todos sus hijos le son pasados a delete()

Si un hijo deja de ser referido por un padre persistente, no pasa nada en especial: la aplicación debería borrar el hijo
explícitamente si hace falta (a menos que haya cascade="delete-orphan", en cuyo caso la clase el hijo que se ha
vuelto huérfano será borrado).

Por último, note que la propagación de operaciones en cascada se le puede aplicar a un objeto al momento de invocarlo o
al momento de efectuar "flush". Todas las operaciones, si están habilitadas, se propagan en cascada hacia todas las
entidades que estén a su alcance cuando la operación es ejecutada. Sin embargo, save-upate y delete-orphan son
transitivas hacia las entidades asociadas cuando ocurre el "flush" de la sesión.

10.12. Usar metadatos


Hibernate requiere un modelo muy frondoso, a un meta-nivel, de todas sus entidades y "value-types". De tanto en tanto,
este modelo le es muy útil a la aplicación misma. Por ejemplo, la aplicación podría usar los metadatos de Hibernate para
implementar un algoritmo inteligente de "copia profunda" (deep copy) que entienda cuáles objetos deben ser copiados (por
ejemplo, "value types" mutables) y cuáles no (por ejemplo, "value types" inmutables y, posiblemente, entidades
asociadas).

Hibernate expone metadatos mediante las interfaces ClassMetadata y CollectionMetadata, y la jerarquía de Type.
Instancias de las interfaces de metadatos se obtienen de la SessionFactory.

Cat fritz = ......;


ClassMetadata catMeta = sessionfactory.getClassMetadata(Cat.class);

Object[] propertyValues = catMeta.getPropertyValues(fritz);


String[] propertyNames = catMeta.getPropertyNames();
Type[] propertyTypes = catMeta.getPropertyTypes();

// obtiene un Map de todas las propiedades que no sean colecciones o asociaciones


Map namedValues = new HashMap();
for ( int i=0; i<propertyNames.length; i++ ) {
if ( !propertyTypes[i].isEntityType() && !propertyTypes[i].isCollectionType() ) {

116 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

namedValues.put( propertyNames[i], propertyValues[i] );


}
}

Capítulo 11. Transacciones y concurrencia


El punto más importante acerca de Hibernate y el control de concurrencia, es que es muy fácil de entender. Hibernate usa
directamente las conecciones JDBC y los recursos de JTA sin agregar ningún mecanismo adicional de "locking". Le
recomendamos que pase un buen tiempo revisando la documentación sobre JDBC, ANSI, y cómo funciona el aislamiento
de transacciones en su base de datos en particular.

Hibernate no efectúa "lock" de objetos en memoria. Si aplicación puede esperar el comportamiento que esté definido por
el nivel de aislamiento de sus transacciones de base de datos. Note que, gracias a la sesión, que es también un caché con
alcance a la transacción, Hibernate provee lecturas repetibles para la búsqueda por identificadores, y consultas de entidad
(no consultas "de reporte" que devuelvan valores escalares).

Además de versionar para lograr un control de concurrencia optimista, Hibernate también ofrece una API (mínima) para
efectuar un "lock" de filas pesimista, usando la sintaxis SELECT FOR UPDATE. El control de concurrencia optimista, y esta
API, se discuten más adelante en este capítulo.

Comenzaremos la discusión sobre el control de concurrencia en Hibernate involucrando los conceptos de configuración
(Configuration), la fábrica de sesiones (SessionFactory), y la sesión, así como las transacciones de base de datos y las
conversaciones largas.

11.1. La sesión y el alcance (scope) de las transacciones


La fábrica de sesiones (SessionFactory) es un objeto seguro en cuanto al acceso por threads múltiples (thread-safe), y es
costosa de crear. Se la concibió para ser compartida por todos los threads de la aplicación. Se la crea una sola vez,
normalmente durante el arranque de la aplicación, a partir de una instancia de Configuration.

Una sesión individual (Session), en cambio, es barata de crear, y no es segura en cuanto al acceso por múltiples threads
(no es "threadsafe"). Se espera que sea usada una sola vez, para una interacción simple o "request" (una sola solicitud, una
sola "conversación" o "unidad de trabajo"), y luego sea descartada. Una sesión no intentará obtener una conexión de
JDBC, ni una fuente de datos (Connection, Datasource) a menos que sea necesario. Por lo tanto, no consume recursos
mientras no es usada.

Para completar este panorama se debe pensar también en las transacciones de base de datos. Una transacción de base de
datos debe ser lo más corta posible, para reducir las posibilidades de conflictos de "lock" en la base de datos. Las
transacciones largas impiden que la aplicación sea "escalable" (es decir, que se adapte con facilidad a una mayor demanda
y tamaño), por no poder soportar una mayor carga de demandas concurrentes. Por tal motivo, mantener una transacción
abierta mientras el usuario "piensa" y hasta que la unidad de trabajo se complete, casi nunca es una buena idea.

¿Cuál es el alcance de una unidad de trabajo? ¿Una simple sesión de Hibernate puede abarcar varias transacciones de base
de datos, o sus alcances son similares y con una relación uno a uno? ¿Cuándo se debe abrir y cerrar una sesión, y cómo se
demarcan los límites de una transacción de base de datos?

11.1.1. Unidad de trabajo

¡Antes que nada, no emplee la práctica desaconsejada (antipattern) de una-sesión-por-operación, es decir, no abra y
cierre una sesión para cada llamado a la base de datos en un mismo thread! En una aplicación, los llamdos a la base de
datos son hechos en un orden planificado, y son agrupados en unidades de trabajo atómicas. (Nótese que esto también
significa que tener la conexión en auto-commit después de cada comando SQL es inútil en una aplicación, esta modalidad
de trabajo es más acorde con trabajo ad-hoc en una consola SQL. Hibernate inhabilita el auto-commit inmediatamente, o
espera que el servidor de aplicaciones lo haga). Las transacciones de base de datos nunca son opcionales, toda
comunicación con la base de datos debe ocurrir dentro de una transacción, ya sea para escribir o para leer datos. Como se
dijo anteriormente, el comportamiento "auto-commit" debe evitarse, dado que es improbable que un conjunto de muchas
pequeñas transacciones tenga mejor performance que una unidad de trabajo claramente definida. Esta última es también
más flexible y extensible.

El patrón más común, en una aplicación cliente/servidor multiusuario, es "una sesión por cada solicitud" (session-
per-request) . Según este modelo, una solicitud o "request" del cliente se envía al servidor (en donde está ejecutándose la

117 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

capa de persistencia Hibernate), se abre una nueva sesión, y todas las operaciones de base de datos son ejecutadas e esta
unidad de trabajo. Una vez que el trabajo se haya completado (y la respuesta para el cliente se haya preparado), la sesión
sufre un "flush" y se cierra. También se usaría una sola transacción de base de datos para atender la solicitud del cliente,
comenzándola y efectuando "commit" cuando se abra y cierre la conexión, respectivamente. La relación entre sesión y
transacción es una a una, y este modelo es perfectamente adecuado para muchas aplicaciones.

El desafío radica en la implementación. Hibernate ya trae un sistema incluido que se puede usar para simplificar el uso de
este patrón, y manejar lo que se considera la "sesión actual". Todo lo que el programador debe hacer, es comenzar una
transacción cuando haya que procesar una solicitud (request) al servidor, y finalizar la transacción antes de que la
respuesta le sea enviada al cliente. Esto se puede lograr de varias maneras: las soluciones más comunes son: -un filtro
(ServletFilter) -un interceptor basado en AOP, que tenga sus "pointcuts" en los métodos del servicio, -un contenedor
de proxies/intercepciones, etc. Una manera estándar de implementar aspectos que conciernen de una misma forma a varios
niveles y áreas de la aplicación (en inglés, "cross-cutting aspects"), es usar un contenedor de EJB, el cual puede
implementar transacciones de una manera declarativa y manejada por el contenedor mismo (CMT). Si se decide usar la
demarcación de transacciones programática, prefiérase la API de Transaction de Hibernate, que es fácil y portátil.

El código de su aplicación puede acceder a la "sesión actual" para procesar una request, simplemente invocando
sessionFactory.getCurrentSession() en cualquier lugar, y tan a menudo como haga falta. Siempre se obtendrá una
sesión que estará dentro del alcance o "scope" de la transacción de base de datos actual. Esto tiene que ser configurado, ya
sea para entornos de recursos locales o para JTA (véase la Sección 2.5, “Sesiones contextuales”.

A veces es conveniente extender los alcances de la sesión y de la transacción de base de datos hasta que la vista o "view"
le haya sido presentada al usuario. Esto es especialmente útil en aplicaciones basadas en servlets, las cuales utilizan una
fase separada de presentacíón posterior al procesamiento de la solicitud o "request". Extender la transacción de base de
datos hasta que la "view" sea presentada es fácil si se implementa un interceptor propio. Sin embargo, no es fácil de hacer
si uno se apoya en un EJBs con transacciones CMT, dado que la transacción se completará tras el return de los métodos de
los EJBs, antes de que la presentación de ninguna view hubiere comenzado. Visite el sitio de web de Hibernate para
consejos y ejemplos acerca de este patrón Open Session in View.

11.1.2. Conversaciones largas

El patrón "una-sesión-por-solicitud" (session-per-request) no es el único concepto útil que puede usarse para diseñar
unidades de trabajo. Muchos procesos de negocios requieren toda una serie de interacciones con el usuario, entretejidas
con accesos a la base de datos. En las aplicaciones de web y corporativas, no es aceptable que una transacción de base de
datos dure a lo largo de toda la interacción con el usuario. Considere el siguiente ejemplo:

Aparece la primera ventana de diálogo, los datos que ve el usuario han sido cargados en una sesión y transacción de
base de datos en particular. El usuario es libre de modificar los objetos.

El usuario pulsa "Grabar" luego de 5 minutos, y espera que sus modificaciones sean hechas persistentes; también
espera haber sido la única persona que haya editado esa información, y que no puedan haber ocurrido otras
modificaciones conflictivas.

A esto le llamamos una "unidad de trabajo"; desde el punto de vista del usuario, una conversación (o transacción de la
aplicación). Hay varias maneras de implementar esto en su aplicación.

Una primera implementación ingenua sería manetner la sesión y la transacción de base de datos abiertas durante el tiempo
que el usuario se tome para pensar, lo cual ejerce un "lock" sobre la base de datos para impedir modificaciones
concurrentes, y garantizar aislamiento y atomicidad. Esto es, por supuesto, una práctica a evitar o "anti-patrón" (anti-
pattern), dado que los conflictos de lock no le permitirán a nuestra aplicación adaptarse a una mayor demanda por usuarios
concurrentes.

Claramente, debemos usar varias transacciones de BD para implementar la conversación. En este caso, mantener un
aislamiento entre los procesos de negocio se vuelve en parte responsabilidad de la capa de la aplicación. Una simple
conversación normalmente abarca varias transacciones de base de datos. Será atómica si sólo una de dichas transacciones
(la última) es la que almacena los datos modificados, todas las otras simplemente leen datos (por ejemplo, en una ventana
de diálogo tipo "paso a paso" o "wizard", que abarque varios ciclos solicitud/respuesta). Esto es más fácil de implementar
de lo que parece, especialmente si se utilizan las ventajas que provee Hibernate:

Versionado automático: Hiebernate puede efectuar automáticamente por usted un control de concurrencia
optimista, puede detectar automáticamente si ocurrió una modificación durante el tiempo que el usuario se tomó
para reaccionar. Usualmente, esto sólo se verifica al final de la conversación.

118 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Objetos desprendidos: si se decide usar el patrón una sesión por solicitud (session-per-request) ya discutido, todas
las instancias cargadas se convertirán en "desprendidas" (detached) durante el tiempo que el usario se tome para
pensar. Hibernate le permite reasociar estos objetos y persistir las modificaciones; este patrón se llama una-sesión-
por-solicitud-con-objetos-desprendidos. Para aislar modificaciones concurrentes se usa versionado automático.

Sesión larga (o "extendida"): la sesión de Hibernate puede desconectarse de la conexión JDBC subyacente luego de
que la transacción haya ejecutado su commit, y reconectada cuando ocurra una nueva solicitud del cliente. Este
patrón se conoce como una-sesión-por-conversación y hace que la reasociación de objetos sea innecesaria. Para
aislar modificaciones concurrentes se usa versionado automático, y a la sesión no se le permite hacer "flush"
automático, sino explícito.

Ambos patrones, una-sesión-por-spolicitud y una-sesión-por-conversación tienen ventajas y desventajas. Las


discutiremos más adelante, en el contexto del control de concurrencia optimista.

11.1.3. Considerar la identidad de los objetos

Una aplicación puede aceder en forma concurrente al mismo estado persistente en dos sesiones diferentes. Pero una
instancia de una clase persistente nunca se comparte entre dos instancias de Session. Por lo tanto, hay dos nociones
diferentes de identidad:

Identidad de base de datos


foo.getId().equals( bar.getId() )

Identidad de JVM
foo==bar

Entonces, para objetos asociados a una sesión en particular, (esto es, en el mismo alcance o "scope" de una sesión) las dos
nociones son equivalentes, e Hibernate garantiza que la identidad JVM equivale a la identidad de BD. Sin embargo, si la
aplicación accede en forma concurrente al mismo dato, puede ocurrir que la misma identidad persistente esté contenida en
dos instancias de objeto en dos sesiones distintas. En este caso la identidad persistente o de base de datos existe, pero la
identidad de JVM no, los objetos son "diferentes". Estos conflictos se resuelven a nivel usando versionado automático
(cuando ocurren los "flush"/"commit"), usando el enfoque optmista.

Este enfoque deja que Hibernate se preocupe por la concurrencia. También provee la mejor "escalabilidad", dado que
garantizar la identidad sólo a nivel de unidades de trabajo en un thread simple no requiere un costoso "lock" ni otros
medios de sincronización. La aplicación no necesita sincronizar ningún objeto, siempre y cuando se atenga a que se usará
un solo thread por sesión. Dentro de una sesión, la aplicación puede usar == tranquilamente para comparar objetos.

Sin embargo, una aplicación que use == fuera de una sesión, se puede topar con resultados inesperados. Esto puede ocurrir
incluso en lugares insólitos, por ejemplo, si se colocan dos instancias desprendidas en el mismo Set, existe la posibilidad de
que ambas tengan la misma identidad de base de datos (es decir, que representen la misma fila) pero no la misma identidad
JVM. El programador debe sustituir los métodos equals() y hashCode() en las clases persistentes, e implementar su
propia noción de igualdad entre objetos. Sólo una advertencia: nunca use el identificador de base de datos para
implementar igualdad; use una "clave de negocios", una combinación única y normalmente inmutable de atributos. Si la
instancia transitoria es almacenada en un Set, cambiar el hashcode rompe el contrato del Set. Los atributos de las "claves
de negocio" no necesitan ser tan estables como las claves primarias de una base de datos. Sólo necesitan poder establecer,
de manera estable, diferencias o igualdad entre los objetos que estén en un Set. Vea el sitio de web de Hibernate para una
discusión más detallada sobre este tema. También note que éste no es un problema de Hibernate, sino la manera en que los
objetos de Java implementan identidad e igualdad.

11.1.4. Problemas comunes

Nunca use los anti-patrones una-sesión-por-cada-interacción-con-el-usuario ni una-sesión-para-toda-la-aplicación (por


supuesto, puede haber raras excepciones a esta regla). Note que los problemas que listamos a continuación pueden
aparecer incluso si se están usando los patrones que sí recomendamos. Asegúrese de que entiende las implicancias de la
decisión de diseño que tome.

Una sesión (Session) no es segura en cuanto a acceso concurrente por múltiples threads (no es "thread-safe"). Las
cosas que se supone funcionen en forma concurrente, como las HTTP requests, los session beans, o los workers de
Swing workers, causarían condiciones de conflicto por recursos conocidas como "race conditions" si la instancia de
una sesión de Hibernate fuese compartida. Si la sesión de Hibernate está contenida en una sesión de HTTP

119 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

(HttpSession, la cual se discute más adelante), se debería considerar el sincronizar el acceso a la sesión HTTP. De
otra manera, cualquier usuario que cliquee "reload" lo suficientemente rápido, es probable que termine usando la
misma sesión (de Hibernate) en dos threads ejecutándose en forma concurrente.

Si Hibernate produce una excepción, significa que hay que desandar (rollback) la transacción de base de datos, y
cerrar la sesión inmediatamente (como se discute luego en más detalle),. Si la sesión está asociada a la aplicación,
debe detenerse la aplicación. Efectuar un "rollback" de la transacción no restaura los objetos de negocio al estado en
el que estaban antes de que la transacción ocurriese. Esto significa que el estado de la base de datos y el de los
objetos de negocio sí quedan fuera de fase. Normalmente esto no es problema, porque las excepciones no son
recuperables, y de todos modos es necesario comenzar todo de nuevo luego de un "rollback".

La sesión almacena en su caché cada objeto que esté en estado persistente (habiendo vigilado y comprobado
Hibernate si su estado es "sucio"). Esto significa que crecerá para siempre, hasta provocar un error de memoria
(OutOfMemoryException) si se la deja abierta por mucho tiempo o simplemente se le cargan demasiados datos. Una
solución para esto, es invocar clear() y evict() para manejar el caché de la sesión, pero lo que más
probablemente se debería considerar es un procedimiento de base de datos almacenado (stored procedure) si se
necesitan operaciones masivas de datos. Algunas soluciones se muestran en el Capítulo 13, Procesamiento en lotes.
Mantener una sesión abierta durante toda la interacción con el usuario también aumenta la probabilidad de que los
datos se vuelvan "rancios".

11.2. Demarcación de transacciones de base de datos


La demarcación de las transacciones de base de datos (o de sistema) siempre es necesaria. No puede ocurrir ninguna
comunicación con la base de datos si no es dentro de una transacción. (Esto parece confundir a algunos programadores
acostumbrados a trabajar siempre en modo "auto-commit"). Siempre deben emplearse límites de transacción claros,
incluso para las operaciones de lectura. Dependiendo del niverl de aislamiento y de las posibilidades de la base de datos,
esto puede no ser obligatorio, pero no hay ninguna desventaja ni se puede dar ninguna razón en contra de demarcar
explícitamente, siempre, los límites de una transacción. Una única transacción de base de datos, ciertamente tendrá mucha
mejor performance que múltiples pequeñas transacciones, incluso al leer datos.

Una aplicación de Hibernate puede ser ejecutada en un entorno "no manejado", "no administrado" (es decir,
autosuficiente, una simple aplicación de web o Swing), y también en entornos J2EE "administrado" (managed
environments). En un entorno "no manejado", Hibernate normalmente es responsable por su propio "pool" de conexiones.
El programador tiene que establecer los limites de las transacciones manualmente (en otras palabras, invocar los métodos
begin, commit y rollback de las transacciones él mismo). Un entorno "administrado", normalmente provee transacciones
administradas por el contenedor ("container-managed transactions o CMT por sus siglas en inglés), con la disposición de
las tranascciones definida declarativamente en los archivos de descripción de despliegue o "deployment descriptors" de los
session beans EJB, por ejemplo. En tal caso, la demarcación manual o "programática" de las transacciones no es necesaria.

Sin embargo, a veces es deseable mantener la capa de persistencia portátil entre entornos administrados y no
administrados. Utilizar la demarcación programática se puede usar.en ambos casos. Hibernate ofrece una API llamada
Transaction que se traduce en el sistema nativo de transacciones del entorno en el cual el programa haya sido
desplegado. Esta API es opcional, pero recomendamos usarla siempre, a menos que el código esté en un session bean,
dentro de un servidor con CMT.

Usualmente, finalizar una sesión involucra las siguientes fases:

invocar el "flush" de la sesión

invocar "commit" en la transacción de base de datos

cerrar la sesión

manejar las excepciones que hayan podido ocurrir

Efectuar el "flush" de la sesión ha sido discutido antes; ahora le daremos una mirada más detallada a la demarcación de
transacciones y al manejo de excepciones, tanto en un entorno administrado como no administrado.

11.2.1. Entornos no administrados

Si la capa de persistencia e Hibernate se ejecuta en un entorno no-administrado, las coneiones a la base de datos
usualmente son manejadas por un "pool" de conexiones simple (es decir, no una DataSource) del cual Hibernta obtiene las

120 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

conexiones que necesite. El estilo de manejo de sesión/tranascción se ve así:

// Non-managed environment idiom


Session sess = factory.openSession();
Transaction tx = null;
try {
tx = sess.beginTransaction();

// efectuar algo de trabajo


...

tx.commit();
}
catch (RuntimeException e) {
if (tx != null) tx.rollback();
throw e; // o mostrar un mensaje de error
}
finally {
sess.close();
}

No hace falta invocar el "flush" de la sesión explícitamente: el llamaod a commit() dispara automáticamente la
sincronización (dependiendo del Modo de "flush" para la sesión. Un llamado a close() marca el fi de la sesión. La
implicancia más importante de close() es que la conexión JDBC será cedidad por la sesión. Este código Java es portátil,
y puede ejecutarse tanto en entornos administrados como no administrados.

Una solución muchio más flexible (que ya viene incorporad en Hibernate), es el manejo del contexto de "sesión actual",
como se describión anterioromente.

// Estilo para un entorno no-administrado con getCurrentSession()


try {
factory.getCurrentSession().beginTransaction();

// efectuar algo de trabajo


...

factory.getCurrentSession().getTransaction().commit();
}
catch (RuntimeException e) {
factory.getCurrentSession().getTransaction().rollback();
throw e; // o mostrar un mensaje de error
}

Se podrán encontrar fragmentos de código como éstos en cualquier aplicación normal. Las excepciones fatales, de sistema,
deberían ser capturadas en la capa superior. En otras plabras, el código que ejecuta los llamados a Hibernate, en la capa de
persistencia, y el código que maneja las RuntimeExceptions (y normalmente hace las tareas de limpeza y salida) están en
capas diferentes. El "manejo de contexto actual" hecho por Hibernate puede simplificar este diseño considerablemente,
todo lo que se necesita es accedeso a la SessionFactory. El manejo de excepciones se discute más adelante en este
capítulo.

Note que debería seleccionarse org.hibernate.transaction.JDBCTransactionFactory (el valor pord efecto), y, para
el segundo ejemplo, el valor "thread" para hibernate.current_session_context_class.

11.2.2. Usar JTA

Si la capa de persistencia se ejecuta en un servidor de aplicaciones (por ejemplo, detrás de EJB session beans), cada
conexión de base de datos obtenida por Hibernate será automáticamente parte de la transacción global JTA. También se
puede instalar una implementación JTA autónoma, y usarla sin EJB. Hibernate ofrece dos estrategias para integración con
JTA:

Si se usan tranascciones manejadas por beans (bean-managed transactions o BMT por sus siglas en inglés), Hibernate le
dirá al servidor de aplicaciones que empiece y finalice una transaccción BMT si se usa la API de Transaction API. De
este modo, le código e manejo de transacciones para un entorno no administrado es idéntico al de un entorno
administrado.

// estilo BMT
Session sess = factory.openSession();
Transaction tx = null;
try {

121 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

tx = sess.beginTransaction();

// efectuar algo de trabajo


...

tx.commit();
}
catch (RuntimeException e) {
if (tx != null) tx.rollback();
throw e; // o mostrar un mensaje de error
}
finally {
sess.close();
}

Si se desea usar la sesión asociada a una transacción, es decir, la funcionalidad getCurrentSession() para una más fácil
propagación del contexto, se debe usar la API de JTA UserTransaction directamente:

// estilo BMT con getCurrentSession()


try {
UserTransaction tx = (UserTransaction)new InitialContext()
.lookup("java:comp/UserTransaction");

tx.begin();

// efectuar algo de trabajo con la sesión asociada a la transacción


factory.getCurrentSession().load(...);
factory.getCurrentSession().persist(...);

tx.commit();
}
catch (RuntimeException e) {
tx.rollback();
throw e; // o mostrar un mensaje de error
}

Con CMT, la demarcación de transacciones se hace en los descriptores de despliegue (dployment descriptors) del session
bean, no se hace programáticamente. Así que el código queda reducido a:

// estilo CMT
Session sess = factory.getCurrentSession();

// efectuar algo de trabajo


...

En un entorno CMT/EJB incluso el rollback ocurre automáticamente, puesto que una RuntimeException no capturada
emitida por el método de un session bean le dice al contenedor que efectúe rollback en la transacción global. Esto
significa que no hace falta usar la API de Transaction API de Hibernate en absoluto con BMT or CMT, e igualmente
se obtiene propagación automática de la sesión "actual" asociada a la transacción.

Cuando se elige la fábrica (factory) de transacciones, note que debería elegirse


org.hibernate.transaction.JTATransactionFactory si se usa JTA directamente (BMT), y debería usarse
org.hibernate.transaction.CMTTransactionFactory en un session bean CMT. Más aún, asegúrese de que su
hibernate.current_session_context_class ha sido o bien eliminado (por compatibilidad hacia a trás o "backwards
compatibility"), o bien puesto a "jta".

La operación getCurrentSession() tiene una desventaja en un entorno JTA:, Hay una advertencia sobre el uso del
modo de liberación de conexiones after_statement, el cual es el valor por defecto. Debido a una tonta limitación de la
especificación JTA, para Hibernate no es posible limpiar automáticamente instancias no cerrada de ScrollableResults o
Iterator que hayan sido devueltas por scroll() o iterate(). Usted debe liberar el cursor de base de datos subyacente
invocando explícitamente cursor by calling ScrollableResults.close() o Hibernate.close(Iterator) en el bloque
finally. (Por supuesto, la mayoría de las aplicaciones pueden fácilmente evitar usar en absoluto scroll() o iterate()
en el código JTA o CMT).

11.2.3. Manejo de excepciones

Si la sesión provoca una excepción (incluida cualquier SQLException), se debería efectuar un rollback de la transacción
de base de datos inmediatamente, invocar Session.close() y descartar la instancia de la sesión. Algunos métodos de
Session no dejarán a la sesión en un estado consistente. Ninguna excepción generada por Hibernate puede ser tratada

122 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

como recuperable. Asegúrese de que la sesión será cerrada invocando close() en el bloque finally.

HibernateException, la cual envuelve la mayoría de los errores que ocurren en la capa de persistencia de Hibernate, es
una excepción del tipo "unchecked" (esto es, que no requiere captura obligatoriamente en tiempo de compilación). No lo
era en versiones anteriores de Hibernate. En nuestra opinión, no se debería forzar al programador a capturar excepciones
de bajo nivel en la capa de persistencia. En la mayoría de los sistemas, las excepciones "unchecked" son manejadas en uno
de los primeros "marcos" de la pila de invocaciones a métodos (es decir, en las capas más "altas" de la aplicación), y se le
presenta un mensaje de error al usuario de la aplicación, o se adopta algún otro curso de acción apropiado. Note que
Hibernate puede también emitir otras exceptiones, además de HibernateException. Éstas son, de nuevo, no
recuperables, y se debe adoptar las medidas apropiadas para lidiar con ellas.

Hibernate envuelve las excepciones SQLException generadas al interactuar c con la base de datos en una
JDBCException. De hecho, Hibernate intentará convertir la excepción en una subclase de JDBCException que tenga más
sentido. La SQLException subyacente está siempre disponible via JDBCException.getCause(). Hibernate convierte las
SQLException en subclases apropiadas de JDBCException usando el conversor SQLExceptionConverter que está
asociado a la fábrica SessionFactory. POr defecto, el SQLExceptionConverter es definido de acuerdo al dialecto SQL
elegido. Pero es posible enchufar una implementación a medida (ver los javadocs para la clase
SQLExceptionConverterFactory por detalles). Los subtipos estándar de JDBCException son:

JDBCConnectionException: indica un error en la comunicación con la JDBC subyacente

SQLGrammarException: indica un error sintáctico o gramatical con el SQL emitido

ConstraintViolationException: indica alguna forma de violación de una constraint de integridad

LockAcquisitionException: indica un error al adquirir el nivel de "lock" necesario para efectuar la operación
solicitada.

GenericJDBCException: una excepción genérica que no cae en ninguna de las otras categorías.

11.2.4. Expiración de transacciones

Una característica extremadamente importante provista por un entorno aministrado, como EJB, la cual nunca es provista
por un entorno no administrado, es la expiración de las transacciones, o "transaction timeout". Los "timeouts" de las
transacciones que ninguna trnasacción "rebelde" ocupe indefinidamente los recursos del sistema sin devolverle respuesta
alguna al usuario. Fuera de un entorno administrado (JTA), Hibernate no puede proveer esta funcionalidad en forma
completa. Sin embargo, puede al menos controlar las operaciones de acceso a datos, asegurándose de que los "puntos
muertos" (deadlocks) y las consultas con resultados enormes estén limitadas a un tiempo definido. En un entorno
administrado, Hibernate puede delegar el "timeout" de las transacciones en JTA. Esta funcionalidad es abstraída por el
objeto Transaction de Hibernate.

Session sess = factory.openSession();


try {
//set transaction timeout to 3 seconds
sess.getTransaction().setTimeout(3);
sess.getTransaction().begin();

// do some work
...

sess.getTransaction().commit()
}
catch (RuntimeException e) {
sess.getTransaction().rollback();
throw e; // or display error message
}
finally {
sess.close();
}

Note que setTimeout() no puede ser llamada desde un bean en CMT, en donde los "timeouts" de las transacciones son
definidos declarativamente.

11.3. Control optimista de concurrencia

123 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

El único abordaje consistente con una alta concurrencia y un alta "escalabilidad", es el control de concurrencia optimista
con versionado. El chequeo de versiones usa números de versión, o timestamps, para detectar adtualizaciones conflictivas
(y evitar que se pierdan modificaciones). Hibernate ofrece 3 enfoques distintos para escribir una aplicación que use
concurrencia optimista. Los casos de uso que mostraremos ocurren en el contexto de una conversación larga, pero el
chequeo de versiones tiene la ventaja de también prevenir la pérdida de modificaciones en transacciones de base de datos
simples.

11.3.1. Chequeo de versión hecho por la aplicación

En una implementación sin mucha ayuda de Hibernate, cada interacción con la base de datos ocurre en una nueva sesión,
y el programador es responsable de recargar todas las instancias persistentes desde la base de datos antes de manipularlas.
Este enfoque fuerza a la aplicacíón a efectuar su propio chequeo de versiones, para asegurar el aislamiento en al
conversación transaccional. Este abordaje es el menos eficiente en términos de acceso a la base de datos, y es el más
parecido a los "entity beans" de EJB.

// foo es una instancia cargada por una sesión previa


session = factory.openSession();
Transaction t = session.beginTransaction();

int oldVersion = foo.getVersion();


session.load( foo, foo.getKey() ); // cargar el estado actual
if ( oldVersion != foo.getVersion() ) throw new StaleObjectStateException();
foo.setProperty("bar");

t.commit();
session.close();

(N.del.T):"foo" y "bar", de origen incierto, son locuciones que se usan en inglés como ejemplos de nombres para
cualquier cosa, especialmente en el ámbito de la computación. Si se usa "foo" como nombre en un ejemplo, casi siempre
se espera que el siguiente nombre sea "bar".
La propiedad version se mapea usando <version>, e Hibernate incrementará el número automáticamente durante el
"flush" si la entidad está sucia.

Por supuesto, si se opera en un entorno de baja concurrencia de datos, y no se requiere un chequeo de versiones, se puede
usar este abordaje pero saltearse el chequeo de versiones. En tal caso, la estrategia por defecto para conversaciones largas
será "el último commit es el que gana". Tenga en cuenta que esto puede confundir a los usuarios de la aplicación, dado
que pueden experimentar pérdidas de actualizaciones sin mensajes de error, o sufrir la posibilidad de conflictos cuando los
cambios se sincronicen.

Claramente, el chequeo manual de versiones sólo es factible en circunstancias muy triviales, y en la mayoría de las
aplicaciones no es práctico. A menudo hay que chequear no sólo instancias simples, sino todo un árbol de objetos
modificados. Hibernate ofrece chequeo automático de versión, usando uno de estos paradigmas de diseño: "sesión
extendida", o "instancais desprendidas".

11.3.2. Sesión extendida y versionado automático

Una sola instancia de sesión y sus clases persistentes son usadas por toda la conversación (lo cual se conoce como
una-sesión-por-conversación). Hibernate chequea las instancias de las versiones cuando ocurre el "flush", emitiendo una
excepción si se detecta modificaciones concurrentes. El capturar y manejar estas excepciones queda a cargo del
programador (opciones comunes que se le dan al usuario son: sincronizar los cambios manualmente, o recomenzar la
conversación de negocios con datos que no estén "rancios").

La sesión es desconectada de toda conexión JDBC subyacente mientras se espera que el usuario complete su interacción.
Esta estrategia es la más eficiente en términos de acceso a la base de datos. La aplicación no necesita preocuparse por
chequear versiones o reasociar instancias desprendidas, ni tiene que recargar instancias en cada transacción de base de
datos.

// foo es una instancia cargada anteriormente por una sesíón anterior


Transaction t = session.beginTransaction(); // obtiene una nueva conexión JDBC, comienza la transac

foo.setProperty("bar");

session.flush(); // sólo para la última transacción de la conversación


t.commit(); // tambien devuelve la conexión JDBC
session.close(); // sólo para la última transacción de la conversación

124 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

El objeto foo todavía sabe en qué sesión fue cargado. Comenzar una nueva transacción de base de datos en una sesión
vieja, obtiene una nueva conexión y reanuda la sesión. Efectuar un "commit" de una transacción de base de datos
desconecta una sesión de la coinexión JDBC y devuelve la conexión al "pool". Tras la reconexión, para forzar un chequeo
de versión en los datos que no se estén actualizando, se puede invocar Session.lock() con LockMode.READ en cualquier
objeto que pueda haber sido actualizado por otra transacción. Usted no necesita un "lock" sobre cualquier dato que usted
esté actualizando. Usualmente, se le debe asignar FlushMode.MANUAL a una sesión extendida, de manera que, en realidad,
sólo a la última transacción de base de datos se le permita persistir todas las modificaciones hechas durante esa
conversación. Por eso, sólo esta última transacción de BD incluiría la operación flush(), y luego también el llamado de la
sesión a close() para cerrar la conversación.

Este patrón es problemático si la sesión es demasiado grande para como para ser almacenada durante el tiempo que el
usuario se toma para reaccionar. Por ejemplo, una HttpSession debería mantenerse tan chica como sea posible. Como la
sesión es también el caché de primer nivel, y contiene todos los objetos cargados, probablementa sólo podamos usar esta
estrategia en uns pocos ciclos solicitur/respuesta. Una sesión debería usarse para una sola conversación, dado que pronto
contendrá datos "pasados".

(Note que versiones anteriores de Hibernate requerían que la sesión se conectara y desconectara explícitamente. Dichos
métodos ahora son obsoletos (deprecated), y comenzar y terminar una transacción tiene el mismo efecto).

También note que se debería mantener la sesión desconectada cerca de la capa de persistencia. En otras palabras, use un
"stateful session bean" de EJB para contener la sesión en un entorno de tres capas, y no la transfiera al entorno de web (ni
siquiera la serialice para transferirla a otra capa separada) para almacenarla en una HttpSession.

El patrón de "sesión extendida" una-sesión-por-conversación, es más difícil de implementar con control automático del
contexto de sesión actual. Para esto se necesita proveer una implementación a medida de CurrentSessionContext. Vea
la Wiki de Hibernate para ejemplos:

11.3.3. Objetos desprendidos y versionado automático

Cada interacción con el repositorio persistente ocurre en una nueva sesión. Sin embargo, las mismas instancias persistentes
son reusadas para cada interacción con la DB. La aplicación manipula el estado de las instancias desprendidas,
orignialmente cargadas en otra sesión, y lyego las reasocia usando Session.update(), Session.saveOrUpdate(), or
Session.merge().

// foo es una instancia cargada en una sesión previa


foo.setProperty("bar");
session = factory.openSession();
Transaction t = session.beginTransaction();
session.saveOrUpdate(foo); // use merge() si "foo" puede haber sido cargada anteriormente
t.commit();
session.close();

De nuevo, Hibernate chequeará ls versiones de instancia al ocurrir el "flush", lanzando una excepción si han ocurrido
actualizaciones conflictivas.

Se puede también invocar lock() en lugar de update(), y usar LockMode.READ (efectuando un cheque de versión,
eludiando todos los cachés) si se está seguro de que el objeto no ha sido modificado.

11.3.4. Crear un método a medida para el versionado automático

Se puede inhabilitar el incremento automático de versión de Hibernate para propiedades y colecciones específicas,
asignándole al atributo de mapeo optimistic-lock el valor false. Hibernate no incrementará más las versiones, si la
propiedad está sucia.

Los sistemas de base de datos anticuados a menudo son estáticos y no pueden ser modificados. U otras aplicaciones
pueden estar accediendo a dichas base de datos, sin saber cómo manejar números de versiones ni incluso timestamps. En
ambos casos, el versionado no puede basarse en una columna en particular de una tabla. Para forzar un chequeo de
versión sin un mapeo de versión o timestamp, con una comparación del estado de todos los campos en una fila, habilite
optimistic-lock="all" en el mapeo de <class>. Note que esto sólo funciona, conceptualmente, si Hibernate puede
comparar el estado nuevo con el viejo, es decir, si se usa una sola sesión larga y no una-sesión-por-solicitud-con-objetos-
desprendidos.

A veces la modificacion concurrente puede ser permitida, simepre y cuando los cambios que hayan sido efectuados no se
superpongan. Si se asigna optimistic-lock="dirty" al mapear la <class>, Hibernate sólo comparará los campos sucios

125 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

cuando ocurra el "flush".

En ambos casos, sea con columnas dedicadas de versión/timestamp, o con comparación completa/de campos sucios,
Hibernate usa un solo comando UPDATE (con una cláusula WHERE apropiada) por entidad para ejecutar el chequeo de
versión y actualizar la información. Si se desa usar persistencia transitiva para la reasociación en cascada de entidades
relacionadas, es posible que Hibernate ejecute algunos UPDATEs innecesarios. Esto normalmente no es un problema, pero
puede que se ejecuten triggers on update en la base de datos, cuando no se les ha efectuado ningún cambio a las instancias
desprendidas. Se puede personalizar este comportamiento más a medida, asignando select-before-update="true" em
el mapeo de <class>, forzando a Hibernate a practicar un SELECT de la instancia para asegurarse de que realmente
hayan ocurrido cambios, antes de actualizar la fila.

11.4. "Lock" pesimista


La idea no es que los programadores inviertan demasiado tiempo preocupándose por estrategias de "lock". Normalmente,
es suficiente que se especifique un nivel de asilamiento para las conexiones JDBC y después simplemente dejar que la
base de datos haga todo el trabajo. No obstante, los programadores avanzados probablemente deseen a veces obtener
"locks" pesimistas exclusivos, o recapturar locks al comienzo de una transacción nueva.

Hibernate siempre usará el mecanismo de lock de la base de datos, ¡nunca "lock" de objetos en memoria!

La clase LockMode define los distintos niveles de "lock" que pueden ser adquiridos por Hibernate. Un lock se obtiene por
los mecanismos siguientes:

LockMode.WRITE se adquiere automáticamente cuando Hibernate actualiza o inserta una fila.

LockMode.UPGRADE puede ser adquirido ante una solicitud explícita del usuario, usando SELECT ... FOR UPDATE
en una base de datos que soporte dicha sintaxis.

LockMode.UPGRADE_NOWAIT puede ser adquirido ante una solicitud explícita del usuario, usando SELECT ... FOR
UPDATE NOWAIT bajo Oracle.

LockMode.READ es adquirido automáticamente cuando Hibernate lee datos bajo los niveles de aislamiento
"Repeatable Read" o "Serializable". Puede ser readquirido por solicitud específica del usuario.

LockMode.NONE representa la ausencia de "locks". Todos los objetos pasan a este modo de lock al final de una
transacción. Los objetos asociados con la sesión a través de un llamado a update() o saveOrUpdate() también
comienzan en este modo de "lock".

La "solicitud (request) explícita del usuario" se expresa de alguna de las siguientes maneras:

Un llamado a Session.load(), especificando un LockMode.

Un llamado a Session.lock().

Un llamado a Query.setLockMode().

Si Session.load() es invocado con UPGRADE o UPGRADE_NOWAIT, y el objeto requerido aún no había sido cargado por la
sesión, el objeto es cargado usando SELECT ... FOR UPDATE. Si se invoca load() para un objeto que ya está cargado,
con un "lock" menos restrictivo que el que se está pidiendo, Hibernate invoca lock() para dicho objeto.

Session.lock() preactica un chequeo del número de versión si el "lock" especificado es READ, UPGRADE o
UPGRADE_NOWAIT. (En el caso de UPGRADE o UPGRADE_NOWAIT, se usa SELECT ... FOR UPDATE).

Si la base de datos no soporta el lock mode solicitado, Hibernate usará un modo alternativo apropiado (en lugar de emitir
una excepción). Esto asegura que la aplicación sea portátil.

11.5. Modos de liberación de conexiones


El antiguo comportamento de Hibernate (2.x) en relación con el manejo de conexiones JDBC era que un sesión obtenía
una conexión cuando se necesitaba por primera vez, y luego se aferraba a dicha conexión hasta que la sesión se cerrara.
Hibernate 3.x introdujo la sesión de "modos de liberación de conexiones". Note que la discusión siguiente, sólo es
pertinente para conexiones provistas a través de un ConnectionProvider; las conexiones provistas por el usuario quedan
afuera del horizonte de esta discusión. Los diferentes modos de liberación se identifican con valores enumerados de

126 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

org.hibernate.ConnectionReleaseMode:

ON_CLOSE: es esencialmenteel comportamiento anticuado descrito anteriormente. La sesión de Hibernate obtiene


una conexión cuando es necesario efectuar algún acceso a JDBC, y retiene dicha conexión hasta que la sesión se
cierre.

AFTER_TRANSACTION: dice que se liberen las conexiones una vez que la org.hibernate.Transaction se haya
completado.

AFTER_STATEMENT (también conocida como "liberación agresiva"): dice que las conexiones se liberen luego de la
ejecución de todos y cada uno de los comandos. Esta liberación agresiva es omitida si el comando deja recursos
abiertos que aún estén asociados con la sesión actual; actualmente la única situación en la que esto ocurre es cuando
se usa un org.hibernate.ScrollableResults.

El parámetro de configuración hibernate.connection.release_mode se usa para especificar qué modo de liberación


usar. Los valores posibles son:

auto (el valor por defecto): esta opción le delega la decisión al modo de liberación recibido por el método
org.hibernate.transaction.TransactionFactory.getDefaultReleaseMode(). Con JTATransactionFactory,
devuelve ConnectionReleaseMode.AFTER_STATEMENT; con JDBCTransactionFactory, devuelve
ConnectionReleaseMode.AFTER_TRANSACTION. Casi nunca es buena idea cambiar el comportamiento por
defecto, porque las fallas en relación con este valor normalmente indican errores de programación (bugs) o
suposiciones incorrectas en el código.

on_close: indica que se use ConnectionReleaseMode.ON_CLOSE. Este valor se conserva por compatibilidad hacia
atrás, pero su uso se desaconseja.

after_transaction: indica que se use ConnectionReleaseMode.AFTER_TRANSACTION. Este valor no debería


ser usado en entornos JTA. También note que, con ConnectionReleaseMode.AFTER_TRANSACTION, si una
sesión se considera que está en modo auto-commit, las conexiones serán liberadas como si el modo de liberación
fuera AFTER_STATEMENT.

after_statement: indica que se usa ConnectionReleaseMode.AFTER_STATEMENT. Adicionalmente, el


ConnectionProvider que esté configurado es consultado para comprobar si soporta este valor
(supportsAggressiveRelease()). Si no, el modo de liberación es reinicializado a
ConnectionReleaseMode.AFTER_TRANSACTION. Este valor es seguro sólo en entornos en donde se puede o bien
recapturar la misma conexión JDBC subyacente cada vez que hacemos un llamado a
ConnectionProvider.getConnection(), o bien en entornos con auto-commit en donde no importa si es la misma
conexión la que nos es devuelta.

Capítulo 12. Interceptores y eventos


A menudo es útil que la aplicación reaccione a ciertos eventos que ocurren dentro de Hibernate. Esto permite la
implementación de ciertos tipos de funcionalidad genérica, y extender la funcionalidad de Hibernate.

12.1. Interceptores
La interfaz Interceptor provee métodos de retorno o "callbacks" desde la sesión a la aplicación, permitiéndole a la
aplicación inspeccionar y/o manipular propiedades de un objeto persistente antes de grabarlo. Un uso posible de esto es,
escribir información de seguimiento/auditoría. Por ejemplo, el interceptor siguiente asigna automáticamente la propiedad
createTimestamp cuando se crea un Auditable, y actualiza la propiedad lastUpdateTimestamp cuando un Auditable
es actualizado.

Se puede o bien implementar Interceptor directamente, o (preferentemente) extender EmptyInterceptor.

package org.hibernate.test;

import java.io.Serializable;
import java.util.Date;
import java.util.Iterator;

import org.hibernate.EmptyInterceptor;
import org.hibernate.Transaction;

127 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

import org.hibernate.type.Type;

public class AuditInterceptor extends EmptyInterceptor {

private int updates;


private int creates;
private int loads;

public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Ty
// no hace nada
}

public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] pre
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, T
if ( entity instanceof Auditable ) {
loads++;
}
return false;
}

public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, T
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 afterTransactionCompletion(Transaction tx) {


if ( tx.wasCommitted() ) {
System.out.println("Creaciones: " + creates + ", Actualizaciones: " + updates, "Cargas:
}
updates=0;
creates=0;
loads=0;
}

Los interceptores vienen en dos variantes: Con alcance de sesión (session-scoped), y con alcance de toda la fábrica de
sesiones (sessionfactory-scoped).

Un interceptor con alcance de sesión se especifica cuando la sesión se abra usando uno de los métodos adicionales
(overloaded) SessionFactory.openSession() que acepta un Interceptor como parámetro.

Session session = sf.openSession( new AuditInterceptor() );

Un interceptor con alcance de SessionFactory se registra con el objeto Configuration antes de consctruir la
SessionFactory. En este caos, el interceptor provisto será a aplicado a todas las sesiones abiertas por esa fábrica de
sesiones. Esto es cierto, a menos que se use una sesión que haya sido abierta especificando explícitamente otro
interceptor. El código de los interceptores de alcance de SessionFactory debe ser seguro en cuando a acceso concurrente
(thread-safe), dado que su código será (potencialmente) usado por varias sesiones al mismo tiempo. Asegúrese de no
almacenar estado específico de una sesión.

new Configuration().setInterceptor( new AuditInterceptor() );

128 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

12.2. Sistema de eventos


Si se tiene que reaccionar a eventos específicos de la capa de persistencia, se puede hacer uso de la arquitectura de
eventos de Hibernate3. El sistema de eventos puede ser usado por añadidura a los interceptores, o en reemplazo de ellos.

Esencialmente todos los métodos de la interfaz Session están correlacionados con un evento. Hay un LoadEvent, un
FlushEvent, etc. (consulte la DTD del archivo de configuración XML o el paquete org.hibernate.event para una lista
completa de los tipos de evento definidos). Cuando se invoca uno de estos métodos, la sesión de Hibernate genera el
evento apropiado y lo pasa a los "listeners" (escuchas) de eventos que hayan sido configurados para ese tipo. De fábrica y
por defecto, esos "listeners" implementan el mismo procesamiento en el que esos métodos habrían resultado. De todos
modos, usted es libre de crear listener a medida, que implemente alguna de la interfaces correspondientes (por ejemplo, el
evento LoadEvent es procesado por una implementación registrada de la interfaz LoadEventListener interface), en cuyo
caso la implementación es la que se vuelve responsable de procesar todo llamado a load() que la sesíón haga.

A los efectos prácticos, los "listeners" deben ser considerados singletons, lo cual significa que serán compartidos entre
solicitudes de clientes, y no deberían almacenar estado como variables de instancia.

Un listener a medida debería implementar la interfaz apropiada para el evento que quiera procesar, y/o extender una de las
clases utilitarias de base (o incluso los listeners que ya vienen incluidos en Hibernate, dado que fueron declarados como
no-finales con ese propósito). Los listeners a medida pueden ser registrados programáticamente a través del objeto
Configuration, o especificados declarativamente en el archivo de configuración XML de Hibernate (no se soporta la
configuración declarativa mediante el archivo ".properties"). He aquí un ejemplo de un listener a medida para el evento
"load".

public class MyLoadListener implements LoadEventListener {


// éste es el único método definido por la interfaz LoadEventListener
public void onLoad(LoadEvent event, LoadEventListener.LoadType loadType)
throws HibernateException {
if ( !MySecurity.isAuthorized( event.getEntityClassName(), event.getEntityId() ) ) {
throw MySecurityException("Unauthorized access");
}
}
}

También se necesita una entrada de configuración, indicándole a Hibernate que use este listener, además del listener por
defecto.

<hibernate-configuration>

<session-factory>
...
<event type="load">
<listener class="com.eg.MyLoadListener"/>
<listener class="org.hibernate.event.def.DefaultLoadEventListener"/>
</event>
</session-factory>

</hibernate-configuration>

O se lo puede registrar programáticamente:

Configuration cfg = new Configuration();


LoadEventListener[] stack = { new MyLoadListener(), new DefaultLoadEventListener() };
cfg.EventListeners().setLoadEventListeners(stack);

Los listeners registrados declarativamente no pueden compartir instancias. Si la misma clase es usada por muchos
elementos <listener/>, cada referencia resultará en una instancia separada de esa clase. Se se necesita la capacidad de
compartir instancias de listener entre distintos tipos de listener, se debe usar la forma programática de registrarlos.

¿Por qué impementar una interfaz y también definir un tipo específico durante la configuración? Porque una
implementación de listener puede estar implementando más de una interfaz. Al tener que especificar también el tipo
durante el registro, se vuelve fácil habilitar/inhanilitar los listeners a medida durante la cofiguración.

12.3. Seguridad declarativa de Hibernate

129 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

La seguridad declarativa de Hibernate usualmente se maneja en una capa de "fachada de sesión" (session façade). A partit
de Hibernate3, a algunas acciones se les puede asignar permisos vía JACC, y se pueden autorizar vía JAAS. Ésta es una
funcionalidad opcional, construida encima de la arquitectura de eventos.

Primero se deben configurar los listeners de eventos, para habilitar el uso de autorizaciones JAAS.

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

Note que <listener type="..." class="..."/> es simplemente una forma abreviada de <event type="...">
<listener class="..."/></event> para cuando hay exactamente un listener para un tipo de evento en particular.

Luego, todavía en hibernate.cfg.xml, se vinculan los permisos a roles:

<grant role="admin" entity-name="User" actions="insert,update,read"/>

<grant role="su" entity-name="User" actions="*"/>

Los nombres de los roles son aquéllos que sean comprendidos por su proveedor JACC.

Capítulo 13. Procesamiento en lotes


Un abordaje ingenuo al problema de insertar 100.000 filas en una base de datos podría verse así:

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();

Esto fallaría. provocando un OutOfMemoryException más o menos alrededor de la línea número 50.0000. Esto se debe a
que Hibernate guarda todas las instancias de Customer en el caché de sesión a medida que las va insertando.

En este capítulo le mostramos cómo evitar este problema. Pero primero, si se está usando procesamiento en lotes (en
inglés, "batch processing"), es indispensable habilitar el uso del procesamiento en lotes JDBC, si se pretende alcanzar una
performance razonable. Asígnele un valor razonable al tamaño del lote JDBC, digamos, de 10 a 50.

hibernate.jdbc.batch_size 20

Note que Hibernate inhabilita la inserción por lotes a nivel de JDBC en forma transparente si se está usando un generador
de identificadores identiy.

Tal vez se quiera realizar este tipo de trabajo en un proceso en donde la interacción con el caché de 2do nivel esté
completamente inhabilitada:

hibernate.cache.use_second_level_cache false

Pero esto no es absolutamente necesario, dado que se puede inhabilitar el CacheMode específicamente, para anular la
interacción con el caché de 2do nivel.

13.1. Inserciones en lotes


Cuando se persisten los objetos, se debe efectuar el flush() de la sesión, y luego clear() con regularidad, para controlar
el tamaño del caché de 1er nivel.

Session session = sessionFactory.openSession();


Transaction tx = session.beginTransaction();

130 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

for ( int i=0; i<100000; i++ ) {


Customer customer = new Customer(.....);
session.save(customer);
if ( i % 20 == 0 ) { //20, igual que el tamaño del lote JDBC
//aplicarle "flush" a un lote de inserts y liberar memoria
session.flush();
session.clear();
}
}

tx.commit();
session.close();

13.2. Actualizaciones en lotes


Para capturar y modificar data se aplican las mismas ideas. Adicionalmente, se necesita usar scroll() para tomar ventaja
de los cursores que existan del lado del servidor de BD, para consultas que devuelvan muchas filas de datos.

Session session = sessionFactory.openSession();


Transaction tx = session.beginTransaction();

ScrollableResults customers = session.getNamedQuery("GetCustomers").setCacheMode(CacheMode.IGNORE).


int count=0;
while ( customers.next() ) {
Customer customer = (Customer) customers.get(0);
customer.updateStuff(...);
if ( ++count % 20 == 0 ) {
//aplicarle "flush" a un lote de actualizaciones y liberar memoria
session.flush();
session.clear();
}
}

tx.commit();
session.close();

13.3. La interfaz StatelessSession


Alternativamente, Hibernate provee una API orientada a comandos, que puede ser usada por datos que fluyan desde y
hacia la base de datos en forma de objetos desprendidos. Una StatelessSession (sesión sin estado) no tiene un contexto
de persistencia asociado ni provee mucha de la semántica de alto nivel respecto del ciclo de vida, que sí proveen las
sesiones normales; en particular, no tiene un caché de 1er nivel ni interactúa con un caché de 2do nivel. No implementa un
write-behind transaccional ni chequeo automático de datos "sucios". Las operaciones que se efectúen usando una sesión
sin estado nunca se propagan en cascada. Las sesiones sin estado ingoran las colecciones y los modelos de eventos e
interceptores. También son vulnerables a los efectos de "data aliasing", dado que carecen de caché de 1er nivel. Una
sesión sin estado es una abstracción de más bajo nivel, muy cercana a la JDBC subyacente.

StatelessSession session = sessionFactory.openStatelessSession();


Transaction tx = session.beginTransaction();

ScrollableResults customers = session.getNamedQuery("GetCustomers")


.scroll(ScrollMode.FORWARD_ONLY);
while ( customers.next() ) {
Customer customer = (Customer) customers.get(0);
customer.updateStuff(...);
session.update(customer);
}

tx.commit();
session.close();

Note que, en este ejemplo de código, las instancias de Customer (cliente) devueltas por la consulta son automáticamente
desprendidas. Nunca están asociadas con ningún contexto de persistencia.

Se considera que las operaciones insert(), update() y delete() definidas por la interfaz StatelessSession son
operaciones directamente a nivel de fila de la base de datos, lo cual resulta en la ejecución inmediata de un SQL INSERT,
UPDATE o DELETE, respectivamente. Por esto, su semántica es muy diferente de las operaciones save(),
saveOrUpdate() y delete() definidas por la interfaz Session.

131 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

13.4. Operaciones del tipo "Lenguaje de Modificacion de Datos" (DML-style)


Como se discutió anteriormente, el mapeo automático objeto/relacional automático y transparente se ocupa de la
administración del "estado" de los objetos. Esto significa que el estado de un objeto dado está disponible en memoria, por
lo que manipular la BD directamente usando comandos SQL del tipo "operaciones de modificación de datos" (data
modification language o DML por sus siglas en inglés: es decir, INSERT, UPDATE, DELETE) no afectará el estado en
memoria. De todos modos, el lenguaje HQL de Hibernate provee métodos para este tipo de ejecución de comandos de
modificación de datos, llamada "modificación de datos en masa" o "bulk-style DML". Véase HQL.

La pseudo-sintaxis para os comandos UPDATE y DELETE es: ( UPDATE | DELETE ) FROM? NombreDeLaEntidad (WHERE
where_conditions)?. Algunos puntos a destacar:

en la cláusula "from", la palabra FROM es optativa

Sólo se puede nombrar una entidad en la cláusula "from"; opcionalmente, ésta puede tener un alias. Si lo tiene,
entonces cualquier referencia a propiedades debe estar calificada usando dicho alias. Si la entidad no tiene alias,
entonces es ilegal que las propiedades estén calificadas.

En estas consultas HQL de "modificación en masa" no se puede especificar ningún joins (ni implícito, ni explícito).
Sí se pueden usar subconsultas (subqueries) en la cláusula "where", y estas subconsultas sí pueden contener joins.

La cláusula "where" también es optativa.

A modo de ejemplo: para ejecutar un HQL UPDATE, use el método Query.executeUpdate() (el método se bautizó en
honor al método de JDBC PreparedStatement.executeUpdate()):

Session session = sessionFactory.openSession();


Transaction tx = session.beginTransaction();

String hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName";
// o también: 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();

Por defecto, los comandos UPDATE, no afectan las propiedades version ni timestamp de las entidades involucradas; esto es
consistente con la especificación de EJB3. Sin embargo, se puede forzar a Hibernate a reinicializar adecuadamente los
valores de las propiedades version y timestamp usando un a "actualización versionada" (versioned update). Esto se
logra agregando la palabra VERSIONED luego de UPDATE keyword.

Session session = sessionFactory.openSession();


Transaction tx = session.beginTransaction();
String hqlVersionedUpdate = "update versioned Customer set name = :newName where name = :oldName";
int updatedEntities = s.createQuery( hqlUpdate )
.setString( "newName", newName )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();

Note que los tipos de versión a medida (org.hibernate.usertype.UserVersionType) no se permiten en conjunción con
comandos update versioned.

Para ejecutar un HQL DELETE, use el mismo método Query.executeUpdate():

Session session = sessionFactory.openSession();


Transaction tx = session.beginTransaction();

String hqlDelete = "delete Customer c where c.name = :oldName";


// o también: String hqlDelete = "delete Customer where name = :oldName";
int deletedEntities = s.createQuery( hqlDelete )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();

132 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

El valor int devuelto por el método Query.executeUpdate() indica el número de entidades afectadas por la operación.
Considere que esto puede corresponder o no con el número de filas afectadas en la base de datos. Una operación HQL en
masa puede resultar en la ejecución de múltiples comandos SQL (para las joined-subclass, por ejemplo). El número
devuelto indica el número real de entidades afactadas por el comando. Volviendo al ejemplo de la joined-subclass, un
comando DELETE ejecutado contra una de las subclases podría resultar en borrados no sólo en la tabla a la cual la
subclase está mapeada, sino también borrados en la tabla "raíz", y potencialmente en otras tablas de joined-subclass más
abajo en la jerarquía de herencias.

La pseudo-sintaxis de los comandos INSERT es: INSERT INTO NombreDeLaEntidad lista_de_propiedades


comando_select.

Algunos puntos a destacar:

Sólo se soporta la forma INSERT INTO ... SELECT ... ; la forma INSERT INTO ... VALUES ... no.

La especificación de la lista_de_propiedades es análoga a la especificación de columnas en un comando SQL


INSERT. Para entidades que estén involucradas en una herencia mapeada, sólo las propiedades que estén definidas
directamente en ese nivel de clase en particular pueden ser usadas en la lista_de_propiedades: las propiedades de la
superclase no están permitidas, y las de subclases no tienen sentido. En otras palabras, los comandos INSERT son
inherentemente polimórficos.

el comando_select puede ser cualquier consulta válida de selección HQL, con la precaución de que los tipos de
retorno deben corresponder con los tipos esperados por el insert. Actualmente, esto es verificado durante la
compilación, en lugar de relegar dicho chequeo a la base de datos. Note, sin embargo, que esto podría causar
problemas entre tipos que Hibernate considere o no "equivalentes" más que "iguales". Por ejemplo, para Hibernate
no son iguales los tipos org.hibernate.type.DateType y org.hibernate.type.TimestampType, aunque la base
de datos no diferencie entre ambos o sea capaz de manejar la conversión entre ambos.

En relación a la propiedad id, el comando de inserción ofrece dos opciones. Se puede o bien especificarla
explícitamente en la lista_de_propiedades (en cuyo caso el valor se toma del comando_select) o se puede omitir de
la lista de propiedades (en cuyo caso se usa un valor generado). Esta última opción está disponible solamente
cuando se usen generadores de id que operen dentro de la base de datos, intentar usarla con generadores del tipo
"residente en memoria" causará una excepción durante el parsing del comando. Para los efectos de esta discusión,
se consideran "generadores residentes en memoria" org.hibernate.id.SequenceGenerator (y sus subclases), y
cualquier implementación de org.hibernate.id.PostInsertIdentifierGenerator.
org.hibernate.id.TableHiLoGenerator tampoco se puede usar, porque no expone una manera facitble de
obtener sus valores en un comando "select".

Para las propiedades mapeadas como version o timestamp, el comando INSERT da dos opciones: se puede o bien
especificarlas explícitamente en la lista_de_propiedades (en cuyo caso el valor se toma del comando_select) o se las
puede puede omitir de la lista de propiedades (en cuyo caso se usa el valor semilla o "seed value" definido por
org.hibernate.type.VersionType).

Un ejemplo de ejecución del comando HQL INSERT:

Session session = sessionFactory.openSession();


Transaction tx = session.beginTransaction();

String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c wh
int createdEntities = s.createQuery( hqlInsert ).executeUpdate();
tx.commit();
session.close();

Capítulo 14. HQL: El lenguaje de consultas de Hibernate


Hibernate está equipado con un lenguaje de consultas extremadamente potente: HQL. Aunque cuenta con una sintaxis
deliberadamente parecida a SQL, HQL es totalmente orientado a objetos, capaz de entender nociones como herencia,
polimorfismo y asociación.

14.1. Relevancia de mayúsculas y minúsculas


En las consultas, el uso de mayúsculas o minúsculas es irrelevante, excepto por los nombres de propiedades y clases de
Java. De modo que SeLeCT es lo mismo que sELEct y lo mismo que SELECT, pero org.hibernate.eg.FOO no es lo

133 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

mismo que org.hibernate.eg.Foo, y foo.barSet no es lo mismo que foo.BARSET.

Este manual usa minúsculas para las palabras HQL. A algunos usuarios les parece que las consultas escritas todas en
mayúsula son más legibles, pero a nosotros esta convención nos parece fea cuando está inserta en código Java.

14.2. La cláusula "from"


La consulta de Hibernate más simple tiene esta forma:

from eg.Cat

la cual simplemente devuelve todas las instancias de la clase eg.Cat.


Normalmente no necesitamos calificar el nombre de la clase de esta manera, dado que el valor por defecto es
auto-import. Así que casi siempre escribimos:

from Cat

La mayoría de las veces hará falta asignarle un alias, dado que querremos referirnos a Cat en otras partes de la consulta:

from Cat as cat

Esta consulta le asigna el alias cat a las instancias de Cat, de manera que podamos usar dicho alias más adelante en la
consulta. La palabra as es optativa, también podríamos escribir:

from Cat cat

Pueden aparecer varias clases, resultando en un producto cartesiano o "cross join".

from Formula, Parameter

from Formula as form, Parameter as param

Se considera una práctica buena el nombrar los alias en las consultas usando una minúscula inicial, en concordancia con
los estándares de nombrado para variables locales en Java. (por ejemplo domesticCat).

14.3. Asociaciones y "joins"


Se les puede asignar alias a entidades asociadas, o incluso a elementos de una colección de valores, usando la palabra
join.

from Cat as cat


inner join cat.mate as mate
left outer join cat.kittens as kitten

from Cat as cat left join cat.mate.kittens as kittens

from Formula form full join form.parameter param

Los tipos de join son un préstamo de la especificación ANSI de SQL.


inner join

left outer join

right outer join

full join (el cual normalmente no es muy útil)

Las frases inner join, left outer join and right outer join pueden abreviarse así:

from Cat as cat


join cat.mate as mate

134 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

left join cat.kittens as kitten

Y se pueden especificar condiciones adicionales del join, usando la palabra with:

from Cat as cat


left join cat.kittens as kitten
with kitten.bodyWeight > 10.0

Adicionalmente, un join calificado con la palabra "fetch" (captura) permite que las asociaciones o colecciones de valores
sean inicializados junto con sus objetos padres, usando un solo SELECT. Esto es particularmente útil en el caso de las
colecciones; efectivamente reemplaza los "outer joins" y las inicializaciones haraganas (lazy) del archivo de mapeo para
asociaciones y colecciones. Vea la Sección 19.1, “Estrategias de captura (fetch)” para más información.

from Cat as cat


inner join fetch cat.mate
left join fetch cat.kittens

A un join del tipo "fetch" normalmente no se le asignan alias, porque los objetos asociados que devuelve no deberían ser
usados en la cláusula "where", ni en ninguna otra cláusula. Además, los objetos asociados no son devueltos directamente
en el resultado de la consulta; en cambio, se puede acceder a ellos a través del objeto padre. La única razón por la que se
podría necesitar un alias, es si se está usando un join tipo "fetch" recursivo a otra colección más.

from Cat as cat


inner join fetch cat.mate
left join fetch cat.kittens child
left join fetch child.kittens

Note que la construcciones con fetch no deben ser usadas en consultas que luego invoquen iterate() (aunque sí se
puede con scroll()). Tampoco debería usarse fetch con consultas que usen setMaxResults() o setFirstResult()
(los métodos de paginación), dado que dichas operaciones se basan en el número de filas del resultado, el cual
normalmente contendrá duplicados debido a esta "captura ansiosa", y por lo tanto la cantidad de filas no será la que se
espera. Tampoco se debe usar fetch junto con condiciones "with" ad hoc. Al efectuar un join tipo "fetch" con más de una
colección, es posible crear un producto cartesiano, así que tenga cuidado en este caso. Usar joins "fetch" con múltiples
colecciones, además, da a menudo resultados inesperados con los mapeos de "bag", así que tenga cuidado acerca de cómo
formula sus consultas en este caso. Por último, note que las construcciones full join fetch y right join fetch no
tienen sentido.

Si se está usando captura haragana (lazy fetching) a nivel de las propiedades, con instrumentación bytecode, es posible
forzar a Hibernate para que capture esas propiedades haraganas inmediatamente en la primera consulta, usando fetch
all properties.

from Document fetch all properties order by name

from Document doc fetch all properties where lower(doc.name) like '%cats%'

14.4. Formas de la sintaxis de los "joins"


HQL soporta dos formas de asociación por "joins": implícita y explícita.

Las consultas mostradas en la sección anterior usan todas la forma explícita en donde la palabra "join" es
explícitamente usada en la cláusula "from". Ésta es la forma que se recomienda.

La forma implícita no usa la palabra "join". En lugar de eso, las asociaciones son "des-referidas" (dereferenced) usando
la notación de puntos. Los joins implícitos pueden aparecer en cualquiera de las cláusulas HQL (select, from, where).
Un join implícito se traduce en "inner joins" en el comando SQL resultante.

from Cat as cat where cat.mate.name like '%s%'

14.5. Referirse a la propiedada identificadora


En general, hay 2 maneras de referirse a la propiedad indentificadora de una entidad:

135 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

La propiedad especial id puede ser usada para referirse a la propiedad indentificadora de una entidad siempre y
cuando dicha entidad no haya definido otra propiedad no-identificadora también llamada "id".

Si la entidad define una propiedad indentificadora con nombre, se puede usar dicho nombre.

Las referencias a identificadores compuestos siguen las mismas relgas. Si la entidad tiene una propiedad no-identificadora
llamda "id", la propiedad identificadora compuesta sólo puede ser referida por el nombre asignado. En caso contrario, se
puede usar la propiedad especial id para referirse a la propiedad identificadora.

Nota: esto ha cambiado significativamente a partir de la versión 3.2.2. En versiones anteriores, id siempre hacía alusión a
la propiedad identificadora, sin importar el verdadero nombre. A consecuencia de esto, cuando había propiedades
no-identificadoras llamadas id, las consultas no podían referirse a ellas.

14.6. La cláusula "select"


La cláusula select elige qué objetos y propiedades serán devueltos como conjunto de resultados o "resultset". Sea:

select mate
from Cat as cat
inner join cat.mate as mate

Esta consulta seleccionará los mates (en inglés, "compañeros") de otros Cats. En realidad, se puede expresar esta consulta
de una forma más compacta, como:

select cat.mate from Cat cat

Las consultas pueden devolver propiedades de cualquier tipo de valor, incluidos valores de tipo "componente":

select cat.name from DomesticCat cat


where cat.name like 'fri%'

select cust.name.firstName from Customer as cust

Las consultas pueden devolver múltiples objetos, en un array Object[],

select mother, offspr, mate.name


from DomesticCat as mother
inner join mother.mate as mate
left outer join mother.kittens as offspr

o como una List,

select new list(mother, offspr, mate.name)


from DomesticCat as mother
inner join mother.mate as mate
left outer join mother.kittens as offspr

o como un verdadero objeto Java, seguro en cuanto a tipo (typesafe)

select new Family(mother, mate, offspr)


from DomesticCat as mother
join mother.mate as mate
left join mother.kittens as offspr

asumiendo que la clase Family tuviere el constructor apropiado

Se les pueden asignar alias a las expresiones seleccionadas usando as:

select max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n


from Cat cat

Esto es de lo más util cuando se usa en conjunción con select new map:

136 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

select new map( max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n )


from Cat cat

Esta consulta devuelve un Map de los alias a los valores seleccionados.

14.7. Funciones agregadas


Las consultas HQL incluso pueden devolver los resultados de funciones agregadas que fueran aplicadas a propiedades:

select avg(cat.weight), sum(cat.weight), max(cat.weight), count(cat)


from Cat cat

Las funciones agregadas que se soportan son:


avg(...), sum(...), min(...), max(...)

count(*)

count(...), count(distinct ...), count(all...)

En la cláusula "select" se pueden usar operadores aritméticos, concatenación, y funciones SQL reconocidas:

select cat.weight + sum(kitten.weight)


from Cat cat
join cat.kittens kitten
group by cat.id, cat.weight

select firstName||' '||initial||' '||upper(lastName) from Person

Las palabras distinct y all pueden ser usadas, y con la misma semántica que en SQL.

select distinct cat.name from Cat cat

select count(distinct cat.name), count(cat) from Cat cat

14.8. Consultas polimórficas


Una consulta como:

from Cat as cat

devuelve no sólo instancias de Cat, sino también de las subclases como DomesticCat. Las consultas de Hibernate pueden
nombrar cualquier clase o interfaz Java en la cláusula from. La consulta devolverá las instancias de todas las clases
persistentes que extiendan o implementen dicha clase o interfaz. La siguiente consuta devuelve todos los objetos
persistentes:

from java.lang.Object o

Sea una interfaz Named, implementada por varias clases persistentes:

from Named n, Named m where n.name = m.name

Note que las dos últimas consultas requerirán más de dos comandos SQL SELECT. Esto implica que un cláusula order by
no ordenaría correctamente la totalidad del conjunto de resultados, y que no se puede usar Query.scroll() para
navegarlos.

14.9. la cláusula "where"


La cláusula where permite acotar la lista de instancias devueltas. Si no hay alias, uno puede referirse a las propiedades por
su nombre:

137 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

from Cat where name='Fritz'

si hay un alias, úsese un nombre de propiedad calificado:

from Cat as cat where cat.name='Fritz'

devuelve las instancias de gatos llamados 'Fritz'.

select foo
from Foo foo, Bar bar
where foo.startDate = bar.date

devolverá las instancias de Foo para las cuales exista una instancia de Bar cuya propiedad date sea igual a la propiedad
startDate de Foo. Las expresiones con "path" compuesto hacen que la cláusula "where" sea extremadamente poderosa.
Considere:

from Cat cat where cat.mate.name is not null

Esta consulta se traduciría en un comando SQL con varios (inner) joins. Si se escribiera algo como esto:

from Foo foo


where foo.bar.baz.customer.address.city is not null

en SQL se acabaría con una consulta que requeriría 4 joins de tablas.

El operador = puede usarse para comparar no sólo propiedades, sino también instancias:

from Cat cat, Cat rival where cat.mate = rival.mate

select cat, mate


from Cat cat, Cat mate
where cat.mate = mate

La propiedad id (en minúscula), puede usarse para hacer referencia al identificador único de un objeto. Véase la
Sección 14.5, “Referirse a la propiedad identificadora” para más información.

from Cat as cat where cat.id = 123

from Cat as cat where cat.mate.id = 69

La segunda consulta es eficiente, no requiere joins.

También se pueden usar propiedades de los identificadores compuestos. Supongamos que Person tiviera un identificador
compuesto que consistiese en country y medicareNumber. (de nuevo, véase la Sección 14.5, “Referirse a la propiedad
identificadora” para más información acerca de referirse a las propiedades identificadoras):

from bank.Person person


where person.id.country = 'AU'
and person.id.medicareNumber = 123456

from bank.Account account


where account.owner.id.country = 'AU'
and account.owner.id.medicareNumber = 123456

Nuevamente, la segunda consulta no requiere "joins"

Del mismo modo, la propiedad especial class accede al valor discriminador de una instancia, en caso de que se esté
usando persistencia polimórfica. Un nombre de clase de Java incrustado en la cláusula "where" será traducido como su
valor de discriminador.

from Cat cat where cat.class = DomesticCat

Tambiém se pueden usar componentes, o tipos a medida compuestos, o las propiedades de dichos tipos de
componentes/tipos. Vea la Sección 14.17, “Componentes” para más información.

138 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Un tipo "any" tiene las propiedades especiales id y class, que permiten expresar un join de la siguiente manera (en donde
AuditLog.item es una propiedad mapeada con <any>):

from AuditLog log, Payment payment


where log.item.class = 'Payment' and log.item.id = payment.id

Note que, en la consulta precedente, log.item.class y payment.class se estarían refiriendo a valores de columnas
completamente diferentes de la base de datos.

14.10. Expresiones
Las expresiones que se permiten en la cláusula where incluyen la mayoría de las que se podría escribir en SQL:

operadores matemáticos +, -, *, /

operadores de comparación binaria =, >=, <=, <>, !=, like

operaciones lógicas and, or, not

paréntesis ( ), para indicar agrupamientos

in, not in, between, is null, is not null, is empty, is not empty, member of and not member of

la forma de "case" simple, "Simple" case, case ... when ... then ... else ... end, y la forma de "case"
llamada "searched", case when ... then ... else ... end

concatención de cadenas ...||... or concat(...,...)

current_date(), current_time(), current_timestamp()

second(...), minute(...), hour(...), day(...), month(...), year(...),

Cualquier función u operador definido por EJB-QL 3.0: substring(), trim(), lower(), upper(), length(),
locate(), abs(), sqrt(), bit_length(), mod()

coalesce() y nullif()

str() para convertir valores numéricos o temporales en una cadena legible

cast(... as ...), en donde el segundo argumento es el nombre de un tipo de Hibernate, y extract(... from
...) si la base de datos subyacente soporta las funciones ANSI cast() y extract().

la función HQL index(), que se aplica a los alias de una coleción asociada indexada.

funciones HQL que aceptan expresiones tipo "path" con valor de colección: size(), minelement(),
maxelement(), minindex(), maxindex(), junto con las funciones especiales elements() e indices, las cuales
pueden ser cuantificadas usando some, all, exists, any, in.

cualqiuer función escalar SQL soportada por la base de datos, como sign(), trunc(), rtrim(), sin()

parámetros ppsicionales al estilo JDBC

parámetros nombrados: :name, :start_date, :x1

expresiones SQL literales 'foo', 69, 6.66E+2, '1970-01-01 10:00:01.0'

constantes public static final de java eg.Color.RAYADO

in y between pueden ser usados como sigue:

from DomesticCat cat where cat.name between 'A' and 'B'

from DomesticCat cat where cat.name in ( 'Foo', 'Bar', 'Baz' )

y la forma negada puede ser escrita:

139 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

from DomesticCat cat where cat.name not between 'A' and 'B'

from DomesticCat cat where cat.name not in ( 'Foo', 'Bar', 'Baz' )

Del mismo modo. is null y is not null pueden ser usadas para chequear valores nulos.

Se puede usar fácilmente valores booleanos, declarando sustituciones de consulta HQL en la configuración de Hibernate:

<property name="hibernate.query.substitutions">true 1, false 0</property>

Esto reemplazará las palabras true y false con los valores literales 1 y 0 en el SQL traducido desde este HQL.

from Cat cat where cat.alive = true

Se puede chequear el tamaño de la colección con la propiedad especial size, o con la función especial size().

from Cat cat where cat.kittens.size > 0

from Cat cat where size(cat.kittens) > 0

Para las colecciones indexadas, es preferible referirse a los valores mínimos y máximos usando las funciones minindex y
maxindex. Análogamente, se puede aludir a los elementos mínimo y máximo de una colección de un tipo básico utilizando
las funciones minelement y maxelement.

from Calendar cal where maxelement(cal.holidays) > current_date

from Order order where maxindex(order.items) > 100

from Order order where minelement(order.items) > 10000

Las funciones SQL any, some, all, exists, in se soportan cuando se les pasa como parámetro el conjunto de
elementos o índices de una colección (éstas son las funciones elements e indices), o el resultado de una subconsulta
(véase abajo).

select mother from Cat as mother, Cat as kit


where kit in elements(foo.kittens)

select p from NameList list, Person p


where p.name = some elements(list.names)

from Cat cat where exists elements(cat.kittens)

from Player p where 3 > all elements(p.scores)

from Show show where 'fizard' in indices(show.acts)

Note que las construcciones: size, elements, indices, minindex, maxindex, minelement, maxelement sólo pueden ser
usadas en la cláusula "where" en Hibernate3

Uno se puede referir a los elementos en las colecciones indexadas (arrays, lists, maps) por índice (sólo en la clausula
"where").

from Order order where order.items[0].id = 1234

select person from Person person, Calendar calendar


where calendar.holidays['national day'] = person.birthDay
and person.nationality.calendar = calendar

select item from Item item, Order order


where order.items[ order.deliveredItemIndices[0] ] = item and order.id = 11

select item from Item item, Order order

140 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

where order.items[ maxindex(order.items) ] = item and order.id = 11

La expresión dentro de [] puede incluso ser una expresión aritmética.

select item from Item item, Order order


where order.items[ size(order.items) - 1 ] = item

HQL también trae una función index() ya incorporada, para elementos de una asociación de-uno-a-muchos, o una
colección de valores.

select item, index(item) from Order order


join order.items item
where index(item) < 5

Pueden ser usadas las funciones SQL escalares que la DB subyacente soporte:

from DomesticCat cat where upper(cat.name) like 'FRI%'

Si todo esto aún no lo ha convencido, piense cuánto más largo y menos legible habría sido esta consulta en SQL:

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)

Para que se dé una idea, algo como:

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
)

14.11. La cláusula "order by"


La lista devuelta por una consulta puede ser ordenada por cualquier propiedad de las clases o componentes devueltos:

from DomesticCat cat


order by cat.name asc, cat.weight desc, cat.birthdate

Las palabras opcionales asc o desc indican orden ascendente o descendente respectivamente.

14.12. La cláusula "group by"


Una consulta que devuelva valores agregados, puede ser agrupada por cualquier propiedad de las clases o componentes
devueltos.

select cat.color, sum(cat.weight), count(cat)


from Cat cat
group by cat.color

141 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

select foo.id, avg(name), max(name)


from Foo foo join foo.names name
group by foo.id

También se permite la cláusula having:

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)

si la base de datos subyacente lo soporta (por ejemplo, MySQL no), dentro de having se permiten funciones SQL, y
dentro del order by se permiten funciones agregadas.

select cat
from Cat cat
join cat.kittens kitten
group by cat.id, cat.name, cat.other, cat.properties
having avg(kitten.weight) > 100
order by count(kitten) asc, sum(kitten.weight) desc

Note que ni la cláusula group by ni la order by pueden contener expresiones aritméticas. Y también que Hibernate no
"expande" una entidad agrupada: así que no se puede escribir group by cat si todas las propiedades de cat son
no-agregadas; hay que listar todas las propiedades no-agregadas explícitamente.

14.13. Subconsultas
Para las base de datos que soporten subconsultas (subqueries), Hibernate soporta subconsultas dentro de una consulta.
Una subconsulta debe estar rodeada por paréntesis (a menudo por un llamado a una función SQL agregada). Incluso se
permiten subconsultas correlacionadas (subconsultas que hagan referencia a un alias en la consulta exterior.

from Cat as fatcat


where fatcat.weight > (
select avg(cat.weight) from DomesticCat cat
)

from DomesticCat as cat


where cat.name = some (
select name.nickName from Name as name
)

from Cat as cat


where not exists (
from Cat as mate where mate.mate = cat
)

from DomesticCat as cat


where cat.name not in (
select name.nickName from Name as name
)

select cat.id, (select max(kit.weight) from cat.kitten kit)


from Cat as cat

Note que las subconsultas HQL pueden ocurrir sólo en las cláusulas "select" o "where".

Note que las subconsultas también pueden utilizar sintaxis de constructor del valor de fila (row value constructor).
Vea la Sección 14.18, “Constructor del valor de fila” para más detalles.

14.14. Ejemplos de HQL


Las consultas en Hibernate pueden llegar a ser bastante potentes y complejas. De hecho, el poder del lenguaje de consultas
es una de las características más "vendedoras" de Hibernate. He aquí algunas consultas de ejemplo, muy similares a las
que he usado en un proyecto reciente. ¡Note que la mayoría de las consultas que usted escribirá serán mucho más simples
que éstas!

142 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

La siguiente consulta devuelve el id de la orden, el número de items y el valor total de todas las órdenes impagas por un
cliente en particular, y dado un valor total mínimo, ordenando los resultados por valor total. Para determinar los precios,
usa el catálogo actual. El SQL resultante, efectuado contra las tablas ORDER, ORDER_LINE, PRODUCT, CATALOG y PRICE
tiene 4 "inner joins" y una subconsulta (no correlacionada).

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

¡Qué monstruo! En realidad, en la vida real no soy muy amigo de las subconsultas, así que mi consulta quedó más bien de
esta manera:

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

La consulta siguiente cuenta el número de pagos en cada estado, exceptuando los pagos que figuren como "aprobación
pendiente" (AWAITING_APPROVAL) en donde el cambio de estado más reciente haya sido efectuado por el usuario actual.
Se traduce en una consulta SQL con 2 inner joins y una subconsulta correlacionada, contra las clases PAYMENT,
PAYMENT_STATUS y PAYMENT_STATUS_CHANGE.

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

Si yo hubiera mapeado la colección statusChanges collection como una lista, la consulta habría sido mucho más fácil de
escribir.

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

143 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

La siguiente consulta usa la función isNull() de MS SQL Server para devolver todas las cuentas y pagos no efectuados a
la organización a la cual pertenece el usuario actual. Se traduce en un SQL con tres inner joins, un outer join y un
subselect contra las tablas the ACCOUNT, PAYMENT, PAYMENT_STATUS, ACCOUNT_TYPE, ORGANIZATION y ORG_USER.

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

En algunas DB, debemos deshacernos del subselect correlacionado.

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

14.15. Actualizaciones y borrados en masa


HQL ahora soporta comandos update, delete e insert ... select .... Vea la Sección 13.4, “Operaciones del tipo
"Lenguaje de Modificacion de Datos" (DML-style)” para más detalles.

14.16. Consejos y trucos


Se puede contar el número de resultados de una consulta sin necesidad de devolver todo el resultado:

( (Integer) session.createQuery("select count(*) from ....").iterate().next() ).intValue()

Para ordenar un resultado de acuerdo al tamaño de las colecciones de hijos, use la consulta siguiente:

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)

Si su base de datos soporta subselects, se puede poner una condición en cuanto al tamaño de la selección, en la cláusula
"where" de su consulta.

from User usr where size(usr.messages) >= 1

si su base de datos no soporta subconsultas, use la consulta siguiente:

select usr.id, usr.name


from User usr.name
join usr.messages msg
group by usr.id, usr.name
having count(msg) >= 1

Como esta solución no puede devolver a un User que tenga 0 mensajes a causa del inner join, la siguiente forma también
es útil:

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

Se pueden asociar las propiedades de un JavaBean a parámetros nombrados de una consulta:

Query q = s.createQuery("from foo Foo as foo where foo.name=:name and foo.size=:size");

144 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

q.setProperties(fooBean); // fooBean has getName() and getSize()


List foos = q.list();

Las colecciones se pueden volver paginables, usando la interfaz Query más un filtro:

Query q = s.createFilter( collection, "" ); // el filtro trivial


q.setMaxResults(PAGE_SIZE);
q.setFirstResult(PAGE_SIZE * pageNumber);
List page = q.list();

Los elementos de las colecciones pueden ser ordenados o agrupados usando un filtro de consultas:

Collection orderedCollection = s.filter( collection, "order by this.amount" );


Collection counts = s.filter( collection, "select this.type, count(this) group by this.type" );

Se puede averiguar el tamaño de una colección sin inicializarla:

( (Integer) session.createQuery("select count(*) from ....").iterate().next() ).intValue();

14.17. Componentes
En HQL, se pueden usar componentes, casi en cualquiera de las mismas formas en que se puede usar "value types"
simples. Pueden aparecer en la cláusula "select":

select p.name from Person p

select p.name.first from Person p

en donde la propiedad "name" de Person es un componente. Los componentes también pueden ser usados en cláusulas
"where":

from Person p where p.name = :name

from Person p where p.name.first = :firstName

Los componentes también pueden ser usados en cláusulas "order by":

from Person p order by p.name

from Person p order by p.name.first

Otro uso común de los componentes, es en los constructores de valor de fila.

14.18. Sintaxis del "Constructor de Valor de Fila" (row value constructor)


HQL soporta el uso de la sintaxis de Constructores de Valor de Fila (row value constructors), a veces también
llamada sintaxis de "t-upla", incluso aunque la base de datos subyacente no soporte este concepto. Nos estamos refiriendo
a a comparaciones multi-valores, típicamente asociadas con componentes. Considérese una entidad "Person", que define
un componente "name":

from Person p where p.name.first='John' and p.name.last='Jingleheimer-Schmidt'

Esta sintaxis es válida, aunque un tanto locuaz. Sería bueno poder hacerla un poco más concisa y usar la sintaxis de
"constructor de valor de fila":

from Person p where p.name=('John', 'Jingleheimer-Schmidt')

También sería útil especificar esto en la cláusula "select":

select p.name from Person p

145 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Otro momento en que la sintaxis de constructor de valor de fila puede ser beneficiosa, es cuando se usen
subconsultas que necesiten comparar contra valores múltiples:

from Cat as cat


where not ( cat.name, cat.color ) in (
select cat.name, cat.color from DomesticCat cat
)

Una cosa a considerar, al decidir si se quiere usar esta sintaxis, es que la consulta será dependiente del orden de las
sub-propiedades del componente en los metadatos.

Capítulo 15. Consultas Criteria


Hibernate posee una API para consultas "criteria" (por criterios), intuitiva y extensible.

15.1. Crear una instancia de Criteria


La interfaz org.hibernate.Criteria representa una consulta contra una clase persistente en particular. La sesión actúa
como fábrica de instancias de Criteria.

Criteria crit = sess.createCriteria(Cat.class);


crit.setMaxResults(50);
List cats = crit.list();

15.2. Acotar el resultado

(N.del.T):en inglés, "criterio" se dice "criterion", y en plural, es "criteria". La interfaz, muy usada, de Hibernate que
denomina a este tipo de consultas, se llama "Criteria". Una interfaz mucho menos conocida, "Criterion", en un paquete
del mismo nombre, casi nunca son usados directamente por el programador, porque le son agregados a un "Criteria"
ente bambalinas.

Una "criterion" individual es una instancia de la interfaz org.hibernate.criterion.Criterion. La clase


org.hibernate.criterion.Restrictions define métodos "fábrica" para obtener ciertos tipos de Criterion que ya
vienen incluidos.

List cats = sess.createCriteria(Cat.class)


.add( Restrictions.like("name", "Fritz%") )
.add( Restrictions.between("weight", minWeight, maxWeight) )
.list();

Las "Restrictions" (restricciones) pueden ser agrupadas lógicamente.

List cats = sess.createCriteria(Cat.class)


.add( Restrictions.like("name", "Fritz%") )
.add( Restrictions.or(
Restrictions.eq( "age", new Integer(0) ),
Restrictions.isNull("age")
) )
.list();

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();

Hay un buen rango de tipos de "criterion" que ya vienen incluidos (subclases de Restrictions), pero uno en especial
permite especificar SQL directamente:

146 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

List cats = sess.createCriteria(Cat.class)


.add( Restrictions.sqlRestriction("lower({alias}.name) like lower(?)", "Fritz%", Hibernate.STRI
.list();

El comodín {alias} será reemplazado con el alias de fila de la entidad consultada.

Un camino alternativo para obtener un "criterion" es obtenerlo a partir de una instancia de Property. Se puede crear una
Property invocando Property.forName().

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();

15.3. Ordenar el resultado


Se puede ordenar los resultados usando org.hibernate.criterion.Order.

List cats = sess.createCriteria(Cat.class)


.add( Restrictions.like("name", "F%")
.addOrder( Order.asc("name") )
.addOrder( Order.desc("age") )
.setMaxResults(50)
.list();

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();

15.4. Asociaciones
Se puede especificar constraints en entidades relacionadas, navegando a través de asociaciones usando
createCriteria().

List cats = sess.createCriteria(Cat.class)


.add( Restrictions.like("name", "F%") )
.createCriteria("kittens")
.add( Restrictions.like("name", "F%") )
.list();

note que el segundo createCriteria() devuelve una instancia nueva de Criteria, la cual se refiere a los elementos de
la colección kittens.

La siguiente forma alternativa es útil en ciertas circunstancias:

List cats = sess.createCriteria(Cat.class)


.createAlias("kittens", "kt")
.createAlias("mate", "mt")
.add( Restrictions.eqProperty("kt.name", "mt.name") )
.list();

(createAlias() no crea una instancia nueva de Criteria.)

Note que las colecciones de "kittens" (gatitos) contenidas en las instancias de Cat devueltas por las dos consultas
anteriores, ¡no están pre-filtradas por el Criteria! Si desea obrener sólo los "kittens" que cumplen con el Criteria, debe usar
un ResultTransformer.

List cats = sess.createCriteria(Cat.class)

147 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

.createCriteria("kittens", "kt")
.add( Restrictions.eq("name", "F%") )
.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP)
.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");
}

15.5. Captura dinámica de asociaciones


Se puede especificar semántica de asociaciones en tiempo de ejecución, usando setFetchMode().

List cats = sess.createCriteria(Cat.class)


.add( Restrictions.like("name", "Fritz%") )
.setFetchMode("mate", FetchMode.EAGER)
.setFetchMode("kittens", FetchMode.EAGER)
.list();

Esta consulta capturará tanto mate como kittens mediante un "outer join". Vea la Sección 19.1, “Estrategias de captura
(fetch)” para más información.

15.6. Consultas "Example"


La clase org.hibernate.criterion.Example permite construir consultas "criterion" a partir de una instancia dada.

Cat cat = new Cat();


cat.setSex('F');
cat.setColor(Color.BLACK);
List results = session.createCriteria(Cat.class)
.add( Example.create(cat) )
.list();

Las propiedades versión, identificadores y asociaciones son ignoradas. Por defecto, las propiedades con valor nulo son
excluidas.

Se puede ajustar cómo el Example se aplica:

Example example = Example.create(cat)


.excludeZeroes() //excluir las propiedades con valor cero
.excludeProperty("color") //excluir una propiedad llamada "color"
.ignoreCase() //efectuar comparaciones de cadenas sin importar mayúsculas/minúscul
.enableLike(); //usar "like' para las comparaciones de cadenas
List results = session.createCriteria(Cat.class)
.add(example)
.list();

Se puede incluso usar Examples para ubicar criteria en objetos asociados:

List results = session.createCriteria(Cat.class)


.add( Example.create(cat) )
.createCriteria("mate")
.add( Example.create( cat.getMate() ) )
.list();

15.7. Proyecciones, agregado y agrupamiento


La clase org.hibernate.criterion.Projections (proyecciones) es una fábrica de instancias de la clase Projection.
Le aplicamos una proyección a una consulta llamando a setProjection().

List results = session.createCriteria(Cat.class)


.setProjection( Projections.rowCount() )
.add( Restrictions.eq("color", Color.BLACK) )
.list();

148 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

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();

En una consulta de tipo Criteria, no es necesario que haya explícitamente "group by"s. Algunos tipos de proyección son
las denominadas proyecciones agrupadoras (grouping projections) las cuales aparecen en el SQL como cláusulas group
by.

Optativamente se le puede asignar un alias a la proyección, de manera que las Restrictions y los Orders se puedan referir a
él. He aquí dos maneras de hacer esto:

List results = session.createCriteria(Cat.class)


.setProjection( Projections.alias( Projections.groupProperty("color"), "colr" ) )
.addOrder( Order.asc("colr") )
.list();

List results = session.createCriteria(Cat.class)


.setProjection( Projections.groupProperty("color").as("colr") )
.addOrder( Order.asc("colr") )
.list();

Los métodos alias() y as() simplemente envuelven una instancia de Projection dentro de otra instancia de
Projection con alias. Como atajo, se puede asignar un alias al agregar la proyección a una lista de proyecciones:

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();

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();

También se pueda usar Property.forName() para expresar proyecciones:

List results = session.createCriteria(Cat.class)


.setProjection( Property.forName("name") )
.add( Property.forName("color").eq(Color.BLACK) )
.list();

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();

15.8. Consultas y subconsultas desprendidas (detached)

149 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

La clase DetachedCriteria permite crear una consulta por fuera del alcance de una sesión, y más tarde ejecutarla usando
alguna sesión arbitraria.

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();

Una DetachedCriteria también puede usarse para expresar una subconsulta. Instancias de Criterion que involucren
subconsultas pueden ser obtenidas via subconsultas o Property.

DetachedCriteria avgWeight = DetachedCriteria.forClass(Cat.class)


.setProjection( Property.forName("weight").avg() );
session.createCriteria(Cat.class)
.add( Property.forName("weight").gt(avgWeight) )
.list();

DetachedCriteria weights = DetachedCriteria.forClass(Cat.class)


.setProjection( Property.forName("weight") );
session.createCriteria(Cat.class)
.add( Subqueries.geAll("weight", weights) )
.list();

Incluso son posibles las subconsultas correlacionadas:

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();

15.9. Consultas por identificador natural


Para la mayoría de las consultas, incluidas las consultas Criteria, el caché de consultas no es muy eficiente, porque la
invalidación del caché ocurre con demasiada frecuencia. Pero hay un tipo especial de consulta con la cual se puede
optimizar el algoritmo de invalidación: la búsqueda por clave natural constante. En algunas aplicaciones, este tipo de
consulta ocurre frecuentemente. La API de Criteria posee disposiciones especiales para este caso de uso.

Primero se debe mapear la clave natural de su entidad usando <natural-id>, y habilitando el caché de 2do nivel.

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

Note que esta funcionalidad no ha sido concebida para usarse con entidades de clave natural mutable.

A continuación, hay que habilitar el caché de consultas de Hibernate.

Finalmente, Restrictions.naturalId() permite hacer un uso más eficiente del algoritmo de caché.

session.createCriteria(User.class)
.add( Restrictions.naturalId()
.set("name", "gavin")

150 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

.set("org", "hb")
).setCacheable(true)
.uniqueResult();

Capítulo 16. SQL nativo


También se puede expresar consultas en el dialecto SQL nativo de su base de datos. Esto es útil si se quiere utilizar
características particulares de una base de datos, como "hints" en las consultas, o la palabra CONNECT en Oracle. También
provee un estadio de migración claro desde una aplicación basada en SQL/JDBC hacia Hibernate.

Hibernate3 permite especificar SQL escrito a mano (incluyendo procedimientos almacenados o "stored procedures") para
todas las operaciones de creación, modificación, borrado y carga.

16.1. Usar un SQLQuery


La ejecución de SQL nativo se controla a través de la interfaz SQLQuery, la cual se obtiene invocando
Session.createSQLQuery(). A continuación se describe cómo usar esta API para consultar:

16.1.1. Consultas escalares

La consulta SQL más básica consiste en obtener una lista de valores escalares.

sess.createSQLQuery("SELECT * FROM CATS").list();


sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").list();

Estas dos consultas devolverán una List de arrays de Objects (Object[]) con valores escalares para cada columna de la
tabla CATS. Hibernate usará ResultSetMetadata para deducir el orden y tipo de los valores escalares devueltos.

Para evitar el gasto extra de llamar ResultSetMetadata, o simplemente para se más explícito acerca de qué se devuelve,
se puede usar addScalar().

sess.createSQLQuery("SELECT * FROM CATS")


.addScalar("ID", Hibernate.LONG)
.addScalar("NAME", Hibernate.STRING)
.addScalar("BIRTHDATE", Hibernate.DATE);

Esta consulta especificó:

la cadena SQL de la consulta

las columnas y tipos a devolver

Esto aún devolverá un array de Objetcs, pero ahora no usará ResultSetMetadata sino que obtendrá explícitamente las
columnas ID, NAME and BIRTHDATE como un Long, una String y un Short, respectivamente, a partir del resultado
subyacente. Esto tambíen significa que sólo serán devueltas esas 3 columnas, incluso cuando la consulta esté usando * y
pueda devolver más columnas que las 3 listadas.

Es posible omitir la información de tipo para algunos o todos los escalares:

sess.createSQLQuery("SELECT * FROM CATS")


.addScalar("ID", Hibernate.LONG)
.addScalar("NAME")
.addScalar("BIRTHDATE");

Ésta es esencialmente la misma consulta que antes, pero ahora se usa ResultSetMetaData par decidir el tipo de NAME Y
BIRTHDATE, mientras que el tipo de ID se especifica explícitamente.

Cómo los java.sql.Types devueltos del ResultSetMetaData se mapean a Hibernate, es controlado por el dialecto. Si un tipo
en particular no está mapeado o no se resuelve en el tipo esperado, es posible modificarlo a medida, usando llamados a
registerHibernateType en el dialecto.

16.1.2. Consultas con entidades

151 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Las consultas precedentes consistían todas en devolver valores escalares, básicamente devolviendo los valores "crudos"
desede el resultset. A continuación se muestra cómo obtener objetos de entidad desde una consulta nativa, a través de
addEntity().

sess.createSQLQuery("SELECT * FROM CATS").addEntity(Cat.class);


sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").addEntity(Cat.class);

Esta consulta especificó:

la cadena SQL de la consulta

la entidad devuelta por la consulta

Suponiendo que Cat esté mapeado como clase, con las columnas ID, NAME y BIRTHDATE, las 2 consultas precedentes
devolverán una List en la cual cada elemento es una entidad Cat.

Si la entidad está mapeada a otra entidad con una asociación many-to-one (de-muchos-a-uno), es necesario devolver
también eso al efectuar la consulta nativa, de otro modo ocurrirá un error específico de base de datos del tipo "columna no
encontrada". Las columnas adicionales serán devueltas automáticamente cuando se use la notación *, pero se prefiere la
forma explícita, como en el siguiente ejemplo en donde tenemos una relación 'de-muchos-a-uno' con un Dog:

sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, DOG_ID FROM CATS").addEntity(Cat.class);

Esto permitirá que cat.getDog() funcione correctamente.

16.1.3. Manipular colecciones y asociaciones

Es posible asociar Dog Dog en forma "ansiosa" a fin de evitar el posible viaje extra ida y vuelta a la base de datos para
inicializar el proxy. Esto se hace usando el método addJoin(), el cual permite efectuar el vínculo con asociaciones o
colecciones.

sess.createSQLQuery("SELECT c.ID, NAME, BIRTHDATE, DOG_ID, D_ID, D_NAME FROM CATS c, DOGS d WHERE c
.addEntity("cat", Cat.class)
.addJoin("cat.dog");

En este ejemplo, los Cats devueltos tendrán su propiedad dog enteramente inicializada, sin necesitad de un viaje adicional
de ida y vuelta a la BD. Note que hemos agregado un nombre de alias ("cat") para poder especificar la propiedad de
destino del join. Es posible hacer este mismo tipo de join "ávido" para colecciones, por ejemplo, si el Cat tuviera varios
Dogs asociados de-uno-a-muchos.

sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, D_ID, D_NAME, CAT_ID FROM CATS c, DOGS d WHERE c.I
.addEntity("cat", Cat.class)
.addJoin("cat.dogs");

Llegados a este punto, casi hemos alcanzado el límite de lo que se puede hacer con consultas nativas, antes de empezar a
mejorar su SQL para que sean usables en Hibernate. El problema comienza cuando se devuelven múltiples entidades del
mismo tipo, o cuando los alias/nombres de columna empleados por defecto no son suficientes.

16.1.4. Devolver múltiples entidades

En los ejemplos precedentes, se asume que los nombres de columna del resultado son los mismos que los nombres de
columna especificados en el documento de mapeo. Esto puede ser problematico cuando las consultas SQL tiene "joins"
entre varias tablas, dado que pueden aparecer los mismos nombres de columna en más de una tabla.

En el ejemplo siguiente (que muy probablemente fallará), se necesita inyectar el alias de columna:

sess.createSQLQuery("SELECT c.*, m.* FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")


.addEntity("cat", Cat.class)
.addEntity("mother", Cat.class);

Lo que se intenta es que esta consulta devuelva dos instancias de Cat por fila: un gato y su madre. Esto fallará, dado que
hay un conflicto de nombres, porque están mapeados a los mismos nombres de columna, y, en algunas bases de datos, los
alias de columna devueltos muy probablemente tendrán la forma "c.ID", "c.NAME", etc., lo cual no coincide con las

152 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

columnas especificadas en el mapeo ("ID" y "NAME").

La forma siguiente no es vulnerable a esta duplicación del nombre de columna:

sess.createSQLQuery("SELECT {cat.*}, {mother.*} FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")


.addEntity("cat", Cat.class)
.addEntity("mother", Cat.class);

Esta consulta especificó:

la cadena de la consulta SQL, con parámetros de sustitución (placeholders) para que hibernate inyecte los alias de
columna

las entidades devueltas por la consulta

La notación con {cat.*} y {mother.*} usada anteriormente es taquigrafía por "todas las propiedades". Alternativamente,
se puede listar las columnas explícitamente, pero incluso en ese caso dejamos que Hibernate inyecte los alias de columna
SQL para cada propiedad. El parámetro de sustitución para un alias de columna es simplemente el nombre de la propiedad
calificado por el alias de la tabla. En el ejemplo siguiente, obtenemos los Cats (gatos) y sus madres desde una tabla
diferente (cat_log) a la declarada en los metadatos de mapeo. Note que incluso se puede usar los alias de propiedad en la
cláusula "where" si se quiere.

String sql = "SELECT ID as {c.id}, NAME as {c.name}, " +


"BIRTHDATE as {c.birthDate}, MOTHER_ID as {c.mother}, {mother.*} " +
"FROM CAT_LOG c, CAT_LOG m WHERE {c.mother} = c.ID";

List loggedCats = sess.createSQLQuery(sql)


.addEntity("cat", Cat.class)
.addEntity("mother", Cat.class).list();

16.1.4.1. Alias y referencias a propiedades

Par la mayoría de los casos, inyección de alias es todo lo que se necesita. Pero para consultas relacionadas con mapeos
más complejos, como discriminadores de herencia o propiedades compuestas, hay algunos alias específicos que Hibernate
debe usar para preservar el correcto funcionamiento de la inyección de alias.

La tabla siguiente muestra las diferentes posibilidades al usar inyección de alias. Nota: los nombres de alias en el resultado
son ejemplos, cada alias tendrá un nombre único y probablemente diferente cuando se use.

Tabla 16.1. Nombres de inyección de alias

Descripción Sintaxis Ejemplo


Una propiedad simple {[aliasname].[propertyname] A_NAME as {item.name}

{[aliasname].[componentname]. CURRENCY as {item.amount.currency},


Una propiedad compuesta [propertyname]} VALUE as {item.amount.value}

El discriminador de una
{[aliasname].class} DISC as {item.class}
entidad
Todas las propiedades de
{[aliasname].*} {item.*}
una entidad
Una clave de colección {[aliasname].key} ORGID as {coll.key}

El id de una colección {[aliasname].id} EMPID as {coll.id}

El elemento de una
{[aliasname].element} XID as {coll.element}
colección
Una propiedad de elemento {[aliasname].element.
NAME as {coll.element.name}
en la colección [propertyname]}

Todas las propiedades del


{[aliasname].element.*} {coll.element.*}
elemento en la colección

153 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Descripción Sintaxis Ejemplo


Todas las propiedades de la
{[aliasname].*} {coll.*}
colección

16.1.5. Devolver entidades no administradas

Es posible aplicarles un ResultTransformer a las consultas SQL nativas, permitiendo, por ejemplo, devolver entidades
no-manejadas.

sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS")


.setResultTransformer(Transformers.aliasToBean(CatDTO.class))

La consulta especificó:

la cadena SQL de la consulta

un transformador de resultado (result transformer)

La consulta precedente devolverá una lista de CatDTO, la cual habrá sido instanciada y habrá inyectado los valores de
NAME y BIRTHNAME en las propiedades o campos correspondientes.

16.1.6. Manejar herencia

Las consultas de SQL nativas que consulten entidades que estén mapeadas como parte de una herencia, deben incluir
todas las propiedades para la clase base y las subclases.

16.1.7. Parámetros

Las consultas SQL nativas soportan parámetros tanto nombrados como posicionales:

Query query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like ?").addEntity(Cat.class);


List pusList = query.setString(0, "Pus%").list();

query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like :name").addEntity(Cat.class);


List pusList = query.setString("name", "Pus%").list();

16.2. Consultas SQL nombradas


Las consultas SQL nombradas (named SQL queries) pueden definirse en el documento de mapeo y ser invocadas
exactamente en la misma forma que una consulta HQL nombrada. En este caso, no necesitamos invocar addEntity().

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

List people = sess.getNamedQuery("persons")


.setString("namePattern", namePattern)
.setMaxResults(50)
.list();

Los elementos <return-join> y <load-collection> se usan, respectivamente, para asociaciones de tipo "join", y para
definir consultas que inicialicen colecciones.

<sql-query name="personsWith">
<return alias="person" class="eg.Person"/>
<return-join alias="address" property="person.mailingAddress"/>

154 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

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>

Una consulta SQL nombrada puede devolver un valor escalar. Hya que declarar el alias de la columna y el tipo de
Hibernate usando el elemento <return-scalar> element:

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

Se puede externalizar el resultado, mapeando información en un elemento <resultset> para reutilizarlo, ya sea en otras
consultas nombradas, o a través de la API setResultSetMapping().

<resultset name="personAddress">
<return alias="person" class="eg.Person"/>
<return-join alias="address" property="person.mailingAddress"/>

</resultset>

<sql-query name="personsWith" resultset-ref="personAddress">


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>

Alternativamente, se puede usar la información de mapeo del resultset contenida en los archivos hbm, directamente en el
código Java.

List cats = sess.createSQLQuery(


"select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
)
.setResultSetMapping("catAndKitten")
.list();

16.2.1. Usar return-property para especificar nombres de columna/alias explícitamente

Con <return-property> se le puede deicr a Hibernate explícitamente qué alias de columna usar, en lugar de usar la
sintaxis {} para dejar que Hibernate inyecte sus propios alias.

<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,

155 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

person.AGE AS myAge,
person.SEX AS mySex,
FROM PERSON person WHERE person.NAME LIKE :name

</sql-query>

<return-property> también funciona con múltiples columnas. Esto soluciona la limitación de la sintaxis {}, la cual no
permite un control tan minucioso de las propiedades multicolumna.

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

Note que en este ejemplo utilizamos <return-property> en combinación con la sintaxis {} para inyectar, permitiéndole
al usuario decidir cómo prefiere referirse a columnas y propiedades.

Si su mapeo tiene un discriminador, debe usarse <return-discriminator> para especificar la columna discriminadora.

16.2.2. Usar procedimientos almacenados para efectuar consultas

Hibernate 3 introduce soporte para consultas vía procedimientos almacenados (stored procedures) y funciones. La
mayoría de la documentación que sigue es equivalente para ambos. El procedimiento/función debe devolver un conjunto
resultado o "resultset" como primer parámetro de salida, a fin de poder trabajar con Hibernate. A continuacíón, un ejemplo
de función en Oracle 9 o superior:

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;

Para usar esta consulta en Hibernate, hay que mapearla con una consulta "nombrada".

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

156 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Note que los procedimientos almacenados actualmente sólo devuelven escalares o entidades. <return-join> y <load-
collection> no se soportan.

16.2.2.1. Reglas y limitaciones en el uso de procedimientos almacenados

Para usar procedimientos almacenados con Hibernate, el procedimiento o función debe seguir ciertas reglas. Si no sigue
esas reglas, es incompatible con Hibernate. Si aún se desea usar estos procedimientos incompatibles, hay que ejecutarlos
via session.connection(). Las reglas son diferentes para cada base de datos, dado que cada proveedor de base de datos
tiene una semántica y una sintaxis distinta pars los procedimientos almacenados.

Los procedimientos almacenados no pueden ser paginados con setFirstResult()/setMaxResults().

La invocación recomendada es el estádar de SQL92: { ? = call functionName(<parameters>) } or { ? = call


procedureName(<parameters>}. Los llamdos nativos no se soportan.

Para Oracle, se aplican las siguientes reglas:

Las funciones deben devolver un resultado "resultset". El primer parámetro de los procedimientos almacenados
debe ser un parámeto de tipo "OUT" que devuelva un resultset. Esto se logra usando un tipo SYS_REFCURSOR en
Oracle 9 o 10. En Oracle hay que definir un tipo REF CURSOR, consulte la literatura de Oracle.

Para Sybase o MS SQL server se aplican las siguientes reglas:

El procedimiento debe devolver un resultset. Note que, como estos servidores son capaces de devolver múltiples
resultsets y contadores de actualización, Hibernate recorrerá los resultados y tomará el primer resultado que sea
resultset. El resto se descarta.

Si usted puede habilitar SET NOCOUNT ON en su procedimiento, seguramente éste será más eficiente, pero esto no es
obligatorio.

16.3. SQL a medida para crear, modificar o borrar


Hibernate3 puede usar comandos SQL a medida para operaciones de creación, actualización y borrado. Los "persistores"
de clases y colecciones en Hibernate ya contienen un conjunto de cadenas (insertsql, deletesql, updatesql etc.) generadas
en tiempo de configuración. Las tags de mapeo <sql-insert>, <sql-delete>, y <sql-update> suplantan easas cadenas

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

El SQL es ejecutado directamente en su base de datos, así que usted es libre de utilizar cualquier dialecto que desee. Por
supuesto, usar SQL específico de una base de datos reducirá la "portabilidad" de su aplicación.

Los procedimientos almacenados se soportan, si se configura el atributo callable:

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

157 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

El orden de los parámetros posicionales es ahora esencial, dado que deben estar en la misma secuencia en que Hibernate
los espera.

Se puede ver el orden esperado, habilitando logueo a nivel "debug" en org.hibernate.persister.entity. Con esta
categoría habilitada,, Hibernate imprimirá el SQL estático que se usa para crear entidades, actualizarlas, borrarlas, etc.
(Para ver la secuencia esperada, recuerde no incluir su SQL a medida en los archivos de mapeo, dado que éste suplantará
al SQL estático generado por Hibernate).

En la mayoría de los casos, es obligatorio (o más bien, fuertemente recomendado) que los procedimientos almacenados
devuelvan el número de filas insertadas/actualizadas/borradas, porque Hibernate realiza algunos chequeos en tiempo de
ejecución para verificar el éxito del comando. Hibernate siempre registra el primer parámetro del "statement" como de
salida y numérico, para las operaciones de ABM (alta-baja-modificación):

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;

16.4. SQL a medida para cargar


Se puede también declarar comandos SQL (o HQL) a medida para cargar entidades:

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

Esta es, simplemente, una declaración de una consulta nombrada (named query), como se discutió anteriormente. Se
puede hacer referencia a esta consulta nombrada en el mapeo de clases:

<class name="Person">
<id name="id">
<generator class="increment"/>
</id>

<property name="name" not-null="true"/>


<loader query-ref="person"/>
</class>

Esto funciona incluso con procedimientos almacenados.

Se puede incluso definir un a consulta para cargar la colección:

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


<key/>

<one-to-many class="Employment"/>
<loader query-ref="employments"/>
</set>

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

158 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Hasta se puede definir un "cargador de entidades" que cargue una coleccíón mediante captura con "join":

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

Capítulo 17. Filtrar datos


Hibernate3 provee un enfoque innovador para manejar datos que tengan "reglas de visibilidad". Un filtro de Hibernate es
un filtro global, nombrado y parametrizado, que puede estar habilitado o inhabilitado para una sesión en particular.

17.1. Filtros de Hibernate


Hibernate3 incorpora la capacidad de predefinir criterios de filtrado, y adjuntar esos filtros tanto a nivel de la clase como a
nivel de la colección. Un cirterio de filtrado es la capacidad de definir una cláusula de restricción, muy similar al atributo
"where" disponible en la clase y varios elementos de colecciones. Excepto que estas condiciones de filtrado pueden ser
parametrizadas. La aplicación puede, entoces, tomar decisiones en tiempo de ejecución acerca de si un filtro debería estar
habilitado, y cuáles deberían ser sus parámetros. Los filtros pueden ser usados como vistas (views) de la base de datos,
pero parametrizados dentro de la aplicación.

Para usar filtros, primero tienen que ser definidos y adjuntados al elemento de mapeo correspondiente. Para definir un
filtro, use el elemento <filter-def/> dentro de un elemento de <hibernate-mapping/>:

<filter-def name="myFilter">
<filter-param name="myFilterParam" type="string"/>
</filter-def>

Luego, este filtro puede ser adosado a una clase:

<class name="myClass" ...>


...
<filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
</class>

o a una colección:

<set ...>
<filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
</set>

o incluso a ambas, o a varias de ambas, al mismo tiempo.

Los métodos en Session son: enableFilter(String filterName), getEnabledFilter(String filterName), y


disableFilter(String filterName). Por defecto, los filtros no están habilitados para una sesión dada; deben ser
habilitados explícitamente mediante el uso del método Session.enableFilter(), el cual devuelve una instancia de la
interfaz Filter. Usando el filtro simple definido antes, esto se vería así:

session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");

Note que hay métodos en la interfaz org.hibernate.Filter que permiten el encadenamiento de filtros, muy común en
Hibernate.

Un ejemplo completo, usando datos con el patrón de programación conocido como "fecha efectiva" (effective date):

<filter-def name="effectiveDate">
<filter-param name="asOfDate" type="date"/>

159 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

</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 que esto asume que los registros no-terminales tinenen una fecha de finalización efect
a la cual se le asignó el máximo valor de fecha posible en la BD, para simplificar.
-->
<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>

Luego, para asegurarse de que siempre se obtiene el registro más actual, simplemente habilite el filtro en la sesión, antes de
capturar datos de empleados:

Session session = ...;


session.enableFilter("effectiveDate").setParameter("asOfDate", new Date());
List results = session.createQuery("from Employee as e where e.salary > :targetSalary")
.setLong("targetSalary", new Long(1000000))
.list();

En el HQL precedente, incluso si sólo se mencionó una condición relativa al salario en los resultados, la consulta
devolverá los empleados que con un salario mayor a un millón y, a causa del filtro habilitado, que estén actualmente
activos.

Nota: si se planea usar filtros con "outer joins" (sea a través de HQL o de estrategias de "fetch") tenga cuidado con la
dirección de la expresión de condición. Lo más seguro es configurarlo como "left outer join"; en general, ponga el
parámetro primero, seguido por el nombre o nombres de columna luego del operador.

Luego de haber sido definido, un filtro puede ser adosado a entidades y/o colecciones, cada una con su propia condición.
Eso puede ser tedioso cuando las condiciones son las mismas en todos los casos. Por esto, <filter-def/> permite definir
una condición por defecto, sea como un atributo, o como CDATA:

<filter-def name="myFilter" condition="abc > xyz">...</filter-def>


<filter-def name="myOtherFilter">abc=xyz</filter-def>

Entonces, esta condición por defecto será usada siempre que el filtro se adjunte a algo sin haber especificado una
condición. Note que esto significa que se puede dar una condición específica como parte de la adjunción del filtro, que
suplanta a la condición por defecto para ese caso en particular.

Capítulo 18. Mapeo XML


Note que ésta es una característica experimental en Hibernate 3.0, y se encuentra actualmente en muy activo
desarrollo.

18.1. Trabajar con datos XML


Hibernate le permite trabajar con datos persistentes en XML de la misma forma en que lo haría con POJOs. Un árbol XML
válido puede ser concebido como sencillamente otra forma de representar los datos relacionales a nivel de objetos, en
lugar de los POJOs.

Hibernate soporta la API dom4j para manipular los árboles XML. Se puede escribir consultas que obtengan árboles dom4j

160 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

desde la base de datos, y lograr que cualquier modificación practicada al XML se sincronice automáticamente a la base de
datos. Incluso se puede tomar un documento XML, analizarlo usando dom4j, y escribirlo en la base de datos con
cualquiera de las operaciones básicas de Hibernate: persist(), saveOrUpdate(), merge(), delete(),
replicate() (merge aún no se soporta).

Esta habilidad tiene muchos usos, incluyendo: importación/exportación de datos, externalización de entidades via JMS o
SOAP, y producción de reportes basada en XSLT.

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 para mapear, sólo el documento XML.

18.1.1. Especificar el mapeo XML y el mapeo de la clase al mismo tiempo

He aquí un ejemplo de mapeo de un POJO y XML simultáneamente:

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

18.1.2. Especificar sólo un mapeo XML

He aquí un ejemplo en el cual no hay clase POJO:

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

Este mapeo permite acceder a los datos como si fueran un árbol dom4j, o una representación en forma de pares
nombre/valor (Maps de Java). Los nombres de las propiedades son construcciones puramente lógicas, a las que se puede
hacer referencia en consultas HQL.

18.2. Metadatos de mapeo XML


Muchos elementos de mapeo de Hibernate aceptan el atributo node (nodo). Esto permite especificar el nombre de un
atributo o elemento XML que contenga la propiedad o dato de entidad. El formato del atributo node debe ser uno de los
siguientes:

"element-name": mapea al elemento XML especificado

"@attribute-name": mapea al atributo XML especificado.

".": mapea al elemento padre.

"element-name/@attribute-name": mapea al elemento especificado del atributo especificado.

Para asociaciones y asociaciones a valores simples, hay un atributo embed-xml adicional. Si se especifica embed-
xml="true", lo cua es el valor por defecto, el árbol XML para la entidad asociada (la de tipo colección o "value type")
será incrustada directamente en el árbol XML de la entidad que posee la asociación. En caso contrario, si embed-
xml="false", entonces sólo el valor del identificador al que se hace referencia aparecerá en el XML (para asociaciones

161 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

de "value type"s) y para las colecciones no aparecerá nada.

Debería tenerse cuidado de no asignarles embed-xml="true" a demasiadas asociaciones, ¡dado que XML no sabe lidiar
muy bien con recursividad!

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

en este caso, hemos decidido incrustar una colección de ids de cuentas (accounts), pero no los datos de las cuentas
propiamente dichas. La siguiente consulta HQL:

from Customer c left join fetch c.accounts where c.lastName like :lastName

Devolvería datos como éstos:

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

Si se asigna embed-xml="true" en el mapeo <one-to-many>, los datos se verían así:

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

18.3. Manipular datos XML

162 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Vamos a leer y actualizar documentos XML en la aplicación. Lo hacemos obteniendo una sesión dom4j:

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++ ) {
//le agrega los datos del cliente (customer) al documento XML
Element customer = (Element) results.get(i);
doc.add(customer);
}

tx.commit();
session.close();

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);
//cambia el nombre del cliente en el XML y en la BD
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();

Combinar esta funcionalidad con la operación replicate() es extremadamente útil para implementar la
importación/exportación de datos basados en XML:

Capítulo 19. Mejorar la performance

19.1. Estrategias de captura (fetch)


Una estrategia de captura es la estrategia que Hibernate usará para obtener los objetos asociados, cuando Hibernate
necesite navegar una asociación. Las estrategias de captura pueden ser declaradase en los metadatos de mapeo, o bien ser
sustituidas para una consulta HQL o Criteria en particular.

Hibernate3 define las siguientes estategias de captura:

captura por Join Hibernate obtiene la instancia o colección asociada, agregándole un OUTER JOIN al SELECT
mismo.

captura por Select: se usa un segundo SELECT para obtener la entidad o colección asociada. A menos que se
inhabilite la captura haragana (lazy fetching) especificando lazy="false", este segundo SELECT sólo se ejecutará
cuando realmente se acceda a la asociación.

captura poe Subselect: se usa un segundo SELECT para obtener todas las entidades o colecciones asociadas, que se
habían obtenido en el SELECT anterior. A menos que se inhabilite la captura haragana (lazy fetching) especificando
lazy="false", este segundo SELECT sólo se ejecutará cuando realmente se acceda a la asociación.

captura por lotes (batch fetching): es una estrategia de optimización de la captura por Select; Hibernate obtiene no
una, sino un lote (batch) de instancias de entidad o colección en un solo SELECT, especificando no una, sino una
lista de claves primarias o foráneas.

Hibernate también distingue entre:

captura inmediata: la asociación. colección o atributo son capturados inmediatamente, cuando el dueño es cargado.

163 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

captura haragana de colecciones: una colección es capturada sólo cuando la aplicacíón invoca alguna operación en
esa colección (éste es el comportamiento por defecto para las colecciones).

captura "súper-haragana" de colecciones (extra-lazy collection fetching): se accede a elementos individuales de la


colección desde la base de datos, sólo en la medida en que se necesita. Hibernate no intenta capturar toda la
colección en memoria, a menos que sea absolutamente necesario (esto es lo adecuado para colecciones muy
extensas).

captura por Proxy: una asociación de un solo valor se captura cuando se invoca sobre éste cualquier otro método
que no sea su getter/setter.

captura sin proxy: una asociación de un solo valor se captura cuando se accede a la variable de instancia . En
comparación con la captura por proxy, este abordaje es menos haragán (se accede a la asociación incluso si sólo se
usa el identificador), pero más transparente, dado que no hay ningún proxy visible para la aplicacíón. Este abordaje
requiere instrumentación bytecode de tiempo de compilación, y sólo raramente es necesario.

captura haragana de atributos: un atributo o asociación de valor simple, se capturan cuando se accede a la
instancia o asociación. Este abordaje requiere instrumentación bytecode de tiempo de compilación, y sólo raramente
es necesario.

Aquí tenemos dos nociones ortogonales: cuándo la asociación se captura, y cómo se captura (qué SQL se usa). ¡A no
confundir una con la otra! Se usa la palabra fetch para ajustar la eprformance. Se puede usar la palabra lazy para definir
un contrato sobre qué estará disponible y qué estará desprendido para una clase en particular.

19.1.1. Trabajar con asociaciones haraganas

Hibernate3 usa por defecto "select haragán" para las colecciones, y "captura haragana por proxy" para las asociaciones de
valor simple. Estos valores por defecto tienen sentido casi siempre, y para casi todas las asociaciones.

Nota: si se inicializa la variable de configuración hibernate.default_batch_fetch_size, Hibernate usará la


optimización de captura por lotes para las capturas haraganas. Esta optimización puede también ser habilitada a un nivel
más granular.

De todos modos, la captura haragana plantea un problema del cual hay que ser consciente: acceder a asociaciones
haraganas por fuera del contexto de una sesión abierta de Hibernate, resultará en una excepción. Por ejemplo.

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!

Como la colección "permissions" no estaba inicializada cuando la sesión fue cerrada, la colección será incapaz de cargar
su estado. Hibernate no soporta la inicialización haragana de objetos desprendidos. La solución es mover el código que
lee de la colección a justo antes de que la transacción invoque "commit".

Alternativamente, podríamos usar una colección o asociación no haragana, especificando lazy="false" para el mapeo de
la asociación. De todos modos, se espera que se use inicialización haragana en casi todos los casos. Si se definen
demasiadas asociaciones no haraganas, ¡Hibernate terminará cargando toda la base de datos en memoria para cada
transacción!

Por otra parte, a menudo querremos elegir captura por join (la cual que es naturalmente no haragana) en lugar de captura
por select, para una transacción en particular. Ahora veremos cómo diseñar una estrategia de captura a medida. En
Hibernate3, los mecanismos para elegir una estrategia de captura son idénticos para las asociaciones de valor simple y para
las colecciones.

19.1.2. Ajustar las estrategias de captura

La captura por select (el comportamiento por defecto) es extremadamente vulnerable a los problemas de N+1 SELECTS.

164 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Así que tal vez querramos habilitar la captura por join en el documento de mapeo.

<set name="permissions" fetch="join">


<key column="userId"/>
<one-to-many class="Permission"/>
</set

<many-to-one name="mother" class="Cat" fetch="join"/>

La estrategia de captura (fetch) definida en el documentod e mapeo afecta a:

la obtención de datos via get() o load()

la obtención de datos que ocurre implícitamente cuando se navega la asociación

las consultas Criteria

las consultas HQL (si se usa la captura por subselect )

Sin importar qué estrategia de captura se use, se garantiza que el árbol "no haragán" definido se cargará en memoria. Note
que esto puede resultar en que se usen varios SELECTs inmediatamente para ejecutar una consulta HQL en particular.

Normalmente no usaremos el documento de mapeo para ajustar el tipo de captura. En cambio, mentendremos el
comportamiento por defecto, y lo sustituiremos para una transacción en particular, usando left join fetch en HQL.
Esto le dice a Hibernate que capture la asociacíón en forma ansiosa, en el primer SELECT, usando un outer join. En la
API de consultas Criteria, se usaría setFetchMode(FetchMode.JOIN).

Si se deseare cambiar la estrategia de captura usada por get() or load(), simplemente habría que usar una consulta
Criteria, por ejemplo:

User user = (User) session.createCriteria(User.class)


.setFetchMode("permissions", FetchMode.JOIN)
.add( Restrictions.idEq(userId) )
.uniqueResult();

(Éste es el equivalente en Hibernate de lo que algunas soluciones ORM llaman un plan de captura o "fetch plan").

Una forma completamente distinta de evitar los problemas con N+1 selects es usar el caché de 2do nivel.

19.1.3. Proxies de asociaciones de un solo extremo

La captura haragana para colecciones se implementa usando la implementación para colecciones persistentes de Hibernate
mismo. Sin embargo, se necesita un mecanismo diferente para el comportamiento haragán en las asociaciones de extremo
simple. A la entidad blanco de la asociación se le debe crear un "proxy". Hibernate implementa los proxies de
inicialización haragana para objetos persistentes usando "mejora de bytecode en tiempo de ejecución" (a través de la
excelente biblioteca CGLIB).

Hibernate3 genera proxies por defecto (durante el arranque) para todas las clases persistentes, y los usa para habilitar la
captura haragana de asociaciones 'de-muchos-a-uno' (many-to-one) y de-uno-a-uno (one-to-one).

El archivo de mapeo puede declarar una interfaz para ser usada como la "interfaz de proxy" para una clase determinada,
con el atributo proxy. Hibernate usa por defecto una subclase de la clase. Tenga en cuenta que la clase a la cual se le
crea el proxy debe implementar un constructor por defecto (sin parámetros), con visibilidad por defecto o "package"
por lo menos. ¡Recomendamos este constructor para todas las clases persistentes!

Hay algunas dificultades muy comunes, de las cuales conviene estar al tanto a la hora de extender este enfoque a clases
polimórficas; por ejemplo:

<class name="Cat" proxy="Cat">


......
<subclass name="DomesticCat">
.....
</subclass>
</class>

165 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Primero que nada, las instancias de la clase Cat jamás podrán ser convertidas con "cast" a DomesticCat, incluso cuando
la instancia subyacente sí es una instancia de DomesticCat:

Cat cat = (Cat) session.load(Cat.class, id); // instancia un proxy (no llama a la base de datos)
if ( cat.isDomesticCat() ) { // llama a la base de datos para inicializar el proxy
DomesticCat dc = (DomesticCat) cat; // ¡Error!
....
}

En segundo lugar, es posible romper el operador ==.de proxies.

Cat cat = (Cat) session.load(Cat.class, id); // instancia un proxy de Cat


DomesticCat dc = (DomesticCat) session.load(DomesticCat.class, id); // adquiere un nuevo proxy par
System.out.println(cat==dc); // falso

Sin embargo, esta situación no es tan mala como parece. Incluso si ahora tenemos dos referencias a objetos "proxy"
diferentes, la instancia subyacente aún es el mismo objeto:

cat.setWeight(11.0); // llama a la base de datos para inicializar el proxy


System.out.println( dc.getWeight() ); // 11.0

En tercer lugar, no se puede usar un proxy CGLIB para clases final ni para clases que tengan métodos final.

Por último, si su clase persistente adquiere recursos durante la inicialización (por ejemplo, en inicializadores o
constructores por defecto), entonces dichos recursos serán también adquiridos por el proxy. La clase proxy es una
verdadera subclase de la clase persistente.

Estos problemas se deben todos a limitaciones fundamentales del modelo de herencia única de Java. Si usted quiere evitar
estos problemas, cada una de sus clases persistentes debe implementar una interfaz que declare sus métodos de negocio. Y
usted debe especificar estas interfaces en el documento de mapeo. Por ejemplo:

<class name="CatImpl" proxy="Cat">


...

<subclass name="DomesticCatImpl" proxy="DomesticCat">


...
</subclass>
</class>

en donde CatImpl implementa la interfaz Cat y DomesticCatImpl implementa la interfaz DomesticCat. Así, los proxies
para instancias de Cat y DomesticCat pueden ser devueltos por load() o por iterate(). (Note que list()
normalmente no devuelve proxies.)

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();

Las relaciones también son incializadas en forma haragana. Esto significa que toda propiedad debe ser declarada como de
tipo Cat, no CatImpl.

Ciertas operaciones no requieren inicialización de proxies:

equals(), si la clase persistente no sustituye equals()

hashCode(), si la clase persistente no sustituye hashCode()

el getter del método identificador

Hibernate detectará las clases persistentes que sustituyan (override) equals() o hashCode().

Eligiendo lazy="no-proxy" en lugar del lazy="proxy" que viene por defecto, podemos evitar los problemas asociados
con la conversión ("cast") entre tipos. Sin embargo, necesitaremos instrumentación de bytecode en tiempo de compilación,
y todas las operaciones resultarán en una inicialización de proxy inmediata.

19.1.4. Inicializar proxies y colecciones

166 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Si se trata de acceder a una colección o proxy no inicializados fuera del alcance de una sesión, Hibernate emitirá una
LazyInitializationException. Es decir, cuando la entidad que posea la colección o que tienga una referencia al proxy
esté en un estado desprendido.

A veces necesitamos asegurarnos de que un proxy o colección sean inicializados antes de cerrar la sesión. Por supuesto,
siempre podemos forzar la inicialización invocando cat.getSex() o cat.getKittens().size(), por ejemplo. Pero esto
sería confuso para quien leyere el código, e inconveniente para cualquier código que intentare ser genérico.

Los métodos estáticos Hibernate.initialize() e Hibernate.isInitialized() le proveen a la aplicación una forma


conveniente de trabajar con colecciones o proxies que hayan sido inicializados en forma haragana.
Hibernate.initialize(cat) forzará la inicialización del proxy, cat, siempre y cuando su sesión esté aún abierta.
Hibernate.initialize( cat.getKittens() ) tiene un efecto similar para la colección de kittens (gatitos).

Otra opción es mantener la sesión abierta hasta que todas las colecciones y proxies necesarios hayan sido cargados. En la
arquitectura de algunas aplicaciones, particularmente en donde el código que accede a Hibernate y el código que lo usa
están en distinas capas, o en distintos procesos físicos, asegurarse de que una única sesión se mantenga abierta cuando una
colección se inicialice puede llegar a ser un problema. Hay básicamente dos maneras de lidiar con esto:

En una aplicación de web, se puede usar un filtro de servlet para cerrar la sesión sólo cuando realmente se llegue al
final de la request, una vez que la presentación de la interfaz de usuario (view) ha sido completada (el patrón de
programación Open Session in View ). Por supuesto, esto crea una pesada responsabilidad en cuando a la corrección
del manejo de excepciones por parte de la infraestructura de la aplicación. Es de vital importancia que la sesión sea
cerrada y la transacción concluida antes de retornar el control al usuario, incluso cuando ocurriere una excepción
mientras se presentare la interfaz de usuario. Vea la wiki de Hibernate para ejemplos de este patrón "Open Session
in View".

En aplicaciones que tengan una capa de negocios separada, la lógica de negocio debe "preparar" todas las
colecciones que vayan a ser necesitadas por la capa de web, antes de retornar. Esto significa que la capa de
negocios debería cargar todos los datos que hagan falta para un caso en particular, y devolvérselos a la capa de
interfaz de usuario/web. Normalmente, la aplicación llama a Hibernate.initialize() para cada colección que
vaya a ser necesaria en la capa de web (este llamado ocurre antes de que la sesión se cierre), u obtiene la colección
en forma ansiosa usando una consulta de Hibernate con cláusula FETCH o con un FetchMode.JOIN en el Criteria.
Esto es normalmente más fácil si se adopta el patrón de programación Command en lugar del patrón Session
Façade.

También se puede adjuntar un objeto previamente cargado a una nueva sesión, con merge() o lock() antes de
acceder a colecciones (u otros proxies) no inicializados. No, Hibernate no debería hacer esto automáticamente,
¡puesto que ello introduciría una semántica de transaccion ad hoc!

A veces usted no querrá inicializar toda una colección muy extensa, pero sí necesitará un poco de información acerca de
ella (por ejemplo, su tamaño), o un subconjunto de sus datos.

Se puede usar un filtro de colección para obtener el tamaño de una colección sin inicializarla:

( (Integer) s.createFilter( collection, "select count(*)" ).list().get(0) ).intValue()

El método createFilter() se usa también para obtener eficientemente subconjuntos de una colección sin necesidad de
inicializarla toda.

s.createFilter( lazyCollection, "").setFirstResult(0).setMaxResults(10).list();

19.1.5. Usar la captura por lotes

Hibernate puede hace un uso eficiente de la captura por lotes, es decir que puede cargar varios "proxies" no inicializados
cuando se accede a uno. (O colecciones. La captura por lotes es una optimización del tipo de captura por "SELECT
haragán"). Hay dos maneras de ajustar la captura por lotes: en la clase, o a nivel de la colección.

La captura por lotes a nivel de la clase es más fácil de entender. Imagínese que tiene la siguiente situación en tiempo de
ejecución: Hay 25 instancias de Cat cargadas en la sesión; cada Cat tiene una referencia a su dueño, una instancia de
Person. La clase Person está mapeada con un proxy, lazy="true". Si se itera a través de todos los gatos (cats) y se
invoca getOwner() en cada uno de ellos, Hibernate por defecto ejecutará 26 comandos SELECT, para obtener los proxies
de los dueños. Se puede ajustar este comportamiento especificando un batch-size (tamño de lote) en el mapeo de
Person:

167 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

<class name="Person" batch-size="10">...</class>

Hibernate ahora ejecutará sólo 3 consultas: el patrón es 10, 10, 5.

También se puede habilitar la captura en lotes de colecciones. Por ejemplo, si cada Person tiene un colección haragana de
Cats, y en un momento hay 10 personas cargadas en la sesión, iterar a través de todas esas personas generaría 10
SELECTs, uno por cada llamado a getCats(). Si se habilita la captura por lotes para las colecciones de cats en el mapeo
de Person, Hibernate pre-capturará las colecciones:

<class name="Person">

<set name="cats" batch-size="3">


...
</set>
</class>

Con un a batch-size igual a 3, Hibernate cargará 3, 3, 3, 1 colecciones en 4 SELECTs. De nuevo, el valor del atributo
dependerá del número esperado de colecciones no inicializadas para una sesión en particular.

La captura por lotes es particularmente útil si se tiene un árbol anidado de items, por ejemplo, la típica "Lista de
Materiales" necesarios para construir una pieza (bill of Materials o BOM, por sus siglas en inglés). (Aunque para árboles
de "mayormente lectura" un set anidado o un "path materialziado" serían mejores opciones) .

19.1.6. Usar la captura por subselect

Si una colección o proxy de valor simple haraganes tienen que ser capturados, Hibernate los carga todos, volviendo a
ejecutar la consulta original en un subselect. Esto funciona de la misma manera que la captura por lotes, pero sin la carga
"parte por parte".

19.1.7. Usar la captura de propiedades haragana

Hibernate3 soporta la captura haragana de propiedades individuales. Esta técnica de optimización también se conoce
como grupos de captura (en inglés "fetch groups"). Por favor, dese cuenta de que esto es un extra más publicitario que
funcional, dado que, en la práctica, optmizar las lecturas de filas es mucho más importante que optimizar las lecturas de
columna. De todos modos, leer sólo algunas propiedades de una clase puede ser útil en algunos casos extremos, en donde
tablas anticuadas/heredadas tengan cientos de columnas y el modelo de datos no pueda ser mejorado.

Para habilitar la carga haragana de propiedades, configure el atributo lazy en mapeos de propiedad específicos:

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

¡La captura haragana de propiedades requiere instrumentación bytecode de tiempo de compilación! Si sus clases
persistentes han sido mejoradas, Hibernate ignorará silenciosamente la configuración de propiedades a "lazy", y se
resignará a usar la captura inmediata usual.

Para la instrumentación bytecode, use la siguiente tarea de Ant:

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

168 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

</fileset>
</instrument>
</target>

Una manera diferente (y tal vez mejor) de evitar lecturas de columa innecesarias, al menos en transacciones de
sólo-lectura, es usar las capacidades de proyección (Projections) que tienen las consultas HQL y Criteria. Esto evita la
necesidad del bytecode de tiempo de compiación, y es ciertamente una solución preferible.

Se puede forzar la captura ansiosa usual de propiedades, usando fetch all properties en HQL.

19.2. El caché de 2do nivel


Una sesión (Session) de Hibernate, es un caché de datos persistentes a nivel de la transacción. Es posible configurar un
caché a nivel de cluster, o a nivel de la JVM (a nivel de la SessionFactory o fábrica de sesiones), que se aplique a una o
más clases en particular, y/o a una o más colecciones en particular. Se puede incluso "enchufar" un caché en cluster. Hay
que tener cuidado, porque los cachés nunca están al tanto de los cambios que le hayan sido hechos al repositorio de datos
persistente por otra aplicación (aunque sí pueden ser configurados para que sus datos cacheados expiren regularmente
luego de un cierto tiempo).

Existe la opción de decirle a Hibernate qué implementación de cacheo usar, especificando el nombre de una clase que
implemente org.hibernate.cache.CacheProvider, usando la propiedad hibernate.cache.provider_class.
Hibernate trae incorporada una buena cantidad de integraciones con proveedores de cachés open-source, (listados a
continuación); además, se puede implementar un caché propio y enchufarlo como se indicó anteriormente. Nota: las
versiones hasta 3.2 exclusive usaban EhCache como su proveedor por defecto. Éste ya no es el caso a partir de 3.2.

Table 19.1. Prveedores de Caché

Soporta el
Seguro para
caché de
Caché Clase del proveedor Tipo usar en
consultas
clusters
(Query)
Hashtable
(no se espera
que sea org.hibernate.cache.HashtableCacheProvider memoria sí
usado en
producción)
EHCache org.hibernate.cache.EhCacheProvider memoria, disco sí
OSCache org.hibernate.cache.OSCacheProvider memoria, disco sí
en sí
SwarmCache org.hibernate.cache.SwarmCacheProvider cluster(multicast (invalidación
de ip) de cluster)
en cluster
sí (requiere
JBoss Cache (multicast de sí
org.hibernate.cache.TreeCacheProvider sincronización
1.x ip), (replicación)
de relojes)
transaccional
en cluster sí
sí (requiere
JBoss Cache (multicast de (replicación
org.hibernate.cache.jbc2.JBossCacheRegionFactory sincronización
2 ip), o
de relojes)
transaccional invalidación)

19.2.1. Mapeos de caché

El elemento <cache> del mapeo de una clase o colección tiene la forma siguiente:

<cache
usage="transactional|read-write|nonstrict-read-write|read-only" (1)
region="RegionName" (2)
include="all|non-lazy" (3)

169 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

/>

(1)
usage (obligatorio) especifica la estrategia de cacheo transactional, read-write, nonstrict-read-write or
read-only

(2)
region (optativo, por defecto, el nombre de rol de la clase o colección): especifica el nombre de la región del
do
caché de 2 nivel.

(3)
include (optativo, por defecto, all) non-lazy especifica que las propiedades de la entidad mapeadas con
lazy="true" no deben ser mapeadas cuando esté habilitada la captura haragana a nivel de cada atributo.

Alternativamente (y tal vez preferiblemente) se puede especificar elementos <class-cache> y <collection-cache> en


el archivo hibernate.cfg.xml.

EL atributo usage especifica una estrategia de concurrencia de caché.

19.2.2. Estrategia de sólo-lectura

Si su aplicación necesita leer pero nunca modificar las instancias de una clase persistente, se puede usar un caché
read-only. Esta es la estrategia más simple y la de mejor performance. También es perfectamente segura de utilizar en un
cluster.

<class name="eg.Immutable" mutable="false">

<cache usage="read-only"/>
....
</class>

19.2.3. Estrategia de lecto-escritura

Si la aplicación necesita actualizar datos, puede ser apropiado usar una caché de lecto-escritura (read-write). Esta
estrategia de cacheo nunca debería ser usada si se requiere aislamiento de transacciones serializables. Si el caché se usa en
un entorno JTA, se debe especificar la propiedad hibernate.transaction.manager_lookup_class, especificando una
estrategia para obtener la propiedad TransactionManager de JTA. En otros entornos, hay que asegurarse de que la
transacción esté completa para cuando ocurran Session.close() o Session.disconnect(). Si se quiere usar esta
estrategia en un cluster, hay que asegurarse de que la implementación subyacente de caché soporta "locking". Los
proveedores de caché que vienen ya incorporados no lo soportan.

<class name="eg.Cat" .... >

<cache usage="read-write"/>
....
<set name="kittens" ... >
<cache usage="read-write"/>
....
</set>
</class>

19.2.4. Estrategia de lecto-escritura no estricta

Si la aplicación necesita actualizar datos, pero sólo ocasionalmente (es decir, si es improbable que dos transacciones traten
de actualizar el mismo ítem simultáneamente), y no se requiere un aislamiento de transacciones estricto, puede ser
apropiado un caché "de lecto-escritura no estricta" (nonstrict-read-write). Si el caché se usa en un entorno JTA, se
debe especificar hibernate.transaction.manager_lookup_class. En otros entornos, hay que asegurarse de que la
transacción esté completa para cuando ocurran Session.close() o Session.disconnect().

19.2.5. Estrategia transaccional

170 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

La estrategia transaccional (transactional) provee soporte para proveedores de caché enteramente transaccionales,
como JBoss TreeCache. Tales cachés sólo pueden ser usados en un entorno JTA, y se debe especificar
hibernate.transaction.manager_lookup_class.

19.2.6. Compatibilidad entre el proveedor de caché y la estrategia de concurrencia

Importante

Ninguno de los proveedores de caché soporta todas las estrategias de concurrencia.

La siguiente table muestra qué proveedores son compatibles con qué estrategias de concurrencia.

Tabla 19.2. Soporte de estrategias de concurrencia de caché

lecto-escritura no lecto-
Caché sólo-lectura transaccional
estricta escritura
Hashtable (no concebida para uso en
sí sí sí
producción)
EHCache sí sí sí
OSCache sí sí sí
SwarmCache sí sí
JBoss Cache 1.x sí sí
JBoss Cache 2 sí sí

19.3. Admninistrar los cachés


Siempre que un objeto se le pase a save(), update() o saveOrUpdate(), y cada vez que se obtenga un objeto usando
load(), get(), list(), iterate() o scroll(), dicho objeto se agrega al caché interno de la sesión.

Cuando a continuación se invoca flush(), el estado de dicho objeto será sincronizado con la base de datos. Si no se
quiere que esta sincronización ocurra, o si se está procesando una cantidad inmensa de objetos que haga necesario manejar
la memoria eficientemente, se puede usar el método evict() para quitar objetos y sus colecciones del caché de primer
nivel.

ScrollableResult cats = sess.createQuery("from Cat as cat").scroll(); //un resultset inmenso


while ( cats.next() ) {
Cat cat = (Cat) cats.get(0);
doSomethingWithACat(cat);
sess.evict(cat);
}

La Session también provee un método contains() para determinar si una instancia ya pertenece al caché de sesión.

Para desalojar a todos los objetos del caché de sesión, llame Session.clear()

Para el caché de 2do nivel, hay métodos definidos en SessionFactory para desalojar el estado cacheado de una instancia,
de toda una clase, de la instancia de una colección, o de un rol de colección completo.

sessionFactory.evict(Cat.class, catId); //desaloja una instancia de Cat en particular


sessionFactory.evict(Cat.class); //desaloja todos los Cats
sessionFactory.evictCollection("Cat.kittens", catId); //desaloja un colección de kittens en particu
sessionFactory.evictCollection("Cat.kittens"); //desaloja todas lsa colecciones de kittens

El argumento CacheMode controla cómo una sesión en particular interactúa con el caché de 2do nivel.

CacheMode.NORMAL: lee y escribe items en el caché de 2do nivel

171 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

CacheMode.GET: lee items del caché de 2do nivel, pero no escribe en el caché de 2do nivel excepto cuando se
actualicen datos.

CacheMode.PUT:escribe items en el caché de 2do nivel, pero no lee.

items en el caché de 2do nivel, pero lee del caché de 2do nivel, elude los efectos de
CacheMode.REFRESH: escribe
do
hibernate.cache.use_minimal_puts, forzando un refresco del caché de 2 nivel para todos los items leídos de
la base de datos.

Para navegar por ls contenidos del caché de 2do nivel, o de una región de cacheo de consultas, use la API de Statistics:

Map cacheEntries = sessionFactory.getStatistics()


.getSecondLevelCacheStatistics(regionName)
.getEntries();

Necesitará habilitar estadísticas, y, optativamente, forzar a Hibernate a que escriba las entradas de caché en un formato
más legible por seres humanos:

hibernate.generate_statistics true
hibernate.cache.use_structured_entries true

19.4. El caché de consultas (query cache)


Los resultados de las consultas también pueden ser cacheados. Esto sólo esútil para consultas que son ejecutadas
frecuentemente, y con los mismos parámetros. Para usar el caché de consutas, primero hay que habilitarlo:

hibernate.cache.use_query_cache true

Esta configuración provoca la creación de dos nuevas regiones de caché: una que contendrá los resultados cacheados de
las consultas (org.hibernate.cache.StandardQueryCache), y la otra conteniendo la fecha y hora de las actualizaciones
más recientes hechas en las tablas "consultables" (org.hibernate.cache.UpdateTimestampsCache). Note que el caché
de consultas no cachea el estado de las entidades mismas contenidas en el resultado; cachea solamente los valores de los
identificadores, y los resultados de tipo "value type". Así que el caché de consultas siempre debería ser usado en
conjunción con el caché de 2do nivel.

A la mayoría de las consultas no les representa ninguna ventaja el ser cacheadas, así que las consultas no se cachean por
defecto. Par habilitar el cacheo, invoque Query.setCacheable(true). Este llamado permite que la consulta, al ser
ejecutada, busque resultados ya existentes, o agregue sus resultados al caché.

Si se require un control más granular sobre las políticas de expiración de los cachés, se puede especificar una región de
caché para una consulta en particular, invocando Query.setCacheRegion().

List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger")


.setEntity("blogger", blogger)
.setMaxResults(15)
.setCacheable(true)
.setCacheRegion("frontpages")
.list();

Si la consulta forzare un refresco de esta región de caché en particular, habría que invocar
Query.setCacheMode(CacheMode.REFRESH). Esto es particularmente útil para los casos en donde los datos subyacentes
pudieren haber sido actualizados por un proceso separado (por ejemplo, no por Hibernate), y le permite a la aplicación
refrescar selectivamente un resultado en particular. Esto es más eficiente que el desalojo de toda una región de cachés via
SessionFactory.evictQueries().

19.5. Comprender la performance de las colecciones


Ya hemos invertido bastante tiempo en hablar sobre las colecciones. En esta sección, enfatizaremos un par de asuntos
más, referentes a cómo se comportan las colecciones en tiempo de ejecución.

172 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

19.5.1. Taxonomía

Hibernate define tres tipos básicos de colección:

colección de valores

asociaciones de-uno-a-muchos

asociaciones de-muchos-a-muchos

Esta clasificación distingue entre las varias relaciones de tablas y claves foráneas, pero en realidad no nos dice todo lo que
necesitamos saber acerca del modelo relacional. Para comprender cabalmente la estructura relacional y las características
de performance, debemos considerar también la estructura de la clave primaria que es usada por Hibernate para actualizar
o borrar colecciones de filas. Esto a su vez sugiere la siguiente clasificación:

colecciones indexadas

sets

bags

Todas las colecciones indexadas (maps, lists, arrays), tienen una clave primaria que consiste en las columnas de <key> e
<index>. En este caso, las actualizaciones a la colección son extremadamente eficientes: la clave primaria puede ser
indexada eficientemente y una fila en particular puede ser localizada eficientemente cuando Hibernate trate de actualizarla
o borrarla.

Los sets tienen una clave primaria que consiste en una <key> y columnas elemento. Esto puede ser menos eficiente para
algunos tipos de elemento de colección, particularmente los elementos compuestos, o grandes campos de texto o binarios;
la base de datos puede no ser capaz de indexar tan eficientemente una clave primaria compleja. Por otra parte, para
asociaciones de-uno-a-muchos o de-muchos-a-muchos, particularmente cuando se usen identificadores sintéticos, es muy
probable que sea igual de eficiente. (Nota aparte: si se quiere que SchemaExport realmente cree por sí solo la clave
primaria de un <set>, debe declararse not-null="true" para todas las columnas).

los mapeos <idbag> definen una clave sustituta, así que son muy eficientes de actualizar. De hecho, son el mejor caso.

Las bags son el peor caso. Como una bag permite valores de elemento duplicados y no tiene columnas índice, no se puede
definir ninguna clave primaria. Hibernate no tiene forma de distinguir entre filas duplicadas. Hibernate resuelve este
problema borrando completamente la colección (con un simple DELETE) y recreándola cada vez que algo cambia. Esto
puede llegar a ser muy ineficiente.

Note que para una asociación de-uno-a-muchos, la "clave primaria" piede no ser la clave primiaria física de la tabla en la
base de datos. Pero incluso en este caso, la clasificación recién descripta es aún útil (aún refleja cómo es que Hibernate
"localiza" filas individuales de una colección).

19.5.2. Las lists, maps, idbags y sets son las colecciones más eficientes de actualizar

Basándose en la discusión precedente, debería quedar claro que las colecciones indexadas y (normalmente) los sets
permiten el desempeño más eficiente en términos de agregar, quitar, o actualizar elementos.

Podría decirse las colecciones indexadas tienen una ventaja más sobre los sets en las asociaciones de-muchos-a-muchos o
colecciones de valores: a causa de la estructura de un Set, Hibernate nunca efectúa un UPDATE de una fila cuando un
elemento se "cambia". Los cambios en un Set siempre requieren INSERT(s) y DELETE(s) de filas individuales. Esta
consideración, de nuevo, no se aplica a las asociaciones de-uno-a-muchos.

Tras observar que los arrays no pueden ser haraganes, concluiríamos que las listas, mapas e idbags son los tipos (no
inversos) de colección con mejor performance, seguidos de cerca por los sets. Los sets son el tipo más común de colección
en Hibernate, porque la semántica de un set es la más natural para un modelo relacional.

Sin embargo, en un modelo de dominio Hibernate bien diseñado, usualmente vemos que la mayóría de las coleccioes son
en realidad asociaciones de-uno-a-muchos con inverse="true". Para estas asociaciones, el UPDATE es manejado por el
extremo 'de-muchos-a-uno' de la asociación, así que estas disquisiciones sobre la performance al actualizar colecciones
simplemente no son relevantes.

173 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

19.5.3. Las bags y lists son las colecciones inversas más eficientes

No descarte a las bags para siempre, todavía. Hay un caso en particular en el cual las bags (y también las lists) tienen
mucha mejor performance que los sets. Para una colección con inverse="true" (la relación bidireccional estándar
estándar de-uno-a-muchos, por ejemplo, ¡podemos agregar un elemento a una bag o list sin necesidad de inicializar
(mediate una captura) los elementos de la bag! Esto se debe a que Collection.add() y Collection.addAll() siempre
tienen que devolver "true" para una bag o list, al contrario de lo que ocurre con un Set. Esto puede volver el código
siguiente mucho más rápido:

Parent p = (Parent) sess.load(Parent.class, id);


Child c = new Child();
c.setParent(p);
p.getChildren().add(c); // ¡no es necesario capturar toda la colección!
sess.flush();

19.5.4. Borrado en una pasada

Ocasionalmente, borrar elementos de una colección puede volverse extremadamente ineficiente. Hibernate no es
completamente estúpido, así que sabe que no lo tiene que hacer en el caso de una colección nueva y vacía, si uno invoca
list.clear(), por ejemplo. En este caso, Hibernate emite un solo DELETE, y listo.

Supongamos que se agrega un simple elemento a una colección de tamaño 20, y luego se quitan 2 elementos. Hibernate
emitirá un comando INSERT y dos DELETEs (a menos que la colección sea una bag). Esto es deseable.

Sin embargo, suponga que quitamos 18 elementos, dejando 2, y luego agregamos el nuevo elemento. Aquí hay dos
maneras de proceder:

borrar 18 filas una por una, y luego insertar 3 filas

borrar toda la colección (usando un solo DELETE) e insertar todos los 5 elementos actuales, uno por uno.

Hibernate no es lo suficientemente astuto como para darse cuenta de que la segunda opción probablemente sea más
rápida.(Y probablemente sea mejor así, tal comportamiento podría confundir a los triggers de la BD, etc).

Afortunadamente, podemos forzar este comportamiento (la estrategia número 2) en cualquier momento, descartando (es
decir des-referenciando) la colección original, y devolviendo una colección recientemente instanciada con todos los
elementos activos. Esto puede ser muy útil y poderoso a veces.

Por supuesto, los borrados en una pasada no son relevantes cuando tratamos con colecciones mapeadas con
inverse="true".

19.6. Monitorear la performance


Optimizar no es muy útil sin monitoreo y acceso a medidas concretas de performance. Hibernate provee un rango muy
completo de valores numéricos acerca de sus operaciones internas. Las estadísticas en Hibernate son accesibles por cada
SessionFactory.

19.6.1. Monitorear una SessionFactory

Se puede acceder a mediciones de la SessionFactory de dos maneras: La primera opción es llamar a


sessionFactory.getStatistics() y leer o mostrar la Statistics usted mismo.

Hibernate también puese usar JMX para publicar mediciones, si se habilita el MBean StatisticsService MBean. Se
puede habilitar un solo MBean para todas las SessionFactorys, o uno para cada una. Vea el siguiente código para
ejemplos mínimos de configuración:

// servicio de registro de MBean para una SessionFactory específica


Hashtable tb = new Hashtable();
tb.put("type", "statistics");
tb.put("sessionFactory", "myFinancialApp");
ObjectName on = new ObjectName("hibernate", tb); // nombre del objeto MBean

174 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

StatisticsService stats = new StatisticsService(); // implementación del MBean


stats.setSessionFactory(sessionFactory); // liga las estadísticas a la SessionFactory
server.registerMBean(stats, on); // registra el Mbean em el servidor

// servicio de registro de MBean para todas las SessionFactorys


Hashtable tb = new Hashtable();
tb.put("type", "statistics");
tb.put("sessionFactory", "all");
ObjectName on = new ObjectName("hibernate", tb); // nombre del objeto MBean

StatisticsService stats = new StatisticsService(); // implementación del MBean


server.registerMBean(stats, on); // Registra el MBean en el servidor

A hacer: esto no tiene sentido. En el primer caso, obtenemos y usamos el MBean dorectamente. En el segundo, debemos
dar el nombre JNDI en el cual la fábrica de sesiones está contenida antes de usarla. Usar
hibernateStatsBean.setSessionFactoryJNDIName("my/JNDI/Name")

Se puede activar/desactivar el monitoreo para una SessionFactory:

en tiempo de configuración, asígnesele a hibernate.generate_statistics el valor false

en tiempo de ejecución, sf.getStatistics().setStatisticsEnabled(true)


ohibernateStatsBean.setStatisticsEnabled(true)

Las estadísticas pueden ser reinicializadas en forma programática, usando el método clear(). Se puede mandar un
sumario al log (a nivel info). usando el método logSummary().

19.6.2. Mediciones

Hibernate provee un buen número de mediciones, desde muy básicas, hasta información especializada que sólo es
relevante en ciertos escenarios. Todos los contadores relevantes están descriptos en la API de Statistics, en tres
categorías:

Las mediciones relacionadas con el uso de la sesión en general, tales como el número de desiones abiertas, el
número de conexiones JDBC obtenidas, etc.

Mediciones relacionadas con las entidades, colecciones, consultas y cachés como un todo (es decir, mediciones
globales).

Mediciones detalladas relacionadas con una entidad, colección, consulta o región de caché en particular.

Por ejemplo, se puede chequear la proporción de veces en que se da en el blanco, no se da en el blanco, o se inserta en el
caché (en inglés: hit, miss and put ratio), para las entidades, las colecciones y las consultas, y el tiempo que, en promedio,
necesita una consulta. Tenga en cuenta que el número de milisegundos es sólo una aproximación en Java. Hibernate está
atado a la precisión de la JVM, y en algunas plataformas ésta tiene solo un grado de exactitud de 10 segundos.

Para acceder a las mediciones globales (es decir, no atadas a una entidad, caché, etc en particular) se usan simples
métodos "getter". Se puede acceder a las mediciones de una entidad, colección o región de caché en particular a través del
nombre, y a través de su representación en HQL o en SQL para las consultas. Por favor refiérase a los JavaDocs de las
APIs de Statistics, EntityStatistics, CollectionStatistics, SecondLevelCacheStatistics, y
QueryStatistics para más información. El siguiente código muestra un ejemplo simple:

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" );

175 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Para trabajar en todas las entidades, colecciones, consultas y regiones de caché, se pued obtener la lista de nombres de
entidades, colecciones, consultas y regiones con los siguientes métodos: getQueries(), getEntityNames(),
getCollectionRoleNames(), y getSecondLevelCacheRegionNames().

Capítulo 20. Guía de las herramientas (Toolset)


Hay un conjunto de plugins de Eclipse, tasks de Ant y herramientas de consola, que permiten uar Hibernate para practicar
ingeniería reversa, y también "de ida" (roundtrip engineering).

Las Herramientas de Hibernate (Hibernate Tools) actualmente incluyen plugins para el entorno ECLIPSE, así como
herramientas de Ant para efectuar ingeniería reversa de base de datos existentes.

Editor de mapeo: Un editor de archivos XML de mapeo de Hibernate, que soporta auto-compleción y resaltado de
sintaxis. También soporta auto-compleción semántica para los nombres de las clases y para los nombres de campos
y propiedades, haciéndolo mucho más versátil que un editor de XML común.

Consola: La consola es una bueva vista (view) de Eclipse. Además de una visión jarárquica general de la
configuración de la consola, también permite la ejecución de consultas HQL contra la base de datos, y nevegar los
resultados directamente en Eclipse.

Wizards para desarrollo: Con las herramientas de Hibernate Eclipse se proveen varios "wizards".
(N.del T): se les suele llamar "wizards" o hechiceros a los programas que van nagevando de una pantalla a la otra
de manera secuencial, para permitirle al usuario realizar una operación compleja guiándolo a través de unas
pocas opciones simples por vez.
Se puede usar un "wizard" para generar rápidamente archivos de configuración de Hibernate, o se se le puede hacer
una ingeniería reversa completa a un esquema de DB existente, convirtiéndolo en archivos fuente tipo POJO y
archivos de mapeo de Hibernate. El wizard para ingeniería reversa soporta patrones (templates) configurables a
medida.

Tareas (tasks) de Ant:

Por favor refiérase al paquete Hibernate Tools y a su documentación para más información.

Por otra parte, el paquete principal de Hibernate ya trae incluida una herramienta (que puede hasta puede ser usada
instantáneamente desde dentro de Hibernate): SchemaExport, también llamada hbm2ddl.

20.1. Generación automática del esquema de base de datos


El lenguaje de definición de datos (o DDL por su siglas en inglés) puede ser generado por una utilidad de Hibernate a
partir de los archivos de mapeo. El esquema generado incluye las constraints de integridad referencial (claves primarias y
foráneas) para las tablas de entidades y colecciones. También se crean tablas y secuencias para los generadores de
identificadores.

Se debe especificar el dialecto SQL (Dialect) a través de la propiedad hibernate.dialect cuando se use esta
herramienta, dado que el DDL es altamente dependiente del proveedor de DB.

Primero, retoque sus archivos de mapeo para mejorar el esquema a generar.

20.1.1. Retocar el esquema de base de datos

Muchos elementos de mapeo de Hibernate definen atributos opcionales llamados length, precision y scale. Se puede
configurar el largo, precisión y escala respectivamente de una columna con estos atributos.

<property name="zip" length="5"/>

<property name="balance" precision="12" scale="2"/>

Algunas tags también aceptan un atributo not-null (para generar una constraint NOT NULL en las columnas de la tabla),
y un atributo unique (para generar constraints de unicidad en las columnas de la tabla).

<many-to-one name="bar" column="barId" not-null="true"/>

176 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

<element column="serialNumber" type="long" not-null="true" unique="true"/>

Se puede usar un atributo unique-key para agrupar columnas en una simple constraint de unicidad. Al presente, el valor
especificado del atributo unique-key no es usado para nombrar la constraint en el DDL generado, solamente para agrupar
a las columnas en el archivo de mapeo.

<many-to-one name="org" column="orgId" unique-key="OrgEmployeeId"/>


<property name="employeeId" unique-key="OrgEmployee"/>

Un atributo index especifica el nombre de un índice a crear utilizando la columna o columnas mapeadas. Se puede agrupar
múltiples columnas en en mismo índice, simplemente especificando el mismo nombre de índice.

<property name="lastName" index="CustName"/>


<property name="firstName" index="CustName"/>

Un atributo foreign-key puede ser usado para sustituir el nombre generado de cualquier constraint de clave foránea.

<many-to-one name="bar" column="barId" foreign-key="FKFooBar"/>

Muvhos elementos de mapeo también aceptan un elemento <column> hijo. Esto es particularmente útil al mapear tipos
multicolumna:

<property name="name" type="my.customtypes.Name"/>


<column name="last" not-null="true" index="bar_idx" length="30"/>
<column name="first" not-null="true" index="bar_idx" length="20"/>
<column name="initial"/>
</property>

El atributo default permite especificar un valor por defecto pra la columna (debería asignársele el mismo valor a la
propiedad mapeada antes de grabar una nueva instancia de la clase mapeada).

<property name="credits" type="integer" insert="false">


<column name="credits" default="10"/>
</property>

<version name="version" type="integer" insert="false">


<column name="version" default="0"/>
</property>

El atributo sql-type le permite al usuario sustituir el mapeo por defecto de un tipo de dato Hibernate a un tipo de dato
SQL.

<property name="balance" type="float">


<column name="balance" sql-type="decimal(13,3)"/>
</property>

El atributo check permite especificar una constraint de chequeo.

<property name="foo" type="integer">


<column name="foo" check="foo > 10"/>
</property>

<class name="Foo" table="foos" check="bar < 100.0">

...
<property name="bar" type="float"/>
</class>

Tabla 20.1. Sumario

Atributo Valores Interpretación


largo numéricos largo de la columna
precision numéricos precisión decimal de la columna
scale numérico escala decimal de la columna

177 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Atributo Valores Interpretación


not-null true|false especifica que la columna debería ser no anulable
unique true|false especifica que la columna debería tener una constraint de unicidad
index index_name especifica el nombre de un índice multicolumna
unique-key unique_key_name especifica el nombre de una constraint de unicidad multicolumna
especifica el nombre de una constraint de clave foránea generada para una
asociación, para un elemento de mapeo <one-to-one>, <many-to-one>, <key>,
foreign-key foreign_key_name
o <many-to-many>. Note que los extremos con inverse="true" serán
ignorados por el SchemaExport.
tipo SQL de la sustituye el tipo de columna por defecto (atributo del elemento <column>
sql-type
columna solamente)
default expresión SQL especifica un valor por defecto para la columna
check expresión SQL crea una constraint de chequeo, sea para la tabla o para la columna

El elemento <comment> permite especificar comentarios en el esquema generado.

<class name="Customer" table="CurCust">


<comment>Current customers only</comment>

...
</class>

<property name="balance">
<column name="bal">
<comment>Balance in USD</comment>
</column>
</property>

Esto resulta en comandos comment on table o comment on column en el DDL generado, cuando esto se soportada.

20.1.2. Ejecutar la herramienta

La herramienta SchemaExport imprime un script DDL en la salida estándar y/o ejecuta los comandos DDL.

java -cp hibernate_classpaths org.hibernate.tool.hbm2ddl.SchemaExport options mapping_files

Table 20.2. Opciones de línea de comandos para SchemaExport

Opción Descripción
--quiet no imprima el script en la salida estándar
--drop sólo elimine (drop) las tablas
--create sólo cree las tablas
--text n exporte a la base de datos
--output=my_schema.ddl escriba el script DDL en un archivo
--naming=eg.MyNamingStrategy seleccione una NamingStrategy (estrategia de nombrado)
--config=hibernate.cfg.xml lea la configuración de Hibernate de una archivo XML
--properties=hibernate.properties lea las propiedades de la BD de un archivo
--format formatee prolijamente el SQL generado en el script
--delimiter=; agréguele un delimitador de fin de línea al script

También se puede incrustar un SchemaExport dentro de la aplicación

178 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Configuration cfg = ....;


new SchemaExport(cfg).create(false, true);

20.1.3. Propiedades

Se puede especificar propiedades de base de datos:

como propiedades de sistema con -D<propiedad>

en hibernate.properties

en un archivo nombrado, con --properties

Las propiedades que se necesitan son:

Tabla 20.3. Porpiedades de conexión de SchemaExport

Nombre de la propiedad Descripción


hibernate.connection.driver_class clase del driver jdbc
hibernate.connection.url URL jdbc
hibernate.connection.username usuario de la base de datos database user
hibernate.connection.password clave del usuario
hibernate.dialect dialecto

20.1.4. Usar Ant

Se puede llamar al SchemaExport desde un script de Ant

<target name="schemaexport">
<taskdef name="schemaexport" classname="org.hibernate.tool.hbm2ddl.SchemaExportTask" classpathr

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

20.1.5. Actualizaciones incrementales del esquema de base de datos

La herramienta SchemaUpdate actualizará un esquema de base de datos existente con cambios "incrementales". Note que
SchemaUpdate depende grandemente de la API de metadatos de JDBC, así que no funcionará con todos los drivers de
JDBC.

java -cp hibernate_classpaths org.hibernate.tool.hbm2ddl.SchemaUpdate options mapping_files

Table 20.4. SchemaUpdate Command Line Options

Opción Descripción
--quiet no imprima el script en la salida estándar
--text no exporte el script a la base de datos
--naming=eg.MyNamingStrategy seleccione una NamingStrategy

179 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Opción Descripción
--properties=hibernate.properties lea las propiedades de base de datos de un archivo
--config=hibernate.cfg.xml especifique un archivo .cfg.xml

Se puede incrustar un SchemaUpdate en la aplicación

Configuration cfg = ....;


new SchemaUpdate(cfg).execute(false);

20.1.6. Utilizar Ant para las actualizaciones incrementales del esquema de base de datos

Se puede invocar a SchemaUpdate desde el script de Ant:

<target name="schemaupdate">
<taskdef name="schemaupdate" classname="org.hibernate.tool.hbm2ddl.SchemaUpdateTask" classpathr

<schemaupdate properties="hibernate.properties" quiet="no">


<fileset dir="src">
<include name="**/*.hbm.xml"/>
</fileset>
</schemaupdate>
</target>

20.1.7. Validación del esquema de base de datos

La herramienta SchemaValidator validará si el esquema de BD existente "corresponde" con los archivos de mapeo. Note
que el SchemaValidator depende grandemente de la API de metadatos de JDB, así que no funcionará con todos los
drivers JDBC, Esta herramienta es extremadamente útil para tests.

java -cp hibernate_classpaths org.hibernate.tool.hbm2ddl.SchemaValidator options mapping_files

Tabla 20.5. Opciones de línea de comando para SchemaValidator

Opción Descripción
--naming=eg.MyNamingStrategy selecciona una NamingStrategy
--properties=hibernate.properties lee las propiedades de base de datos de una archivo
--config=hibernate.cfg.xml especifica un archivo .cfg.xml

Se puede incrustar un archivo SchemaValidator en la aplicación

Configuration cfg = ....;


new SchemaValidator(cfg).validate();

20.1.8. Usar Ant para la validación de un esquema de base de datos

Se puede llamar al SchemaValidator desde el script de Ant:

<target name="schemavalidate">
<taskdef name="schemavalidator" classname="org.hibernate.tool.hbm2ddl.SchemaValidatorTask" clas

<schemavalidator properties="hibernate.properties">
<fileset dir="src">
<include name="**/*.hbm.xml"/>
</fileset>
</schemavalidator>
</target>

Capítulo 21. Ejemplo: Padre/Hijo

180 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Una de las primeras cosas que los nuevos usuarios tratan de hacer con Hibernate, es modelar una relación del tipo
padre/hijo (en inglés, "parent" y "child" respectivamente). Hay dos abordajes diferentes para esto. Por varias razones, el
abordaje más conveniente, especialmente para usuarios nuevos, es modelar tanto Padre como Hijo como clases de
entidad, con una asociación de-uno-a-muchos (<one-to-many>) del Padre al Hijo. (El enfoque alternativo es declarar el
Hijo como un elemento compuesto ( <composite-element>). Ahora bien, ocurre que la semántica por defecto de una
asociación de-uno-a-muchos (en Hibernate), es mucho más parecida a la semántica usual de una relación padre/hijo, que a
la semántica del mapeo de un elemento compuesto. Explicaremos cómo usar una asociación 'de-uno-a-muchos
bidireccional con propagación en cascada' para modelar una relación padre/hijo eficiente y elegantemente. ¡No es nada
difícil!

21.1. Nota sobre las colecciones


Las colecciones de Hibernate se consideran como una parte lógica de la entidad que las posee, nunca de las entidades
contenidas. ¡Esta distinción es crucial! Y tiene las siguientes consecuencias:

Cuando quitamos/agregamos un objeto de una colección, el número de versión del dueño de la colección se
incrementa.

Si un objeto quitado de una colección es una instancia de un "value type" (es decir, de un elemento compuesto),
dicho objeto dejará de ser persistente y su estado será completamente borrado de la base de datos. Del mismo modo,
agregarle una instancia de "value type" a la colección causará que su estado se vuelva inmediatamente persistente.

Por otra parte, si es una entidad lo que se quita de una colección (en una asociación de-uno-a-muchos o de-muchos-
a-muchos), ésta no será borrada por defecto. Este comportamiento es completamente consistente: ¡un cambio en el
estado interno de una entidad no debería causar que la entidad asociada se esfume! Del mismo modo, agregarle una
entidad a una colección no provoca por defecto que la entidad se vuelva persitente.

En cambio, el comportamiento por defecto es que, agregarle una entidad a una colección, simplemente crea un vínculo
entre las dos entidades, y quitársela borra dicho vínculo. Esto es muy apropiado para todos los casos, excepto para el caso
en que la vida de un hijo dependa del ciclo de vida del padre.

21.2. de-uno-a-muchos bidireccional


Supongamos que se empieza con una simple asociación de-uno-a-muchos de padre a hijo.

<set name="children">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>

If we were to execute the following code

Parent p = .....;
Child c = new Child();
p.getChildren().add(c);
session.save(c);
session.flush();

Hibernate emitirá los siguientes comandos SQL:

un INSERT para crear el registro para c

un UPDATE para crear el vínculo de p a c

Esto es no solamente ineficiente, sino que viola cualquier constraint NOT NULL que pudiere haber en la columna
parent_id column. Podemos areglar la violación de la constraint de nulabilidad especificando not-null="true" en el
mapeo de la colección:

<set name="children">
<key column="parent_id" not-null="true"/>
<one-to-many class="Child"/>
</set>

181 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

De todos modos, ésta no es la solución que se recomienda.

La causa subyacente de este comportamiento es que el vínculo (la clave foránea parent_id) de p a c no se considera
como parte del estado del objeto Child y por lo tanto no es creado durante el INSERT. Así que la solución es volver al
vínculo que es parte del mapeo del objeto Child.

<many-to-one name="parent" column="parent_id" not-null="true"/>

(También es necesario agragar la propiedad parent a la clase Child).

Ahora que la entidad Child está manejando el estado del vínculo, le indicamos a la colección que no actualice el vínculo
ella. Para esto se usa el atributo inverse.

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


<key column="parent_id"/>
<one-to-many class="Child"/>
</set>

El código siguiente sería usado para agregar un nuevo Child:

Parent p = (Parent) session.load(Parent.class, pid);


Child c = new Child();
c.setParent(p);
p.getChildren().add(c);
session.save(c);
session.flush();

¡Y ahora, sólo se emite un comando INSERT!

Para afinar las cosas un poco más, podríamos crear un método addChild() en la clase Parent.

public void addChild(Child c) {


c.setParent(this);
children.add(c);
}

y ahora, el cófigo para agregar un Child se ve así:

Parent p = (Parent) session.load(Parent.class, pid);


Child c = new Child();
p.addChild(c);
session.save(c);
session.flush();

21.3. Ciclo de vida de la propagación en cascada


El llamado explícito a save() aún es molesto. Nos ocuparemos de esto usando la propagación en cascada.

<set name="children" inverse="true" cascade="all">


<key column="parent_id"/>
<one-to-many class="Child"/>
</set>

Esto simplifica el código anterior a

Parent p = (Parent) session.load(Parent.class, pid);


Child c = new Child();
p.addChild(c);
session.flush();

En forma similar, no necesitamos iterar a través de los hijos al grabar o borrar un Parent. Lo siguiente borra a p y a todos
sus hijos de la base de datos:

Parent p = (Parent) session.load(Parent.class, pid);


session.delete(p);
session.flush();

182 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Sin embargo, este código

Parent p = (Parent) session.load(Parent.class, pid);


Child c = (Child) p.getChildren().iterator().next();
p.getChildren().remove(c);
c.setParent(null);
session.flush();

no borrará a c de la base de datos; sólo quitará el vínculo con p (y causará en este caso una violación de constraint NOT
NULL). Hay que borrar al (con delete()) explícitamente al Child.

Parent p = (Parent) session.load(Parent.class, pid);


Child c = (Child) p.getChildren().iterator().next();
p.getChildren().remove(c);
session.delete(c);
session.flush();

Ahora, en este caso, un Child no puede realmente existir sin su padre. Entonces, si quitamos un Child de una colección,
realmente queremos que sea borrado. Para ello debemos usar cascade="all-delete-orphan".

<set name="children" inverse="true" cascade="all-delete-orphan">


<key column="parent_id"/>
<one-to-many class="Child"/>
</set>

Nota: aún cuando el mapeo de la colección especifica inverse="true", lás propagaciones en cascada sí se procesan
iterando a través de los elementos de la colección. Así que si se necesita que una colección sea grabada, borrada o
actualizada en cascada, se debe agregar el atributo "cascade" a la colección. No basta con invocar setParent().

21.4. Las propagaciones en cascada y unsaved-value


Supóngase que cargamos un Parent en una sesión, hacemos algunos cambios en la interfaz de usuario, y deseamos
persistir dichos cambios en una nueva sesión, invocando update(). El Parent contendrá una colección de hijos, y como
la propagación en cascada para "update" está habilitada, Hibernate necesita saber cuáles de los hijos han sido
recientemente instanciados, y cuáles representan filas ya existentes en la base de datos. Asumamos que tanto Parent
como Child tienen identificadores generados del tipo Long. Hibernate Hibernate usará el identificador y los valores de
propiedad de version/timestamp para determinar cuáles de los hijos son nuevos (Vea Sección 10.7, “Detección automática
de estado”). En Hibernate3, ya no es necesario especificar un unsaved-value explícitamente.

El siguiente código actualizará tanto al padre como al hijo, e insertará un nuevo hijo (newChild).

//tanto el padre como el hijo fueron cargados en una sesión anterior


parent.addChild(child);
Child newChild = new Child();
parent.addChild(newChild);
session.update(parent);
session.flush();

Todo esto está muy bien en el caso de un identificador autogenerado, pero ¿qué pasas con los identificadores asignados, o
con los identificadores compuestos? Esto es más difícil, dado que Hibernate no puede usar la propiedad identificadora para
distinguir entre un objeto recientemente instanciado (con un identificador asignado por el usuario) y un objeto cargado en
una sesión previa. En este caso, Hibernate usará o bien las propiedades version/timestamp, o bien consultará el caché de
2do nivel, o, en el peor de los casos, la base de datos, para verificar si la fila existe.

21.5. Conclusión
Esto es bastante para digerir de una vez, y puede parecer confuso la primera vez. Pero en la práctica todo se acomoda y
funciona en una forma muy linda. La mayoría de las aplicaciones Hibernate utilizan el patrón padre/hijo en muchos
lugares.

En el primer párrafo mencionábamos una alternativa. En el caso de los mapeos con <composite-element> no existe
ninguno de estos problemas, que tienen exactamente la semántica de una relación padre/hijo. Desafortunadamente, existen
dos grandes limitaciones al trabajar con clases de elementos compuestos: un elemento compuesto no puede contener
colecciones, y ellos mismos sólo pueden ser hijos de una única entidad padre.

183 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

Capítulo 22. Ejemplo: la aplicación Weblog

22.1. Clases persistentes


Las clases pesistentes representan: un weblog (bitácora de web), y un item publicado en el weblog. Tienen que ser
modeladas según una relación padre/hijo estándar, pero utulizaremos una bag ordenada, en lugar de un set.

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;
}
}

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;

184 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

}
public void setTitle(String string) {
_title = string;
}
}

22.2. Mapeos de Hibernate


Los mapeos de XML deberían resultar bastante sencillos.

<?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>

<?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>

22.3. Código Hibernate


La clase siguiente demuestra algunas de las cosas que se puede hacer con estas clases, usando Hibernate.

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;

185 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

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 HibernateExceptio

BlogItem item = new BlogItem();


item.setTitle(title);
item.setText(text);
item.setDatetime( Calendar.getInstance() );

186 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

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;
}

187 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

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;
}
}

Capítulo 23. Ejemplo: Mapeos varios


Estos capítulos exponen algunos mapeos de asociación más complejos.

23.1. Empleador/Empleado
El modelo siguiente de la relación entre empleador y empleado (Employer y Employee) usa una verdadera clase de
entidad, Employment (empleo) para representar la asociacón. Esto se hace porque puede haber másde un período de
empleo para las mismas dos partes involucradas. Para modelar valores monetarios y nombres de empleados se usan
componentes.

188 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

He aquí un documento de mapeo posible:

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

Y éste es el esquema de tablas generado por SchemaExport.

create table employers (

189 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

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) refer
alter table employment_periods add constraint employment_periodsFK1 foreign key (employee_id) refer
create sequence employee_id_seq
create sequence employment_id_seq
create sequence employer_id_seq

23.2. Autor/Obra
Considérese el modelo siguiente de la relación entre obra, autor y persona (Work, Author y Person respectivamente).
Representamos la relación entre Work y Author como un asociación de-muchos-a-muchos. Elegimos representar la
asociación entre Author y Person como de-uno-a-uno. Otra posibilidad sería hacer que Author extendiere Person.

El siguiente documento de mapeo representa estas relaciones correctamente:

<hibernate-mapping>

<class name="Work" table="works" discriminator-value="W">

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


<generator class="native"/>
</id>

190 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

<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">


<!-- el Autor tiene que tener el mismo identificador que la Persona -->
<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>

En este mapeo hay 4 tablas. works, authors y persons contienen las obras, autores y personas respectivamente.
author_work es una tabla de asociación que vincula a los autores con sus obras. Aquí está el esquema de tablas, tal cual
es generado por SchemaExport.

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

191 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

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

23.3. Cliente/Orden/Producto
Ahora consideremos un modelo de las relaciones entre cliente, orden, ítem (es decir, línea de la orden) y producto
(Customer, Order, LineItem y Product respectivamente). Hay una asociación de-uno-a-muchos entre Customer y
Order, pero ¿cómo deberíamos representar Order / LineItem / Product? Elijo mapear LineItem como una clase de
asociación que representa la asociación de-muchos-a-muchos entre Order y Product. En Hibernate, esto se denomina un
"elemento compuesto" (composite element).

El documento de mapeo:

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

customers, orders, line_items y products contienen los datos de clientes, órdenes, items de las órdenes y productos,
respectivamente. line_items también actúa como una tabla de asociación que vincula órdenes con productos.

create table customers (


id BIGINT not null generated by default as identity,
name VARCHAR(255),

192 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

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

23.4. Ejemplos misceláneos de asociación


Estos ejemplos han sido extraídos todos ellos de la suite de tests de Hibernate. Allí pueden encontrarse muchos otros
ejemplos útiles. Busque en el directorio test de la distribución de Hibernate.

A HACER: revestir esto con palabras

23.4.1. Asociación de-uno-a-uno "con tipo"

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

23.4.2. Ejemplo de clave compuesta

<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">

193 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

<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)

194 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

from LineItem li
where li.productId = productId )
</formula>
</property>

</class>

23.4.3. de-muchos-a-muchos con atributo compartido de clave compuesta

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

23.4.4. Discriminación basada en el contenido

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

195 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

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

23.4.5. Asociaciones en claves alternativas

<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"/>
</id>

<many-to-one name="user" column="userId" property-ref="userId"/>


<property name="type" not-null="true"/>

</class>

Capítulo 24. Prácticas recomendadas


Escriba clases con gran nivel de detalle, y mapéelas usando <component>.

Use una clase Direccion para encapsular calle, barrio, provincia, CodigoPostal. Esto alienta la reutilización
de código y simplifica la reingeniería.

Declare propiedades identificadoras en las clases persistentes.

Hibernate hace que las propiedades identificadoras sean opcionales, pero existe todo tipo de razones por las cuales
habría que usarlos siempre. Recomendamos que estos identificadores sean "sintéticos" (generados, sin ningún

196 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

significado de negcocios).

Identifique claves naturales.

Identifique claves naturales para todas las entidades, y mapéelas usando <natural-id>. Implemente equals() y
hashCode() para comparar las propiedades que componen la clave natural.

Ubique cada mapeo de clase en su propio archivo.

No use un único documento de mapeo monolítico. Mapee com.eg.Foo en el archivo com/eg/Foo.hbm.xml. Esto
tiene sentido, particularmente en un entorno de programación en equipo.

Cargue los archivos de mapeo como recursos.

Despliegue los mapeos junto con las clases que mapean.

Considere externalizar las cadenas SQL de las consultas.

Ésta es una buena práctica si sus consultas utilizan funciones SQL que no sean estándar de ANSI. Externalizarlas
volverá la aplicación más portátil.

Use variables de vínculo.

Como en JDBC, siempre reemplace valores no constantes con "?". ¡Nunca use manipulación de cadenas para unir
un valor no-constante a una consulta! Mejor aún, considere usar parámetros nombrados en las consultas.

No maneje sus propias conexiones JDBC.

Hibernate le permite a la aplicación manejar sus conexiones JDBC, pero esto debe considerarse como un últmo
recurso. Si no puede usar los proveedores de conexiones que ya vienen incluidos, considere proveer su propia
implementación de org.hibernate.connection.ConnectionProvider.

Considere usar un tipo a medida.

Suponga que tiene un tipo Java, digamos de una biblioteca, que necesita ser persistido, pero no provee los métodos
de acceso necesarios para mapearlo como un componente. Usted debería considerar implementar
org.hibernate.UserType. Esta estrategia libera al código de la aplicación de implementar transformaciones desde
o hacia un tipo Hibernate.

En cuellos de botella, use JDBC manual.

En áreas de performance críticas de un sistema, algunos tipos de operación podrían beneficiarse al usar JDBC
directamente. Pero por favor, espere hasta estar seguro de que algo constituye un cuello de botella. Y no asuma que
JDBC es necesariamente más rápida. Si necesita utilizar JDBC directamente, puede valer la pena abrir una sesión y
usar esa conexión JDBC. De este modo, usted aún puede aprovechar la estrategia de transacciones y el proveedor
de conexiones subyacente.

Comprenda el "flush" de sesión

De tanto en tanto, la sesión sincroniza su estado persistente con la base de datos. La performance se resentirá si este
proceso ocurre demasiado seguido. A veces se puede disminuir la cantidad de "flush" innecesarios inhabilitando el
"flushing" automático, o incluso cambiando el orden de las consultas u otras operaciones dentro de una transacción
en particular.

En una arquitectura de tres capas, considere usar objetos desprendidos.

Cuando se usa una arquitectura de servlet/session bean, se pueden pasar objetos persistentes cargados en el session
bean desde y hacia la capa de servlets/JSP. Use una nueva sesión para servir a cada solicitud (request). Use
Session.merge() o Session.saveOrUpdate() para sincronizar objetos con la base de datos.

En una arquitectura de dos capas, considere usar contextos largos de persistencia.

Las transacciones de DB tienen que ser lo más cortas posible, para que la aplicación sea escalable. Sin embargo, a
veces es necesario implementar transacciones "de aplicación" de largo aliento, una unidad de trabajo única desde el
punto de vista del usuario. Una "transacción de aplicación" puede extenderse por varios ciclos solicitud/respuesta
del cliente. Es común usar objetos desprendidos para implementar transacciones de aplicación. Una alternativa,

197 de 198 17/02/2009 09:25 a.m.


HIBERNATE - Persistencia Relacional para Java Idiomático http://www.hibernar.org/documentacion_es/castellano.html

extremadamente apropiada en arquitecturas de dos capas, es mantener un único contacto de persistencia abierto
(una sesión) para todo el ciclo de vida de la "transacción de aplicación", y simplemente desconectarse de la
conexión JDBC al final de cada slicitud, reconectándose al comenzar la solicitud siguiente. Nunca comparta una
misma sesión a través de más de una "transacción de aplicación" o estará trabajando con datos rancios.

No trate las excepciones como si fueran recuperables.

Esto una práctica necesaria más que una "práctica recomendada". Cuando ocurra una excepción, invoque un
rollback de la transacción, y cierre la sesión. Si no lo hace, Hibernate no puede garantizar que el estado en memoria
represente correctamente el estado persistente. Como corolario de esto, no use Session.load() para determinar si
una instancia con un identificador dado existe en la base de datos. En lugar de eso, use Session.get() o una
consulta.

Prefiera la captura haragana (lazy fetching) para las asociaciones.

Use la captura ansiosa (eager fetching) con mesura. Use proxies y colecciones haraganas para la mayoría de las
asociaciones a clases que no es probable que sean retenidas en el caché de 2do nivel. Para asociaciones a clases
cacheadas, en donde exista una probabilidad extremadamante alta de ubicarla en el caché, inhabilite explícitamente
la captura haragana usando lazy="false". Cuando, para un caso de uso en particular, sea apropiado usar una
captura por join, use una consulta con left join fetch.

use el patrón open session in view, o una fase de ensamble (assembly phase) disciplinada para evitar problemas con
datos no capturados.

Hibernate libera al programador de la tediosa tarea de escribir objetos de transferencia de datos (DTO por sus siglas
en inglés). En una arquitectura EJB tradicional, los DTOs tienen un doble propósito: uno, sortear el problema de que
los entity beans no son serializables; el otro, que implícitamente definen una fase de ensamble en donde todos los
datos usados por la interfaz de usuario o "view" tiene que ser capturados y puestos en orden en DTOs, antes de que
el control le sea devuelto a la capa de presentación. Hibernate elimina el primer propósito, pero aún se necesita una
fase de ensamble (imagínese a los métodos de negocio como si tuvieran un estricto contrato con la capa de
presentación acerca de qué datos hay disponibles en los objetos desprendidos), a menos que se esté dispuesto a
mantener la sesión abierta a través de la capa de presentación. Esto no es una limitación de Hibernate, sino un
requerimiento fundamental para un acceso seguro a datos transaccionales.

Considere abstraer su lógica de negocios, separándola de Hibernate.

Oculte el código (Hibernate) de acceso a datos detrás de una interfaz. Combine los partrones DAO y Sesión Thread
Local. Se puede hacer incluso que algunas clases sean persistidas por JDBC escrita a mano, y asociadas a Hibernate
por medio de un UserType. (Este consejo se aplica sólo a aplicaciones "lo suficientemente largas", ¡no es adecuado
para una aplicación con 5 tablas!)

No use mapeos de asociación exóticos.

Los casos de uso bien definidos, que involucren verdaderas asociaciones de-muchos-a-muchos, son raros. En
general se necesita que la "tabla de vínculo" almacene información adicional. En ese caso, es mucho mejor usar dos
asociaciones de-uno-a-muchos a una clase de vínculo intermedia. De hecho, creemos que la mayoría de las
asociaciones son de-uno-a-muchos y de-uno-a-uno. Hay que tener cuidado al usar cualquier otro estilo de
asociación, y preguntarse si es verdaderamente necesario.

Prefiera las asociaciones bidireccionales.

Las asociaciones unidireccionales son más difíciles de consultar. En aplicaciones grandes, casi todas las asociaciones
deben ser navegables por consultas en ambas direcciones.

198 de 198 17/02/2009 09:25 a.m.

También podría gustarte