====== Muchos a muchos ======
La relación muchos a muchos consiste en que un objeto //A// tenga una lista de otros objetos //B// y también que el objeto //B// a su vez tenga la lista de objetos //A//.De forma que al persistirse cualquier objeto también se persista la lista de objetos que posee.
===== 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:
* ''Profesor''
* ''Modulo''
Estas dos clases van a tener una relación muchos a muchos.
public class Profesor implements Serializable {
private int id;
private String nombre;
private String ape1;
private String ape2;
private Set modulos=new HashSet();
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 Modulo implements Serializable {
private int idModulo;
private String nombre;
private Set profesores=new HashSet();
public Modulo() {
}
public Modulo(int idModulo, String nombre) {
this.idModulo = idModulo;
this.nombre = nombre;
}
}
En el listado 1 podemos ver cómo la clase ''Profesor'' tiene una propiedad de tipo java.util.Set|Set llamada ''modulos'' de la clase ''Modulo'' (línea 6) y además la clase ''Modulo'' también posee un java.util.Set|Set de objetos ''Profesor'' (línea 24).
El mecanismo que usamos en Java para almacenar la lista de objetos es el interfaz java.util.Set|Set. No vamos a usar el interfaz java.util.List|List o un array ya que dichas formas implican un orden de los objetos mientras que usando un java.util.Set|Set no hay ningún tipo de orden.
En la clases Java ''Profesor'' y ''Modulo'' 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 la relación entre ''Profesor'' y ''Modulo''.
class Profesor
Profesor : int id
Profesor : String nombre
Profesor : String ape1
Profesor : String ape2
class Modulo
Modulo: int idModulo
Modulo: String nombre
Profesor "1" --> "0..n" Modulo: modulos
Profesor "0..n" <-- "1" Modulo: profesores
===== 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 Modulo <>
Modulo: INTEGER idModulo
Modulo: VARCHAR nombre
class ProfesorModulo <>
ProfesorModulo: INTEGER idProfesor
ProfesorModulo: INTEGER idModulo
Profesor "1" -- "0..n" ProfesorModulo
ProfesorModulo "0..n" -- "1" Modulo
Podemos ver cómo en este caso las tablas ''Profesor'' y ''Modulo'' se relacionan mediante la nueva tabla ''ProfesorModulo'' que contiene las claves primarias de ambas tablas.
===== Fichero de mapeo ''.hbm.xml'' =====
Al persistir dos clases serán necesarios dos ficheros de persistencia:
* ''Profesor.hbm.xml''
* ''Modulo.hbm.xml''
==== Profesor.hbm.xml ====
El fichero ''Profesor.hbm.xml'' quedará de la siguiente forma
El fichero básicamente contiene lo que se ha explicado en las lecciones anteriores excepto por el tag '''' (líneas 10 a 15).
=== Tag set ===
El tag '''' se utiliza para definir una lista desordenada entre las dos clases Java.
== Atributos ==
* **name**: Es el nombre de la propiedad Java del tipo java.util.Set|Set en la cual se almacenan todos los objetos relacionados.En nuestro ejemplo el valor es ''modulos'' ya que es la propiedad que contiene el java.util.Set|Set.
* **table**: Es el nombre de la tabla de la base de datos que contiene la relación //muchos a muchos//.En nuestro ejemplo es la tabla ''ProfesorModulo''.
* **cascade**: Como ya hemos explicado en anteriores lecciones, este atributo indica que se realizan las mismas operaciones con el objeto principal que con los objetos relacionados, es decir si uno se borra los otros también, etc. Su valor habitual es ''all''. Más información en [[unidades:03_relaciones:06_cascade]]
* **inverse**: Como ya hemos ido explicando durante el curso, la documentación de hibernate no es clara explicando su significado. En el caso de las relaciones //muchos a muchos// es necesario poner un lado de la relación con el valor ''true'' y el otro con el valor ''false''. En ''Profesor'' pondremos el valor a ''true'' y en la parte de ''Modulo'' la estableceremos a ''false'' aunque no hay problema en establecerla al revés.
Recuerda poner en este lado de la relación el valor de ''inverse="true"'' y en el otro lado de la relación ''inverse="false"''.
Si ambos valores son ''true'' no se realizará ninguna inserción en la tabla ''ProfesorModulo'' y si ambos valores son ''false'' se realizará dos veces la inserción en la tabla ''ProfesorModulo'' dando un error de clave primaria duplicada.
Es decir que en este caso ''inverse'' controla cuándo se realiza la inserción en la tabla ''ProfesorModulo'': si se realiza al guardar el ''Profesor'' o al guardar el ''Modulo''.
== Tags anidados ==
* **key** Este tag contiene otro anidado llamado ''column'' con el atributo ''name'', el cual contiene el nombre de una columna de la base de datos. Esta columna debe ser una de la tabla de la relación ''muchos a muchos'' y ser el nombre de la columna que contiene la clave ajena de tabla que estamos persistiendo. En nuestro ejemplo es ''idProfesor'' ya que es el nombre de la clave ajena que se encuentra en la tabla ''ProfesorModulo''.
* **many-to-many** Este tag contiene el atributo ''class'' con el FQCN de la clase Java con la que se establece la relación. En nuestro ejemplo es el nombre de la clase ''Modulo'' cuyo FQCN es ''ejemplo09.Modulo''.
Podemos pensar que el valor del atributo ''class'' en el tag ''many-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:
Exception java.lang.NullPointerException
==== Modulo.hbm.xml ====
El fichero ''Modulo.hbm.xml'' quedará de la siguiente forma:
El fichero básicamente contiene lo que se ha explicado en las lecciones anteriores excepto por el tag '''' (líneas 8 a 13).
=== Tag set ===
El tag '''' se utiliza para definir una lista desordenada entre las dos clases Java.
== Atributos ==
* **name**: Es el nombre de la propiedad Java del tipo java.util.Set|Set en la cual se almacenan todos los objetos relacionados.En nuestro ejemplo el valor es ''profesores'' ya que es la propiedad que contiene el java.util.Set|Set.
* **table**: Es el nombre de la tabla de la base de datos que contiene la relación //muchos a muchos//.En nuestro ejemplo es la tabla ''ProfesorModulo''.
* **cascade**: Como ya hemos explicado en anteriores lecciones, este atributo indica que se realizan las mismas operaciones con el objeto principal que con los objetos relacionados, es decir, si uno se borra los otros también, etc. Su valor habitual es ''all''. Más información en [[unidades:03_relaciones:06_cascade]].
* **inverse**: Como ya hemos ido explicando durante el curso, la documentación de hibernate no es clara explicando su significado. En el caso de las relaciones //muchos a muchos// es necesario poner un lado de la relación con el valor ''true'' y el otro con el valor ''false''. En ''Modulo'' pondremos el valor a ''false'' y en la parte de ''Profesor'' la estableceremos a ''true'' aunque no hay problema en establecerla al revés.
Recuerda poner en este lado de la relación el valor de ''inverse="false"'' y en el otro lado de la relación ''inverse="true"''.
Si ambos valores son ''true'' no se realizará ninguna inserción en la tabla ''ProfesorModulo'' y si ambos valores son ''false'' se realizará dos veces la inserción en la tabla ''ProfesorModulo'' dando un error de clave primaria duplicada.
Es decir, que en este caso ''inverse'' controla cuándo se realiza la inserción en la tabla ''ProfesorModulo'' si bien al guardarla en ''Profesor'' o en ''Modulo''.
== Tags anidados ==
* **key** Este tag contiene otro anidado llamado ''column'' con el atributo ''name'' el cual contiene el nombre de una columna de la base de datos. Esta columna debe ser una de la tabla de la relación ''muchos a muchos'' y ser el nombre de la columna que contiene la clave ajena de tabla que estamos persistiendo. En nuestro ejemplo es ''idModulo'' ya que es el nombre de la clave ajena que se encuentra en la tabla ''ProfesorModulo''.
* **many-to-many** Este tag contiene el atributo ''class'' con el FQCN de la clase Java con la que se establece la relación. En nuestro ejemplo es el nombre de la clase ''Modulo'' cuyo FQCN es ''ejemplo09.Profesor''.
Podemos pensar que el valor del atributo ''class'' en el tag ''many-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:
Exception org.hibernate.MappingException: An association from the table ProfesorModulo does not specify the referenced entity
===== Anotaciones =====
Para usar anotaciones 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:
@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;
@ManyToMany(cascade = {CascadeType.ALL})
@JoinTable(name="ProfesorModulo", joinColumns={@JoinColumn(name="IdProfesor")}, inverseJoinColumns={@JoinColumn(name="IdModulo")})
private Set modulos=new HashSet();
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 ''módulos'' (línea 20) se han añadido dos anotaciones para indicar la relación //muchos a muchos//.
* **ManyToMany**:Como su nombre indica le dice a Hibernate que la propiedad contendrá una lista de objetos que participa en una relación //muchos a muchos//.
* **cascade**: Este atributo tiene el mismo significado que el del fichero de mapeo de Hibernate. Más información en [[unidades:03_relaciones:06_cascade]].
* **JoinTable**: Esta anotación contiene la información sobre la tabla que realiza la relación //muchos a muchos//
* **name**: Nombre de la tabla que realiza la relación //muchos a muchos//. En nuestro ejemplo es ''ProfesorModulo''.
* **joinColumns**: Contiene cada una de las columnas que forman la clave primaria de esta clase que estamos definiendo. Cada columna se indica mediante una anotación ''@JoinColumn'' y en el atributo ''name'' contiene el nombre de la columna.
* **inverseJoinColumns**: Contiene cada una de las columnas que forman la clave primaria de la clase clase con la que tenemos la relación. Cada columna se indica mediante una anotación ''@JoinColumn'' y en el atributo ''name'' contiene el nombre de la columna.
El código de la clase Modulo es el siguiente:
@Entity
@Table(name="Modulo")
public class Modulo implements Serializable {
@Id
@Column(name="IdModulo")
private int idModulo;
@Column(name="nombre")
private String nombre;
@ManyToMany(cascade = {CascadeType.ALL},mappedBy="modulos")
private Set profesores=new HashSet();
public Modulo() {
}
public Modulo(int idModulo, String nombre) {
this.idModulo = idModulo;
this.nombre = nombre;
}
}
A la propiedad ''profesores'' (línea 13) se han añadido dos anotaciones para indicar la relación //muchos a muchos//.
* **ManyToMany**:Indica que la propiedad contiene una lista de objetos que participan en una relación //muchos a muchos//.
* **cascade**: Este atributo tiene el mismo significado que el del fichero de mapeo de Hibernate. Mas información en [[unidades:03_relaciones:06_cascade]].
* **mappedBy**: Contiene el nombre de la propiedad Java de la otra clase desde la cual se relaciona con ésta. En nuestro ejemplo es la propiedad ''modulos''.
Al poner el atributo ''mappedBy'' ya no es necesario incluir la anotación ''@JoinTable'' ya que dicha información ya se indica en el otro lado de la relación.
====== Código Java ======
Ahora que ya tenemos preparadas las clase Java para que puedan persistirse veamos el código necesario para persistirlas.
Profesor profesor1=new Profesor(11, "Isabel", "Fuertes", "Gascón");
Profesor profesor2=new Profesor(12, "Jose", "Valenciano", "Gimeno");
Modulo modulo1=new Modulo(1, "Sistemas Operativos en Red");
Modulo modulo2=new Modulo(2, "Entornos de desarrollo");
Modulo modulo3=new Modulo(3, "Sistemas Informáticos");
profesor1.getModulos().add(modulo1);
profesor1.getModulos().add(modulo2);
profesor2.getModulos().add(modulo3);
modulo1.getProfesores().add(profesor1);
modulo2.getProfesores().add(profesor1);
modulo3.getProfesores().add(profesor2);
Session session=sessionFactory.openSession();
session.beginTransaction();
session.save(profesor1);
session.save(profesor2);
session.getTransaction().commit();
session.close();
La explicación del código es la siguiente:
* En las líneas 1 y 2 se crean dos objetos ''Profesor''
* En las líneas 4, 5 y 6 se crean tres objetos ''Modulo''.
* De las líneas 8 a 10 se añaden los módulos a los profesores.
* De las líneas 12 a 14 se añaden los profesores a los módulos.
* En las líneas 20 y 21 se guardan los dos objetos ''Profesor'' y automáticamente se guardan también los módulos.
Apreciar cómo el código Java sigue siendo sencillo y no se complica prácticamente nada al guardarlo en la base de datos.Solo estamos añadiendo la complejidad en el ficheros de mapeo de Hibernate o en las anotaciones.
En este ejemplo no se ha creado el objeto java.util.HashSet|HashSet ya que en el propio código de cada clase ya se creaba y de esa forma nos ahorramos unas líneas código aqui.