Tabla de Contenidos

Uno a muchos (desordenada)

La relación uno a muchos consiste simplemente en que un objeto padre tenga una lista sin ordenar de otros objetos hijo de forma que al persistirse el objeto principal también se persista la lista de objetos hijo. Esta relación también suele llamarse maestro-detalle o padre-hijo.

Clases Java

Antes de entrar en cómo se implementa en Hibernate , veamos las clases Java y las tablas que definen la relación uno a muchos.

Para nuestro ejemplo vamos a usar las clases:

Estas dos clases van a tener una relación uno a muchos.

1 | Listado 1.Relación 1 a n
public class Profesor implements Serializable  {
    private int id;
    private String nombre;
    private String ape1;
    private String ape2;
    private Set<CorreoElectronico> correosElectronicos;
 
 
    public Profesor(){ 
    }
 
    public Profesor(int id, String nombre, String ape1, String ape2) {
        this.id = id;
        this.nombre = nombre;
        this.ape1 = ape1;
        this.ape2 = ape2;
    }
 
}
 
public class CorreoElectronico implements Serializable {
    private int idCorreo;
    private String direccionCorreo;
    private Profesor profesor;
 
    public CorreoElectronico() {
 
    }
 
    public CorreoElectronico(int idCorreo,String direccionCorreo,Profesor profesor) {
        this.idCorreo=idCorreo;
        this.direccionCorreo=direccionCorreo;
        this.profesor=profesor;
    }
}

En el listado 1 podemos ver cómo la clase Profesor tiene una propiedad llamada correosElectronicos de la clase CorreoElectronico (línea 6) y además la clase CorreoElectronico también posee referencia a Profesor ya que hemos definido que la relación es bidireccional desde Profesor hacia Direccion y viceversa.

El mecanismo que usamos en Java para almacenar una serie de objetos hijo es el interfaz Set. No vamos a usar el interfaz List o un array ya que dichas formas implican un orden de los objetos hijo mientras que usando un Set no hay ningún tipo de orden. En la siguiente lección se explica cómo usar una lista ordenada de objetos hijo.

En la clases Java Profesor y CorreoElectronico no se han incluido los métodos get/set de cada propiedad para facilitar la lectura pero deben estar en la clase Java.

En el siguiente diagrama UML se ve que la relación desde Profesor hacía CorreoElectronico y viceversa.


class Profesor
Profesor : int id
Profesor : String nombre
Profesor : String ape1
Profesor : String ape2


class CorreoElectronico
CorreoElectronico: int idCorreo
CorreoElectronico: String direccionCorreo


Profesor "1" -- "0..n" CorreoElectronico: correosElectronicos

Tablas

La tablas de base de datos quedarían de la siguiente forma:


class Profesor <>
Profesor : INTEGER id
Profesor : VARCHAR nombre
Profesor : VARCHAR ape1
Profesor : VARCHAR ape2


class CorreoElectronico <
>
CorreoElectronico: INTEGER idCorreo
CorreoElectronico: VARCHAR direccionCorreo
CorreoElectronico: INTEGER idProfesor


Profesor "1" -- "0..n" CorreoElectronico

Podemos ver cómo la tabla CorreoElectronico contiene como clave ajena la clave primaria de la tabla Profesor y de esa forma se establece la relación uno a muchos.

Fichero de mapeo ''.hbm.xml''

Al persistir dos clases serán necesarios dos ficheros de persistencia:

  • Profesor.hbm.xml
  • CorreoElectronico.hbm.xml

Profesor.hbm.xml

El fichero Profesor.hbm.xml quedará de la siguiente forma:

1| Fichero Profesor.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
  <class name="ejemplo05.Profesor" >
    <id column="Id" name="id" type="integer"/>
    <property name="nombre" />
    <property name="ape1" />
    <property name="ape2" />
 
    <set name="correosElectronicos"  cascade="all" inverse="true"  >
        <key>
            <column name="idProfesor"  />
        </key>            
        <one-to-many class="ejemplo05.CorreoElectronico" />
    </set>
  </class>
</hibernate-mapping>

El fichero básicamente contiene lo que se ha explicado en las lecciones anteriores excepto por el tag <set> (líneas 10 a 15).

Tag set

El tag <set> se utiliza para definir una relación uno a muchos desordenada entre las dos clases Java.

Atributos
  • name: Es el nombre de la propiedad Java del tipo Set en la cual se almacenan todos los objetos hijos.En nuestro ejemplo el valor es correosElectronicos ya que es la propiedad que contiene el Set.
  • cascade: Como ya hemos explicado en anteriores lecciones, este atributo indica que se realizan las mismas operaciones con el objeto padre que con los objetos hijos, es decir si uno se borra los otros también, etc. Su valor habitual es all.
  • inverse: Este atributo se utiliza para minimizar las SQLs que lanza Hibernate contra la base de datos.En este caso concreto debe establecerse a true para evitar una sentencia SQL de UPDATE por cada hijo. Al final de esta sesión se indican enlaces donde intentan explicar (desde mi punto de vista con poco éxito) el funcionamiento del atributo inverse.
Mas información sobre el atributo cascade en Cascade
La documentación de hibernate no ayuda mucho a entender el atributo inverse.Se muestran a continuación algunas explicaciones que se dan sobre ello:

Marks this collection as the “inverse” end of a bidirectional association.

For you, and for Java, a bi-directional link is simply a matter of setting the references on both sides correctly. Hibernate, however, does not have enough information to correctly arrange SQL INSERT and UPDATE statements (to avoid constraint violations). Making one side of the association inverse tells Hibernate to consider it a mirror of the other side. That is all that is necessary for Hibernate to resolve any issues that arise when transforming a directional navigation model to a SQL database schema. The rules are straightforward: all bi-directional associations need one side as inverse. In a one-to-many association it has to be the many-side, and in many-to-many association you can select either side.

Tags anidados
  • key Este tag contiene otro anidado llamado column con el atributo name que indica el nombre de una columna de la base de datos. Esta columna debe ser de la tabla hijo y ser el nombre de la clave ajena a la tabla padre. En nuestro ejemplo es idProfesor ya que es el nombre de la clave ajena en la tabla CorreoElectronico.
  • one-to-many Este tag contiene el atributo class con el FQCN de la clase Java hija. En nuestro ejemplo es el nombre de la clase CorreoElectronico cuyo FQCN es ejemplo05.CorreoElectronico.
Podemos pensar que el valor del atributo class en el tag one-to-many sea opcional ya que hibernate podría ser capaz de deducirlo como ha hecho en otras ocasiones, sin embargo no es así. En caso de no indicarlo se producirá la siguiente excepción:
org.hibernate.MappingException: Association references unmapped class: null

CorreoElectronico.hbm.xml

El fichero CorreoElectronico.hbm.xml quedará de la siguiente forma:

1| Fichero CorreoElectronico.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
  <class name="ejemplo05.CorreoElectronico" >
    <id column="IdCorreo" name="idCorreo" type="integer"/>
    <property name="direccionCorreo" />
 
 
    <many-to-one name="profesor">
            <column name="idProfesor"  />
    </many-to-one>
 
 
  </class>
</hibernate-mapping>

El fichero básicamente contiene lo que se ha explicado en las lecciones anteriores excepto por el tag <many-to-one> (líneas 9 a 11).

Tag many-to-one

El tag <many-to-one> se utiliza para definir una relación muchos a uno entre las dos clases Java.

Atributos
  • name: Es el nombre de la propiedad Java que enlaza con el objeto padre. En nuestro ejemplo el valor es profesor ya que es la propiedad que contiene la referencia a la clase Profesor.
Tags anidados
  • column Este tag contiene el atributo name que indica el nombre de una columna de la base de datos. Esta columna debe ser de la tabla hijo y ser el nombre de la clave ajena a la tabla padre. En nuestro ejemplo es idProfesor ya que es el nombre de la clave ajena en la tabla CorreoElectronico.

Anotaciones

Para usar notaciones deberemos modificar el código fuente de las clases Java y no usar los ficheros .hbm.xml.

El código fuente de la clase Profesor queda de la siguiente forma:

1 | Clase Profesor anotada
@Entity
@Table(name="Profesor")
public class Profesor implements Serializable  {
 
    @Id
    @Column(name="Id")
    private int id;
 
    @Column(name="nombre")
    private String nombre;
 
    @Column(name="ape1")
    private String ape1;
 
    @Column(name="ape2")    
    private String ape2;
 
    @OneToMany(mappedBy="profesor",cascade= CascadeType.ALL)
    private Set<CorreoElectronico> correosElectronicos;
 
 
    public Profesor(){ 
    }
 
    public Profesor(int id, String nombre, String ape1, String ape2) {
        this.id = id;
        this.nombre = nombre;
        this.ape1 = ape1;
        this.ape2 = ape2;
    }
}

A la propiedad correosElectronicos se ha añadido una anotación para indicar la relación uno a muchos.

  • OneToMany:Como su nombre indica le dice a Hibernate que esta propiedad contendrá la lista de hijos.
    • mappedBy: Este atributo contendrá el nombre de la propiedad Java de la clase hija que enlaza con la clase padre. En nuestro ejemplo es el nombre de la propiedad profesor que se encuentra en la clase CorreoElectronico.
    • cascade: Este atributo tiene el mismo significado que el del fichero de mapeo de Hibernate. Mas información en Cascade.

El código de la clase CorreoElectronico es el siguiente:

1 | Clase CorreoElectronico anotada
@Entity
@Table(name="CorreoElectronico")
public class CorreoElectronico implements Serializable {
 
    @Id
    @Column(name="IdCorreo")    
    private int idCorreo;
 
    @Column(name="DireccionCorreo")
    private String direccionCorreo;
 
    @ManyToOne
    @JoinColumn(name="IdProfesor")
    private Profesor profesor;
 
    public CorreoElectronico() {
 
    }
 
    public CorreoElectronico(int idCorreo,String direccionCorreo,Profesor profesor) {
        this.idCorreo=idCorreo;
        this.direccionCorreo=direccionCorreo;
        this.profesor=profesor;
    }
}

A la propiedad profesor se han añadido dos anotaciones para indicar la relación:

  • ManyToOne:Al ser el otro lado de la relación indicamos que desde este lado es una relación muchos a uno.
  • JoinColumn: Indicaremos el nombre de la columna que en la tabla hija contiene la clave ajena a la tabla padre. En nuestro ejemplo es la columna de la base de datos IdProfesor que se encuentra en la tabla CorreoElectronico la cual enlaza con la tabla Profesor.

Código Java

Ahora que ya tenemos preparadas las clase Java para que puedan persistirse veamos el código necesario para persistirlas.

1 | Persistiendo la clase Profesor
Profesor profesor=new Profesor(7, "Sara", "Barrrera", "Salas");
Set<CorreoElectronico> correosElectronicos=new HashSet<>();
correosElectronicos.add(new CorreoElectronico(3, "sara@yahoo.com",profesor));
correosElectronicos.add(new CorreoElectronico(2, "sara@hotmail.com",profesor));
correosElectronicos.add(new CorreoElectronico(1, "sara@gmail.com",profesor));
 
profesor.setCorreosElectronicos(correosElectronicos);
 
Session session=sessionFactory.openSession();
session.beginTransaction();
 
session.save(profesor);
 
 
session.getTransaction().commit();
session.close();

La explicación del código es la siguiente:

  • En la línea 1 se crea el objeto Profesor
  • En la segunda línea se crea el objeto HashSet que implementa el interfaz Set el cual contendrá la lista de hijos.
  • Desde las líneas 3 a la 5 se crean los objetos CorreoElectronico y se añaden al Set.
  • En la línea 7 se establece la relación entre la lista de hijos (CorreoElectronico) y el padre (Profesor).
  • En la línea 12 se guarda el objeto Profesor y automáticamente se guardan también sus hijos.

Referencias