2 de agosto de 2009

Hibernate - Parte 5: Relaciones / Muchos a Uno

Ya hemos visto, en los tutoriales anteriores, cómo generar 4 de los 7 tipos de relaciones que podemos crear con Hibernate.

En esta ocasión veremos cómo crear relaciones uno a muchos unidireccionales (ya que, como dije en el tercer tutorial, las relaciones uno a muchos bidireccionales son iguales a las relaciones muchos a uno bidireccionales ^-^).

Lo primero que haremos es crear un proyecto en NetBeans (menú "File -> New Project... -> Java -> Java Application"). Le damos un nombre y una ubicación al proyecto y nos aseguramos de que las opciones "Create Main Class" y "Set as Main Project" estén habilitadas. Presionamos el botón "Finish" y veremos aparecer nuestra clase en el editor."Main".

Agregamos la biblioteca de Hibernate, que creamos en el primer tutorial de la serie. Hacemos clic derecho en el nodo "Libraries" del proyecto. En el menú contextual que se abre seleccionamos la opción "Add Library...":



En la ventana que se abre seleccionamos la biblioteca "Hibernate":



Presionamos el botón "Add Library" para que la biblioteca se agregue a nuestro proyecto. Aprovechamos también para agregar el conector de MySQL. Debemos tener los siguientes archivos en nuestro proyecto:



Ahora creamos dos paquetes, uno con el nombre "modelo", que contendrá las clases entidades, y otro con el nombre "mapeos" que contendrá los archivos de mapeo XML. Hacemos clic derecho en el nodo del paquete que se creó al generar el proyecto. En el menú contextual que se abre seleccionamos la opción "New -> Java Package..." y creamos los dos paquetes.



Que deben quedar así:



Aprovecharemos para crear nuestro archivo de configuración de Hibernate, "hibernate.cfg.xml", el cual será muy parecido al del primer tutorial:

<!DOCTYPE hibernate-configuration PUBLIC 
  "-//Hibernate/Hibernate Configuration DTD 3.0//EN" 
  "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
  <session-factory>
  
    <!-- parametros para la conexion a la base de datos -->
    <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="connection.url">jdbc:mysql://localhost/hibernaterelaciones</property>
    <property name="connection.username">usuario</property>
    <property name="connection.password">password</property>
    
    <!-- Configuracion del pool interno -->
    <property name="connection.pool_size">1</property>
    
    <!-- Dialecto de la base de datos -->
    <property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
    
    <!-- Otras propiedades importantes -->
    <property name="show_sql">true</property>
    <property name="hbm2ddl.auto">create-drop</property>
    
    <!-- Aqui iran los archivos de mapeo -->

  </session-factory>
</hibernate-configuration>


Para los ejercicios usaremos una base de datos llamada "hibernaterelaciones", en MySQL.

Nota: Yo usaré dos proyectos, uno para usar archivos de mapeo y otro para usar anotaciones y que el código de ambos no se mezcle.

Pues bien, comencemos con este quinto tutorial:

Las relaciones muchos a uno en realidad son muy parecidas a las relaciones uno a muchos, con la excepción de que, en el caso de las relaciones unidireccionales, solo el lado muchos de la relación sabe de la existencia de esta.

Es como si tuviéramos una clase Televidente, que tiene una relación muchos a uno unidireccional con una clase CadenaTelevisiva. Donde el Televidente (el lado muchos de la relación) tiene una CadenaTelevisiva favorita (el lado uno de la relación). Pero CadenaTelevisiva no tiene una referencia hacia cada uno de sus televidentes (desde los Televidentes podemos llegar a una CadenaTelevisiva, pero desde la CadenaTelevisiva no podemos llegar a los Televidentes).

Comencemos con los ejemplos para que esto quede más claro.


5 - Relaciones Muchos a Uno Unidireccionales


Primero crearemos la clase CadenaTelevisiva, dentro del paquete “modelo”. Esta será una clase muy simple y solamente tendrá dos atributos: id y nombre. La clase CadenaTelevisiva queda así:

public class CadenaTelevisiva 
{
    private long id;
    private String nombre;

    public CadenaTelevisiva()
    {
    }

    public long getId()
    {
        return id;
    }

    protected void setId(long id)
    {
        this.id = id;
    }

    public String getNombre()
    {
        return nombre;
    }

    public void setNombre(String nombre)
    {
        this.nombre = nombre;
    } 
}


Como dije antes: esta clase no tiene ninguna referencia a la clase Televidente (aunque si fuera una relación bidireccional debería tener un arreglo de Televidentes), ya que será esta última la que este consciente de la relación.

La clase Televidente, también colocada en el paquete “modelo”, queda de esta forma:

public class Televidente 
{
    private long id;
    private String nombre;
    private CadenaTelevisiva cadenaFavorita;

    public Televidente()
    {
    }

    public CadenaTelevisiva getCadenaFavorita()
    {
        return cadenaFavorita;
    }

    public void setCadenaFavorita(CadenaTelevisiva cadenaFavorita)
    {
        this.cadenaFavorita = cadenaFavorita;
    }

    public long getId()
    {
        return id;
    }

    protected void setId(long id)
    {
        this.id = id;
    }

    public String getNombre()
    {
        return nombre;
    }

    public void setNombre(String nombre)
    {
        this.nombre = nombre;
    }
}


Como podemos ver, en este caso Televidente tiene una referencia a CadenaTelevisiva, la cual usa el identificador cadenaFavorita.

Ahora veamos cómo representar esta relación. Comenzaremos, como siempre haciendo uso de archivos de mapeo:


5.1 Relaciones Muchos a Uno Unidireccionales con Archivos de Mapeo


Creamos un nuevo documento XML en el paquete “mapeos”. Le damos el nombre “CadenaTelevisiva.hbm” (el asistente se encargará de colocar el .xml):



Presionamos el botón “Next >” e indicamos que queremos crear un documento XML bien formado (la primer opción) y presionamos el botón “Finish”.

Eliminamos en el contenido del archivo creado y lo reemplazamos con el siguiente:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
    <class name="hibernate.relaciones.modelos.CadenaTelevisiva" table="CADENAS_TELEVISIVAS">

    </class>
</hibernate-mapping>


El mapeo de la clase CadenaTelevisiva el similar al que expliqué en el primer tutorial de la serie y, al final, queda de la siguiente forma:

<hibernate-mapping>
    <class name="hibernate.relaciones.modelos.CadenaTelevisiva" table="CADENAS_TELEVISIVAS">
        <id name="id">
            <generator class="identity" />
        </id>

        <property name="nombre" />
    </class>
</hibernate-mapping>




Ahora crearemos el mapeo de la clase “Televidente”, llamado “Televidente.hbm.xml” en el paquete “mapeos”, de la misma forma que lo hicimos para CadenaTelevisiva. El mapeo, excluyendo el elemento que representa la relación, debe estar de esta forma:

<hibernate-mapping>
    <class name="hibernate.relaciones.modelos.Televidente" table="TELEVIDENTES">
        <id name="id">
            <generator class="identity" />
        </id>

        <property name="nombre" />
    </class>
</hibernate-mapping> 


Para representar la relación uno a muchos usamos el elemento “<many-to-one>”, que ya habíamos usado en el tutorial anterior. En esta etiqueta colocamos, haciendo uso de se atributo name, el nombre de la propiedad de la clase Televidente que hace referencia a la CadenaTelevisiva, que en este caso es cadenaFavorita:

<many-to-one name="cadenaFavorita" />


Por lo que, finalmente, el mapeo de la clase Televidente, queda de la siguiente forma:

<hibernate-mapping>
    <class name="hibernate.relaciones.modelos.Televidente" table="TELEVIDENTES">
        <id name="id">
            <generator class="identity" />
        </id>

        <property name="nombre" />

        <many-to-one name="cadenaFavorita" />

    </class>
</hibernate-mapping>


Ahora probemos que la configuración funciona. Para que la prueba sea más fácil usaremos la clase HibernateUtil que creamos en el primer tutorial.

No olviden que, además, debemos colocar en el archivo hibernate.cfg.xml las indicaciones de dónde están los archivos de mapeo que acabamos de crear:

<mapping resource="hibernate/relaciones/mapeos/CadenaTelevisiva.hbm.xml" />
<mapping resource="hibernate/relaciones/mapeos/Televidente.hbm.xml" />


En la prueba crearemos tres objetos CadenaTelevisiva, cadena 1, cadena 2, y cadena 3, respectivamente, y dos objetos Televidente, televidente 1, y televidente 2, y relacionaremos la cadena 1 con el televidente 1 y la cadena 3 con el televidente 2.

Veamos cómo queda el código en el método main de nuestra clase Main:
    
public static void main(String[] args)
{
    /* Creamos los tres objetos CadenaTelevisiva */

    CadenaTelevisiva cadena1 = new CadenaTelevisiva();
    cadena1.setNombre("Cadena 1");

    CadenaTelevisiva cadena2 = new CadenaTelevisiva();
    cadena2.setNombre("Cadena 2");

    CadenaTelevisiva cadena3 = new CadenaTelevisiva();
    cadena3.setNombre("Cadena 3");


    /* Guardamos estos tres objetos CadenaTelevisiva en la base de datos */
    Session sesion = HibernateUtil.getSessionFactory().openSession();
    sesion.beginTransaction();
    sesion.save(cadena1);
    sesion.save(cadena2);
    sesion.save(cadena3);
    sesion.getTransaction().commit();
    sesion.close();


    /* Creamos dos objetos Televidente */
    Televidente televidente1 = new Televidente();
    televidente1.setNombre("Televidente 1");
    televidente1.setCadenaFavorita(cadena1);

    Televidente televidente2 = new Televidente();
    televidente2.setNombre("Televidente 2");
    televidente2.setCadenaFavorita(cadena3);

    /* Guardamos los dos objetos Televidente en la base de Datos */
    sesion = HibernateUtil.getSessionFactory().openSession();
    sesion.beginTransaction();
    sesion.save(televidente1);
    sesion.save(televidente2);
    sesion.getTransaction().commit();
    sesion.close();
}


Listo, esto es todo lo que necesitamos para probar nuestro ejemplo.

Ahora veamos que, en la base de datos, estén las 3 cadenas televisivas y los 2 televidentes que creamos y que, las referencias de los televidentes a las cadenas estén correctos:



Como podemos ver, los datos se han guardado de forma correcta ^-^.

Ahora veamos cómo podemos hacer lo mismo usando anotaciones.


5.2 Relaciones Muchos a Uno Unidireccionales Anotaciones


Recuerden que pasa usar anotaciones debemos agregar al proyecto la biblioteca "HibernateAnotaciones" que creamos en el segundo tutorial.

Las anotaciones (así como los archivos de configuración) de ambas clases (omitiendo la que indica la relación) serán las mismas que expliqué en el segundo tutorial de esta serie, quedan de la siguiente forma.

Esta es la clase CadenaTelevisiva:

@Entity
public class CadenaTelevisiva implements Serializable
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    private String nombre;

    public CadenaTelevisiva()
    {
    }

    public long getId()
    {
        return id;
    }

    protected void setId(long id)
    {
        this.id = id;
    }

    public String getNombre()
    {
        return nombre;
    }

    public void setNombre(String nombre)
    {
        this.nombre = nombre;
    }
}


Y esta es la clase Televidente:

@Entity
public class Televidente implements Serializable
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    private String nombre;
    private CadenaTelevisiva cadenaFavorita;

    public Televidente()
    {
    }

    public CadenaTelevisiva getCadenaFavorita()
    {
        return cadenaFavorita;
    }

    public void setCadenaFavorita(CadenaTelevisiva cadenaFavorita)
    {
        this.cadenaFavorita = cadenaFavorita;
    }

    public long getId()
    {
        return id;
    }

    protected void setId(long id)
    {
        this.id = id;
    }

    public String getNombre()
    {
        return nombre;
    }

    public void setNombre(String nombre)
    {
        this.nombre = nombre;
    }
}


Para representar la relación usamos la anotación @ManyToOne, que también vimos en el tutorial anterior. Así que el atributo cadenaFavorita queda anotado de esta forma:

@ManyToOne 
private CadenaTelevisiva cadenaFavorita;


Y la clase Televidente queda, por lo tanto, de esta forma:

@Entity
public class Televidente implements Serializable
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    private String nombre;

    @ManyToOne
    private CadenaTelevisiva cadenaFavorita;

    public Televidente()
    {
    }

    public CadenaTelevisiva getCadenaFavorita()
    {
        return cadenaFavorita;
    }

    public void setCadenaFavorita(CadenaTelevisiva cadenaFavorita)
    {
        this.cadenaFavorita = cadenaFavorita;
    }

    public long getId()
    {
        return id;
    }

    protected void setId(long id)
    {
        this.id = id;
    }

    public String getNombre()
    {
        return nombre;
    }

    public void setNombre(String nombre)
    {
        this.nombre = nombre;
    }
}


Ahora usaremos la clase HibernateUtil que creamos en el segundo tutorial para comprobar que todo funcione correctamente. No olviden agregar al archivo hibernate.cfg.xml la ubicación de las clases que acabamos de anotar:
    
<mapping class="hibernate.relaciones.muchos.uno.anotaciones.modelo.CadenaTelevisiva" />
<mapping class="hibernate.relaciones.muchos.uno.anotaciones.modelo.Televidente" />


En este ejemplo haremos lo mismo que hace unos instantes:

Crearemos tres objetos CadenaTelevisiva, cadena 1, cadena 2, y cadena 3, respectivamente, y dos objetos Televidente, televidente 1, y televidente 2, y relacionaremos la cadena 1 con el televidente 1 y la cadena 3 con el televidente 2.

Para lo cual colocaremos el siguiente código en el método main de nuestra clase Main:
    
public static void main(String[] args)
{
    /* Creamos los tres objetos CadenaTelevisiva */

    CadenaTelevisiva cadena1 = new CadenaTelevisiva();
    cadena1.setNombre("Cadena 1");

    CadenaTelevisiva cadena2 = new CadenaTelevisiva();
    cadena2.setNombre("Cadena 2");

    CadenaTelevisiva cadena3 = new CadenaTelevisiva();
    cadena3.setNombre("Cadena 3");


    /* Guardamos estos tres objetos CadenaTelevisiva en la base de datos */
    Session sesion = HibernateUtil.getSessionFactory().openSession();
    sesion.beginTransaction();
    sesion.save(cadena1);
    sesion.save(cadena2);
    sesion.save(cadena3);
    sesion.getTransaction().commit();
    sesion.close();


    /* Creamos dos objetos Televidente */
    Televidente televidente1 = new Televidente();
    televidente1.setNombre("Televidente 1");
    televidente1.setCadenaFavorita(cadena1);

    Televidente televidente2 = new Televidente();
    televidente2.setNombre("Televidente 2");
    televidente2.setCadenaFavorita(cadena3);

    /* Guardamos los dos objetos Televidente en la base de Datos */
    sesion = HibernateUtil.getSessionFactory().openSession();
    sesion.beginTransaction();
    sesion.save(televidente1);
    sesion.save(televidente2);
    sesion.getTransaction().commit();
    sesion.close();
}


Ahora comprobemos que en la base de datos tengamos las 3 cadenas televisivas y los 2 televidentes:



Como podemos observar todo ha salido como lo esperábamos ^-^.

Esto es todo con respecto a las relaciones unidireccionales. Como les decía, las relaciones Muchos a Uno bidireccionales, son igual a las relaciones Uno a Muchos bidireccionales que vimos en el tutorial anterior por lo que hemos terminado con este tutorial.

Si tienen alguna duda, comentario o sugerencia no duden en preguntar.

Saludos y gracias.

Descarga los archivos de este tutorial desde aquí:

Entradas Relacionadas: