martes, 10 de febrero de 2015

Integración y manejo de transacciones con Hibernate

A lo largo de una serie de tutoriales hemos visto las bondades que nos ofrece el framework Spring para el manejo del ciclo de vida de beans, inyección de dependencia y configuración a través de anotaciones y de archivos de configuración en XML.

Además ya hemos visto cómo trabajar con el framework Hibernate para el manejo de persistencia de nuestra aplicación. Este framework nos permite olvidarnos de escribir queries a mano para centrarnos en la lógica y el flujo de la aplicación, además de proporcionar mecanismos de caché de datos.

En este tutorial veremos cómo integrar estos dos frameworks para que Spring se encargue del manejo de las sesiones y las transacciones que Hibernate usa.

Spring soporta integración con varios frameworks de persistencia como Hibernate, Java Persistence API (JPA), Java Data Objects (JDO) e iBatis (ahora myBatis) para realizar la implementación de los DAOs (Data Access Object) y la estrategia de transacciones. Además podemos configurar todas las características que nuestro framework de ORM soporte, a través del mecanismo de inyección de dependencias de Spring.
El framework de Spring agrega mejoras significativas a la capa de ORM de nuestra elección cuando creamos aplicaciones que hacen uso de acceso a datos. Podemos dejarle a Spring tanto del trabajo de integración como queramos, teniendo la tranquilidad de usar un API probada y optimizada. También nos permite configurarlo de distintas maneras y a distintos niveles haciéndolo muy flexible al momento de tener que modificar algún aspecto del manejo o implementación de persistencia y/o transacciones de nuestra aplicación.

Suficiente teoría. Comencemos con la parte práctica del tutorial.

Lo primero que haremos es crear un nuevo proyecto en NetBeans. Para esto vamos al Menú "File->New Project...". En la ventana que se abre seleccionamos la categoría "Java" y en el tipo de proyecto "Java Application". Le damos una ubicación y un nombre al proyecto, en mi caso será "SpringHibernateIntegracion". Nos aseguramos que la opción "Create Main Class" esté habilitada y la damos un nombre (incluyendo los paquetes) a la nueva clase, en mi caso será "com.javatutoriales.spring.integration.hibernate.Main" (lo sé, es un poco larga pero esto sirve para que se más claro lo que estamos haciendo). Presionamos el botón "Finish" y veremos aparecer en el editor nuestra clase "Main".

*Nota: Yo usaré dos proyectos, uno para usar archivos de configuración en XML y otro para usar anotaciones y que el código de ambos no se mezcle.

Agregamos la biblioteca de "Spring4" que creamos en el primer tutorial de la serie de Spring y la biblioteca "Hibernate4" que creamos en el primer tutorial de la serie de Hibernate. También agregaremos el driver de MySQL, a través de la biblioteca "MySQL JDBC Driver" (incluida en el NetBeans).

*Nota: Estamos actualizando los tutoriales de Hibernate para hacer uso de la versión 4, por lo que por ahora sólo deben saber que necesitan los siguientes jars, de la versión 4.2.7 de Hibernateantlr-2.7.7.jardom4j-1.6.1.jarhibernate-commons-annotations-4.0.2.Final.jarhibernate-core-4.2.7.SP1.jarhibernate-jpa-2.0-api-1.0.1.Final.jarjavassist-3.18.1-GA.jarjboss-logging-3.1.0.GA.jarjboss-transaction-api_1.1_spec-1.0.1.Final.jar

Hacemos clic derecho sobre el nodo "Libraries" del proyecto, en el menú que aparece elegimos la opción "Add Library..."




En la ventana que se abre seleccionamos las bibliotecas "Hibernate4", "Spring4" y "MySQL JDBC Driver":



Hasta el momento nuestras bibliotecas deben verse de la siguiente forma:



Adicionalmente debemos agregar los jars con las clases necesarias de Spring para el manejo de persistencia:
  • spring-orm-4.1.3.RELEASE.jar
  • spring-jdbc-4.1.3.RELEASE.jar
  • spring-tx-4.1.3.RELEASE.jar

Y si queremos que Spring maneje nuestras transacciones debemos agregar además los siguientes jars:
  • spring-aop-4.1.3.RELEASE.jar
  • com.springsource.org.aopalliance-1.0.0.jar (que pueden descargar desde java2s).

Nuestras bibliotecas finalmente deben verse así:



Ahora que ya tenemos las librerías, el siguiente paso es crear la base de datos. Usaremos una base de datos en MySQL que se llamara "tutorial_hibernate". En esta base de datos pondremos sólo una tabla que será "CONTACTO". Para crear esta tabla usaremos el siguiente script:
CREATE TABLE contacto (
ID bigint(20) NOT NULL AUTO_INCREMENT,
NOMBRE varchar(255) DEFAULT NULL,
telefono varchar(255) DEFAULT NULL,
email varchar(255) DEFAULT NULL,
PRIMARY KEY (ID)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8

También crearemos una clase, cuyas instancias serán guardadas en la tabla anterior. Para esto, primero crearemos un nuevo paquete llamado "entidades", y en este paquete creamos una nueva clase llamada "Contacto", que debe implementar la interface "java.io.Serializable":
public class Contacto implements Serializable{
}

Contacto será una clase muy sencilla que nos servirá para transportar los datos (un TO [Transfer Object] o DTO [Data Transfer Object]) desde y hacia la base de datos, por lo que sólo tendrá unos cuantos atributos, que serán equivalentes a las columnas de la tabla "contacto" que tenemos en la base de datos (también colocaremos getters y setters de estos atributos):
public class Contacto implements Serializable {
private long id;
private String email;
private String nombre;
public long getId() {
private String telefono;
return id; }
this.id = id;
protected void setId(long id) { } public String getNombre() {
this.nombre = nombre;
return nombre; } public void setNombre(String nombre) { }
public void setEmail(String email) {
public String getEmail() { return email; } this.email = email; }
public void setTelefono(String telefono) {
public String getTelefono() { return telefono; } this.telefono = telefono; }
}

En el código anterior, seguramente notaron que el método "setId" está declarado como "protected" y no como "public". Esto ocurre porque en este caso no queremos que el usuario pueda establecer el valor del identificador del objeto, queremos que sea la base de datos quien genere este valor y Hibernate quien lo coloque en el objeto. Haciendo el método protegido nos aseguramos de que sólo un objeto que extienda de "Contacto" pueda establecer el valor (la cual, por cierto, es la forma de trabajar de Hibernate).

En la clase "Contacto" pondremos además un par de constructores, uno vacío y otro que reciba todos los atributos (excepto el id). El primer constructor es un requisito de Hibernate y también puede ser "protected" por las mismas razones que expusimos hace un momento, y el segundo es para facilitarnos a nosotros la creación de los objetos.
protected Contacto() {
}
public Contacto(String nombre, String email, String telefono) {
this.nombre = nombre; this.email = email;
}
this.telefono = telefono;

La clase "Contacto" completa queda de la siguiente forma:
public class Contacto implements Serializable {
private long id;
private String email;
private String nombre;
protected Contacto() {
private String telefono;
}
public Contacto(String nombre, String email, String telefono) {
this.nombre = nombre; this.email = email; this.telefono = telefono; }
this.id = id;
public long getId() { return id; } protected void setId(long id) { }
this.nombre = nombre;
public String getNombre() { return nombre; } public void setNombre(String nombre) { }
this.email = email;
public String getEmail() { return email; } public void setEmail(String email) { } public String getTelefono() {
}
return telefono; } public void setTelefono(String telefono) { this.telefono = telefono;
}

Ahora necesitamos una interface que defina las operaciones que podremos realizar en la capa de persistencia, sobre los registros de la tabla "contacto". El crear esta interface nos ayudará a que nuestra aplicación mantenga un bajo acoplamiento, separando el código de lógica de negocio del de acceso a datos, de esta forma podremos cambiar la implementación del DAO siempre que use la misma interface, y nuestra capa de lógica ni siquiera notará el cambio. Crearemos una nueva interface llamada "ContactosDAO", dentro de un paquete "persistencia" de nuestra aplicación. Esta interface tendrá 5 métodos que nos permitirán guardar nuevos Contactos, actualizarlos, eliminarlos, y buscarlos. La interface "ContactosDAO" queda de la siguiente forma:

public interface ContactosDAO {
void actualizaContacto(Contacto contacto) throws HibernateException;
void eliminaContacto(Contacto contacto) throws HibernateException;
Contacto obtenContacto(long idContacto) throws HibernateException;
long guardaContacto(Contacto contacto) throws HibernateException; List<Contacto> obtenListaContactos() throws HibernateException;
}

El siguiente paso es crear una implementación de la interface anterior, que contenga la lógica para conectarse a la base de datos. Como en nuestro caso la implementación será realizada con Hibernate, nos basaremos en lo que aprendimos en la serie de tutoriales deHibernate. Recordemos que para poder hacer operaciones sobre la base de datos lo primero que necesitamos es obtener una instancia de la clase "org.hibernate.SessionFactory". Obtener esta instancia es un trabajo "pesado", esto quiere decir que se necesita algo de tiempo para hacerlo y que consume una cantidad considerable de memoria, por lo tanto la recomendación es que esta instancia sea creada una sola vez, y que sea al inicio de la aplicación. 

En los tutoriales de Hibernate lográbamos esto con una clase de utilidad llamada "HibernateUtil", o extendiendo de una clase base "AbstracHibernateDAO", y creábamos la instancia de "SessionFactory" en un bloque de inicialización estática dentro de e esta clase.

En esta ocasión Spring se encargará de crear esta instancia, lo único que tendremos que hacer es configurar algunos datos para la creación de este "SessionFactory", en el archivo de configuración de Spring y este se encargará del resto. Además haremos uso de la inyección de dependencias para poder acceder a la instancia de "SessionFactory" creada por Spring. Veremos cómo hacer esto un poco más adelante en el tutorial.

Creamos una nueva clase, en el paquete "persistencia", llamada "ContactosDAOHibernateImpl". Esta clase implementará la interface "ContactosDAO":
public class ContactosDAOHibernateImpl implements ContactosDAO{
}


Antiguamente Spring proporcionaba "templates" para realizar los queries y mapearlos a objetos de negocio de nuestra aplicación. Sin embargo, ahora la estrategia recomendada es escribir las implementaciones de los DAO usando Hibernate "plano", o sea sin hacer uso de ninguna dependencia de Spring.

Basándonos en la serie de tutoriales de Hibernate, declararemos tres variables de instancia en esta clase. La primera será una instancia de "SessionFactory", que es el objeto que usamos para crear los objetos "org.hibernate.Session". La segunda será una instancia de "Session", que nos proporciona las operaciones para guardar, eliminar y obtener objetos desde y hacia la base de datos; y la tercera será una instancia de "org.hibernate.Transaction", que es un objeto que define una unidad de trabajo en la base de datos.


public class ContactosDAOHibernateImpl implements ContactosDAO {
private Session sesion;
private SessionFactory sessionFactory;
private Transaction transaction;
}

También colocaremos un setter para el "SessionFactory". ¿Por qué? Porque recordemos que en este caso será Spring quien se encargará de crear este objeto, y lo pasará a nuestra clase a través de ese setter:
public class ContactosDAOHibernateImpl implements ContactosDAO {
private Session sesion;
private SessionFactory sessionFactory;
private Transaction transaction;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory; }
}

Ahora colocaremos dos métodos privados en esta clase, que nos ayudarán a iniciar una operación, iniciando una sesión y una transacción; y a manejar los errores en caso de una excepción, haciendo un rollback de la transacción. Estos métodos los tomaremosdel tutorial de Hibernate con una ligera modificación.


El primer método se llamará "iniciaOperacion", y se encargará de iniciar una sesión, usando el objeto "SessionFactory"; y de iniciar una transacción:

private void iniciaOperacion() throws HibernateException {
sesion = sessionFactory.getCurrentSession();
}
transaction = sesion.beginTransaction();


El segundo se llamará "manejaExcepcion", y nos permitirá hacer un rollback de la transacción en caso de que exista un error, o sea, que una "HibernateException" sea lanzada mientras trabajamos con la base de datos. Este código (aunque es sólo una línea) lo ponemos en este método por si después quisiéramos agregar más operaciones dentro de este método, como por ejemplo mandar el error a una bitácora o imprimir el stacktrace de la excepción.

private void manejaExcepcion(HibernateException he) throws HibernateException {
transaction.rollback();
}


Ahora colocaremos a implementación de los métodos de la interface "ContactosDAO". Estas implementaciones también las hemos tomado de los tutoriales de Hibernate, por lo que no daremos una explicación de los mismos (además, parece que son bastante intuitivos). La clase "ContactosDAOHibernateImpl" completa, omitiendo los imports, queda de la siguiente forma:
public class ContactosDAOHibernateImpl implements ContactosDAO {
private Session sesion;
private SessionFactory sessionFactory;
private Transaction transaction;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory; } @Override
try {
public long guardaContacto(Contacto contacto) throws HibernateException { long id = 0; iniciaOperacion();
manejaExcepcion(he);
id = (Long) sesion.save(contacto); transaction.commit(); } catch (HibernateException he) { throw he; } finally {
iniciaOperacion();
sesion.close(); } return id; } @Override public void actualizaContacto(Contacto contacto) throws HibernateException { try {
sesion.close();
sesion.update(contacto); transaction.commit(); } catch (HibernateException he) { manejaExcepcion(he); throw he; } finally { } }
} catch (HibernateException he) {
@Override public void eliminaContacto(Contacto contacto) throws HibernateException { try { iniciaOperacion(); sesion.delete(contacto); transaction.commit();
Contacto contacto = null;
manejaExcepcion(he); throw he; } finally { sesion.close(); } } @Override public Contacto obtenContacto(long idContacto) throws HibernateException { try {
public List<Contacto> obtenListaContactos() throws HibernateException {
iniciaOperacion(); contacto = (Contacto) sesion.get(Contacto.class, idContacto); } finally { sesion.close(); } return contacto; } @Override List<Contacto> listaContactos = null;
private void iniciaOperacion() throws HibernateException {
try { iniciaOperacion(); listaContactos = sesion.createQuery("from Contacto").list(); } finally { sesion.close(); } return listaContactos; } sesion = sessionFactory.getCurrentSession();
}
transaction = sesion.beginTransaction(); } private void manejaExcepcion(HibernateException he) throws HibernateException { transaction.rollback();
}

Ahora crearemos el archivo de configuración de Spring. En este archivo declararemos los beans que Spring usará para crear la conexión hacia la base de datos, el objeto sessionFactory, y para indicar las clases que Hibernate usará como entidades. En este momento colocaremos sólo los elementos esenciales, y los demás los dejaremos pendientes por un momento, ya que algunas cosas dependen de si estamos trabajando con anotaciones o con archivos de mapeo.

Para crear este archivo hacemos clic derecho sobre el nodo "Source Packages" de nuestro proyecto, y luego en "New" -> "SpringXMLConfig":


Si no les aparece esta opción (como es mi caso), seleccionen la opción "Other...", y en la ventana que aparece seleccionen "Other" como categoría, y "SpringXMLCongfig" como tipo de archivo.

A este archivo le daremos "applicationContext" como nombre (NetBeans se encargará de poner la extensión .xml). Antes de crear el archivo NetBeans preguntará los namespaces de Spring que queremos incluir en el archivo. Recodemos que un namespace nos permite separar elementos de configuración de un módulo de Spring de los elementos de otro módulo, de forma que sea más claro lo que estamos configurando. Nosotros seleccionaremos los namapaces "aop", "context" y "tx" (opcionalmente puede seleccionar también el namespace "jee" si van a trabajar con JNDI), los cuales permiten configurar los elementos de programación orientada a aspectos (hablaremos de esto más adelante), escaneo y configuración de componentes, y manejo de transacciones, respectivamente:


Hacemos clic en el botón "Finish", con lo que deberemos de ver nuestro archivo de configuración, el cual tiene más o menos el siguiente contenido:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
</beans>
Lo primero que haremos es crear el dataSource, o la conexión hacia la base de datos.
Spring ofrece varias opciones para configurar esta fuente de datos, entre las que se incluyen:
Data sources a través de JNDI
Data sources a través de drivers JDBC
Data sources que usan un pool de conexiones
Obviamente hay algunas diferencias en las características de cada uno de estos tipos de data sources.
Si queremos simular un ambiente real de producción, o los parámetros de nuestra conexión cambiarán dependiendo del ambiente en el que estemos (por ejemplo: desarrollo, pruebas, producción) o estamos desarrollando un proyecto que será instalado en múltiples clientes, o simplemente no tenemos los datos de la conexión, pero nos será proporcionada a través de JNDI (por ejemplo si instalamos nuestra aplicación en un servidor de aplicaciones), lo más recomendable es usar un data source a través de JNDI.
El elemento "<jee:jndi-lookup>" permite recuperar cualquier objeto, incluyendo data sources, de JNDI y ponerlo disponible como un bean de Spring. El atributo "jndi-name", de este elemento, se usa para especificar el nombre del recurso JNDI. Finalmente, si la aplicación se ejecuta en un servidor de aplicaciones, podemos establecer la propiedad "resource-ref" a "true", con lo que el nombre del recurso será precedido con "comp/env/".
Por ejemplo, si tenemos un recurso de tipo data source, cuyo nombre es "conexionTutorial", entonces haremos referencia a este recurso de la siguiente forma:
<jee:jndi-lookup id="dataSource" jndi-name="/jdbc/conexionTutorial" resource-ref="true" />


En caso de que no estemos trabajando con JNDI la siguiente mejor opción es usar un data source que mantenga un pool de conexiones, y declarar los datos para esta conexión directamente en Spring. La forma más común de hacer esto es usando el proyecto Data Base Connection Pool de Apache. Aunque tiene varias clases para proporcionar el pooling, la que se usa con más frecuencia es "BasicDataSource".


Para crear este data source, declaramos un bean de Spring, y establecemos los atributos necesarios para la conexión, como el nombre del driver a usar; la URL, username y password de la conexión; y el tamaño inicial del pool:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost/tutorial_hibernate" />
<property name="username" value="user" />
<property name="password" value="password" />
<property name="initialSize" value="5" />
</bean>
<property name="maxActive" value="10" />


Para ver qué otros atributos soporta esta bean, pueden revisar la documentación de la clase "BasicDataSource".

Si no necesitamos cosas tan "sofisticadas" como un pool de conexiones o si, como en nuestro caso, estamos haciendo sólo un ejemplo sencillo, podemos usar un driver JDBC como data source. Spring viene con dos clases que pueden servirnos de data source:

DriverManagerDataSource. Regresa una nueva conexión cada vez que una conexión es requerida.
SingleConnectionDataSource. Regresa la misma conexión cada vez que una conexión es requerida.

La configuración de ambas clases es igual. En nuestro caso elegiremos la clase "DriverManagerDataSource". Aquí nuevamente crearemos un bean de Spring y estableceremos la información requerida para la conexión a la base de datos (como el nombre del driver a usar; la URL, username y password de la conexión):
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="username" value="user" />
<property name="url" value="jdbc:mysql://localhost/tutorial_hibernate" />
<property name="password" value="password" />
</bean>

Nosotros elegiremos trabajar con este último data source, por lo que hasta ahora nuestro archivo de configuración de Spring (omitiendo los namespaces) se ve de la siguiente forma:
<beans>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="username" value="user" />
<property name="url" value="jdbc:mysql://localhost/tutorial_hibernate" />
<property name="password" value="password" /> </bean>
</beans>
El siguiente paso es crear el SessionFactory que, como recordaremos, nos permitirá crear objetos "Session" con los que interactuaremos con la base de datos. Para crear este objeto nuevamente declararemos un bean de Spring. La clase que usemos dependerá del framework ORM que estemos usando. Como en nuestro caso usamos Hibernate 4, la clase que tenemos que usar es "org.springframework.orm.hibernate4.LocalSessionFactoryBean":
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
</bean>
Dentro de este bean podemos configuraremos algunos parámetros del framework ORM que estemos usando, en este caso Hibernate. Aquí pondremos parámetros que normalmente colocamos en el archivo de configuración de Hibernate ("hibernate.cfg.xml"), junto con algunos otros datos.

Alguna de esta información dependerá si estamos trabajando con archivos de configuración en XML, o si estamos trabajando con anotaciones. Veremos la configuración común para ambas opciones y un poco más adelante veremos los elementos particulares para cada forma de trabajo.

Lo primero que debemos hacer es indicar el data source que se usará para conectarse a la base de datos, para ello usamos la propiedad "dataSource" y hacemos referencia al bean "dataSource" que creamos hace un momento:

<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
</bean>
</property>


Lo siguiente es configurar las propiedades de Hibernate que no tienen que ver con la conexión a la base de datos, como por ejemplo pool de conexiones, caché, el dialecto que se usará, los archivos de mapeo o clases anotadas que representan las entidades, etc. De hecho, si ya contamos con un archivo "hibernate.cfg.xml" o simplemente queremos apartar esta información del archivo de configuración de Spring (por ejemplo si queremos que esta configuración pueda ser modificada por otra persona o no queremos llenar este archivo con mucha información), este bean nos permite hacer referencia a un archivo de configuración de Hibernate del cual leerá estos parámetros. Para esto usamos la propiedad "configLocation", al cual le pasamos una lista de ubicaciones de los archivos que utilizará, en este caso sólo tendremos uno, por lo que la propiedad queda de esta forma:

<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>

Creamos este archivo, en la raíz del nodo "Source Packages" del proyecto, como un documento XML:



En este archivo pondremos la información del dialecto de la base de datos, indicaremos que no queremos que se muestre el SQLgenerado por Hibernate, y que la base de datos debe eliminarse ni actualizarse cada vez que hagamos una conexión. El archivo hasta ahora tiene el siguiente contenido:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!-- Dialecto de la base de datos -->
<property name="show_sql">false</property>
<!-- Otras propiedades importantes -->
</hibernate-configuration>
<property name="hbm2ddl.auto">none</property>
</session-factory>

Lo siguiente dependerá de si estamos usando archivos de mapeo en XML o anotaciones. En el primer caso debemos indicar dónde se encuentra el archivo de mapeo de la clase "Contacto" (archivo que no hemos creado todavía). Supongamos que lo hemos puesto en un directorio especial para los mapeos, debajo del paquete de las entidades, entonces tendremos que usar el atributo "resource" del elemento "mapping" de la siguiente forma:
<mapping resource="com/javatutoriales/spring/integration/hibernate/entidades/mapeos/Contacto.hbm.xml"/>

Si por el contrario, estuviéramos usando anotaciones, deberemos usar el atributo "class" del elemento "mapping", haciendo referencia a la clase "Contacto", de esta forma:
<mapping class="com.javatutoriales.spring.integration.hibernate.entidades.Contacto"/>

El archivo de configuración final de Hibernate depende entonces de si usamos anotaciones o archivos de mapeo. El archivo de configuración de Spring hasta ahora se ve de la siguiente forma:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="username" value="user" />
<property name="url" value="jdbc:mysql://localhost/tutorial_hibernate" /> <property name="password" value="password" /> </bean>
<ref bean="dataSource"/>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource"> </property> <property name="configLocation">
<mapping resource="com/javatutoriales/spring/integration/hibernate/entidades/mapeos/Contacto.hbm.xml"/>
<value>classpath:hibernate.cfg.xml</value> </property> <!-- mapping class="com.javatutoriales.spring.integration.hibernate.entidades.Contacto"/--> </bean>
</beans>

Ahora, ¿qué pasa si no queremos poner esta información en el archivo de Hibernate, sino directamente en el archivo de Spring? Para eso podemos usar la propiedad "hibernateProperties" del bean "sessionFactory". En esta propiedad colocaremos un conjunto de, valga la redundancia, propiedades que indican los valores de las (si, es la tercera vez) propiedades de Hibernate. Para reproducir lo que teníamos en el archivo de configuración de Hibernate, debemos colocar las propiedades de esta forma:
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>

Los más observadores se habrán percatado de que aún no hemos colocado la información de en dónde se encuentran las entidades (o los archivos de mapeos de las mismas) que serán manejadas por Hibernate. Para esto tenemos cuatro maneras, las primeras dos para cuando trabajamos con archivos de mapeo en XML.

La primera de las formas consiste en indicar, a través de la propiedad "mappingResources" del bean "sessionFactory", donde se encuentra cada uno de los archivos de mapeo de nuestras entidades, a través de una lista de valores; de la siguiente forma:
<property name="mappingResources">
<list>
<value>com/javatutoriales/spring/integracion/mapeos/Contacto.hbm.xml</value>
</list>
</property>

Esto está bien si tenemos pocas entidades pero en aplicaciones de tamaño mediano a grande este no será el caso, y no sólo tener colocar todos los archivos de mapeo puede ser tardado, sino que el tener que estar quitando los de las entidades que quitamos del diseño de nuestro sistema, o tener que recordar agregar los nuevos, puede ser una labor propensa a errores. Debido a esto les recomiendo el uso de otra propiedad del bean "sessionFactory", llamada "mappingDirectoryLocations" (la cual es la segunda forma de indicar dónde están nuestras entidades). En esta propiedad lo único que debemos hacer es indicar los lugares en los que se encuentran los archivos de mapeo, y digo lugares por si tenemos nuestros archivos de mapeo esparcidos por varios lugares de nuestra aplicación (lo cual no debería de pasar), la propiedad de configura de la siguiente forma:
<property name="mappingDirectoryLocations">
<list>
<value>classpath:com/javatutoriales/spring/integracion/mapeos/</value>
</list>
</property>

Y la configuración completa de nuestro bean "sessionFactory" se ve de la siguiente forma:
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
</property>
<property name="hibernateProperties">
<props>
<property name="mappingDirectoryLocations">
<prop key="hibernate.show_sql">true</prop> </props> </property> <list>
</bean>
<value>classpath:com/javatutoriales/spring/integracion/mapeos/</value> </list>
</property>

La tercera forma de configurar la ubicación de nuestras entidades manejadas por Hibernate es para cuando anotamos estas entidades. Podemos usar la propiedad "annotatedClasses" para indicar cada una de las clases que están anotadas como entidades, de la siguiente manera:
<property name="annotatedClasses">
<list>
<value>com.javatutoriales.spring.integracion.entidades.Contacto</value>
</list>
</property>

Aquí tendremos el mismo problema que cuando indicamos los archivos de mapeo uno por uno, por lo que les recomiendo que usen la cuarta forma, que es a través de la propiedad "packagesToScan" del bean "sessionFactory", en la cual indicamos la lista de los paquetes en los que se encuentran nuestras entidades, de la siguiente manera:

<property name="packagesToScan">
<list>
<value>com.javatutoriales.spring.integracion.entidades</value>
</list>
</property>

Por lo que la configuración del bean "sessionFactory" queda de la siguiente forma si trabajamos con anotaciones:
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">none</prop>
<prop key="hibernate.show_sql">true</prop> </props> </property>
<value>com.javatutoriales.spring.integration.hibernate.entidades</value>
<property name="packagesToScan"> <list> </list> </property>
</bean>

Y así si trabajamos con archivos de mapeo:
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">none</prop>
<prop key="hibernate.show_sql">true</prop> </props> </property>
<value>classpath:com/javatutoriales/spring/integration/hibernate/entidades/mapeos</value>
<property name="mappingDirectoryLocations"> <list> </list> </property>
</bean>

Ahora que tenemos la estructura básica para los ejemplos dividiremos este tutorial en dos partes. Recordemos que cada uno de estos frameworks tiene dos formas de ser configurados: con archivos de configuración en XML y con Anotaciones. En la primer parte del tutorial veremos sólo la parte de trabajo con archivos de configuración en XML, y en la segunda parte veremos cómo trabajar con anotaciones. Pero recuerden que pueden usar ambas formas de trabajo mezcladas a su conveniencia.


 Fuente:enlace

Si quieres seguir aprendiendo con nosotros, puedes ingresar a nuestros 
Cursos y Diplomados de Tecnología Web visita www.uneweb.edu.ve  para más información

No hay comentarios:

Publicar un comentario

Entradas populares