Tabla de Contenidos
Mapeo de una Entidad
Una entidad va a ser una simple clase Java que deseamos persistir en la base de datos.
Este tutorial está dividido en 3 partes:
La clase Java
La clases Java deberán tener las siguientes características:
- Deben tener un constructor público sin ningún tipo de argumentos 1).
- Para cada propiedad que queramos persistir debe haber un método get/set asociado.
- Implementar el interfaz Serializable 2)
- 1|Profesor.java
public class Profesor implements Serializable { private int id; private String nombre; private String ape1; private String ape2; 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 int getId() { return id; } public void setId(int id) { this.id = id; } public String getNombre() { return nombre; } public void setNombre(String nombre) { this.nombre = nombre; } public String getApe1() { return ape1; } public void setApe1(String ape1) { this.ape1 = ape1; } public String getApe2() { return ape2; } public void setApe2(String ape2) { this.ape2 = ape2; } }
Vemos en el código fuente cómo la clase Profesor
tiene un constructor sin ningún tipo de argumentos (Linea 7). Además para las propiedades id
,nombre
,ape1
y ape2
están el par de métodos get
y set
y por último implementa el interfaz Serializable (Línea 1).
¿Ya podemos persistir la clase Profesor
usando hibernate? Pues NO, debemos indicarle a hibernate toda la metainformación relativa a esta clase. Hay que explicarle como se mapeará el objeto en una base de datos relacional 3) , indicando para ello en que tabla de base de datos se debe guardar cuál es la clave primaria de la tabla, las columnas que tiene, etc.
Fichero de mapeo ''.hbm.xml''
Para cada clase que queremos persistir se creará un fichero xml con la información que permitirá mapear la clase a una base de datos relacional. Este fichero estará en el mismo paquete que la clase a persistir.
En nuestro caso, si queremos persistir la clase Profesor
deberemos crear el fichero Profesor.hbm.xml
en el mismo paquete que la clase Java.
.hbm.xml
esté en otro paquete distinto al de la clase Java. En este sentido suele haber 2 posibilidades:- Almacenar el fichero
.hbm.xml
en el mismo paquete que la clase Java a la que hace referencia. - Crear un árbol alternativo de paquetes donde almacenar los ficheros
.hbm.xml
. Por ejemplo, si tenemos el paquete raízcom.miempresa.proyecto.dominio
donde se guardan todas las clases Java a persistir, crear otro paquete llamadocom.miempresa.proyecto.persistencia
donde almacenar los ficheros.hbm.xml
.
La ventaja de la segunda opción es que en caso de que no queramos usar Hibernate, simplemente hay que borrar toda la carpeta com.miempresa.proyecto.persistencia
y ya está, mientras que la ventaja de la primera opción es que la clase Java y su correspondiente fichero de mapeo están mas juntos facilitando en caso de algún cambio en la clase Java el cambio en el fichero de mapeo.
Mi opinión personal es que es mejor usar la segunda opción ya que quedan más independizadas las clases de negocio del método de persistencia.
- 1|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="ejemplo01.Profesor" table="Profesor" > <id column="Id" name="id" type="integer"/> <property name="nombre" /> <property name="ape1" /> <property name="ape2" /> </class> </hibernate-mapping>
Podemos ver cómo el fichero Profesor.hbm.xml
es un típico fichero xml.
- En la linea 1 vemos la declaración de que es un fichero XML.
- En la linea 2 se aprecia la declaración del DOCTYPE junto con la referencia al documento http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd DTD que permite validarlo. Es decir que si nos descargamos el fichero hibernate-mapping-3.0.dtd podremos saber todos los elementos que hay en un fichero de mapeo.
- El nodo raiz del documento xml se llama
<hibernate-mapping>
y se encuentra en la línea 3. - La parte interesante de este fichero empieza en la línea 4. Vemos el tag
<class>
que nos indica que vamos a mapear una clase.- En el atributo
name
deberemos poner el FQCN 4) de la clase que queremos mapear.Es decir el nombre de la clase incluyendo el paquete en el que se encuentra. - El atributo
table
nos indica el nombre de la tabla en la que vamos a mapear la clase. Este atributo es opcional si el nombre de la clase Java y el de la tabla coinciden.
- El tag
<id>
de la línea 5 se usa para indicar la propiedad de la clase que es la clave primaria.- El atributo
name
es el nombre de la propiedad Java que contiene la clave primaria. - El atributo
column
contiene el nombre de la columna de la base de datos asociado a la propiedad. Este atributo es opcional si el nombre de la propiedad Java y el nombre de la columna coinciden. - El atributo
type
indica el tipo de la propiedad Java. Este atributo no es necesario puesto que Hibernate por defecto ya usa el tipo de la propiedad Java. Mas información en Tipos básicos.
- El tag
<property>
de las líneas 6 a la 8 se usa para declarar más propiedades Java para ser mapeadas en la base de datos. Si no declaramos las propiedades Java mediante este tag no se leerán o guardarán en la base de datos.- El atributo
name
es el nombre de la propiedad Java que queremos mapear a la base de datos. - El atributo
column
contiene el nombre de la columna de la base de datos asociado a la propiedad. Este atributo es opcional si el nombre de la propiedad Java y el nombre de la columna coinciden.
column
puedes especificar un nombre de columna en la tabla distinto del nombre de la propiedad en la clase Java.
<?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" table="Profesor" > <id column="Id" name="id" type="integer"/> <property name="nombre" /> <property name="ape1" column="primer_apellido" /> <property name="ape2" column="segundo_apellido" /> </class> </hibernate-mapping>
.hbm.xml
pero por ahora simplemente hemos visto lo mas básico. Durante el resto del curso iremos viendo muchas mas opciones de este fichero.
DOCTYPE
antes de la versión 3.6 era 5) :
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
Así que no confundirlo al hacer algún copy-empastre desde algún tutorial de Internet de versiones anteriores.
get/set
para que hibernate acceda a los campos. Sin embargo nos puede interesar que no estén alguno de esos métodos para que el usuario no pueda cambiar o leer los valores. En ese caso le deberemos decir a Hibernate que acceda directamente a las propiedades privadas, ya que por suerte Hibernate sabe hacerlo.
Para ello modificaremos el fichero .hbm.xml
añadiendo el atributo access=“field”
a la propiedad sobre la que queremos que acceda directamente.
Por ejemplo si no quisieramos tener un getNombre()
o setNombre()
de la clase Profesor
deberíamos cambiar el fichero Profesor.hbm.xml
añadiendo en la definición de la columna profesor el texto access=“field”
, quedando en ese caso de la siguiente forma:
<property name="nombre" access="field" />
La propiedad access=“field”
también puede aplicarse al tag <id>
como a los diversos tag que definen una propiedad en Hibernate.
Realmente como norma general siempre deberíamos utilizar al tributo access=“field”
ya que así, podremos decidir tranquilamente si poner o no los métodos get'/set
y además dichos métodos get'/set
podrían tener reglas o calculos que hicieran que se generaran errores en nuestra aplicación al ser cargados desde Hibernate.Más información sobre este tema en Avoiding Anemic Domain Models with Hibernate
Sin embargo, durante el resto del curso no haremos uso de esta característica para simplificar las explicaciones/ejemplos.
Anotaciones
En el apartado anterior hemos visto cómo mediante un fichero .hbm.xml
podemos especificar cómo mapear la clases Java en tablas de base de datos.
Desde hace algunos años en Java se ha creado el concepto llamado El infierno XML.Este infierno ha consistido en que en demasiados frameworks 6) se hacía un uso intensivo de XML , siendo el XML un formato de ficheros demasiado largo de escribir , muy repetitivo , verboso, etc. Ésto ha llevado a crear una solución para evitar los ficheros XML de persistencia en hibernate 7): El uso de Anotaciones Java en el propio código. Estas anotaciones permiten especificar de una forma más compacta y sencilla la información de mapeo de las clases Java.
Inicialmente Hibernate creó sus propia anotaciones en el paquete org.hibernate.annotations pero a partir de la versión 4 de Hibernate la mayoría de dichas anotaciones han sido java.lang.Deprecated y ya no deben usarse. Las anotaciones que deben usarse actualmente son las del estándar de JPA que se encuentran en el paquete javax.persistence. Sin embargo hay características específicas de Hibernate que no posee JPA lo que hace que aun sea necesario usar alguna anotación del paquete org.hibernate.annotations pero en ese caso Hibernate 4 no las ha marcado como java.lang.Deprecated
Veamos ahora el ejemplo de la clase Profesor
pero mapeada con anotaciones.
- 1|Profesor.java anotado
@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; 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 int getId() { return id; } public void setId(int id) { this.id = id; } public String getNombre() { return nombre; } public void setNombre(String nombre) { this.nombre = nombre; } public String getApe1() { return ape1; } public void setApe1(String ape1) { this.ape1 = ape1; } public String getApe2() { return ape2; } public void setApe2(String ape2) { this.ape2 = ape2; } }
Las anotaciones que se han usado son las siguientes:
@Entity
: Se aplica a la clase e indica que esta clase Java es una entidad a persistir. Es una anotación estándar de JPA. En nuestro ejemplo estamos indicando que la claseProfesor
es una entidad que se puede persistir.@Table(name=“Profesor”)
: Se aplica a la clase e indica el nombre de la tabla de la base de datos donde se persistirá la clase. Es opcional si el nombre de la clase coincide con el de la tabla. Es una anotación estándar de JPA. En nuestro ejemplo estamos indicando que la claseProfesor
se persistirá en la tablaProfesor
de la base de datos.@Id
: Se aplica a una propiedad Java e indica que este atributo es la clave primaria. Es una anotación estándar de JPA. En nuestro ejemplo estamos indicando que la propiedad Javaid
es la clave primaria.@Column(name=“Id”)
: Se aplica a una propiedad Java e indica el nombre de la columna de la base de datos en la que se persistirá la propiedad. Es opcional si el nombre de la propiedad Java coincide con el de la columna de la base de datos. Es una anotación estándar de JPA. En nuestro ejemplo estamos indicando que la propiedad Javaid
se persistirá en una columna llamadaId
.@Column(name=“nombre”)
: Se aplica a una propiedad Java e indica el nombre de la columna de la base de datos en la que se persistirá la propiedad. Es opcional si el nombre de la propiedad Java coincide con el de la columna de la base de datos. Es una anotación estándar de JPA. En nuestro ejemplo estamos indicando que la propiedad Javanombre
se persistirá en una columna llamadanombre
.@Column(name=“ape1”)
: Es igual al caso anterior pero para la propiedadape1
.@Column(name=“ape2”)
: Es igual al caso anterior pero para la propiedadape2
.
.hbm.xml
y las anotaciones es que en el fichero es obligatorio indicar todas las propiedades que queremos que se persistan en la base de datos, mientras que usando las anotaciones éso no es necesario. Usando anotaciones se persisten todas las propiedades que tengan los métodos get/set
.
.hbm.xml
, si mediante el uso de los métodos get/set
o mediante el acceso a las propiedades.Veamos como se especifica ésto mediante notaciones:- Si colocamos las anotaciones sobre las propiedades , el acceso será a las propiedades y no serán necesarios los métodos
get/set
. - Si colocamos las anotaciones sobre los métodos
get()
, el acceso será mediante los métodosget/set
.
Personalmente siempre coloco las anotaciones sobre las propiedades ya que así tengo todas ellas más agrupadas visualmente y queda el código más legible.