Herramientas de usuario

Herramientas del sitio


unidades:02_hibernate:02_entidad

Diferencias

Muestra las diferencias entre dos versiones de la página.


unidades:02_hibernate:02_entidad [2023/04/07 21:26] (actual) – creado - editor externo 127.0.0.1
Línea 1: Línea 1:
 +====== 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]]
 +  * [[#Fichero de mapeo ''.hbm.xml'']]
 +  * [[#Anotaciones]]
 +
 +===== 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 ((El contructor puede ser privado pero disminuirá el rendimiento de hibernate. Véase: [[http://docs.jboss.org/hibernate/orm/4.1/quickstart/en-US/html_single/#hibernate-gsg-tutorial-basic-entity|2.2. The entity Java class]])).
 +  * Para cada propiedad que queramos persistir debe haber un método get/set asociado.
 +  * Implementar el interfaz <javadoc jdk7>java.io.Serializable|Serializable</javadoc> (( No es obligatorio que se implemente el interfaz <javadoc jdk7>java.io.Serializable|Serializable</javadoc> pero sí recomendable. 
 +Más información sobre este tema las podemos encontrar en:  
 +  * [[http://jcp.org/aboutJava/communityprocess/final/jsr220/index.html|JSR-000220 Enterprise JavaBeans 3.0 (Final Release)]]
 +  * [[http://stackoverflow.com/questions/2020904/when-and-why-jpa-entities-should-implement-serializable-interface|stackoverflow.When and why JPA entities should implement Serializable interface?]]
 +  * [[http://stackoverflow.com/questions/2726300/do-hibernate-table-classes-need-to-be-serializable|stackoverflow.Do Hibernate table classes need to be Serializable?]] 
 +))
 +
 +<code java 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;
 +    }
 +}
 +</code>
 +
 +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 <javadoc jdk7>java.io.Serializable|Serializable</javadoc> (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 (( Recordar que hibernate es un ORM, que significa mapeo objeto-relacional )) , 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.
 +
 +<note tip>
 +Nada impide que el fichero ''.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í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.
 +</note>
 +
 +<code xml 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>
 +</code>
 +
 +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 [[wpes>DOCTYPE]] junto con la referencia al documento [[http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd]] [[wpes>DTD]] que permite validarlo. Es decir que si nos descargamos el fichero [[http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd|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 ((Fully-Qualified Class Name)) 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 [[unidades:04_claves_primarias_y_tipos_datos:01_tipos_basicos]].
 +  * 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. 
 +
 +<note important>
 +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.
 +<code 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" column="primer_apellido" />
 +    <property name="ape2" column="segundo_apellido" />
 +  </class>
 +</hibernate-mapping>
 +</code>
 +</note>
 +
 +<note tip>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. </note>
 +
 +<note tip>
 +Si buscamos documentación sobre hibernate podemos encontrar que el ''DOCTYPE'' antes de la versión 3.6 era (( [[https://community.jboss.org/wiki/HibernateCoreMigrationGuide36|Hibernate Core Migration Guide : 3.6]] )) :
 +
 +  <!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.
 +
 +</note>
 +
 +<note important>
 +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:
 +<code xml>
 +<property name="nombre" access="field" />
 +</code>
 +
 +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 [[http://wrschneider.blogspot.com.es/2005/01/avoiding-anemic-domain-models-with.html|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.
 +</note>
 +===== 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 [[https://www.google.es/search?q=java+infierno+xml|El infierno XML]].Este infierno ha consistido en que en demasiados frameworks (( incluido hibernate )) 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 ((y en muchos otros frameworks)): El uso de [[wpes>Anotación_Java|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 <javadoc h41>org.hibernate.annotations.package-summary|org.hibernate.annotations</javadoc> pero a partir de la versión 4 de Hibernate la mayoría de dichas anotaciones han sido <javadoc jdk>java.lang.Deprecated</javadoc> y ya no deben usarse. Las anotaciones que deben usarse actualmente son las del estándar de JPA que se encuentran en el paquete <javadoc jee5>javax.persistence.package-summary|javax.persistence</javadoc>. 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 <javadoc h41>org.hibernate.annotations.package-summary|org.hibernate.annotations</javadoc> pero en ese caso Hibernate 4 no las ha marcado como <javadoc jdk>java.lang.Deprecated</javadoc>
 +
 +Veamos ahora el ejemplo de la clase ''Profesor'' pero mapeada con anotaciones.
 +
 +<code java 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;
 +    }
 +}
 +</code>
 +
 +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''.
 +
 +<note important>
 +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''.
 +</note>
 +
 +<note important>
 +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.
 +</note>