Ejemplo sencillo con asociaciones en Hibernate

De ChuWiki

Introducción[editar]

Vamos a ver, siguiendo una traducción libre de la documentación de Hibernate, un ejemplo sencillo en el que se asocian en base de datos dos entidades: Person y Event, de forma que una persona puede tener asociados varios eventos. Para este ejemplo seguiremos desarrollando el del tutorial anterior, el Ejemplo sencillo con Hibernate. Todo el código de este ejemplo lo tienes disponible en chuidiang-ejemplos

La clase Person[editar]

La clase Person será un java bean sencillo, con los atributos id, firstname, lastname y age. El método setId(), siguiendo las recomendaciones de Hibernate, será privado para que sólo Hibernate pueda fijar el valor del id de una Person. La clase Person tendrá además un atributo importante, que es el objeto principal de este ejemplo: un Set de Event (un conjunto de eventos). Se elige la interface Set de java ya que no nos importa el orden en que estén colocados los eventos. El código de la clase Person será el siguiente

package com.chuidiang.ejemplos.hibernate.ejemplo2;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import com.chuidiang.ejemplos.hibernate.ejemplo1.Event;

/**
 * Clase Person, persistente en base de datos en este ejemplo y con varios Event
 * asociados.
 * 
 * @author Chuidiang
 * 
 */
public class Person {
    /** Clave primaria */
    private Long id;
    private int age;
    private String firstname;
    private String lastname;

    public Person() {
    }

    public Long getId() {
        return id;
    }

    private Set events = new HashSet();

    public Set getEvents() {
        return events;
    }

    public void setEvents(Set events) {
        this.events = events;
    }

    /** Metodo setter privado, hibernate puede llamarlo, pero nosotros no */
    private void setId(Long id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getFirstname() {
        return firstname;
    }

    public void setFirstname(String firstname) {
        this.firstname = firstname;
    }

    public String getLastname() {
        return lastname;
    }

    public void setLastname(String lastname) {
        this.lastname = lastname;
    }

    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("Persona: " + id + " - " + firstname + " - " + lastname
                + " - " + age);
        Iterator<Event> eventos = getEvents().iterator();
        while (eventos.hasNext())
            sb.append(System.getProperty("line.separator") + "   Evento : "
                    + eventos.next().toString());
        return sb.toString();
    }
}

Fichero de mapeo de la clase Person[editar]

Ahora debemos crear el fichero .hbm.xml de la clase Person. En ese fichero, además de los atributos normales, pondremos una etiqueta xml <set...> para indicar que la clase Person lleva un conjunto de cosas. Dentro de esa etiqueta, indicaremos cual es la clave primaria de Person con la etiqueta xml <key...> y también qué tipo de relación hay entre Person y Event con la etiqueta <many-to-many...>. El fichero Person.hbm.xml con esta relación lo situaremos junto al fichero Person.java en el proyecto. El contenido del fichero Person.hbm.xml es el siguiente

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
	<class name="com.chuidiang.ejemplos.hibernate.ejemplo2.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="com.chuidiang.ejemplos.hibernate.ejemplo1.Event" />
		</set>
	</class>
</hibernate-mapping>

Los atributos de las etiquetas que relacionan Person con Event son:

  • <set name="events" ...> : Nombre del atributo con los eventos de la clase Person
  • <set ... table="PERSON_EVENT"> : Nombre de la tabla de base de datos que relacionará Person y Event
  • <key column="PERSON_ID"/> : Clave de la tabla Person que se usará para como id para relacionar con los Event.
  • <many-to-many column="EVENT_ID" ...> : Clave de la tabla Event que se usará como id para relacionar con las Person.
  • <many-to-many ... class="com...Event"> : Clase de la tabla Event.

Fichero de configuración de Hibernate[editar]

El fichero de configuración de Hibernate es similar al del ejemplo anterior, pero añadiendo al final un <mapping resource=...> más para la clase Person. El fichero lo situaremos en el directorio raíz del proyecto con el nombre hibernate2.cfg.xml y su contenido será el siguiente

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
	<session-factory>
		<!-- Database connection settings -->
		<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="connection.url">jdbc:mysql://localhost/hibernate</property>
		<property name="connection.username">hibernate</property>
		<property name="connection.password">hibernate</property>
		<!-- JDBC connection pool (use the built-in) -->
		<property name="connection.pool_size">1</property>
		<!-- SQL dialect -->
		<property name="dialect">org.hibernate.dialect.MySQL5Dialect</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">create</property>
		<mapping resource="com/chuidiang/ejemplos/hibernate/ejemplo1/Event.hbm.xml" />
		<mapping resource="com/chuidiang/ejemplos/hibernate/ejemplo2/Person.hbm.xml" />
	</session-factory>
</hibernate-configuration>

El código del main[editar]

Vamos ahora con el código de ejemplo. Al igual que en el ejemplo anterior haremos una clase HibernateUtil en la que se carga el fichero de configuración hibernate2.cfg.xml.

En el constructor de nuestra clase de ejemplo llamaremos a métodos creados al efecto para crear y almacenar un evento en base de datos, una persona en base de datos, relacionar la persona con el evento y finalmente crear y asociar un nuevo evento a la persona. El trozo de código del constructor es este

    public Ejemplo2() {
        // Se crea y guarda en bd un evento
        Long idEvent = createAndStoreEvent("El Event", new Date());

        // Se crea y guarda en bd una persona
        Long idPerson = createAndStorePerson("Juan", "Cortés", 34);

        // Se asocian en bd persona y evento
        addPersonToEvent(idPerson, idEvent);

        // Se añade un nuevo evento a la persona
        addNewEventToPerson("Nuevo evento", new Date(), idPerson);

        // Se listan las personas con sus eventos
        listPersons();
    }

Los dos métodos createAndStore*() no tienen nada especial y ya comentamos el de createAndStoreEvent() en el ejemplo anterior, así que no los comentaremos aquí.

El método addPersonToEvent(idPerson, idEvent) carga de base de datos la persona y el evento cuyos id se pasan y los relaciona, símplemente añadiendo el Event en el bean Person, llamado a persona.getEvents().add(evento). Basta con eso, y luego hacer el commit() para que la relación se almacene en base de datos. El código de este método es el siguiente

    private void addPersonToEvent(Long personId, Long eventId) {
        // Sesion
        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        session.beginTransaction();

        // Se cargan perona y evento con su id.
        Person aPerson = (Person) session.load(Person.class, personId);
        Event anEvent = (Event) session.load(Event.class, eventId);

        // Se añade el evento a la persona usando métodos normales del 
        // java bean Person
        aPerson.getEvents().add(anEvent);

        // Se termina la transaccion
        session.getTransaction().commit();
    }

El método addNewEventToPerson() es otra forma de hacer lo mismo, pero usando un evento nuevo, que no está previamente guardado en base de datos (aunque debemos salvarlo en el proceso). Símplemente creamos el Event sobre la marcha, lo salvamos en base de datos y lo asociamos a la Person, haciendo el commit() al final. El código de este método es el siguiente

    private void addNewEventToPerson(String eventName, Date eventDate,
            Long personId) {
        // Sesion
        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        session.beginTransaction();

        // Se carga la persona de bd
        Person aPerson = (Person) session.load(Person.class, personId);

        // Se crea un evento y se salva
        Event e = new Event();
        e.setTitle(eventName);
        e.setDate(eventDate);
        session.save(e);

        // Se añade el evento a la persona y se salva
        aPerson.getEvents().add(e);
        session.save(aPerson);

        // fin de sesion
        session.getTransaction().commit();
    }

Aquí tienes el código completo de Ejemplo2.java