Hibernate.org Community Documentation

- Anterior
- Siguiente

## Capítulo 1. Tutorial

Dirigido a los nuevos usuarios, este capítulo brinda una introducción a Hibernate paso por paso, empezando con una aplicación simple usando una base de datos en memoria. Este tutorial se basa en un tutorial anterior que Michael Gloegl desarrolló. Todo el código se encuentra en el directorio `tutorials/web` de la fuente del proyecto.

## Importante

Este tutorial se basa en que el usuario tenga conocimiento de Java y SQL. Si tiene un conocimiento muy limitado de JAVA o SQL, le aconsejamos que empiece con una buena introducción a esta tecnología antes de tratar de aprender sobre Hibernate.

## Nota

La distribución contiene otra aplicación de ejemplo bajo el directorio fuente del proyecto `tutorial/eg`.

## 1.1. Parte 1 - La primera aplicación Hibernate

Para este ejemplo, vamos a configurar una aplicación base de datos pequeña que pueda almacenar eventos a los que queremos asistir e información sobre los anfitriones de estos eventos.

## Nota

Aunque puede utilizar cualquier base de datos con la que se sienta bien, vamos a usar HSQLDB (una base de datos Java en-memoria) para evitar describir la instalación/configuración de cualquier servidor de base de datos en particular.

### 1.1.1. Configuración

Lo primero que tenemos que hacer es configurar el entorno de desarrollo. Vamos a utilizar el "diseño estándar" apoyado por muchas herramientas de construcción tal como Maven. Maven, en particular, tiene un buen recurso que describe este diseño. Como este tutorial va a ser una aplicación web, vamos a crear y a utilizar los directorios `src/main/java`, `src/main/resources` y `src/main/webapp`.

Vamos a usar Maven en este tutorial, sacando ventaja de sus funcionalidades de administración de dependencias transitivas así como la habilidad de muchos IDEs para configurar automáticamente un proyecto para nosotros con base en el descriptor maven.

    <project xmlns="http://maven.apache.org/POM/4.0.0"

             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

        <modelVersion>4.0.0</modelVersion>

        <groupId>org.hibernate.tutorials</groupId>

        <artifactId>hibernate-tutorial</artifactId>

        <version>1.0.0-SNAPSHOT</version>

        <name>First Hibernate Tutorial</name>

        <build>

             <!-- we dont want the version to be part of the generated war file name -->

             <finalName>${artifactId}</finalName>

        </build>

        <dependencies>

            <dependency>

                <groupId>org.hibernate</groupId>

                <artifactId>hibernate-core</artifactId>

            </dependency>

            <!-- Because this is a web app, we also have a dependency on the servlet api. -->

            <dependency>

                <groupId>javax.servlet</groupId>

                <artifactId>servlet-api</artifactId>

            </dependency>

            <!-- Hibernate uses slf4j for logging, for our purposes here use the simple backend -->

            <dependency>

                <groupId>org.slf4j</groupId>

                <artifactId>slf4j-simple</artifactId>

            </dependency>

            <!-- Hibernate gives you a choice of bytecode providers between cglib and javassist -->

            <dependency>

                <groupId>javassist</groupId>

                <artifactId>javassist</artifactId>

            </dependency>

        </dependencies>

    </project>

## Sugerencia

It is not a requirement to use Maven. If you wish to use something else to build this tutorial (such as Ant), the layout will remain the same. The only change is that you will need to manually account for all the needed dependencies. If you use something like Ivy providing transitive dependency management you would still use the dependencies mentioned below. Otherwise, you'd need to grab all dependencies, both explicit and transitive, and add them to the project's classpath. If working from the Hibernate distribution bundle, this would mean `hibernate3.jar`, all artifacts in the `lib/required` directory and all files from either the `lib/bytecode/cglib` or `lib/bytecode/javassist` directory; additionally you will need both the servlet-api jar and one of the slf4j logging backends.

Guarde este archivo como `pom.xml` en el directorio raíz del proyecto.

### 1.1.2. La primera clase

Luego creamos una clase que representa el evento que queremos almacenar en la base de datos, es una clase JavaBean simple 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;

        }

    }

Esta clase utiliza convenciones de nombrado estándares de JavaBean para los métodos de propiedades getter y setter así como también visibilidad privada para los campos. Se recomienda este diseño, pero no se exige. Hibernate también puede acceder a los campos directamente, los métodos de acceso benefician la robustez de la refactorización.

La propiedad `id` tiene un valor identificador único para un evento en particular. Todas las clases de entidad persistentes necesitarán tal propiedad identificadora si queremos utilizar el grupo completo de funcionalidades de Hibernate (también algunas clases dependientes menos importantes). De hecho, la mayoría de las aplicaciones (en especial las aplicaciones web) necesitan distinguir los objetos por identificador, así que usted debe tomar esto como una funcionalidad más que una limitación. Sin embargo, usualmente no manipulamos la identidad de un objeto, por lo tanto, el método setter debe ser privado. Sólamente Hibernate asignará identificadores cuando se guarde un objeto. Como se puede ver, Hibernate puede acceder a métodos de acceso públicos, privados y protegidos, así como también a campos directamente públicos, privados y protegidos. Puede escoger y hacer que se ajuste a su diseño de su aplicación.

El constructor sin argumentos es un requerimiento para todas las clases persistentes, Hibernate tiene que crear objetos por usted utilizando Java Reflection. El constructor puede ser privado; sin embargo, se necesita la visibilidad del paquete para generar proxies en tiempo de ejecución y para la recuperación de datos de manera efectiva sin la instrumentación del código byte.

Duarde este archivo en el directorio `src/main/java/org/hibernate/tutorial/domain`.

### 1.1.3. El archivo de mapeo

Hibernate necesita saber cómo cargar y almacenar objetos de la clase persistente. En este punto es donde entra en juego el archivo de mapeo de Hibernate. Este archivo le dice a Hibernate a que tabla tiene que acceder en la base de datos, y que columnas debe utilizar en esta tabla.

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

    <?xml version="1.0"?>

    <!DOCTYPE hibernate-mapping PUBLIC

            "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

            "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

    <hibernate-mapping package="org.hibernate.tutorial.domain">

    [...]

    </hibernate-mapping

    >

El DTD de Hibernate es sofisticado. Puede utilizarlo para autocompletar los elementos y atributos XML de mapeo en su editor o IDE. Abrir el archivo DTD en su editor de texto es la manera más fácil para obtener una sinopsis de todos los elementos y atributos y para ver los valores por defecto, así como algunos de los comentarios. Note que Hibernate no cargará el fichero DTD de la web, sino que primero lo buscará en la ruta de clase de la aplicación. El archivo DTD se encuentra incluido en `hibernate-core.jar` (también en `hibernate3.jar` si está usando el paquete de la distribución).

## Importante

Omitiremos la declaración de DTD en los ejemplos posteriores para hacer más corto el código. Esto no es opcional.

Entre las dos etiquetas `hibernate-mapping`, incluya un elemento `class`. Todas las clases de entidad persistentes (de nuevo, podrían haber clases dependientes más adelante, las cuales no son entidades de primera clase) necesitan de dicho mapeo en una tabla en la base de datos SQL:

    <hibernate-mapping package="org.hibernate.tutorial.domain">

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

        </class>

    </hibernate-mapping>

Hasta ahora le hemos dicho a Hibernate cómo persistir y cargar el objeto de clase `Event` a la tabla `EVENTS`. Cada instancia se encuentra representada por una fila en esa tabla. Ahora podemos continuar mapeando la propiedad identificadora única a la clave primaria de la tabla. Ya que no queremos preocuparnos por el manejo de este identificador, configuramos la estrategia de generación del identificador de Hibernate para una columna clave primaria sustituta:

    <hibernate-mapping package="org.hibernate.tutorial.domain">

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

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

                <generator class="native"/>

            </id>

        </class>

    </hibernate-mapping>

El elemento `id` es la declaración de la propiedad identificadora. El atributo de mapeo `name="id"` declara el nombre de la propiedad JavaBean y le dice a Hibernate que utilice los métodos `getId()` y `setId()` para acceder a la propiedad. El atributo columna le dice a Hibernate qué columna de la tabla `EVENTS` tiene el valor de la llave principal.

El elemento anidado `generator` especifica la estrategia de generación del identificador (también conocidos como ¿cómo se generan los valores del identificador?). En este caso escogimos `native`, el cual ofrece un nivel de qué tan portátil es dependiendo del dialecto configurado de la base de datos. Hibernate soporta identificadores generados por la base de datos, globalmente únicos así como asignados por la aplicación. La generación del valor del identificador también es uno de los muchos puntos de extensión de Hibernate y puede conectar su propia estrategia.

## Sugerencia

`native` is no longer consider the best strategy in terms of portability. for further discussion, see Sección 28.4, “Generación del identificador”

Por último es necesario decirle a Hibernate sobre las porpiedades de clase de entidad que quedan. Por defecto, ninguna propiedad de la clase se considera persistente:

    <hibernate-mapping package="org.hibernate.tutorial.domain">

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

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

                <generator class="native"/>

            </id>

            <property name="date" type="timestamp" column="EVENT_DATE"/>

            <property name="title"/>

        </class>

    </hibernate-mapping>

Al igual que con el elemento `id`, el atributo `name` del elemento `property` le dice a Hibernate que métodos getter y setter utilizar. Así que en este caso, Hibernate buscará los métodos `getDate()`, `setDate()`, `getTitle()` y `setTitle()`.

## Nota

¿Por qué el mapeo de la propiedad `date` incluye el atributo `column`, pero el de `title` no? Sin el atributo `column` Hibernate utiliza, por defecto, el nombre de propiedad como nombre de la columna. Esto funciona bien para `title`. Sin embargo, `date` es una palabra clave reservada en la mayoría de las bases de datos, así que es mejor que la mapeamos a un nombre diferente.

El mapeo de `title` carece de un atributo `type`. Los tipos que declaramos y utilizamos en los archivos de mapeo no son tipos de datos Java. Tampoco son tipos de base de datos SQL. Estos tipos se llaman tipos de mapeo Hibernate , convertidores que pueden traducir de tipos de datos de Java a SQL y viceversa. De nuevo, Hibernate tratará de determinar el tipo correcto de conversión y de mapeo por sí mismo si el atributo `type` no se encuentra presente en el mapeo. En algunos casos esta detección automática (utilizando Reflection en la clase Java) puede que no tenga lo que usted espera o necesita. Este es el caso de la propiedad `date`. Hibernate no puede saber is la propiedad, la cual es de `java.util.Date`, debe mapear a una columna `date`, `timestamp` o `time` de SQL. Por medio de un convertidor `timestamp`, mapeamos la propiedad y mantenemos la información completa sobre la hora y fecha.

## Sugerencia

Hibernate realiza esta determinación de tipo de mapeo usando reflection cuando se procesan los archivos de mapeo. Esto puede tomar tiempo y recursos así que el rendimiento al arrancar es importante entonces debe considerar el definir explícitamente el tipo a usar.

Guarde este archivo de mapeo como `src/main/resources/org/hibernate/tutorial/domain/Event.hbm.xml`.

### 1.1.4. Configuración de Hibernate

En este momento debe tener la clase persistente y su archivo de mapeo. Ahora debe configurar Hibernate. Primero vamos a configurar HSQLDB para que ejecute en "modo de servidor"

## Nota

Hacemos esto o lo otro y los datos permanecen entre ejecuciones.

Vamos a utilizar el plugin de ejecución Maven para lanzar el servidor HSQLDB ejecutando: `mvn exec:java -Dexec.mainClass="org.hsqldb.Server" -Dexec.args="-database.0 file:target/data/tutorial"`.Lo verá iniciando y vinculandose a un enchufe TCP/IP, allí es donde nuestra aplicación se conectará más adelante. Si quiere dar inicio con una base de datos fresca durante este tutorial, apague HSQLDB, borre todos los archivos en el directorio `target/data` e inicie HSQLDB de nuevo.

Hibernate se conectará a la base de datos de parte de su aplicación así que necesita saber cómo obtener conexiones. Para este tutorial vamos a utilizar un pool de conexiones autónomo (opuesto a `javax.sql.DataSource`). Hibernate viene con soporte para dos pools de conexiones JDBC de código abierto de terceros: c3p0 y proxool. Sin embargo, vamos a utilizar el pool de conexiones incluido de Hibernate para este tutorial.

## Atención

El pool de conexiones de Hibernate no está diseñado para utilizarse en producción. Le faltan varias funcionalidades que se encuentran en cualquier pool de conexiones decente.

Para la configuración de Hibernate, podemos utilizar un archivo `hibernate.properties` simple, un archivo `hibernate.cfg.xml` un poco más sofisticado, o incluso una configuración completamente programática. La mayoría de los usuarios prefieren 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://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

    <hibernate-configuration>

        <session-factory>

            <!-- Database connection settings -->

            <property name="connection.driver_class"

    >org.hsqldb.jdbcDriver</property>

            <property name="connection.url"

    >jdbc:hsqldb:hsql://localhost</property>

            <property name="connection.username"

    >sa</property>

            <property name="connection.password"

    ></property>

            <!-- JDBC connection pool (use the built-in) -->

            <property name="connection.pool_size"

    >1</property>

            <!-- SQL dialect -->

            <property name="dialect"

    >org.hibernate.dialect.HSQLDialect</property>

            <!-- Enable Hibernate's automatic session context management -->

            <property name="current_session_context_class"

    >thread</property>

            <!-- Disable the second-level cache  -->

            <property name="cache.provider_class"

    >org.hibernate.cache.NoCacheProvider</property>

            <!-- Echo all executed SQL to stdout -->

            <property name="show_sql"

    >true</property>

            <!-- Drop and re-create the database schema on startup -->

            <property name="hbm2ddl.auto"

    >update</property>

            <mapping resource="org/hibernate/tutorial/domain/Event.hbm.xml"/>

        </session-factory>

    </hibernate-configuration

    >

## Nota

Observe que este archivo de configuración especifica un DTD diferente

Configure la `SessionFactory` de Hibernate. SessionFactory es una fábrica global responsable de una base de datos en particular. Si usted tiene varias bases de datos, para un inicio más fácil utilice varias configuraciones `<session-factory>` en varios archivos de configuración.

Los primeros cuatro elementos `property` contienen la configuración necesaria para la conexión JDBC. El elemento `property` dialecto especifica la variante SQL en particular que Hibernate genera.

## Sugerencia

In most cases, Hibernate is able to properly determine which dialect to use. See Sección 28.3, “Resolución del dialecto” for more information.

La administración de la sesión automática de Hibernate para contextos de persistencia es particularmente útil en este contexto. La opción `hbm2ddl.auto` activa la generación automática de los esquemas de la base de datos directamente en la base de datos. Esto se puede desactivar, eliminando la opción de configuración o redirigiéndolo a un archivo con la ayuda de la tarea de Ant `SchemaExport`. Finalmente, agregue a la configuración el/los fichero(s) de mapeo para clases persistentes.

Guarde este archivo como `hibernate.cfg.xml` en el directorio `src/main/resources`.

### 1.1.5. Construcción con Maven

Ahora vamos a construir el tutorial con Maven. Es necesario que tenga instalado Maven; se encuentra disponible en la página de descargas Maven. Maven leerá el archivo `/pom.xml` que creamos anteriormente y sabrá cómo realizar algunas tareas de proyectos básicos. Primero, vamos a ejecutar la meta `compile` para asegurarnos de que podemos compilar todo hasta el momento:

    [hibernateTutorial]$ mvn compile
    [INFO] Scanning for projects...
    [INFO] ------------------------------------------------------------------------
    [INFO] Building First Hibernate Tutorial
    [INFO]    task-segment: [compile]
    [INFO] ------------------------------------------------------------------------
    [INFO] [resources:resources]
    [INFO] Using default encoding to copy filtered resources.
    [INFO] [compiler:compile]
    [INFO] Compiling 1 source file to /home/steve/projects/sandbox/hibernateTutorial/target/classes
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESSFUL
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 2 seconds
    [INFO] Finished at: Tue Jun 09 12:25:25 CDT 2009
    [INFO] Final Memory: 5M/547M
    [INFO] ------------------------------------------------------------------------

### 1.1.6. Inicio y ayudantes

Es el momento de cargar y almacenar algunos objetos `Event`, pero primero tiene que completar la configuración con algo de código de infraestructura. Tiene que iniciar Hibernate construyendo un objeto `org.hibernate.SessionFactory` global y almacenarlo en algún lugar de fácil acceso en el código de la aplicación. Una `org.hibernate.SessionFactory` se utiliza para obtener instancias `org.hibernate.Session`. Una `org.hibernate.Session` representa una unidad de trabajo mono-hilo. La `org.hibernate.SessionFactory` es un objeto global seguro entre hilos que se instancia una sóla vez.

Vamos a crear una clase de ayuda `HibernateUtil` que se encargue del inicio y haga más práctico el acceso a `org.hibernate.SessionFactory`.

    package org.hibernate.tutorial.util;

    import org.hibernate.SessionFactory;

    import org.hibernate.cfg.Configuration;

    public class HibernateUtil {

        private static final SessionFactory sessionFactory = buildSessionFactory();

        private static SessionFactory buildSessionFactory() {

            try {

                // Create the SessionFactory from hibernate.cfg.xml

                return new Configuration().configure().buildSessionFactory();

            }

            catch (Throwable ex) {

                // Make sure you log the exception, as it might be swallowed

                System.err.println("Initial SessionFactory creation failed." + ex);

                throw new ExceptionInInitializerError(ex);

            }

        }

        public static SessionFactory getSessionFactory() {

            return sessionFactory;

        }

    }

Guarde este código como `src/main/java/org/hibernate/tutorial/util/HibernateUtil.java`

Esta clase no sólamente produce la referencia `org.hibernate.SessionFactory` global en su inicializador estático, sino que también esconde el hecho de que utiliza un singleton estático. También puede que busque la referencia `org.hibernate.SessionFactory` desde JNDI en un servidor de aplicaciones en cualquier otro lugar.

Si usted le da un nombre a `org.hibernate.SessionFactory` en su archivo de configuración, de hecho, Hibernate tratará de vincularlo a JNDI bajo ese nombre después de que ha sido construido. Otra mejor opción es utilizar el despliegue JMX y dejar que el contenedor con capacidad JMX instancie y vincule un `HibernateService` a JNDI. Más adelante discutiremos estas opciones avanzadas.

Ahora necesita configurar un sistema de registro. Hibernate utiliza registros comunes le da dos opciones: Log4J y registros de JDK 1.4. La mayoría de los desarrolladores prefieren Log4J: copie `log4j.properties` de la distribución de Hibernate, se encuentra en el directorio `etc/`) a su directorio `src`, junto a `hibernate.cfg.xml`. Si desea tener una salida más verbosa que la que se proporcionó en la configuración del ejemplo entonces puede cambiar su configuración. Por defecto, sólo se muestra el mensaje de inicio de Hibernate en la salida estándar.

La infraestructura del tutorial está completa y estamos listos para hacer un poco de trabajo real con Hibernate.

### 1.1.7. Carga y almacenamiento de objetos

We are now ready to start doing some real work with Hibernate. Let's start by writing an `EventManager` class with a `main()` method:

    package org.hibernate.tutorial;

    import org.hibernate.Session;

    import java.util.*;

    import org.hibernate.tutorial.domain.Event;

    import org.hibernate.tutorial.util.HibernateUtil;

    public class EventManager {

        public static void main(String[] args) {

            EventManager mgr = new EventManager();

            if (args[0].equals("store")) {

                mgr.createAndStoreEvent("My Event", new Date());

            }

            HibernateUtil.getSessionFactory().close();

        }

        private void createAndStoreEvent(String title, Date theDate) {

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

            session.beginTransaction();

            Event theEvent = new Event();

            theEvent.setTitle(title);

            theEvent.setDate(theDate);

            session.save(theEvent);

            session.getTransaction().commit();

        }

    }

En `createAndStoreEvent()` creamos un nuevo objeto `Event` y se lo entregamos a Hibernate. En ese momento, Hibernate se encarga de SQL y ejecuta un `INSERT` en la base de datos.

A org.hibernate.Session is designed to represent a single unit of work (a single atomic piece of work to be performed). For now we will keep things simple and assume a one-to-one granularity between a Hibernate org.hibernate.Session and a database transaction. To shield our code from the actual underlying transaction system we use the Hibernate `org.hibernate.Transaction` API. In this particular case we are using JDBC-based transactional semantics, but it could also run with JTA.

¿Qué hace `sessionFactory.getCurrentSession()`? Primero, la puede llamar tantas veces como desee y en donde quiera, una vez consiga su `org.hibernate.SessionFactory`. El método `getCurrentSession()` siempre retorna la unidad de trabajo "actual". ¿Recuerda que cambiamos la opción de la configuración de este mecanismo a "thread" en `src/main/resources/hibernate.cfg.xml`? Por lo tanto, el contexto de una unidad de trabajo actual se encuentra vinculada al hilo de Java actual que ejecuta nuestra aplicación.

## Importante

Hibernate ofrece tres métodos de rastreo de sesión actual. El método basado en "hilos" no está dirigido al uso de producción; sólo es útil para prototipos y para tutoriales como este. Más adelante discutiremos con más detalles el rastreo de sesión actual.

Una org.hibernate.Session se inicia cuando se realiza la primera llamada a `getCurrentSession()` para el hilo actual. Luego Hibernate la vincula al hilo actual. Cuando termina la transacción, ya sea por medio de guardar o deshacer los cambios, Hibernate desvincula automáticamente la org.hibernate.Session del hilo y la cierra por usted. Si llama a `getCurrentSession()` de nuevo, obtiene una org.hibernate.Session nueva y obtiene una nueva org.hibernate.Session unidad de trabajo.

En relación con la unidad del campo de trabajo, ¿Se debería utilizar org.hibernate.Session de Hibernate para ejecutar una o varias operaciones de la base de datos? El ejemplo anterior utiliza una org.hibernate.Session para una operación. Sin embargo, esto es pura coincidencia; el ejemplo simplemente no es lo suficientemente complicado para mostrar cualquier otro enfoque. El ámbito de una org.hibernate.Session de Hibernate es flexible pero nunca debe diseñar su aplicación para que utilice una nueva org.hibernate.Session de Hibernate para cada operación de la base de datos. Aunque lo utilizamos en los siguientes ejemplos, considere la sesión-por-operación como un anti-patrón. Más adelante en este tutorial, se muestra una aplicación web real, lo cual le ayudará a ilustrar esto.

See Capítulo 13, Transacciones y concurrencia for more information about transaction handling and demarcation. The previous example also skipped any error handling and rollback.

Para ejecutar esto, utilizaremos el plugin de ejecución Maven para llamar nuestra clase con la configuración de ruta de clase necesaria: `mvn exec:java -Dexec.mainClass="org.hibernate.tutorial.EventManager" -Dexec.args="store"`

## Nota

Es posible que primero necesite realizar `mvn compile`.

Debe ver que Hibernate inicia y dependiendo de su configuración, también verá bastantes salidas de registro. Al final, verá la siguiente línea:

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

Este es el `INSERT` que Hibernate ejecuta.

Para listar los eventos almacenados se agrega una opción al 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 método `listEvents()`:

        private List listEvents() {

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

            session.beginTransaction();

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

            session.getTransaction().commit();

            return result;

        }

Here, we are using a Hibernate Query Language (HQL) query to load all existing `Event` objects from the database. Hibernate will generate the appropriate SQL, send it to the database and populate `Event` objects with the data. You can create more complex queries with HQL. See Capítulo 16, HQL: El lenguaje de consulta de Hibernate for more information.

Ahora podemos llamar nuestra nueva funcionalidad, de nuevo usando el plugin de ejecución Maven: `mvn exec:java -Dexec.mainClass="org.hibernate.tutorial.EventManager" -Dexec.args="list"`

## 1.2. Part 2 - Mapeo de asociaciones

Hasta ahora hemos mapeado una clase de entidad persistente a una tabla aislada. Vamos a construir sobre esto y agregaremos algunas asociaciones de clase. Vamos a agregar personas a la aplicación y vamos a almacenar una lista de eventos en las que participan.

### 1.2.1. Mapeo de la clase Person

El primer corte de la clase `Person` se ve así:

    package org.hibernate.tutorial.domain;

    public class Person {

        private Long id;

        private int age;

        private String firstname;

        private String lastname;

        public Person() {}

        // Accessor methods for all properties, private setter for 'id'

    }

Guarde esto en un archivo llamado `src/main/java/org/hibernate/tutorial/domain/Person.java`

Luego, cree el nuevo archivo de mapeo como `src/main/resources/org/hibernate/tutorial/domain/Person.hbm.xml`

    <hibernate-mapping package="org.hibernate.tutorial.domain">

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

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

                <generator class="native"/>

            </id>

            <property name="age"/>

            <property name="firstname"/>

            <property name="lastname"/>

        </class>

    </hibernate-mapping>

Finalmente, añada el nuevo mapeo a la configuración de Hibernate:

    <mapping resource="org/hibernate/tutorial/domain/Event.hbm.xml"/>

    <mapping resource="org/hibernate/tutorial/domain/Person.hbm.xml"/>

Vamos a crear una asociación entre estas dos entidades. Las personas pueden participar en los eventos y los eventos cuentan con participantes. Las cuestiones de diseño con las que tenemos que tratar son: direccionalidad, multiplicidad y comportamiento de la colección.

### 1.2.2. Una asociación unidireccional basada en Set

Al agregar una colección de eventos a la clase `Person`, puede navegar fácilmente a los eventos de una persona en particular, sin ejecutar una petición explícita - llamando a `Person#getEvents`. En Hibernate, las asociaciones multi-valores se representan por medio de uno de los contratos del marco de colecciones Java; aquí escogimos un `java.util.Set` ya que la colección no contendrá elementos duplicados y el orden no es relevante para nuestros ejemplos.

    public class Person {

        private Set events = new HashSet();

        public Set getEvents() {

            return events;

        }

        public void setEvents(Set events) {

            this.events = events;

        }

    }

Antes de mapear esta asociación, considere el otro lado. Podriamos mantener esto unidireccional o podríamos crear otra colección en el `Event`, si queremos tener la habilidad de navegarlo desde ambas direcciones. Esto no es necesario desde un punto de vista funcional. Siempre puede ejeutar un pedido explícito para recuperar los participantes de un evento en particular. Esta es una elección de diseño que depende de usted, pero lo que queda claro de esta discusión es la multiplicidad de la asociación: "muchos" valuada en ambos lados, denominamos esto como una asociación muchos-a-muchos. Por lo tanto, utilizamos un mapeo muchos-a-muchos de Hibernate:

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

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

            <generator class="native"/>

        </id>

        <property name="age"/>

        <property name="firstname"/>

        <property name="lastname"/>

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

            <key column="PERSON_ID"/>

            <many-to-many column="EVENT_ID" class="Event"/>

        </set>

    </class>

Hibernate soporta un amplio rango de mapeos de colección, el más común `set`. Para una asociación muchos-a-muchos o la relación de entidad n:m, se necesita una tabla de asociación. Cada fila en esta tabla representa un enlace entre una persona y un evento. El nombre de esta tabla se declara con el atributo `table` del elemento `set`. El nombre de la columna identificadora en la asociación, del lado de la persona, se define con el elemento `key`, el nombre de columna para el lado del evento se define con el atributo `column` del `many-to-many`. También tiene que informarle a Hibernate la clase de los objetos en su colección (la clase del otro lado de la colección de referencias).

Por consiguiente, el esquema de base de datos para este mapeo es:

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

### 1.2.3. Trabajo de la asociación

Vamos a reunir a algunas personas y eventos en un nuevo método en `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 utilizando los métodos normales de colección. No hay una llamada explícita a `update()` o `save()`; Hibernate detecta automáticamente que se ha modificado la colección y que se necesita actualizarla. Esto se denomina chequeo automático de desactualizaciones y también puede probarlo modificando el nombre o la propiedad de fecha de cualquiera de sus objetos. Mientras se encuentran en estado persistente, es decir, enlazado a una `org.hibernate.Session` de Hibernate en particular, Hibernate monitorea cualquier cambio y ejecuta SQL de un modo escribe-detrás. El proceso de sincronización del estado de la memoria con la base de datos, usualmente sólo al final de una unidad de trabajo, se denomina vaciado. En nuestro código la unidad de trabajo termina con guardar o deshacer los cambios de la transacción de la base de datos.

Puede cargar una persona y un evento en diferentes unidades de trabajo. También puede modificar un objeto fuera de una `org.hibernate.Session`, cuando no se encuentra en estado persistente (si antes era persistente denominamos a este estado separado ). Inclusive, puede modificar una colección cuando se encuentre separada:

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

            // End of first unit of work

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

            // Begin second unit of work

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

            session2.beginTransaction();

            session2.update(aPerson); // Reattachment of aPerson

            session2.getTransaction().commit();

        }

La llamada a `update` hace que un objeto separado sea persistente de nuevo enlazándolo a una nueva unidad de trabajo, así que cualquier modificación que le realizó mientras estaba separado se puede guardar en la base de datos. Esto incluye cualquier modificación (adiciones o eliminaciones) que le hizo a una colección de ese objeto entidad.

Esto no se utiliza mucho en nuestro ejemplo, pero es un concepto importante que puede incorporar en su propia aplicación. Complete este ejercicio agregando una nueva acción al método main de `EventManager` y llámela desde la línea de comandos. Si necesita los identificadores de una persona y de un evento - el método `save()` los retorna (pueda que necesite modificar algunos de los métodos anteriores para retornar ese 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);

            }

Esto fue un ejemplo de una asociación entre dos clases igualmente importantes: dos entidades. Como se mencionó anteriormente, hay otras clases y tipos en un modelo típico, usualmente "menos importantes". Algunos de ustedes las habrán visto, como un `int` o un `java.lang.String`. Denominamos a estas clases tipos de valor y sus instancias dependen de una entidad en particular. Las instancias de estos tipos no tienen su propia identidad, ni son compartidas entre entidades. Dos personas no referencian el mismo objeto `firstname`, incluso si tienen el mismo nombre. Los tipos de valor no sólo pueden encontrarse en el JDK, sino que también puede escribir por sí mismo clases dependientes como por ejemplo, `Address` o `MonetaryAmount`. De hecho, en una aplicación Hibernate todas las clases JDK se consideran como tipos de valor.

También puede diseñar una colección de tipos de valor. Esto es conceptualmente diferente de una colección de referencias a otras entidades, pero se ve casi igual en Java.

### 1.2.4. Colección de valores

Vamos a agregar una colección de direcciones de correo electrónico a la entidad `Person`. Esto se representará como un `java.util.Set` de las instnaicas `java.lang.String`:

        private Set emailAddresses = new HashSet();

        public Set getEmailAddresses() {

            return emailAddresses;

        }

        public void setEmailAddresses(Set emailAddresses) {

            this.emailAddresses = emailAddresses;

        }

El mapeo de este `Set` es así:

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

                <key column="PERSON_ID"/>

                <element type="string" column="EMAIL_ADDR"/>

            </set>

La diferencia comparado con el mapeo anterior es el uso de la parte `element`, que le dice a Hibernate que la colección no contiene referencias a otra entidad, sino que es una colección de elementos que son tipos de valores, aquí especificamente de tipo `String`. El nombre en minúsculas le dice que es un tipo/conversor de mapeo de Hibernate. Una vez más, el atributo `table` del elemento `set` determina el nombre de la tabla para la colección. El elemento `key` define el nombre de la columna clave foránea en la tabla de colección. El atributo `column` en el elemento `element` define el nombre de la columna donde realmente se almacenarán los valores de la dirección de correo electrónico.

Este es el esquema actualizado:

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

Puede ver que la clave principal de la tabla de colección es, de hecho, una clave compuesta que utiliza ambas columnas. Esto también implica que no pueden haber direcciones de correo electrónico duplicadas por persona, la cual es exactamente la semántica que necesitamos para un conjunto en Java.

Ahora, puede tratar de agregar elementos a esta colección, al igual que lo hicimos antes vinculando personas y eventos. Es el mismo código en Java.

        private void addEmailToPerson(Long personId, String emailAddress) {

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

            session.beginTransaction();

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

            // adding to the emailAddress collection might trigger a lazy load of the collection

            aPerson.getEmailAddresses().add(emailAddress);

            session.getTransaction().commit();

        }

Esta vez no utilizamos una petición de búqueda - fetch - para dar inicio a la colección. Monitoree su registro SQL e intente de optimizar esto con una recuperación temprana.

### 1.2.5. Asociaciones bidireccionales

A continuacion vamos a mapear una asociación bidireccional. Vamos a hacer que la asociación entre persona y evento funcione desde ambos lados en Java. El esquema de la base de datos no cambia así que todavía tendremos una multiplicidad muchos-a-muchos.

## Nota

Una base de datos relacional es más flexible que un lenguaje de programación de red ya que no necesita una dirección de navegación; los datos se pueden ver y recuperar de cualquier forma posible.

Primero, agregue una colección de participantes a la clase `Event`:

        private Set participants = new HashSet();

        public Set getParticipants() {

            return participants;

        }

        public void setParticipants(Set participants) {

            this.participants = participants;

        }

Ahora mapee 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="Person"/>

            </set

    >

Estos son mapeos normales de `set` en ambos documentos de mapeo. Note que los nombres de las columnas en `key` y `many-to-many` se intercambiaron en ambos documentos de mapeo. La adición más importante aquí es el atributo `inverse="true"` en el elemento `set` del mapeo de colección de `Event`.

Esto significa que Hibernate debe tomar el otro lado, la clase `Person`, cuando necesite encontrar información sobre el enlace entre las dos. Esto será mucho más fácil de entender una vez que vea como se crea el enlace bidireccional entre nuestras dos entidades.

### 1.2.6. Trabajo con enlaces bidireccionales

Primero, recuerde que Hibernate no afecta la semántica normal de Java. ¿Cómo creamos un enlace entre `Person` y un `Event` en el ejemplo unidireccional? Agregue una instancia de `Event` a la colección de referencias de eventos de una instancia de `Person`. Si quiere que este enlace funcione bidireccionalmente, tiene que hacer lo mismo del otro lado, añadiendo una referencia `Person` a la colección en un `Event`. Este proceso de "establecer el enlace en ambos lados" es absolutamente necesario con enlaces bidireccionales.

Muchos desarrolladores programan a la defensiva y crean métodos de administración de enlaces para establecer correctamente ambos lados, (por ejemplo, en `Person`):

        protected Set getEvents() {

            return events;

        }

        protected void setEvents(Set events) {

            this.events = events;

        }

        public void addToEvent(Event event) {

            this.getEvents().add(event);

            event.getParticipants().add(this);

        }

        public void removeFromEvent(Event event) {

            this.getEvents().remove(event);

            event.getParticipants().remove(this);

        }

Los métodos get y set para la colección ahora se encuentran protegidos. Esto le permite a las clases en el mismo paquete y a las subclases acceder aún a los métodos, pero impide a cualquier otro que desordene las colecciones directamente. Repita los pasos para la colección del otro lado.

¿Y el atributo de mapeo `inverse`? Para usted y para Java, un enlace bidireccional es simplemente cuestión de establecer correctamente las referencias en ambos lados. Sin embargo, Hibernate no tiene suficiente información para organizar correctamente declaraciones `INSERT` y `UPDATE` de SQL (para evitar violaciones de restricciones). El hacer un lado de la asociación `inverse` le dice a Hibernate que lo considere un espejo del otro lado. Eso es todo lo necesario para que Hibernate resuelva todos los asuntos que surgen al transformar un modelo de navegación direccional a un esquema de base de datos SQL. Las reglas son muy simples: todas las asociaciones bidireccionales necesitan que uno de los lados sea `inverse`. En una asociación uno-a-muchos debe ser el lado-de-muchos; y en una asociación muchos-a-muchos, puede escoger cualquier lado.

## 1.3. Part 3 - La aplicación web EventManager

Una aplicación web de Hibernate utiliza `Session` y `Transaction` casi como una aplicación autónoma. Sin embargo, algunos patrones comunes son útiles. Ahora puede escribir un `EventManagerServlet`. Este servlet puede enumerar todos los eventos almacenados en la base de datos y proporciona una forma HTML para ingresar eventos nuevos.

### 1.3.1. Escritura de un servlet básico

Primero necesitamos crear nuestro servlet de procesamiento básico. Ya que nuestro servlet solo maneja pedidos `GET` HTTP sólamente, solo implementaremos el método `doGet()`:

    package org.hibernate.tutorial.web;

    // Imports

    public class EventManagerServlet extends HttpServlet {

        protected void doGet(

                HttpServletRequest request,

                HttpServletResponse response) throws ServletException, IOException {

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

            try {

                // Begin unit of work

                HibernateUtil.getSessionFactory().getCurrentSession().beginTransaction();

                // Process request and render page...

                // End unit of work

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

            }

            catch (Exception ex) {

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

                if ( ServletException.class.isInstance( ex ) ) {

                    throw ( ServletException ) ex;

                }

                else {

                    throw new ServletException( ex );

                }

            }

        }

    }

Guarde este servlet como `src/main/java/org/hibernate/tutorial/web/EventManagerServlet.java`

El patrón aplicado aquí se llama sesión-por-petición. Cuando una petición llega al servlet, se abre una nueva `Session` de Hibernate por medio de la primera llamada a `getCurrentSession()` en el `SessionFactory`. Entonces se inicia una transacción de la base de datos. Todo acceso a los datos tiene que suceder dentro de una transacción, sin importar que los datos sean leídos o escritos . No utilice el modo auto-commit en las aplicaciones.

No utilice una nueva `Session` de Hibernate para cada operación de base de datos. Utilice una `Session` Hibernate que cubra el campo de todo el pedido. Utilice `getCurrentSession()` para vincularlo automáticamente al hilo de Java actual.

Después, se procesan las acciones posibles del pedido y se entrega la respuesta HTML. Llegaremos a esa parte muy pronto.

Finalmente, la unidad de trabajo termina cuando se completa el procesamiento y la entrega. Si surgió algún problema durante el procesamiento o la entrega , se presentará una excepción y la transacción de la base de datos se deshará. Esto completa el patrón `session-per-request`. En vez del código de demarcación de la transacción en todo servlet, también podría escribir un filtro de servlet. Véa el sitio web de Hibernate y el Wiki para obtener más información sobre este patrón llamado sesión abierta en vista. Lo necesitará tan pronto como considere representar su vista en JSP, no en un servlet.

### 1.3.2. Procesamiento y entrega

Ahora puede implementar el procesamiento del pedido y la representación de la página.

            // Write HTML header

            PrintWriter out = response.getWriter();

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

            // Handle actions

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

                }

            }

            // Print page

           printEventForm(out);

           listEvents(out, dateFormatter);

           // Write HTML footer

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

           out.flush();

           out.close();

Dado que este estilo de codificación con una mezcla de Java y HTML no escalaría en una aplicación más compleja - tenga en cuenta que sólo estamos ilustrando los conceptos básicos de Hibernate en este tutorial. El código imprime una cabecera y un pie de página HTML. Dentro de esta página se imprime una forma HTML para entrada de eventos y se imprime una lista de todos los eventos en la base de datos. El primer método es trivial y su salida se realiza únicamente en 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()` utiliza Hibernate `Session` vinculado al hilo actual para ejecutar una petición:

        private void listEvents(PrintWriter out, SimpleDateFormat dateFormatter) {

            List result = HibernateUtil.getSessionFactory()

                    .getCurrentSession().createCriteria(Event.class).list();

            if (result.size() > 0) {

                out.println("<h2>Events in database:</h2>");

                out.println("<table border='1'>");

                out.println("<tr>");

                out.println("<th>Event title</th>");

                out.println("<th>Event date</th>");

                out.println("</tr>");

                Iterator it = result.iterator();

                while (it.hasNext()) {

                    Event event = (Event) it.next();

                    out.println("<tr>");

                    out.println("<td>" + event.getTitle() + "</td>");

                    out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>");

                    out.println("</tr>");

                }

                out.println("</table>");

            }

        }

Finalmente, la acción `store` se despacha al método `createAndStoreEvent()`, el cual también utiliza la `Session` del hilo actual:

        protected void createAndStoreEvent(String title, Date theDate) {

            Event theEvent = new Event();

            theEvent.setTitle(title);

            theEvent.setDate(theDate);

            HibernateUtil.getSessionFactory()

                    .getCurrentSession().save(theEvent);

        }

El servlet se encuentra completo. Un pedido al servlet será procesado en una sola `Session` y `Transaction`. Como lo vimos antes en la aplicación autónoma, Hibernate puede enlazar automáticamente estos objetos al hilo actual de ejecución. Esto le da la libertad de utilizar capas en su código y acceder a la `SessionFactory` de cualquier manera que lo desee. Usualmente, usted utilizaría un diseño más sofisticado y movería el código de acceso de datos a los objetos de acceso de datos (el patrón DAO). Refiérase al Wiki de Hibernate para ver más ejemplos.

### 1.3.3. Despliegue y prueba

Para implementar esta aplicación para prueba debemos crear una Web ARchive (WAR). Primero debemos definir el descriptor WAR como `src/main/webapp/WEB-INF/web.xml`

    <?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.xsd">

        <servlet>

            <servlet-name>Event Manager</servlet-name>

            <servlet-class>org.hibernate.tutorial.web.EventManagerServlet</servlet-class>

        </servlet>

        <servlet-mapping>

            <servlet-name>Event Manager</servlet-name>

            <url-pattern>/eventmanager</url-pattern>

        </servlet-mapping>

    </web-app>

Para construir y desplegar llame a `mvn package` en su directorio de proyecto y copie el archivo `hibernate-tutorial.war` en su directorio `webapp` Tomcat.

## Nota

If you do not have Tomcat installed, download it from http://tomcat.apache.org/ and follow the installation instructions. Our application requires no changes to the standard Tomcat configuration.

Una vez que se encuentre desplegado y que Tomcat esté ejecutando, acceda la aplicación en `http://localhost:8080/hibernate-tutorial/eventmanager`. Asegúrese de ver el registro de Tomcat para ver a Hibernate iniciar cuando llegue el primer pedido a su servlet (se llama al inicializador estático en `HibernateUtil`) y para obetener la salida detallada si ocurre alguna excepción.

## 1.4. Resumen

Este tutorial abordó los puntos básicos de la escritura de una simple aplicación de Hibernate autónoma y una pequeña aplicación web. Encontrará más tutoriales en el website de Hibernate http://hibernate.org.

Copyright © 2004 Red Hat, Inc.

- AnteriorPrefacio
- Subir
- Inicio
- SiguienteCapítulo 2. Arquitectura