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.
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.
1: public class Profesor implements Serializable { 2: private int id; 3: private String nombre; 4: private String ape1; 5: private String ape2; 6: private Set<CorreoElectronico> correosElectronicos; 7: 8: 9: public Profesor(){ 10: } 11: 12: public Profesor(int id, String nombre, String ape1, String ape2) { 13: this.id = id; 14: this.nombre = nombre; 15: this.ape1 = ape1; 16: this.ape2 = ape2; 17: } 18: 19: } 20: 21: public class CorreoElectronico implements Serializable { 22: private int idCorreo; 23: private String direccionCorreo; 24: private Profesor profesor; 25: 26: public CorreoElectronico() { 27: 28: } 29: 30: public CorreoElectronico(int idCorreo,String direccionCorreo,Profesor profesor) { 31: this.idCorreo=idCorreo; 32: this.direccionCorreo=direccionCorreo; 33: this.profesor=profesor; 34: } 35: }
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.
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.
La tablas de base de datos quedarían de la siguiente forma:
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.
Al persistir dos clases serán necesarios dos ficheros de persistencia:
Profesor.hbm.xml
CorreoElectronico.hbm.xml
El fichero Profesor.hbm.xml
quedará de la siguiente forma:
1: <?xml version="1.0" encoding="UTF-8"?> 2: <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 3: <hibernate-mapping> 4: <class name="ejemplo05.Profesor" > 5: <id column="Id" name="id" type="integer"/> 6: <property name="nombre" /> 7: <property name="ape1" /> 8: <property name="ape2" /> 9: 10: <set name="correosElectronicos" cascade="all" inverse="true" > 11: <key> 12: <column name="idProfesor" /> 13: </key> 14: <one-to-many class="ejemplo05.CorreoElectronico" /> 15: </set> 16: </class> 17: </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).
El tag <set>
se utiliza para definir una relación uno a muchos desordenada entre las dos clases Java.
all
.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
.
cascade
en Cascade
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.
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
.class
con el FQCN de la clase Java hija. En nuestro ejemplo es el nombre de la clase CorreoElectronico
cuyo FQCN es ejemplo05.CorreoElectronico
.
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
El fichero CorreoElectronico.hbm.xml
quedará de la siguiente forma:
1: <?xml version="1.0" encoding="UTF-8"?> 2: <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 3: <hibernate-mapping> 4: <class name="ejemplo05.CorreoElectronico" > 5: <id column="IdCorreo" name="idCorreo" type="integer"/> 6: <property name="direccionCorreo" /> 7: 8: 9: <many-to-one name="profesor"> 10: <column name="idProfesor" /> 11: </many-to-one> 12: 13: 14: </class> 15: </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).
El tag <many-to-one>
se utiliza para definir una relación muchos a uno entre las dos clases Java.
profesor
ya que es la propiedad que contiene la referencia a la clase Profesor
.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
.
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: @Entity 2: @Table(name="Profesor") 3: public class Profesor implements Serializable { 4: 5: @Id 6: @Column(name="Id") 7: private int id; 8: 9: @Column(name="nombre") 10: private String nombre; 11: 12: @Column(name="ape1") 13: private String ape1; 14: 15: @Column(name="ape2") 16: private String ape2; 17: 18: @OneToMany(mappedBy="profesor",cascade= CascadeType.ALL) 19: private Set<CorreoElectronico> correosElectronicos; 20: 21: 22: public Profesor(){ 23: } 24: 25: public Profesor(int id, String nombre, String ape1, String ape2) { 26: this.id = id; 27: this.nombre = nombre; 28: this.ape1 = ape1; 29: this.ape2 = ape2; 30: } 31: }
A la propiedad correosElectronicos
se ha añadido una anotación para indicar la relación uno a muchos.
profesor
que se encuentra en la clase CorreoElectronico
.El código de la clase CorreoElectronico es el siguiente:
1: @Entity 2: @Table(name="CorreoElectronico") 3: public class CorreoElectronico implements Serializable { 4: 5: @Id 6: @Column(name="IdCorreo") 7: private int idCorreo; 8: 9: @Column(name="DireccionCorreo") 10: private String direccionCorreo; 11: 12: @ManyToOne 13: @JoinColumn(name="IdProfesor") 14: private Profesor profesor; 15: 16: public CorreoElectronico() { 17: 18: } 19: 20: public CorreoElectronico(int idCorreo,String direccionCorreo,Profesor profesor) { 21: this.idCorreo=idCorreo; 22: this.direccionCorreo=direccionCorreo; 23: this.profesor=profesor; 24: } 25: }
A la propiedad profesor
se han añadido dos anotaciones para indicar la relación:
IdProfesor
que se encuentra en la tabla CorreoElectronico
la cual enlaza con la tabla Profesor
.Ahora que ya tenemos preparadas las clase Java para que puedan persistirse veamos el código necesario para persistirlas.
1: Profesor profesor=new Profesor(7, "Sara", "Barrrera", "Salas"); 2: Set<CorreoElectronico> correosElectronicos=new HashSet<>(); 3: correosElectronicos.add(new CorreoElectronico(3, "sara@yahoo.com",profesor)); 4: correosElectronicos.add(new CorreoElectronico(2, "sara@hotmail.com",profesor)); 5: correosElectronicos.add(new CorreoElectronico(1, "sara@gmail.com",profesor)); 6: 7: profesor.setCorreosElectronicos(correosElectronicos); 8: 9: Session session=sessionFactory.openSession(); 10: session.beginTransaction(); 11: 12: session.save(profesor); 13: 14: 15: session.getTransaction().commit(); 16: session.close();
La explicación del código es la siguiente:
Profesor
CorreoElectronico
y se añaden al Set
.CorreoElectronico
) y el padre (Profesor
).Profesor
y automáticamente se guardan también sus hijos.inverse
.inverse
.cascade
e inverse
.