Herramientas de usuario

Herramientas del sitio


unidades:02_hibernate:02_entidad

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.

Nada impide que el fichero .hbm.xml esté en otro paquete distinto al de la clase Java. En este sentido suele haber 2 posibilidades:
  1. Almacenar el fichero .hbm.xml en el mismo paquete que la clase Java a la que hace referencia.
  2. Crear un árbol alternativo de paquetes donde almacenar los ficheros .hbm.xml. Por ejemplo, si tenemos el paquete raíz com.miempresa.proyecto.dominio donde se guardan todas las clases Java a persistir, crear otro paquete llamado com.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.
Recuerda que usando el atributo 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>
Como podemos apreciar en el DTD http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd, hay muchos más tags y atributos en los ficheros .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.
Si buscamos documentación sobre hibernate podemos encontrar que el 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.

Hemos comentado que necesitamos los métodos 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 clase Profesor 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 clase Profesor se persistirá en la tabla Profesor 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 Java id 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 Java id se persistirá en una columna llamada Id.
  • @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 Java nombre se persistirá en una columna llamada nombre.
  • @Column(name=“ape1”): Es igual al caso anterior pero para la propiedad ape1.
  • @Column(name=“ape2”): Es igual al caso anterior pero para la propiedad ape2.
Una diferencia importante entre usar el fichero de mapeo .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.
Ya hemos comentado en el apartado anterior sobre como Hibernate accede a los datos al usar el fichero .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étodos get/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.

1)
El contructor puede ser privado pero disminuirá el rendimiento de hibernate. Véase: 2.2. The entity Java class
3)
Recordar que hibernate es un ORM, que significa mapeo objeto-relacional
4)
Fully-Qualified Class Name
6)
incluido hibernate
7)
y en muchos otros frameworks
unidades/02_hibernate/02_entidad.txt · Última modificación: 2023/04/07 21:26 por 127.0.0.1