Los componentes permiten que varias clases relacionadas se almacenen en una única tabla de la base de datos. Es similar a la relación uno a uno desde el punto de vista de Java pero en la base de datos sólo hay una tabla.
Antes de entrar en cómo se implemente en Hibernate , veamos las clases Java y las tablas que definen la relación.
Para nuestro ejemplo vamos a usar las clases:
public class Profesor implements Serializable { private int id; private Nombre nombre; public Profesor(){ } public Profesor(int id, Nombre nombre) { this.id = id; this.nombre=nombre; } } public class Nombre implements Serializable { private String nombre; private String ape1; private String ape2; public Nombre() { } public Nombre(String nombre, String ape1, String ape2) { this.nombre = nombre; this.ape1 = ape1; this.ape2 = ape2; } public String getNombreCompleto() { StringBuilder sb=new StringBuilder(); if ((ape1!=null) && (ape1.trim().length()>0)) { sb.append(ape1); } if ((ape2!=null) && (ape2.trim().length()>0)) { if (sb.length()>0) { sb.append(" "); } sb.append(ape2); } if ((nombre!=null) && (nombre.trim().length()>0)) { if (sb.length()>0) { sb.append(","); } sb.append(nombre); } return sb.toString(); } public String getIniciales() { StringBuilder sb=new StringBuilder(); if ((nombre!=null) && (nombre.trim().length()>0)) { sb.append(nombre.substring(0,1)); } if ((ape1!=null) && (ape1.trim().length()>0)) { sb.append(ape1.substring(0,1)); } if ((ape2!=null) && (ape2.trim().length()>0)) { sb.append(ape2.substring(0,1)); } return sb.toString().toUpperCase(); } }
Lo que hemos hecho es extraer las propiedades nombre
, ape1
y ape2
en una nueva clase llamada Nombre
. ¿Para qué hacer este cambio? Para justificarlo hemos añadido los métodos getNombreCompleto()
y getIniciales()
. Si no creáramos la nueva clase Nombre
sería necesario volver a crear estos métodos en cualquier otra entidad que necesite el nombre.
Profesor
y Nombre
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 desde Profesor
hacia Nombre
.
La tabla de base de datos quedaría de la siguiente forma:
Podemos apreciar que en el diseño de las tabla de la base de datos no existe ninguna tabla Nombre
aunque sí que existe la clase Java Nombre
.
Al persistir las dos clases será necesario un único fichero de persistencia:
Profesor.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="ejemplo04.Profesor" > <id column="Id" name="id" type="integer"/> <component name="nombre"> <property name="nombre" /> <property name="ape1" /> <property name="ape2" /> </component> </class> </hibernate-mapping>
El fichero básicamente es muy sencillo excepto por el nuevo tag component
de la línea 7.
El tag <component>
se utiliza para especificar que la propiedad Java de la clase se persistirá en la propia tabla de la clase principal. En su forma más sencilla contiene sólo un atributo:
name
:Este atributo contiene el nombre de la propiedad Java con la referencia al otro objeto con el que forma la relación. En nuestro ejemplo es el atributo nombre
.
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; @Embedded private Nombre nombre; public Profesor(){ } public Profesor(int id, Nombre nombre) { this.id = id; this.nombre=nombre; } }
En la línea 8 se ha incluido la anotación @Embedded
.
nombre
se guardará en la misma tabla que Profesor
.
La clase Java Nombre
quedará de la siguiente forma:
@Embeddable public class Nombre implements Serializable { @Column(name="nombre") private String nombre; @Column(name="ape1") private String ape1; @Column(name="ape2") private String ape2; public Nombre() { } public Nombre(String nombre, String ape1, String ape2) { this.nombre = nombre; this.ape1 = ape1; this.ape2 = ape2; } public String getNombreCompleto() { StringBuilder sb=new StringBuilder(); if ((ape1!=null) && (ape1.trim().length()>0)) { sb.append(ape1); } if ((ape2!=null) && (ape2.trim().length()>0)) { if (sb.length()>0) { sb.append(" "); } sb.append(ape2); } if ((nombre!=null) && (nombre.trim().length()>0)) { if (sb.length()>0) { sb.append(","); } sb.append(nombre); } return sb.toString(); } public String getIniciales() { StringBuilder sb=new StringBuilder(); if ((nombre!=null) && (nombre.trim().length()>0)) { sb.append(nombre.substring(0,1)); } if ((ape1!=null) && (ape1.trim().length()>0)) { sb.append(ape1.substring(0,1)); } if ((ape2!=null) && (ape2.trim().length()>0)) { sb.append(ape2.substring(0,1)); } return sb.toString().toUpperCase(); } }
A nivel de clase se ha incluido en la línea 1 el atributo @Embeddable
.
Ahora que ya tenemos preparadas las clase Java para que puedan persistirse veamos el código necesario para persistirlas.
Profesor profesor=new Profesor(410, new Nombre("Gabriel", "Sáez", "Izquierdo")); Session session=sessionFactory.openSession(); session.beginTransaction(); session.save(profesor); session.getTransaction().commit(); session.close();
Como podemos ver, el usar componentes no añade ningún tipo de complejidad al código Java que se usa.
Veamos ahora qué diferencias hay entre crear una relación uno a uno y el uso de componentes.