====== 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:
* ''Profesor''
* ''CorreoElectronico''
Estas dos clases van a tener una relación uno a muchos.
public class Profesor implements Serializable {
private int id;
private String nombre;
private String ape1;
private String ape2;
private Set 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 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 //hijo// mientras que usando un
java.util.Set|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:
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 relación //uno a muchos// 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 //hijos//.En nuestro ejemplo el valor es ''correosElectronicos'' ya que es la propiedad que contiene el java.util.Set|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 [[unidades:03_relaciones:06_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:
El fichero básicamente contiene lo que se ha explicado en las lecciones anteriores excepto por el tag '''' (líneas 9 a 11).
=== Tag many-to-one ===
El tag '''' 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:
@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 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 [[unidades:03_relaciones:06_cascade]].
El código de la clase CorreoElectronico es el siguiente:
@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.
Profesor profesor=new Profesor(7, "Sara", "Barrrera", "Salas");
Set 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 java.util.HashSet|HashSet que implementa el interfaz java.util.Set|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 java.util.Set|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 ======
* [[http://www.mkyong.com/hibernate/inverse-true-example-and-explanation/|inverse = “true” example and explanation ]]: Explicación en inglés del atributo ''inverse''.
* [[http://tadtech.blogspot.com.es/2007/02/hibernate-when-is-inversetrue-and-when.html|Hibernate: When is "inverse=true" and when it's not? ]]: Otra explicación en inglés del atributo ''inverse''.
* [[http://www.mkyong.com/hibernate/different-between-cascade-and-inverse/|Different between cascade and inverse ]]: Explicación en inglés de las diferencias entre los atributos ''cascade'' e ''inverse''.