sábado, 27 de junio de 2009

Hibernate - Parte 4: Relaciones / Uno a muchos

En el tutorial anterior dijimos que en las bases de datos existen 7 tipos de relaciones, y vimos cómo crear relaciones uno a uno, tanto unidireccionales com bidireccionales, con Hibernate usando archivos de mapeo y anotaciones (en ejemplos separados). Cubriendo con eso 2 de los 7 tipos.

Ahora veremos como trabajar con relaciones Uno a Muchos, unidireccionales y bidireccionales. Con lo que cubriremos otros dos tipos.

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 en el editor nuestra clase "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.

En las relaciones Uno a Muchos, un objeto de la entidad "A" (el lado uno) está relacionado con muchos objetos de la entidad "B" (el lado muchos). En el caso de que la relación sea unidireccional, solo la entidad "A" tiene una referencia a los objetos de tipo "B" y esta relación está representada por una colección (un List o un Set) y la entidad "A" puede acceder a cada uno de los objetos de tipo "B" de esa colección. Un ejemplo de este tipo de relaciones puede ser una Persona puede tener muchos Libros. Donde la Persona es el lado Uno de la relación, y los Libros son el lado Muchos.

Si la relación es bidireccional, adicionalmente, las entidades "B" tendrán una referencia a la entidad "A" con la que están relacionados. Un ejemplo de esto es un Jefe y sus Empleados. En donde el Jefe es el lado Uno y los Empleados el lado Muchos.

Este tipo de relación se vería de forma gráfica más o menos así:



Bien, ahora comencemos con los ejemplos:


3 - Relaciones Uno a Muchos Unidireccionales


Primero crearemos las clases Persona y Libro (para la relación unidireccional), dentro del paquete "modelo" que creamos hace unos momentos. La clase Libro queda así:

public class Libro  
{ 
    private long id; 
    private String titulo;

    public Libro() 
    { 
    }  

    public long getId() 
    { 
        return id; 
    }  

    protected void setId(long id) 
    { 
        this.id = id; 
    }  
    
    public String getTitulo() 
    { 
        return titulo; 
    }  

    public void setTitulo(String titulo) 
    { 
        this.titulo = titulo; 
    } 
}


Como podemos ver es una clase muy simple, solo tiene dos atributos: id y titulo y, como no tiene ninguna referencia a la clase Persona, no sabe nada de la existencia de una relación con esta.

Pasemos a ver cómo queda la clase Persona:

public class Persona  
{ 
    private long id; 
    private String nombre; 
    private List<Libro> libros = new ArrayList<Libro>();  

    public Persona() 
    { 
    }  

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

    public List getLibros() 
    { 
        return libros; 
    }  

    public void setLibros(List libros) 
    { 
        this.libros = libros; 
    }  

    public void addLibro(Libro libro) 
    { 
        this.libros.add(libro); 
    } 
}


La clase Persona tiene, como habíamos dicho, una referencia a una colección (en este caso a una lista de objetos Libro, por lo que puede acceder a cada uno de los datos de los libros. La lista se inicializa con un ArrayList en el momento de la creación del objeto. Esto nos ayudará a agregar libros tan pronto como creamos un nuevo objeto Persona, sin recibir una NullPointerException.

He agregado además de los setters y los getters para la lista de Libros un método auxiliar llamado "addLibro" que permite agregar un libro a la lista.

Ahora veremos cómo indicarle a Hibernate que existen estas relaciones, primero usando archivos de mapeo y después usando anotaciones:


3.1 Relaciones Uno a Muchos Unidireccionales con Archivos de Mapeo


Creamos un nuevo documento XML en el paquete "mapeos". Le damos el nombre de "Libro.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 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.unomuchos.modelo.Libro" table="LIBROS">

    </class>
</hibernate-mapping>


El mapeo de la clase Libro es 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.unomuchos.modelo.Libro" table="LIBROS"> 

        <id name="id"> 
            <generator class="identity" /> 
        </id> 
      
        <property name="titulo" /> 
    </class> 
</hibernate-mapping>




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

<?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.unomuchos.modelo.Persona" table="PERSONAS">
        <id name="id" column="ID_PERSONA" >
            <generator class="identity" />
        </id>

        <property name="nombre" />

    </class>
</hibernate-mapping>


Noten que en esta ocasión agregue el atributo "column" en el elemento "<id>". Esto es importante ya que deberemos indicar el nombre de esta columna para poder crear la relación (cuál será la llave foránea que se usará).

Ahora ocupémonos de la relación.

Las relaciones uno a muchos se representan usando el elemento acorde con el tipo de colección que estemos usando (list, set, map, array, y primitive-array) existe otro elemento llamado "bag" usado de forma menos frecuente y que tal vez veamos en otro tutorial.

Cada tipo de colección se mapea de forma un poco distinta. Como nosostros estamos usando una lista, veremos cómo mapear esta pero pueden encontrar como mapear los otros tipos de colecciones en esta página.

Para mapear una lista usamos el elemento "<list>". En este elemento indicamos cuál es el nombre del atributo, dentro de la clase Persona, que representa la relación. En este caso el atributo se llama "libros". También aquí indicamos cuáles operaciones queremos que se realicen en cascada. En este caso queremos que todas las operaciones de guardar, actualizar y eliminar que ocurran en el padre sean pasadas a la colección, o sea que cuando guardemos, actualicemos, o eliminemos una Persona, las operaciones pasen también a todos sus Libros relacionados, por lo que usamos el valor "all".

En las relaciones uno a muchos existe dos estilos de cascada especiales llamados "delete-orphan" y "all-delete-orphan" (que solo existen si usamos archivos de mapeo) los cuales se encargan de que, en el caso de que se elimine el objeto padre (Persona), todos los objetos hijos (Libros) serán eliminados de la base de datos. Adicionalmente "all-delete-orphan" se encarga de que todas las otras operaciones que mencionamos antes (guardar, actualizar, y eliminar) también sean realizados en cascada, por lo que usaremos este valor:
    
<list name="libros" cascade="all-delete-orphan">   

</list>


Ahora indicamos cuál será el valor que se usará como llave foránea para relacionar los Libros con la Persona. ¿Recuerdan que indicamos el nombre de la columna para el identificador de la Persona? Pues bien, es aquí en donde usaremos este valor:
    
<key column="ID_PERSONA" /> 


Con esto indicamos que, en la tabla que se creará para mantener las entidades Libro, se debe crear una llave foránea relacionada con el identificador de la tabla Persona, identificada en este caso por la columna "ID_PERSONA". Indicamos el nombre de la columna de forma explícita ya que si dejamos que sea Hibernate quien lo genere, el nombre de la llave foránea (id) será el mismo que el de la llave primara (id) y ocurrirán errores al tratar de insertar entidades (claro que esto también podría solucionarse dando a los atributos nombres distintos desde el principio, pero es cuestión de gustos).

Ahora bien, las listas son una estructura de datos con una característica única: tienen un orden. Esto significa que el orden en el que los elementos entran en la lista es importante e, internamente, se usa un índice para saber el orden de los elementos.

Cuando tratamos de almacenar estos datos nos interesa que en el momento que sean recuperados, los elementos de la lista estén en el mismo orden en el que los guardamos y es por esta razón que se debe usar una columna extra en la tabla generada para guardar este índice (el cual comienza en cero). Para indicar el nombre que tendrá esta columna usamos el elemento "index" y colocamos en su atributo "column" el nombre que tendrá esta columna:
    
<index column="ORDEN" />


Dejamos lo mejor para el final ^-^ ya que ahora debemos indicar qué tipo de relación representa esta colección. Como en este caso estamos representando una relación "uno a muchos", usamos el elemento "<one-to-many>". En el cual debemos indicar de qué clase son las entidades que estamos guardando en la lista (ya que este dato no puede ser obtenido usando reflexión):
    
<one-to-many class="hibernate.relaciones.unomuchos.modelo.Libro" />


Finalmente el mapeo de la relación queda así:

<list name="libros" cascade="all-delete-orphan"> 
    <key column="ID_PERSONA" /> 
    <index column="ORDEN" /> 
    <one-to-many class="hibernate.relaciones.unomuchos.modelo.Libro" /> 
</list>


Y el archivo de mapeo para la entidad Persona completo así:

<hibernate-mapping> 
    <class name="hibernate.relaciones.unomuchos.modelo.Persona" table="PERSONAS"> 
        <id name="id" column="ID_PERSONA"> 
            <generator class="identity" /> 
        </id>  

        <property name="nombre" />  

        <list name="libros" cascade="all-delete-orphan"> 
            <key column="ID_PERSONA" /> 
            <index column="ORDEN" /> 
            <one-to-many class="hibernate.relaciones.unomuchos.modelo.Libro" /> 
        </list>  
    </class> 
</hibernate-mapping>




Para este ejemplo, usaremos la clase HibernateUtil que creamos en el primer tutorial de la serie.

Ahora colocaremos el siguiente código (como siempre auto-explicatvo) como nuestro método main:

public static void main(String[] args) 
{
    /*Primero creamos una persona y la asociamos con dos libros*/
    Libro libro1 = new Libro();
    libro1.setTitulo("20000 leguas de viaje submarino");

    Libro libro2 = new Libro();
    libro2.setTitulo("La maquina del tiempo");

    Persona persona1 = new Persona();
    persona1.setNombre("Persona que se eliminara");

    persona1.addLibro(libro1);
    persona1.addLibro(libro2);


    /*Creamos una segunda persona, que sera eliminada, y la asociamos con otros
    dos libros*/
    Libro libro3 = new Libro();
    libro3.setTitulo("El ingenioso hidalgo don Quijote de la Mancha");

    Libro libro4 = new Libro();
    libro4.setTitulo("La Galatea");

    Persona persona2 = new Persona();
    persona2.setNombre("Alex");

    persona2.addLibro(libro3);
    persona2.addLibro(libro4);

        
    /*En la primer sesion guardamos las dos personas (los libros correspondientes
    seran guardados en cascada*/
    Session sesion = HibernateUtil.getSessionFactory().openSession();
    sesion.beginTransaction();

    sesion.persist(persona1);
    sesion.persist(persona2);

    sesion.getTransaction().commit();
    sesion.close();


    /*En la segunda sesion eliminamos la persona1 (los dos primeros libros seran
    borrados en cascada)*/
    sesion = HibernateUtil.getSessionFactory().openSession();
    sesion.beginTransaction();

    sesion.delete(persona1);

    sesion.getTransaction().commit();
    sesion.close();
}


Según el código anterior en la base de datos debería quedar una Persona, la segunda que guardamos ("Alex"), con sus dos libros respectivos. Comprobemos que esto es verdad.



Podemos ver que efectivamente el resultado es el esperado ^-^.

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


3.2 Relaciones Uno a Muchos Unidireccionales con 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:

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

    public Libro()
    {
    }

    public long getId()
    {
        return id;
    }

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

    public String getTitulo()
    {
        return titulo;
    }

    public void setTitulo(String titulo)
    {
        this.titulo = titulo;
    }
}


Y esta es la de la clase Persona:

@Entity
public class Persona implements Serializable
{
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;
    private String nombre;
    private List<Libro> libros = new ArrayList<Libro>();

    public Persona()
    {
    }

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

    public List getLibros()
    {
        return libros;
    }

    public void setLibros(List libros)
    {
        this.libros = libros;
    }

    public void addLibro(Libro libro)
    {
        this.libros.add(libro);
    }
}


Ahora explicaré la anotación que usaremos para indicar la relación. Esta anotación es "@OneToMany" y la colocamos en el atributo de tipo colección de Libros (en este caso la clase Persona será la dueña de la relación ya que es la única que está consciente de esta).

Igual que lo hicimos en la anotación "@OneToOne" aqui definiremos qué operaciones serán realizadas en cascada. Además también podemos indicar el tipo de "fetch" o recuperación que tendrá la colección. Explico: solo existen dos tipo de recuperación: tardado (lazy)e inmediato (eager). Si decidimos que la recuperación sea "lazy" entonces las entidades relacionadas (los Libros de la colección) no serán recuperados de la base de datos al momento que se recupera la Persona dueña, sino hasta que se usen estos elementos (siempre y cuando estemos dentro de una transacción). Si la recuperación es "eager" entonces los Libros relacionados se recuperarán al mismo tiempo que la Persona dueña. En este caso yo usaré un fetch de tipo eager.

Al final la relación queda de esta forma:

@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
private List<Libro> libros = new ArrayList<Libro>();


Y la clase Persona, con todas sus anotaciones, así:

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

    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
    private List&ltLibro> libros = new ArrayList<Libro>();

    public Persona()
    {
    }

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

    public List getLibros()
    {
        return libros;
    }

    public void setLibros(List libros)
    {
        this.libros = libros;
    }

    public void addLibro(Libro libro)
    {
        this.libros.add(libro);
    }
}


Y listo, con esto ya tenemos una relación Uno a Muchos unidireccional con la Persona como dueña de la relación y varios Libros como entidad inversa.

No olviden que debemos colocar en el archivo de configuración de Hibernate estas clases:

<mapping class="hibernate.relaciones.unomuchos.anotaciones.modelo.Libro" /> 
<mapping class="hibernate.relaciones.unomuchos.anotaciones.modelo.Persona" />


Usaremos la clase HibernateUtil que creamos en el segundo tutorial de la serie para implementar el código de prueba y el siguiente código dentro la clase main:

public static void main(String[] args) 
{ 
    /*Primero creamos una persona y la asociamos con dos libros*/ 
    Libro libro1 = new Libro(); 
    libro1.setTitulo("20000 leguas de viaje submarino");  

    Libro libro2 = new Libro(); 
    libro2.setTitulo("La maquina del tiempo");  

    Persona persona1 = new Persona(); 
    persona1.setNombre("Persona que se eliminara");  
    persona1.addLibro(libro1); 
    persona1.addLibro(libro2);   

    /*Creamos una segunda persona, que sera eliminada, y la asociamos
    con otros dos libros*/ 
    Libro libro3 = new Libro(); 
    libro3.setTitulo("El ingenioso hidalgo don Quijote de la Mancha");  

    Libro libro4 = new Libro(); 
    libro4.setTitulo("La Galatea");  

    Persona persona2 = new Persona(); 
    persona2.setNombre("Alex");  
    persona2.addLibro(libro3); 
    persona2.addLibro(libro4);   

    /*En la primer sesion guardamos las dos personas (los libros
    correspondientes seran guardados en cascada*/ 
    Session sesion = HibernateUtil.getSessionFactory().openSession(); 
    sesion.beginTransaction();  

    sesion.persist(persona1); 
    sesion.persist(persona2);  

    sesion.getTransaction().commit(); 
    sesion.close();   

    /*En la segunda sesion eliminamos la persona1 (los dos primeros
    libros seran borrados en cascada)*/ 
    sesion = HibernateUtil.getSessionFactory().openSession(); 
    sesion.beginTransaction();  

    sesion.delete(persona1);  

    sesion.getTransaction().commit(); 
    sesion.close();     
}


Según el código anterior, debería haber quedado una sola Persona en la base de datos con sus dos Libros relacionados, vemos que esto sea así:



Como podemos ver, todo ha salido bien ^-^. Ahora podemos pasar a ver cómo funciona la relación Uno a Muchos bidireccional.


4 - Relaciones Uno a Muchos Bidireccionales


Las relaciones bidireccionales son muy parecidas a las unidireccionales, con la diferencia que el lado inverso de la relación también sabe de esta, por lo que tiene una referencia al dueño. Veamos esto con un ejemplo para que quede más claro. Nuevamente comenzaremos con un ejemplo usando archivos de mapeo y posteriormente usaremos anotaciones.


4.1 Relaciones Uno a Muchos Bidireccionales con Archivos de Mapeo


Modificaremos un poco nuestra clase "Libro" para agregar una referencia a la "Persona" (con sus correspondientes setters y getters):

private Persona persona;

public Persona getPersona()
{
    return persona;
}

public void setPersona(Persona persona)
{
    this.persona = persona;
}


Por lo que la clase queda de la siguiente forma:

public class Libro 
{
    private long id;
    private String titulo;
    private Persona persona;

    public Libro()
    {
    }

    public long getId()
    {
        return id;
    }

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

    public String getTitulo()
    {
        return titulo;
    }

    public void setTitulo(String titulo)
    {
       this.titulo = titulo;
    }

    public Persona getPersona()
    {
        return persona;
    }

    public void setPersona(Persona persona)
    {
        this.persona = persona;
    }
}


La clase "Persona" queda intacta.

Ahora modificaremos el archivo "Libro.hbm.xml" para que refleje los campos que acabamos de realizar. Pero primero veamos como quedo el archivo de mapeo de la ("PersonaPersona.hbm.xml"):

<hibernate-mapping>
    <class name="hibernate.relaciones.unomuchos.modelo.Persona" table="PERSONAS">
        <id name="id" column="ID_PERSONA">
            <generator class="identity" />
        </id>

        <property name="nombre" />

        <list name="libros" cascade="all-delete-orphan">
            <key column="ID_PERSONA" />
            <index column="ORDEN" />
            <one-to-many class="hibernate.relaciones.unomuchos.modelo.Libro" />
        </list>
    </class>
</hibernate-mapping>


Como podemos ver la relación de Persona a Libros esta mapeada usando el elemento "one-to-many" (una Persona puede tener muchos Libros), por lo que para indicar la relación de Libros a Persona usamos el elemento contrario: "many-to-one" (muchos Libros pertenecen a una Persona).

En este elemento debemos indicar el nombre del elemento que representa la relación (en la clase Libro), que en este caso es "persona", y el nombre de la columna donde se almacena el identificador de la Persona ("ID_PERSONA"). Por lo que el elemento queda así:
     
<many-to-one name="persona" column="ID_PERSONA"  />


Y al final el archivo "Libro.hbm.xml" completo así:

<hibernate-mapping>
    <class name="hibernate.relaciones.unomuchos.modelo.Libro" table="LIBROS">
        <id name="id" column="id">
            <generator class="identity" />
        </id>
        <property name="titulo" />

        <many-to-one name="persona" column="ID_PERSONA"  />
    </class>
</hibernate-mapping>


Y eso es todo ^-^. Probémoslo usando el mismo código que ya teníamos en la clase Main:

public static void main(String[] args)      
{         
    /*Primero creamos una persona y la asociamos con dos libros*/         
    Libro libro1 = new Libro();         
    libro1.setTitulo("20000 leguas de viaje submarino");          
      
    Libro libro2 = new Libro();         
    libro2.setTitulo("La maquina del tiempo");          
    Persona persona1 = new Persona();         
    persona1.setNombre("Persona que se eliminara");          
    persona1.addLibro(libro1);         
    persona1.addLibro(libro2);           
      
    /*Creamos una segunda persona, que sera eliminada, y la asociamos con otros dos libros*/         
    Libro libro3 = new Libro();         
    libro3.setTitulo("El ingenioso hidalgo don Quijote de la Mancha");          
      
    Libro libro4 = new Libro();         
    libro4.setTitulo("La Galatea");          
      
    Persona persona2 = new Persona();         
    persona2.setNombre("Alex");          
    persona2.addLibro(libro3);         
    persona2.addLibro(libro4);           
      
    /*En la primer sesion guardamos las dos personas (los libros correspondientes seran guardados en cascada*/         
    Session sesion = HibernateUtil.getSessionFactory().openSession();         
    sesion.beginTransaction();          
    sesion.persist(persona1);         
    sesion.persist(persona2);          
    sesion.getTransaction().commit();         
    sesion.close();           
      
    /*En la segunda sesion eliminamos la persona1 (los dos primeros libros seran borrados en cascada)*/         
    sesion = HibernateUtil.getSessionFactory().openSession();         
    sesion.beginTransaction();          
    sesion.delete(persona1);          
    sesion.getTransaction().commit();         
    sesion.close();     
}


Ahora comprobemos que en la base de datos solo quedan los datos de la persona2, junto con sus libros correspondientes:



Como podemos ver, todo ha salido bien ^-^.

Para terminar este tutorial veamos cómo trabajar con relaciones Uno a Muchos usando anotaciones.


4.2 Relaciones Uno a Muchos Bidireccionales con Anotaciones


Como ya teníamos una relación uno a muchos unidireccional con anotaciones, representada en el atributo "libros" de la clase "Persona". Siendo la clase Persona la única que esta consciente de la relación (por eso es unidireccional).

Para transformar esta relación en bidireccional lo único que debemos hacer es agregar un atributo que represente la relación con la clase "Persona" en la clase "Libro". De la misma forma que ocurre cuando usamos archivos de mapeo: Como la relación uno a muchos está representada en la clase "Persona" usando la anotación "@OneToMany" (una Persona puede tener muchos Libros), en el lado inverso de la relación debe usarse la anotación inversa "@ManyToOne" (muchos Libros pueden pertenecer a una Persona).

Esta anotación es un poco distinta a la anotación "@OneToOne" que vimos anteriormente. En este caso es importante quién es el dueño de la relación, ya que es el dueño quien determina cómo es que el motor de persistencia hace actualizaciones en la base de datos.

En el caso de las relaciones bidireccionales, usando anotaciones, el dueño SIEMPRE es el lado muchos (esta es una regla). Por lo que en este caso el dueño de la relación es la clase "Libro".

El lado inverso debe saber cuál es el nombre que lo representa en el lado dueño (esto se aclarará en el ejemplo). Para esto se usa el elemento "mappedBy".

Para ver cómo funciona lo dicho vayamos al ejemplo que teníamos. Primero modificaremos la clase "Libro" para agregar el atributo de tipo Persona que representará la relación (recuerden que debemos usar la aniotación "@ManyToOne"):

@ManyToOne
private Persona persona;


Y eso es todo lo que hay que modificar en la clase Libro. Como ven es un cambio muy pequeño. Ahora veamos la clase Persona.

Hace unos momentos dije que la clase Libro es la clase dueña de la relación y la clase Persona es la clase inversa. También dije que la clase inversa debe indicar en la anotación que representa la relación el nombre con el que el lado dueño la representa (que en este caso es "persona") y que esto se hace en el elemento "mappedBy" de la anotación. Por lo que la relación queda representada en la clase "Persona" de la siguiente forma:

@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER, mappedBy="persona")
private List libros = new ArrayList();


Y listo ^-^. Con estos pequeños cambios ya tenemos una relación bidireccional. Al final la clase Persona queda de la siguiente forma:

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

    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER, mappedBy="persona")
    private List libros = new ArrayList();

    public Persona()
    {
    }

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

    public List getLibros()
    {
        return libros;
    }

    public void setLibros(List libros)
    {
        this.libros = libros;
    }

    public void addLibro(Libro libro)
    {
        this.libros.add(libro);
    }
}


Y la clase Libro así:

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

    @ManyToOne
    private Persona persona;

    public Libro()
    {
    }

    public long getId()
    {
        return id;
    }

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

    public String getTitulo()
    {
        return titulo;
    }

    public void setTitulo(String titulo)
    {
        this.titulo = titulo;
    }

    public Persona getPersona()
    {
        return persona;
    }

    public void setPersona(Persona persona)
    {
        this.persona = persona;
    }
}


Probemos que todo funciona correctamente usando el código que teníamos anteriormente en la clase Main:

public static void main(String[] args)
{
    /*Primero creamos una persona y la asociamos con dos libros*/
    Libro libro1 = new Libro();
    libro1.setTitulo("20000 leguas de viaje submarino");

    Libro libro2 = new Libro();
    libro2.setTitulo("La maquina del tiempo");

    Persona persona1 = new Persona();
    persona1.setNombre("Persona que se eliminara");

    persona1.addLibro(libro1);
    persona1.addLibro(libro2);
    libro1.setPersona(persona1);
    libro2.setPersona(persona1);


    /*Creamos una segunda persona, que sera eliminada, y la asociamos con otros dos libros*/
    Libro libro3 = new Libro();
    libro3.setTitulo("El ingenioso hidalgo don Quijote de la Mancha");

    Libro libro4 = new Libro();
    libro4.setTitulo("La Galatea");

    Persona persona2 = new Persona();
    persona2.setNombre("Alex");

    persona2.addLibro(libro3);
    persona2.addLibro(libro4);
    libro3.setPersona(persona2);
    libro4.setPersona(persona2);


    /*En la primer sesion guardamos las dos personas (los libros correspondientes seran guardados en cascada*/
    Session sesion = HibernateUtil.getSessionFactory().openSession();
    sesion.beginTransaction();

    sesion.persist(persona1);
    sesion.persist(persona2);

    sesion.getTransaction().commit();
    sesion.close();


    /*En la segunda sesion eliminamos la persona1 (los dos primeros libros seran borrados en cascada)*/
    sesion = HibernateUtil.getSessionFactory().openSession();
    sesion.beginTransaction();

    sesion.delete(persona1);

    sesion.getTransaction().commit();
    sesion.close();
}


Nuevamente solo deben haber quedado en la base de datos los datos de la persona2 y sus libros relacionados. Comprobemos que efectivamente esto es así:



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

Bien, esto es todo lo que respecta a las relaciones uno a muchos. Con este tutorial ya llevamos 4 de los 7 tipos de relaciones que podemos tener en Hibernate. En los siguientes dos tutoriales veremos los 3 tipos restantes.

Cualquier duda, comentario, o sugerencia por favor no duden en colocarlo.

Saludos.

Descarga los archivos de este tutorial desde aquí:

Entradas Relacionadas:

46 comentarios:

  1. Amigo muchas gracias por tus tutoriales me sirvieron bastante en un proyecto que estoy empezando, tengo una pregunta mira si tuviera un libro ya registrado y lo quiero asignar a dos nuevas personas como lo haria y como aria para mostrar los datos en una tabla gracias.

    ResponderEliminar
  2. Hola Jorge;

    Pues en realidad con este tipo de relación no es posible, ya que la lógica del negocio dice que un libro solo puede pertenecer a una persona. Para hacer lo que queires debes usar una relación Muchos a muchos (@ManyToMany) para permitir que muchos libros puedan pertenecer a muchas personas.

    En ese caso se creará una tabla de unión o join table que almacenará la información de qué libros están relacionados con qué personas.

    Saludos

    ResponderEliminar
  3. Hola. Buscando una solución a un problema que tengo con hibernate es que llegué a este tutorial y me pareció muy bueno y muy claro.
    si me permitís me gustaría hacerte una pregunta:
    Cómo se hace el mapeo en una relación de 1 a muchos en donde el atributo de la clase 1 es un map y no un set? Me salta error y estoy segura que configuré mal el hbm.xml pero no logro entender bien que es, dado que leí el tutorial de hibernate pero no entiendo muy bien cómo funciona con Map.
    Desde ya muchas gracias,

    ResponderEliminar
  4. Hola Matias, muchas gracias por tus comentarios.

    En Java los Maps son una estructura de datos en donde una llave mapea a un valor, o dicho de otra forma, con una identificador puedes obtener un objeto (el cual puede ser un objeto simple, otra colección de objetos, etc.)

    Por lo tanto en Hibernate necesitas indicar cuál es la columna que usas como llave y cuál es la que usas como valor, y de qué entidad. Por ejemplo:


    <map name="fiestas">
    <key column="id"/>
    <map-key column="nombreDeLaFiesta" type="string"/>
    <element column="fechaDeLaFiesta" type="date"/>
    </map>

    Tal vez, como tu dices, hay algún detalle en tu mapeo que pudiera estar mal. ¿Cómo estas haciendo el mapeo?

    Saludos

    ResponderEliminar
  5. Hola Alex: te había escrito un comentario un poco largo, pero al querer publicarlo me saltó error.
    Empiezo de nuevo, pero esta vez te lo envío por partes:
    Las clases que mantienen la relación uno a muchos bidireccional son:
    Infoauto.java
    Integer idInfo;
    String marca, modelo, etc:
    Map anioValor=new HassMap();

    InfoAnioValor.java
    Integer id;
    Infoauto infoauto;
    int anio;
    float valor;

    ResponderEliminar
  6. Los mapings son:
    InfoAnioValor.hb.xml

    class name="nikols.entidades.bases.InfoAnioValor" table="infoaniovalor">
    id column="id" name="id"
    generator class="identity"
    id
    property column="anio" name="anio"/>
    property column="valor" name="valor"/>
    many-to-one name="infoauto" column="idInfo"/>
    class
    hibernate-mapping

    Infoauto.hbm.xml
    class name="nikols.entidades.bases.Infoauto" table="infoauto"
    id name="id" column="idInfo"
    generator class="identity"
    id

    property name="marca" column="marca"
    property name="modelo" column="modelo"

    map name="anioValor" cascade="all-delete-orphan"
    key column="idInfo"
    map-key column="anio" type="int"
    element column="valor" type="float"
    one-to-many class="nikols.entidades.bases.InfoAnioValor"
    map
    class

    ResponderEliminar
  7. Las tablas son:
    infoauto:
    integer idInfo
    varchar marca, modelo, etc



    infoaniovalor:
    integer id
    integer idInfo
    intger anio
    float valor


    El error que me trae al querer insertar un infoauto es:

    ResponderEliminar
  8. INFO: Reading mappings from resource : nikols/entidades/bases/InfoAnioValor.hbm.xml
    16/07/2009 15:02:16 org.hibernate.cfg.HbmBinder bindRootPersistentClassCommonValues
    INFO: Mapping class: nikols.entidades.bases.InfoAnioValor -> infoaniovalor
    16/07/2009 15:02:16 org.hibernate.cfg.Configuration addResource
    INFO: Reading mappings from resource : nikols/entidades/bases/Infoauto.hbm.xml
    16/07/2009 15:02:16 org.hibernate.util.XMLHelper$ErrorLogger error
    GRAVE: Error parsing XML: XML InputStream(38) The content of element type "map" must match "(meta*,subselect?,cache?,synchronize*,comment?,key,(map-key|composite-map-key|map-key-many-to-many|index|composite-index|index-many-to-many|index-many-to-any),(element|one-to-many|many-to-many|composite-element|many-to-any),loader?,sql-insert?,sql-update?,sql-delete?,sql-delete-all?,filter*)".
    Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at nikols.entidades.bases.Infoauto.insertar(Infoauto.java:423)


    Cualquier ayuda será siempre bienvenida!
    Desde ya muchas gracias y saludos.

    ResponderEliminar
  9. Hola Matias, disculpa por haber tardado en contestarte, pero he tenido bastante trabajo y hasta apenas estoy teniendo tiempo para atender el blog.

    ¿Finalmente lograste corregir el error que te daba?

    Saludos

    ResponderEliminar
  10. Hola Alex:
    Soy Irene (el Matias) de los comentarios anteriores. Matias es mi hijo, y los comentarios salieron como Matias porque yo no me di cuenta que era él el que estaba conectado a gmail en ese momento.
    No te preocupes por tu tardanza, yo también anduve bastante ajetreada.
    Te cuento que todavía continúo volviendome loca con ese mapeo.

    Las clases:
    1) Infoauto
    private Integer id;
    ......
    private Map anioValor=new HashMap();

    2) InfoAnioValor
    private Integer id;
    private Integer idInfo;
    private int anio;
    private float valor;

    Las Tablas de Mysql:
    1) infoauto
    integer idInfo (PK not null)
    ......

    2) infoaniovalor
    integer idInfoAnioValor; (PK not-null)
    integer idInfo; (not-null)
    integer anio; (not-null)
    integer valor;

    Los xml:
    los sigo en otro comentario porque la ultima veces no salio porque era muy largo.

    ResponderEliminar
  11. Los xml son:
    1) Infoauto.hbm.xml
    class name="nikols.entidades.bases.Infoauto" table="infoauto"

    id name="id" column="idInfo"
    generator class="identity"
    id
    .............................................

    map name="anioValor" lazy="true" inverse="true"
    key column="idInfo"
    map-key column="anio" type="int"
    one-to-many class="nikols.entidades.bases.InfoAnioValor"
    map


    2) InfoAnioValor.hbm.xml:
    class name="nikols.entidades.bases.InfoAnioValor" table="infoaniovalor"
    id column="id" name="idInfoAnioValor"
    generator class="identity"
    id
    property column="anio" name="anio" not-null="true"
    property column="valor" name="valor"
    many-to-one name="infoauto"
    class="nikols.entidades.bases.Infoauto"
    column="idInfo" not-null="true"

    Saqué todos los < y los /> porque me saltaba error en el comentario.

    el error que me sale en mi aplicacion es NullPointerException, y salta al querer abrir la sesion de hibernate. Aparentemente salta cuando llega a los mapeos de esas dos clases.
    Me estoy volviendo loca. Ya me leí todo el tutorial de la pagina de Hibernate y probé de mil maneras y nada.

    Si llegás a darte cuenta en donde está el error, te pido me avises.
    Muchisimas gracias de antemano, un saludo,
    Irene.

    ResponderEliminar
  12. Hola Irene; Podrías enviarme tu proyecto, junto con tu script de la base de datos a:

    programadorjavablog@gmail.com

    Para que lo pueda revisar con calma y cuidado a ver si logro encontrar el error?.

    Saludos

    ResponderEliminar
  13. muy buenos los tuto,los estoy utilizando, Cuando se viene el que falta muchos a muchos
    gracias por lo bien que has explicado. saludos

    ResponderEliminar
  14. Hola pluchi;

    Muchas gracias por tu comentario. Ya estoy trabajando en el siguiente tutorial, pero es un proceso un poco lento. Yo espero que a más tardar la siguiente semana lo tenga ya publicado.

    Saludos

    ResponderEliminar
  15. Hola, yo estoy empleando una relación muchos a muchos y en la tabla intermedia que se genera quería añadir un nuevo campo fecha. ¿Es esto posible? He buscado por todos los lados y no he encontrado nada.

    Saludos

    ResponderEliminar
  16. que onda man, por problemas por ahi con mi aplicacion encontre este blog que esta bastante bien explicado. Bueno mi duda es, segui mas o menos tu ejemplo con uno mio, pero al momento de hacer el save con un fetch EAGER me marca esto: "cannot simultaneously fetch multiple bags", y si quito el fetch o lo cambio por un lazy me marca que: "Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1". Quizas tu ya con mas experiencia puedas reconocer mi error.

    ResponderEliminar
  17. Hola,

    Tengo una relacion de uno a muchos en donde uso un elemento de la siguiente forma:



    ......property />


    Mi problema consiste al momento de recuperar los elementos ya que siempre me regresa y primer valor como null y posteriormente los registro que tengo en la tabla.

    ¿Alguien me podria decir la forma de que no me regrese ese primer valor null?

    ResponderEliminar
  18. Hola Alex! Soy Irene otra vez. No sé si seguirás por ahí, pero igualmente lanzo la siguiente pregunta:
    Tengo una relacion one-to-many many-to-one.

    Cada vez que genero uno nuevo del lado de one, me genera automáticamente todos los del many. Hasta ahí todo bien. Lo mismo, cuando lo elimno me elimina todos los del many.
    Ahora, cuando lo modifico y le hago un update(), veo que en la tabla de mysql correspondiente a los many, me los borra a todos y los vuelve a generar. Me doy cuenta porque cambia el id de c/u. Existe alguna forma que los actualice sin tener que borrar y agregar?
    Desde ya muchas gracias!!!
    Saludos,
    Irene.

    ResponderEliminar
  19. Alex, excelente tutorial.
    Tengo un problema raro. En una entidad tendo dos relaciones uno a muchos (bidireccionales). Cuando las tengo a las dos falla la inicializacion de hibernate (es lo que pienso porque session me queda en null). Ahora cuando saco una referencia a una de estas relaciones uno a muchos,quedando una relacion muchos a uno (unidirecional) funciona perfecto. Que puede ser? O debo tener en cuenta alguna cuestion con dos o mas relaciones muchos a uno?
    Te cuento que no tira ninguna excepcion de hibernate, cuando llamo a iniciaOperacion devuelve null y va directamente al bloque finally.

    -Por otro lado cuando conviene utilizar relaciones unidireccionles y bidireccionales?

    -Que me aconsejas para realizar validaciones? Tengo una aplicacion web, donde cargo datos desde formularios: utilizo un framework ajax (zk).

    Saludos, muy buen trabajo

    ResponderEliminar
  20. Hola Juan;

    En realidad no deberías tener problema para generar las dos relaciones uno a muchos bidireccionales que comentas. ¿Podrías mostrarme tus archivos de mapeo y tus clases para verlos? Si pudieras mandarmelos por correo seriá mucho mejor

    Por otro lado cuando usar relaciones unidireccionales o bidireccionales depende de tu lógica de negocio. Si necesitas porder ir de ambos lados de la entidad al otro lado usa una bidireccional, si esto no es estrictamente necesario usa una unidireccional. Ahora no se me ocurre un ejemplo concreto de esto, pero puedes ver varios a lo largo de los tutoriales.

    Sobre las validaciones pues también depende de lo que estes haciendo. Yo por lo regular haago validaciones del lado del servidor (en las clases Java que manejan la lógica de la aplicación) y luego si hay algún problema regreso un mensaje a la vista. Supongo que ZK debe tener un mecanismo de validaciones (practicamente todos los frameworks lo tienen) así que además podrías usar el que te recomienden en su propia documentación.

    Saludos.

    ResponderEliminar
  21. Hola.. Tengo un problema con Hibernate y estuve leyendo este tutorial pero no encuentre un ejemplo a mi caso, lo que me sucede es que para mi relacion de OneToMany uso una secuencia y lo que quiero hacer es editar y aveces eliminar registros de esa lista solo que cualquier cambio que hago en mi base de Datos me guarda registros nuevos.. es decir el no edita siempre carga nuevos.. podrias decirme por que??

    ResponderEliminar
  22. Hola Lorena;

    Puede que el problema esté en el valor del cascade que le estas colocando a tu lista en el lado One de tu relación. Si entiendo bien, editas los elementos de la lista (que son tu lado Many) y luego actualizas en tu base de datos tu elemento del lado One?

    Saludos

    ResponderEliminar
  23. Hola Alex mira te felicito por todos tus tutoriales son muy buenos.
    He utilizado todas tus explicaciones y la verdad me ha ido bien. Ahora tengo un problema y es que estoy haciendo un proyecto cuya relacion es de uno a varios, es decir tengo varios profesores que imparten varias o ninguna asignaturas, pero tengo que hacer una consulta y mostrar todas las asignaturas que tiene un profesor y visualizarlos en una lista. lo llevo intentando varios dias pero no lo consigo, haber si me puedes hechar una mano.
    Te lo agradezco muchísimo.
    Gracias

    ResponderEliminar
  24. Que tal....tus tutos son fantasticos como siempre...
    en este mismo tengo un problema, me hace todo sin lanzar error, pero cuando voy a ver las tablas resulta que en la columna de "persona_id" de libro, me los pone en NULL.. y lo he estado mirando y rehaciendo y no encuentro el error....me faltaria alguna anotacion que desconozca?...Saludos.

    ResponderEliminar
  25. @Daniel

    ah me falto mencionar que este error me sale cuando quiero hacerlo por notaciones , en mapeo anda excelente.

    Saludos.

    ResponderEliminar
  26. @Daniel

    Yo eso lo solucioné agregando en el método addLibro de la clase Persona la linea:

    libro.setPersona(this);

    no se si es lo mejor pero es una opción.

    Saludos

    ResponderEliminar
  27. ayuda ando al borde de la deseperacion, tengo una relación de un a muchos en mi proyecto entre dos clases en la cual ambos id son generados por anotaciones de secuencia; tambien tengo anotaciones de inserción, eliminación y edición en cascada de los objetos hijo, el problema radica en que en el momento de insertar el objeto padre hibernate intenta persistir los objetos hijo pero se encuentra con que la llave primario del objeto padre es null AAAAAAAH! estuve no se supone que para eso es la anotacion de generatedValue?? segun estuve leyendo y el metodo persist () genera dicho valor despues de la insercion, pero yo necesito que se genere antes para que las clases hijos puedan tener ese vendito id autogenerado de la clase padre, espero me haya hecho entender y cualquier ayuda les agradeceria mucho.

    ResponderEliminar
  28. La verdad es que todo está muy claro, pero tengo una duda. Y si quieres acceder a la columna "persona_Id" para cambiar su valor por "3" porque tienes a una persona con el Id "3". Como se haría? El Objeto persona no puedes modificarlo porque cambiarías la persona y solo quieres cambiar la referencia de la tabla libro.
    Gracias.

    ResponderEliminar
    Respuestas
    1. Hola Unknown;

      En ese caso tendrías que hacer varios pasos. Lo que quieres hacer en realidad es un cambio de dueño de un libro, por lo que tendría que ser como en la vida real (lo cual en realidad es más seguro porque garantiza la consistencia de los datos, que simplemente moviendo la columna en la base de datos).

      Primero tendrías que buscar al dueño original, quitarle el que quieres cambiar, y guardar a la persona (esto para que su relación con los libros sea guardada sin el libro que acabas de quitar).

      Después tendrías que buscar al nuevo dueño, agregar el libro a su lista de libros, y luego guardar a esta persona (para que las relaciones con sus libros sea almacenada).

      Saludos

      Eliminar
    2. Hola Alex,
      soy Unknown (no se que saldrá ahora cuando grabe, porque a mi me ponia mi nick real.

      Muchas gracias por tu respuesta, era justo mi problema.

      Felicidades por el tutorial, es simplemente, perfecto y claro.

      Un saludo!

      Eliminar
  29. Hola,
    veo lo siguiente:

    Si ahora coges un objeto Persona existente de la DB, y lo usas para grabar un nuevo libro, el primero lo graba bien pero el segundo te deja NULL el ID de PERSONA en la tabla LIBRO y te crea un registro nuevo OK. Si vuelves a grabar, éste te lo deja como NULL de nuevo, y crea otro OK. Alguien sabe como solucionar esto?
    Gracias y un saludo.

    ResponderEliminar
  30. Soy Unknown,
    olvidé comentar antes que esto SOLO pasa cuando hacemos la bidireccionalidad.

    ResponderEliminar
  31. Solucionado. inverse="true". Por si a alguien le pasa lo mismo.

    ResponderEliminar
  32. Hola, les cuento que estoy haciendo una aplicacion donde utilizo lo visto en este blog.. tengo una relacion uno a muchos tal cual como aqui lo presentan (con archivos de mapeo) y al momento de actualizar me arroja la siguiente excepcion:
    org.hibernate.HibernateException: Don't change the reference to a collection with cascade="all-delete-orphan"
    Alguna ayuda?

    ResponderEliminar
  33. Buenos días, me ha ENCANTADO este tutorial. Enhorabuena
    Estoy teniendo unos problemas con los de los 1-n, bien sea unidireccional o bidireccional, y es que, no hace el borrado en cascada, de hecho, en la base de datos, he ido a ver cómo crea la clave externa, y la crea como Delete on Restrict, por tanto, no me borra los libros al borrar la persona, en mi caso, no me borra los comentarios al borrar al post.

    Uso el método delete, y en los mapeos, tengo

































    =====================






















    ====================




    ResponderEliminar
    Respuestas
    1. Perdón, que no lo escapé



      <?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="com.blog.vo.Post1nBiVO" table="posts1nBi">
      <id name="id" type="int" column="idPost" >
      <generator class="identity"/>
      </id>

      <property name="idAutor" type="int" column="idAutor"/>

      <property name="fecha">
      <column name="fecha" />
      </property>
      <property name="titulo">
      <column name="titulo" />
      </property>
      <property name="contenido">
      <column name="contenido"/>
      </property>

      <list name="comentarios" cascade="all-delete-orphan">
      <key column="idPost" /><!-- cuál columna de la tabla post será la clave externa -->
      <index column="ORDEN" /> <!-- para guardar en el mismo orden, una columna nueva -->
      <!-- ahora sí indico que hay una relación de 1 a n -->
      <one-to-many class="com.blog.vo.Comentario1nBiVO" />
      </list>

      </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>
      <class name="com.blog.vo.Comentario1nBiVO" table="comentarios1nBi">
      <id name="id" type="int" column="id" >
      <generator class="identity"/>
      </id>

      <property name="cuerpo">
      <column name="cuerpo" />
      </property>
      <property name="fecha">
      <column name="fecha"/>
      </property>

      <!-- muchos comentarios pertenecen a un post
      indico el nombre del atributo dentro
      de Comentario1nBiVO al cual haré referencia
      con mi clave.
      La columna es la columna de posts, que es clave. -->
      <many-to-one name="postVO" column="idPost" />
      </class>
      </hibernate-mapping>

      Eliminar
  34. tenngo 2 tablas:
    - Roles(role_id, role_name)
    - Users(user_id, user_name, role_id)

    desde una vista .jsp (Con jsf), como obtengo en un datatable el campo role_id en la tabla users

    ResponderEliminar
  35. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  36. Disculpen soy nuevo en hibernate, me gustaria saber como puedo guardar datos sin utilizar una clase main si no directamente desde un jsp..

    ResponderEliminar
  37. buenos días, he encontrado este tutorial y me a parecido estupendo, qusiera que me ayudaran con un problema que tengo; quiero hacer una inserción a una tabla relación pura desde un jsp como podria hacerlo.

    las tabla es (rol_id,privilegio_id)

    gracias.

    ResponderEliminar
  38. Como hago para seleecionar tus libros

    Libros.getAllLibros() con sus titulos y todo, con List nose puede pero con Set si, pero si son dos o mas el set funciona pero ya no guarda.

    ResponderEliminar
  39. Muy buenos tutorial muchas gracias por publicar este material. Ademas de agradecerte me gustaria ver si me podrias ayudar con un problema que me surgio al intentar seguir este tutorial. Cuando hago el mapeo en OneToMany Bidirecional con los archivos xml todo funciona muy bien, pero cuando lo hago con anotaciones no llena la columna ID_PERSONA de la tabla LIBROS. Las clases con sus anotaciones son las siguientes:

    @Entity
    @Table(name="PERSONASBIA")
    public class Persona {
    @Id
    @Column(name="ID_PERSONA")
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Integer idPersona;
    @Column(name="NOMBRE")
    private String nombre;
    @OneToMany(cascade=CascadeType.ALL, mappedBy="persona", fetch=FetchType.EAGER)
    @OrderBy("persona")
    private List libros = new ArrayList();

    @Entity
    @Table(name="LIBROSBIA")
    public class Libro {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="ID_LIBRO")
    private Integer idLibro;
    @Column(name="TITULO")
    private String titulo;
    @ManyToOne
    @JoinColumn(name="ID_PERSONA")
    private Persona persona;

    Los query que hace hibernate son los siguientes:
    Hibernate: alter table LIBROSBIA drop foreign key FK_q588pdebxfexs5uq4m0itjwg9
    Hibernate: drop table if exists LIBROSBIA
    Hibernate: drop table if exists PERSONASBIA
    Hibernate: create table LIBROSBIA (ID_LIBRO integer not null auto_increment, TITULO varchar(255), ID_PERSONA integer, primary key (ID_LIBRO))
    Hibernate: create table PERSONASBIA (ID_PERSONA integer not null auto_increment, NOMBRE varchar(255), primary key (ID_PERSONA))
    Hibernate: alter table LIBROSBIA add constraint FK_q588pdebxfexs5uq4m0itjwg9 foreign key (ID_PERSONA) references PERSONASBIA (ID_PERSONA)
    Jul 08, 2015 9:01:17 PM org.hibernate.tool.hbm2ddl.SchemaExport execute
    INFO: HHH000230: Schema export complete
    Hibernate: insert into PERSONASBIA (NOMBRE) values (?)
    Hibernate: insert into LIBROSBIA (ID_PERSONA, TITULO) values (?, ?)
    Hibernate: insert into LIBROSBIA (ID_PERSONA, TITULO) values (?, ?)
    Hibernate: insert into PERSONASBIA (NOMBRE) values (?)
    Hibernate: insert into LIBROSBIA (ID_PERSONA, TITULO) values (?, ?)
    Hibernate: insert into LIBROSBIA (ID_PERSONA, TITULO) values (?, ?)

    Los datos que genera son los siguientes:
    para select * from PERSONASBIA;
    1 Persona que se eliminara
    2 Alex
    para select * from LIBROSBIA;
    1 20000 leguas de viaje submarino NULL
    2 La maquina del tiempo NULL
    3 El ingenioso hidalgo don Quijote de la Mancha NULL
    4 La Galatea NULL

    Nota: Estoy usando hibernate 4.3

    Agradeceria mucho tu ayuda

    Saludos

    ResponderEliminar