Herramientas de usuario

Herramientas del sitio


unidades:03_relaciones:05_muchos_a_muchos_desordenada

Diferencias

Muestra las diferencias entre dos versiones de la página.


unidades:03_relaciones:05_muchos_a_muchos_desordenada [2023/04/07 21:26] (actual) – creado - editor externo 127.0.0.1
Línea 1: Línea 1:
 +====== 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.
 +
 +<code java 1 | Listado 1.Relación n a n>
 +public class Profesor implements Serializable  {
 +    private int id;
 +    private String nombre;
 +    private String ape1;
 +    private String ape2;
 +    private Set<Modulo> 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<Profesor> profesores=new HashSet();
 +
 +    public Modulo() {
 +        
 +    }
 +
 +    public Modulo(int idModulo, String nombre) {
 +        this.idModulo = idModulo;
 +        this.nombre = nombre;
 +
 +    }
 +}
 +</code>
 +
 +
 +En el listado 1 podemos ver cómo la clase ''Profesor'' tiene una propiedad de tipo <javadoc jdk7>java.util.Set|Set</javadoc> llamada ''modulos'' de la clase ''Modulo'' (línea 6) y además la clase ''Modulo'' también posee un <javadoc jdk7>java.util.Set|Set</javadoc> de objetos ''Profesor'' (línea 24).
 +
 +El mecanismo que usamos en Java para almacenar la lista de objetos es el interfaz <javadoc jdk7>java.util.Set|Set</javadoc>. No vamos a usar el interfaz <javadoc jdk7>java.util.List|List</javadoc> o un array ya que dichas formas implican un orden de los objetos mientras que usando un <javadoc jdk7>java.util.Set|Set</javadoc> no hay ningún tipo de orden.
 +
 +<note important>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.</note>
 +
 +
 +En el siguiente diagrama UML se ve la relación entre ''Profesor'' y ''Modulo''.
 +
 +<uml>
 +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
 +
 +</uml>
 +
 +
 +===== Tablas =====
 +La tablas de base de datos quedarían de la siguiente forma:
 +
 +<uml>
 +class Profesor <<Table>>
 +Profesor : INTEGER id
 +Profesor : VARCHAR nombre
 +Profesor : VARCHAR ape1
 +Profesor : VARCHAR ape2
 +
 +
 +class Modulo <<Table>>
 +Modulo: INTEGER idModulo
 +Modulo: VARCHAR nombre
 +
 +class ProfesorModulo <<Table>>
 +ProfesorModulo: INTEGER idProfesor
 +ProfesorModulo: INTEGER idModulo
 +
 +
 +Profesor "1" -- "0..n" ProfesorModulo 
 +ProfesorModulo "0..n" -- "1" Modulo 
 +</uml>
 +
 +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
 +
 +<code xml 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="ejemplo09.Profesor" >
 +    <id column="Id" name="id" type="integer"/>
 +    <property name="nombre" />
 +    <property name="ape1" />
 +    <property name="ape2" />
 +    
 +    <set name="modulos" table="ProfesorModulo"  cascade="all" inverse="true"  >
 +        <key>
 +            <column name="idProfesor"  />
 +        </key> 
 +        <many-to-many column="IdModulo" class="ejemplo09.Modulo" />
 +    </set>
 +  </class>
 +</hibernate-mapping>
 +</code>
 +
 +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 lista desordenada entre las dos clases Java. 
 +
 +== Atributos ==
 +  * **name**: Es el nombre de la propiedad Java del tipo <javadoc jdk7>java.util.Set|Set</javadoc> en la cual se almacenan todos los objetos relacionados.En nuestro ejemplo el valor es ''modulos'' ya que es la propiedad que contiene el <javadoc jdk7>java.util.Set|Set</javadoc>.
 +  * **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.
 +
 +<note important>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''.
 +</note>
 +
 +== 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''.
 +
 +<note>
 +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
 +  
 +</note> 
 +==== Modulo.hbm.xml ====
 +
 +El fichero ''Modulo.hbm.xml'' quedará de la siguiente forma:
 +
 +<code xml 1| Fichero Modulo.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="ejemplo09.Modulo" >
 +    <id column="IdModulo" name="idModulo" type="integer"/>
 +    <property name="nombre" />
 +    
 +    <set name="profesores" table="ProfesorModulo"  cascade="all" inverse="false"  >
 +        <key>
 +            <column name="idModulo"  />
 +        </key> 
 +        <many-to-many column="IdProfesor" class="ejemplo09.Profesor" />
 +    </set>
 +  </class>
 +</hibernate-mapping>
 +</code>
 +
 +El fichero básicamente contiene lo que se ha explicado en las lecciones anteriores excepto por el tag ''<many-to-one>'' (líneas 8 a 13).
 +
 +=== Tag set ===
 +El tag ''<set>'' se utiliza para definir una lista desordenada entre las dos clases Java. 
 +
 +== Atributos ==
 +  * **name**: Es el nombre de la propiedad Java del tipo <javadoc jdk7>java.util.Set|Set</javadoc> en la cual se almacenan todos los objetos relacionados.En nuestro ejemplo el valor es ''profesores'' ya que es la propiedad que contiene el <javadoc jdk7>java.util.Set|Set</javadoc>.
 +  * **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.
 +
 +<note important>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''.
 +</note>
 +
 +== 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''.
 +
 +<note>
 +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
 +  
 +</note> 
 +
 +===== 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:
 +
 +<code java 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;
 +    
 +    @ManyToMany(cascade = {CascadeType.ALL})
 +    @JoinTable(name="ProfesorModulo", joinColumns={@JoinColumn(name="IdProfesor")}, inverseJoinColumns={@JoinColumn(name="IdModulo")})
 +    private Set<Modulo> 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;
 +    }
 +}
 +</code>
 +
 +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:
 +<code java 1 | Clase Modulo anotada>
 +@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<Profesor> profesores=new HashSet();
 +
 +    public Modulo() {
 +        
 +    }
 +
 +    public Modulo(int idModulo, String nombre) {
 +        this.idModulo = idModulo;
 +        this.nombre = nombre;
 +
 +    }
 +}
 +</code>
 +
 +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''.
 +
 +<note tip>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.</note>
 +====== Código Java ======
 +Ahora que ya tenemos preparadas las clase Java para que puedan persistirse veamos el código necesario para persistirlas.
 +
 +<code java 1 | Persistiendo la clase Profesor>
 +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();
 +</code>
 +
 +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.
 +
 +<note>En este ejemplo no se ha creado el objeto <javadoc jdk7>java.util.HashSet|HashSet</javadoc> 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.</note>