La relación uno a uno en Hibernate consiste simplemente en que un objeto tenga una referencia a otro objeto de forma que al persistirse el primer objeto también se persista el segundo.
En esta lección la relación va a ser unidireccional es decir que que la relación uno a uno va a ser en un único sentido.
Antes de entrar en cómo se implemente en Hibernate , veamos las clases Java y las tablas que definen la relación uno a uno.
Para nuestro ejemplo vamos a usar las clases:
Profesor
Direccion
Estas dos clases van a tener una relación uno a uno.
public class Profesor implements Serializable { private int id; private String nombre; private String ape1; private String ape2; private Direccion direccion; 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 Direccion implements Serializable { private int id; private String calle; private int numero; private String poblacion; private String provincia; public Direccion(){ } public Direccion(int id, String calle, int numero, String poblacion, String provincia) { this.id = id; this.calle = calle; this.numero = numero; this.poblacion = poblacion; this.provincia = provincia; } }
En el listado 1 podemos ver cómo la clase Profesor
tiene una propiedad llamada direccion
de la clase Direccion
(línea 6), mientras que la clase Direccion
no posee ninguna referencia a Profesor
ya que hemos definido una direccionalidad desde Profesor
hacia Direccion
pero no al revés.
Profesor
y Direccion
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 es solo desde Profesor
hacia Direccion
.
La tablas de base de datos quedarían de la siguiente forma:
Podemos apreciar que en el diseño de las tabla de la base de datos ya no existe una columna en Profesor
con la clave primaria de Direccion
ya que si la hubiera sería una relación muchos a uno. Entonces ¿cómo se establece la relación entre las dos filas? Simplemente porque tanto Profesor
como Direccion
deben tener la misma clave primaria y de esa forma se establece la relación.
Al persistir dos clases serán necesarios dos ficheros de persistencia:
Profesor.hbm.xml
Direccion.hbm.xml
El fichero Profesor.hbm.xml
quedará de la siguiente forma
<?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="ejemplo01.Profesor" > <id column="Id" name="id" type="integer"/> <property name="nombre" /> <property name="ape1" /> <property name="ape2" /> <one-to-one name="direccion" cascade="all" /> </class> </hibernate-mapping>
El fichero básicamente contiene lo que se ha explicado en las lecciones anteriores excepto por el tag <one-to-one>
de la línea 10.
El tag <one-to-one>
se utiliza para definir una relación uno a uno entre las dos clases Java. En su forma más sencilla contiene sólamente dos atributos:
name
:Este atributo contiene el nombre de la propiedad Java con la referencia al otro objeto con el que forma la relación uno a uno. En nuestro ejemplo es el atributo direccion
.cascade
: Este atributo indicará a hibernate cómo debe actuar cuando realicemos las operaciones de persistencia de guardar, borrar, leer, etc. En el ejemplo el valor debe ser all
indicando que deberemos realizar la misma operación en Profesor
que enDireccion
.cascade
en Cascade
El fichero Direccion.hbm.xml
quedará de la siguiente forma:
<?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="ejemplo01.Direccion" > <id column="Id" name="id" type="integer"/> <property name="calle"/> <property name="numero"/> <property name="poblacion"/> <property name="provincia"/> </class> </hibernate-mapping>
En el fichero Direccion.hbm.xml
no hay ninguna información relativa a la relación uno a uno puesto que en nuestro ejemplo la relación uno a uno tiene una direccionalidad desde Profesor
hasta Direccion
por lo tanto Direccion
no sabe nada sobre Profesor
y por ello en su fichero de persistencia no hay nada relativo a dicha relación.
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:
import java.io.Serializable; import javax.persistence.*; @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; @OneToOne(cascade=CascadeType.ALL) @PrimaryKeyJoinColumn private Direccion direccion; }
A la propiedad direccion
(línea 24) se han añadido dos anotaciones para indicar la relación uno a uno y que ésta relación se implemente mediante la clave primaria.
cascade
al igual que en el fichero de hibernate.@PrimaryKeyJoinColumn
se producirá un error indicando que falta la columna direccion_Id
en la tabla Profesor
.
@PrimaryKeyJoinColumn
mientras que usando el fichero .hbm.xml
no es necesario indicarlo.
cascade
en Cascade
En código de Direccion
no es necesario indicar nada sobre la relación tal y como hemos explicado en el caso del fichero de hibernate.
import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name="Direccion") public class Direccion implements Serializable { @Id @Column(name="Id") private int id; @Column(name="calle") private String calle; @Column(name="numero") private int numero; @Column(name="poblacion") private String poblacion; @Column(name="provincia") private String provincia; }
Ahora que ya tenemos preparadas las clase Java para que puedan persistirse veamos el código necesario para persistirlas.
Direccion direccion=new Direccion(1, "Plaza del ayuntamiento", 8, "Xativa", "Valencia"); Profesor profesor=new Profesor(1, "Juan", "Perez", "García"); profesor.setDireccion(direccion); Session session=sessionFactory.openSession(); session.beginTransaction(); session.save(profesor); session.getTransaction().commit(); session.close();
Como podemos ver no hay nada nuevo en el código Java para persistir una relación uno a uno, simplemente creamos las 2 clases (Líneas 1 y 2) y establecemos la relación entre ambas asignando al objeto Profesor
la referencia al objeto Direccion
(Línea 3). Por último, simplemente persistimos la clase Profesor
tal y como se ha explicado anteriormente.
Al ejecutar el ejemplo Hibernate vemos cómo se han creado las filas en las tablas Profesor
y Direccion
mientras que desde Java solo se ha persistido la clase Profesor
.
Si vemos el log que se genera al persistir los 2 objetos , podemos ver que se realiza primero una orden SELECT
contra la tabla Direccion
para comprobar si ya existe la dirección en la base de datos. Ésto lo realiza hibernate ya que si ya existe no es necesario insertar la fila de la dirección pero si la fila ya existe pero los datos son distintos, hibernate lanzará un UPDATE
para modificarlos.
Direccion
y Profesor
debe ser la misma para que se establezca correctamente la relación.