lunes, 14 de octubre de 2013

Spring: Mejorando el Trabajo con Hibernate ORM

Recorriendo Spring
Uno de los problemas con el uso de Hibernate es que la aplicación cliente que accede a la base de datos usando Hibernate Framework tiene que depender de la API de Hibernate como configuración, SessionFactory y Session. Estos objetos se encuentran dispersos por todo el código en la aplicación.

Por otra parte, el código de la aplicación tiene que mantener y gestionar manualmente estos objetos. En el caso de Spring, los objetos de negocio pueden ser altamente configurable, con la ayuda de la IOC.

En pocas palabras, el estado de un objeto puede ser externalizado desde el código de aplicación, esto significa que ahora es posible utilizar los objetos Hibernate como bean de spring  y pueden disfrutar de todos los beneficios de Spring.

Spring es un Framework  que desempeña diferentes papeles en muchas áreas de la arquitectura de la aplicación. Una de estas áreas es la persistencia, dado que Spring no proporciona su propio framework de persistencia. En su lugar, proporciona una capa de abstracción sobre JDBC, interacción con ORM en el marco de mapeo como  iBATIS SQL Maps, Hibernate, JDO, OJB Apache y Oracle TopLink.
Esta abstracción permite, la aplicación de acceso a datos de una manera manejables consistente.

Como mejora el trabajo con ORM ¿?

La Capa de abstracción de Spring  abstrae de la aplicación la fábrica de conexiones, la gestión de transacción, y gestiona las  excepciones que utiliza la tecnología de persistencia con la que estemos trabajando en nuestro proyecto.   
Spring proporciona IOC  y AOP, que se puede utilizar en la capa de persistencia, afectando las  transacciones de Hibernate de manera externa y proporciona un enfoque más potente y completo para la gestión de transacciones sin ensuciar el código de nuestro proyecto.

Pero donde trabaja en mi Proyecto ¿?

Spring se encuentra entre las clases de la aplicación y la herramienta de mapeo ORM, realiza transacciones, y gestiona los objetos de conexión. Traduce las excepciones de persistencia subyacente lanzadas por Hibernate, excepciones sin control significativos de tipo DataAccessException las cuales pueden o no ser capturadas.

Pero ya que estamos con el tema datos, cual seria un buen patrón de diseño para trabajar ¿?

En lo personal creo que DAO + Service es una buena practica.

Patron Data Access Object: Aunque se puede obtener un objeto de sesión y conectarse a Hibernate en cualquier parte de la aplicación, se recomienda que todas las interacciones con Hibernate puede hacer sólo a través de distintas clases . Con respecto a esto, hay un patrón de diseño JEE , llamado el patrón DAO que es la moneda corriente en el desarrollo de acceso a datos.
De acuerdo con el patrón DAO , todas las operaciones persistentes se deben realizar a través de clases específicas , técnicamente llamados clases DAO . Estas clases se utilizan exclusivamente para la comunicación con el nivel de datos . 

El propósito de este modelo es separar el código relacionado con la persistencia de la lógica de negocio de la aplicación, lo que hace que para el código más manejable y fácil de mantener , lo que le permite cambiar la estrategia de persistencia , sin cambiar las reglas de negocio o la lógica del flujo de trabajo.

El patrón DAO establece que debemos definir una interfaz DAO correspondiente a cada clase DAO . Esta interfaz DAO describe la estructura de una clase DAO , define todas las operaciones de persistencia que necesita la capa de negocio , y (en las aplicaciones basadas en Spring ) nos permite aplicar la IOC para separar la capa de negocio de la clase DAO.

Patron Service Facade:   En aplicaciónes de acceso a datos, el motivo de la fachada servicio siempre se utiliza en conjunto con el modelo DAO. Este patrón indica que el uso de un objeto intermedio, denominado objeto de servicio, entre todos los objetos de la capa de negocio y los objetos DAO. 

El objeto de servicio reúne los objetos DAO a ser gestionados como una unidad de trabajo. Tenga en cuenta que sólo se crea una clase de servicio para todos DAOs que se implementan en cada caso de uso.

La clase de servicio utiliza las instancias de interfaces de DAO para interactuar con ellos. Estas instancias se crean instancias de las clases concretas DAO por el contenedor IoC en tiempo de ejecución. Por lo tanto, el objeto de servicio no tiene conocimiento de los detalles de implementación reales DAO,  Independientemente de la estrategia de persistencia utiliza la aplicación (incluso si utiliza JDBC), es muy recomendable aplicar el DAO y los patrones de fachada servicio para lograr arquitecturas desacopladas y escalables.


Una Implementación común con Hibernate: En primera instancia la definición de la interface dao.

package com.packtpub.springhibernate.ch13;

import java.util.Collection;


public interface StudentDao {

 public Student getStudent(long id);

 public Collection getAllStudents();

 public Collection getGraduatedStudents();

 public Collection findStudents(String lastName);

 public void saveStudent(Student std);

 public void removeStudent(Student std);
}


El siguiente paso seria la definición de la implementacion dao que utiliza hibernate para realizar las operaciones.


package com.packtpub.springhibernate.ch13;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.HibernateException;
import org.hibernate.Query;

import java.util.Collection;

public class HibernateStudentDao implements StudentDao {

SessionFactory sessionFactory;

 public Student getStudent(long id) {
 Student student = null;
 Session session = HibernateHelper.getSession();
 Transaction tx = null;
 try {
  tx = session.beginTransaction();
  student = (Student) session.get(Student.class, new Long(id));
  tx.commit();
  tx = null;
 } catch (HibernateException e) {
   if (tx != null)
    tx.rollback();
   throw e;
 } finally {
  session.close();
   }
  return student;
 }

 public Collection getAllStudents(){
 Collection allStudents = null;
 Session session = HibernateHelper.getSession();
 Transaction tx = null;
 try {
  tx = session.beginTransaction();
  Query query = session.createQuery(
  "from Student std order by std.lastName, std.firstName");
  allStudents = query.list();
  tx.commit();
  tx = null;
 } catch (HibernateException e) {
   if (tx != null)
    tx.rollback();
   throw e; 
 } finally {
  session.close();
    }
  return allStudents;
 }

 public Collection getGraduatedStudents(){
 Collection graduatedStudents = null;
 Session session = HibernateHelper.getSession();
 Transaction tx = null;
 try {
  tx = session.beginTransaction();
  Query query = session.createQuery(
  "from Student std where std.status=1");
  graduatedStudents = query.list();
  tx.commit();
  tx = null;
 } catch (HibernateException e) {
  if (tx != null)
   tx.rollback();
  throw e;
 } finally {
  session.close();
    }
 return graduatedStudents;
 }

 public Collection findStudents(String lastName) {
 Collection students = null;
 Session session = HibernateHelper.getSession();
 Transaction tx = null;
 try {
  tx = session.beginTransaction();
  Query query = session.createQuery(
   "from Student std where std.lastName like ?");
  query.setString(1, lastName + "%");
  students = query.list();
  tx.commit();
  tx = null;
  } catch (HibernateException e) {
   if (tx != null)
    tx.rollback();
   throw e;
  } finally {
   session.close();
   }
  return students;
 }

 public void saveStudent(Student std) {
 Session session = HibernateHelper.getSession();
 Transaction tx = null;
 try {
  tx = session.beginTransaction();
  session.saveOrUpdate(std);
  tx.commit();
  tx = null;
  } catch (HibernateException e) {
   if (tx != null)
    tx.rollback();
   throw e;
  } finally {
   session.close();
  }
 }

 public void removeStudent(Student std) {
 Session session = HibernateHelper.getSession();
 Transaction tx = null;
 try {
  tx = session.beginTransaction();
  session.delete(std);
  tx.commit();
  tx = null;
  } catch (HibernateException e) {
   if (tx != null)
    tx.rollback();
   throw e;
  } finally {
   session.close();
   }
  }

 public void setSessionFactory(SessionFactory sessionFactory) {
  this.sessionFactory = sessionFactory;
 }
}

Como observación podemos notar en todos los métodos la utilización del manejo de transacciones, el trabajo con el objeto de session y la gestión de excepciones que realizamos en cada uno de los métodos.

Nota: como observación final código repetido por todos lados.

Patrón Template: Para limpiar el código y proporcionar más código manejable , Spring utiliza un modelo llamado patron Template . En este patrón, el objeto se encargara de todas las tareas repetitivas de nuestro codigo. En el caso Hibernate, HibernateTemplate extrae todo el código repetitivo , tales como la obtención de una sesión , la realización de la transacción , y la entrega de excepciones.

Una implementacion de Hibernate con Spring : Con Spring tu no necesitas implementar código para la obtención del objeto de session,  gestionar las transacciones  y manejar las excepciones. Usaremos la instancia HibernateTemplate para delegar la persistencia de llamadas a  Hibernate, esta interaccionara directamente con Hibernate.

Éstos son algunos de los beneficios de la utilización de Spring en la capa de persistencia, en lugar de usar la interacción directa con Hibernate:


  • Con Spring, el objeto HibernateTemplate interactúa con Hibernate. Este objeto se elimina el código repetitivo de las implementaciones de DAO.
  • Cualquier invocación de uno de los métodos de HibernateTemplate produce la excepción DataAccessException genérico en lugar de HibernateException (una excepción específica de Hibernate).
  • Spring nos permite delimitar transacciones declarativa, en lugar de implementar duplicado de código de gestión de transacciones. 
La clase HibernateTemplate usa una SessionFactory interno para obtener el objeto de session para interaccionar con Hibernate. Podremos configurar el objeto SessionFactory via IOC para realizar la inyección en los objetos DAO. 

Spring proporciona su propia jerarquía de excepción, que se encuentra en las jerarquías de excepción de las herramientas de mapeo  que soporta. Se detecta cualquier excepción o error de base de datos que podrían ser lanzados a través de JDBC, o la herramienta de mapeo subyacente, y traduce la excepción detectada a una excepción correspondiente en su propia jerarquía. 

Nota: La jerarquía excepción se define como una subclase de org.springframework.dao.DataAccessException. Spring detecta cualquier excepción lanzada de persistencia subyacente y lo envuelve en una instancia DataAccessException. 

Spring proporciona distintas clases DAO de base para las diferentes tecnologías de acceso a datos que soporta. 

Por ejemplo, Spring ofrece HibernateDaoSupport de Hibernate, SqlMapClientDaoSupport para iBATIS JdoDaoSupport para JDO y SQL Maps. Estas clases envuelven las propiedades y métodos que se requieren en DAO comunes.

Cuando se utiliza Hibernate con Spring, las clases DAO que utilizaremos es  org.springframework.orm.hibernate3.support.HibernateDaoSupport. Esta clase envuelve una instancia de org.springframework.orm.hibernate3.HibernateTemplate, que a su vez envuelve una instancia org.hibernate.SessionFactory. Extendiendo la clase HibernateDaoSupport permite configurar todas las implementaciones DAO como Bean en el interior del contenedor IoC. Su propiedad SessionFactory se configura  a través de un contexto de Spring.

package com.packtpub.springhibernate.ch13;

import java.util.Collection;

public interface StudentDao {
 public Student getStudent(long id);
 public Collection getAllStudents();
 public Collection getGraduatedStudents();
 public Collection findStudents(String lastName);
 public void saveStudent(Student std);
 public void removeStudent(Student std);
}

Nota: HibernateException es lanzada por el incumplimiento al interactuar directamente con Hibernate. Cuando se utiliza Spring, HibernateException se tradujo a DataAccessException por cualquier falla persistencia. Ambas excepciones son sin control, por lo que no es necesario para su captura si no quieres hacerse.

Ahora veremos nuestro código utilizando Spring, heredando de la clase HibernateDaoSupport y implementando mi interfaz antes mencionada.

package com.packtpub.springhibernate.ch13;

import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import java.util.Collection;

public class HibernateStudentDao extends HibernateDaoSupport implements StudentDao 
{

 public Student getStudent(long id) {
     return (Student) getHibernateTemplate().get(Student.class, new Long(id));
 }

 public Collection getAllStudents(){
     return getHibernateTemplate().find("from Student std order by std.lastName, std.firstName");
 }

 public Collection getGraduatedStudents(){
     return getHibernateTemplate().find("from Student std where std.status=1");
 }

 public Collection findStudents(String lastName) {
     return getHibernateTemplate().find("from Student std where std.lastName like ?", lastName + "%");
 }

 public void saveStudent(Student std) {
     getHibernateTemplate().saveOrUpdate(std);
 }

 public void removeStudent(Student std) {
     getHibernateTemplate().delete(std);
 }
}

Que les puedo decir que escucharía la música de ricardo montaner, "soy feliz, soy feliz lalalala" por que redujo mi código  me quito responsabilidades para brindarle al propio framework Spring!!!.

Las operaciones persistentes implementadas por la clase DAO Como se puede ver , todos los métodos persistentes de la clase DAO utiliza el getHibernateTemplate () para acceder al objeto HibernateTemplate.  HibernateTemplate es una clase de Spring que se encarga de  trabajar con Hibernate . Esta clase expone todos los métodos Session de Hibernate , así como una variedad de otros métodos convenientes que las clases DAO pueden necesitar. 

Debido HibernateTemplate tiene metodos que no están expuestos por la interfaz Session , puede utilizar find () y findByCriteria () cuando se desea ejecutar HQL o crear un objeto Criteria. Además, se envuelve todas las excepciones subyacentes lanzadas por el método sesión con instancias del org.springframework.dao.DataAccessException.

Es recomendable siempre usar la clase HibernateDaoSupport como clase base para todas las implementaciones de Hibernate DAO , pero se puede pasar por alto esta clase y trabajar directamente con una instancia HibernateTemplate en las clases DAO . Para ello , defina una propiedad de HibernateTemplate en la clase DAO , que se inicializa y configura a través del contenedor IoC de Spring.


El siguiente código muestra la clase DAO , que ahora utiliza HibernateTemplate directamente . Tenga en cuenta que no se recomienda este método porque hace que te involucres con el objeto HibernateTemplate tanto en la clase DAO como en el contenedor IOC de Spring.


package com.packtpub.springhibernate.ch13;

import org.springframework.orm.hibernate3.HibernateTemplate;

import java.util.Collection;

public class HibernateStudentDao implements StudentDao {

HibernateTemplate hibernateTemplate;

 public Student getStudent(long id) {
  return (Student) getHibernateTemplate().get(Student.class, new Long(id));
 }

 public Collection getAllStudents(){
  return getHibernateTemplate().find("from Student std order by std.lastName, std.firstName");
 }

 public Collection getGraduatedStudents(){
  return getHibernateTemplate().find("from Student std where std.status=1");
 }

 public Collection findStudents(String lastName) {
  return getHibernateTemplate().find("from Student std where std.lastName like "+ lastName + "%");
 }

 public void saveStudent(Student std) {
  getHibernateTemplate().saveOrUpdate(std);
 }

 public void removeStudent(Student std) {
  getHibernateTemplate().delete(std);
 }

 public HibernateTemplate getHibernateTemplate() {
  return hibernateTemplate;
 }

 public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
  this.hibernateTemplate = hibernateTemplate;
 }
}

Nota: Podemos observar una propiedad de la clase que recibe claramente el Hibernatetemplate por setter lo que hace pensar que lógicamente realizaremos una inyección de este objeto desde el spring bean configuration o contenedor de beans.

Configurando Hibernate en un Spring Context


Spring proporciona la clase LocalSessionFactoryBean como una fábrica para un objeto SessionFactory. El objeto LocalSessionFactoryBean se configura como un Bean en el interior del contenedor IoC, ya sea como un  JDBC DataSource local o un Datasource compartido por JNDI.

Definiendo el datasource local para MYSQL


<bean id="dataSource" 
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/gestioncursos</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value></value>
</property>
</bean>

El siguiente punto es como definir la LocalSessionFactoryBean, para esto utiliza 3 propiedades datasourcemappingResources, y hibernateProperties.

Datasource: La forma de conexión con nuestro motor de base de datos.
MappingResources: Hace referencia a los archivo HBM de mapeo Hibernate.
HibernateProperties: Configuración de hibernate que antes estaba en el archivo de configuracion propio de hibernate y que lo podremos realizar desde aquí.


<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
<property name="mappingResources">
<list>
<value>com/packtpub/springhibernate/ch13/Student.hbm.xml</value>
<value>com/packtpub/springhibernate/ch13/Teacher.hbm.xml</value>
<value>com/packtpub/springhibernate/ch13/Course.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect
</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.max_fetch_depth">2</prop>
</props>
</property>
</bean>

Otra posibilidad es la definir nuestro LocalSessionFactoryBean con el viejo archivo de configuracion de hibernate hibernate.cfg.xml, para lo que lo definimos de la siguiente manera.



<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
<property name="configLocation">
<value>/conf/hibernate.cfg.xml</value>
</property>
</bean>

Nota: como podemos observar le indicamos al bean donde se encuentra el archivo de configuración hibernate. Esta es una opción valida para el que quiere seguir manejando la configuración hibernate centralizada en el propio archivo de configuración.

Los que nos queda ahora es definir el bean que hace referencia a nuestro Dao a quien lógicamente le inyectaremos por medio de IOC el SessionFactory para que nuestro dao disponga de la conexion.


<bean id="studentDao" 
class="com.packtpub.springhibernate.ch13.HibernateStudentDao">
<property name="sessionFactory">
<ref local="sessionFactory"/>
</property>
</bean>

Nota: Esta es la configuración DAO para una clase DAO que se extiende HibernateDaoSupport, o utiliza una propiedad SessionFactory directamente.

Cuando la clase DAO tiene una propiedad HibernateTemplate, configurar la instancia DAO de la siguiente manera:


<bean id="studentDao" 
class="com.packtpub.springhibernate.ch13.HibernateStudentDao">
<property name="hibernateTemplate">
<bean 
class="org.springframework.orm.hibernate3.HibernateTemplate">
<constructor-arg>
<ref local="sessionFactory"/>
</constructor-arg>
</bean>
</property>
</bean>

si recordamos para este caso hay una propiedad set para la hibernatetemplate por lo que en primera instancia inyectamos la hibernatetemplate por setter, y a la misma le inyectamos la sessionfactory.

Administrando las transacciones con Spring.

Si bien es un tema que ya abordamos de manera individual en anteriores entregas creo que vamos a dedicarle un momento por que estamos hablando de un escenario de integración donde por supuesto no le brindaremos el detalle que ya dedicamos en la entrada de este tema.

Una de las razones para integrar Hibernate con Spring es la gestión de transacciones. Spring proporciona una capa de abstraccion de transacción sobre la API Transaction de Hibernate, y permite operaciones persistentes para participar en las operaciones globales. 

Por otra parte, Spring ofrece demarcación de transacciones declarativa, que produce más legible y fácil de mantener el código de Java. El enfoque declarativo nos permite cambiar la estrategia de operación con facilidad, sin necesidad de cambiar el código por medio de la utilización de AOP.

Spring Transaction proporciona 2 clases para trabajar con aplicaciones Hibernate:
  • org.springframework.transaction.support.TransactionTemplate para manejarlo por programación.
  • org.springframework.transaction.interceptor.TransactionProxyFactoryBean para manejarlo de manera declarativa.

dicho esto por otro lado proporciona dos administradores de transacciones:
  • org.springframework.orm.hibernate3.HibernateTransactionManager: Es necesario utilizar esta opción cuando la aplicación incluye una fuente de datos única solo para hibernate. Esto cubre las transacciones locales ejecutados en un solo SessionFactory. Este gestor se usa comúnmente, ya que la mayoría de las aplicaciones de Hibernate trabajan con una sola base de datos.
  • org.springframework.transaction.jta.JtaTransactionManager: Este es administrador de transacciones JTA global de Spring. Úsalo cuando la aplicación participa en las operaciones globales en un entorno Java EE, en la que están implicados varios métodos SessionFactory, y las transacciones se encuentran dispersos sobre ellos.
Nota: Configure las transacciones de Spring por la creación de la instancia de transacción-manager como un Bean en el interior del contenedor IoC. La configuración de Bean depende de la estrategia de operación que esté utilizando: local o global.

Configurando transacciones locales: cuando utilicemos una única fuente de datos utilizaremos esta configuración.

<bean id="transactionManager"
class="org.springframework.orm.hibernate3.
HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>

ahora subamos el nivel utilizando un poco de AOP

Finalmente , los casos DAO se envuelven en una transacción de proxy . Las transacciones son temas transversales que no se dedican a la función de un método en particular. En cambio, están dispersos en muchos métodos de persistencia . Con la primavera , funcionalidad DAO se puede dividir en dos módulos :

Implementaciones de DAO , que realizan operaciones de persistencia .
Asesoramiento de transacciones , que define cómo se realizan las operaciones de persistencia en las transacciones.

Modularización implementaciones DAO para llevar a cabo las operaciones de persistencia y no de gestión de transacciones, evita código de gestión de transacciones repetitivo en todos los métodos de persistencia. Para solicitar asesoramiento de transacciones con los métodos de destino de las implementaciones de DAO , necesitamos objetos proxy .

Cada proxy es un objeto que intermedios entre otros dos objetos (un objeto que llama y un objeto invocado ) , y se aplica ( una transacción en nuestro caso ) a la invocación objeto. En realidad , la capa de negocio siempre trabaja con instancias de proxy en lugar de objetos de servicio. He aquí un ejemplo de la definición de proxy transacción :

<bean 
id="studentDao" class="org.springframework.transaction.
interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="target">
<ref bean="persistenceService"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>

El objeto TransactionProxyFactoryBean está configurado con tres propiedades:
TransactionManager, declarado como un grano individual, es un objeto que proporciona una API de abstracción entre el código de la aplicación y Hibernate API transacción.

Target representa el objeto al que la transacción debe ser aplicado.

transactionAttributes define la forma de seleccionar los métodos transaccionales. Por ejemplo, guardar * selecciona todos los métodos del objeto de destino que se inician con la palabra guardar.

En nuestro ejemplo, PROPAGATION_REQUIRED y readOnly especifican, respectivamente, cómo participan múltiples transacciones, y si la transacción se permite sólo leer los datos.

Nota Final: En lo personal les diría que miren la documentación de Spring sobre estos temas por que solo abordamos algunas de la posibilidades que te brinda el framework y de seguro puede cubrir sus necesidades y estoy convencido que puede mejorar tu forma de programar.

El código del proyecto lo puede descargar en codigofuente.zip.

Entendiendo la estructura de mi proyecto.


Podemos observar la estructura del proyecto. 

El codigo proporciona los 3 casos abordados.

  • El manejo de hibernate tradicional 
  • El manejo de spring + Hibernate con hibernatedaosupport .
  • El manejo de spring + Hibernate con hibernatetemplate 
Es claro que hay paquetes son comunes para todos los ejemplos.


En prima instancia el paquete hbm contiene los archivos de mapeo hibernate es un punto comun para los tres casos que abordamos en el tutorial.

La siguiente carpeta en común es com.company.modelo que contiene el pojo que desde luego utilizaran todos los ejemplos.





SoloHibernate: Es el ejemplo que muestra la manera tradicional de trabajar con hibernate y tiene los siguientes paquetes.

  • com.company.solohibernate.dao: contiene la interfaz y la clase de implementacion.
  • com.company.solohibernate.service: contiene la interfaz y la clase de implementacion.
  • com.company.solohibernate.test: contiene la clase test desde donde realizamos pruebas al modelo creado.

Nota: Desde luego cuenta con el archivo de configuración para Hibernate en la caerpeta resource y se llama hibernate.cfg.xml

HibernateDaoSupport: Es el ejemplo donde la clase dao extiene de hibernatedaosupport y tiene los siguiente paquetes.

  • com.company.springhibernate.hibernatedaosupport.dao: contiene la interfaz y la clase de implementacion.
  • com.company.springhibernate.hibernatedaosupport.service: contiene la interfaz y la clase de implementacion.
  • com.company.springhibernate.hibernatedaosupport.test: contiene la clase test desde donde realizamos pruebas al modelo creado.

Nota: Desde luego cuenta con el archivo de configuración Spring bean configuration llamado hibernatedaosupport.xml.

HibernateTemplate: Es el ejemplo donde la clase dao hace uso de manera directa de HibernateTemplate.

  • com.company.springhibernate.dao: contiene la interfaz y la clase de implementacion.
  • com.company.springhibernate.service: contiene la interfaz y la clase de implementacion.
  • com.company.springhibernate.test: contiene la clase test desde donde realizamos pruebas al modelo creado.

Nota: Desde luego cuenta con el archivo de configuración Spring bean configuration llamado hibernatetemplate.xml.

Nota Final: La gestión de transacciones lo deje fuera del ejemplo por una cuestión de tiempo y  que es un tema abordado ya en una anterior entrega con mayor detalle y por que creo que con este tutorial podrán incluir la gestión de transacciones sin problema.

Pero en lo personal recomiendo este libro de referencia para abordar el trabajo de Hibernate con Spring. El libro lo podemos descargar desde aqui. Libro.PDF

viernes, 11 de octubre de 2013

Hibernate: Dao Generic para Implementar Crud en mi Modelo

Para todo el que trabajo alguna vez con hibernate entiende que las operaciones crud dentro de un proyecto son moneda corriente y por ello es necesario utilizar esquemas que permitan evitar la reducción de código en nuestra aplicación que posibilitan un mejor mantenimiento a futuro.

La idea del articulo no es enseñar HIBERNATE sino como puedo mediante la utilización de GENERIC puedo construir dao genericos para las operaciones crud de nuestras clases sin la repetición de estas operaciones en mi proyecto, permitiendo que nos concentremos en las operaciones diferenciales o propias del modelo.

Nota de Actualidad: La posibilidad de definir DAO Genericos para nuestros proyecto es algo en la actualidad solucionado por el soporte que brinda framework como Spring para estas operaciones, pero de todas formas creo que es muy interesante la utilización de Generic para tal fin, por otro lado sera un ejemplo que utilizaremos en posteriores entregas para compararlo con un proyecto elaborado en base a SPRING para tener una percepción de como Spring nos facilita la vida.

Si bien en la actualidad podríamos aplicar una solucion HIBERNATE con Anotation soy de la vieja escuela los que arrancaron haciendo ORM con archivo de mapeo por lo que para nuestro ejemplo generaremos un proyecto usando mapping, pero para no dar vueltas mucho con las librerías, utilizaremos un proyecto simple Maven para que nos facilite la vida la gestión del proyecto.

Nuestro Modelo de Datos

Si bien podríamos utilizar varios de los motores disponibles en mercado, en esta oportunidad utilizaremos MYSQL.

Con el siguiente script prepararíamos nuestros entorno.

create database if not exists `gestioncursos`;
USE `gestioncursos`;
DROP TABLE IF EXISTS `curso`;
CREATE TABLE `curso` (
  `Id` int(11) NOT NULL AUTO_INCREMENT,
  `Nombre` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;

Observación:  En primera instancia si no existe la base de datos gestioncursos la crea, luego realiza una verificación para la tabla y crea la estructura de nuestra tabla, como podemos observar no tenemos grandes relaciones para indagar en el potencial del ORM, lo comento por que no es el objetivo del Tutorial.

Modelo de Trabajo DAO + Service Layer.

"Data Access Object" un patrón de diseño muy común que se usa para diseñar como los objetos de negocio de una aplicación deben usar los objetos encargados del acceso a los datos.



Como pueden ver los objetos del negocio acceden a los DAO a través de sus interfaces, de esta manera no dependen de una implementación en específico lo que hace que esta implementación se pueda cambiar fácilmente. También facilita las pruebas que se pueden realizar sobre esta implementación, ya que se pueden crear implementaciones falsas menos complejas que las implementaciones reales con fines de prueba.

Por ultimo la exposicion de los DAO para ser consumidos se realiza por medio de la capa de servicio que sera la encargada de mediante las implementaciones la utilización de 1 o N objetos DAO para alcanzar su objetivo funcional.

Nota: Posibilita una arquitectura desacoplada que permite un modelo escalable y facilita que las transacciones puedan ser tratadas de manera centralizada por tener un punto de exposición por medio de las capas de servicio.

Entendiendo mi Proyecto 

En primera instancia este sera nuestro proyecto sobre el que vamos a trabajar.

DAO: paquete donde podremos encontrar las interfaces y clases que interaccionan con la base de datos.

Exception: paquete que tendrá clases de excepcion personalizadas.

Generic: paquete que contendra las clases que van a definir las implementaciones dao genericas para las operaciones crud.

mapping: donde encontraremos los archivos de mappeo con nuestras tablas.

Entity: paquete que contendra los pojos de nuestro proyecto.

Service: paquete que tendra las interfaces y clases de los servicios que exponen las necesidades funcionales.

Resource: directorio donde tendremos el archivo de configuracion de Hibernate.

Test: tendremos las clases principales con las que podremos realizar pruebas al proyecto.

pom.xml: nuestro archivo de configuración maven para dependencias.










Definiendo la base de trabajo con Hibernate

pom.xml : Nuestro archivo de dependencias 


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.company</groupId>
  <artifactId>EstudiandoHibernate</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <dependencies>    
    <dependency>
   <groupId>javassist</groupId>
   <artifactId>javassist</artifactId>
   <version>3.12.1.GA</version>
  </dependency>       
    <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.17</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>        
  <dependency>
   <groupId>jstl</groupId>
   <artifactId>jstl</artifactId>
   <version>1.2</version>
  </dependency>
  <dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.1.1</version>
  </dependency>
  <dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-log4j12</artifactId>
   <version>1.6.1</version>
  </dependency>  
        <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-core</artifactId>
   <version>3.6.3.Final</version>
  </dependency>
  </dependencies>
</project>

Nota: que en esta ocacion no tiene grandes cosas, solo incluimos la libreria de hibernate y el conector para MYSQL.

com.company.Modelo.Entity.Curso: definimos nuestra clases pojo asociada a la tabla que vamos a mapear en el ejemplo.


package com.company.Modelo.Entity;

public class Curso implements java.io.Serializable {

 private Integer id;
 private String nombre;

 public Curso() {
 }

 public Curso(String nombre) {
  this.nombre = nombre;
 }

 public Integer getId() {
  return this.id;
 }

 public void setId(Integer id) {
  this.id = id;
 }

 public String getNombre() {
  return this.nombre;
 }

 public void setNombre(String nombre) {
  this.nombre = nombre;
 }

}


Nota: Si observamos es una clase con propiedades que tienen correspondencia con los campos de la tabla que ya mencionamos, un detalle la clase debe ser serializable.

com.company.mapping.Curso.hbm.xml: Nuestro archivo de mapeo Hibernate.


<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 13-sep-2013 15:59:20 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
    <class name="com.company.Modelo.Entity.Curso" table="curso" catalog="gestioncursos">
        <id name="id" type="java.lang.Integer">
            <column name="Id" />
            <generator class="identity" />
        </id>
        <property name="nombre" type="string">
            <column name="Nombre" length="100" />
        </property>
    </class>
</hibernate-mapping>

Nota: En esta oportunidad el archivo no presenta grandes complicaciones, en primera instancia hace referencia a la clase que queremos mapear, define la tabla y la base de datos, por supuesto sin dejar de lado que define la estructura de las propiedades que vamos a mapear.

src/main/resource/hibernate.cfg.xml:  nuestro archivo de configuración Hibernate.


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
  "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
  "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/gestioncursos</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
        <property name="show_sql">true</property>
        <mapping resource="com/company/mapping/Curso.hbm.xml"></mapping>
    </session-factory>
</hibernate-configuration>

Nota: Este archivo loco es el mas importante dado que en este proporciono los datos de conexión y  hace referencia al los archivos hbm que definimos para mapear tabla con clases.

com.company.util.HibernateUtil: definimos nuestro administrador de SessionFactory.


package com.company.util;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {
  
    private static final SessionFactory sessionFactory = buildSessionFactory();
 
    private static SessionFactory buildSessionFactory() {
        try {
            // Create the SessionFactory from hibernate.cfg.xml
            return new Configuration().configure().buildSessionFactory();
        }
        catch (Throwable ex) {
            // Make sure you log the exception, as it might be swallowed
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }
 
    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
 
    public static void shutdown() {
     // Close caches and connection pools
     getSessionFactory().close();
    }
 
}

Nota: Crea nuestra SessionFactory desde el archivo de configuracion Hibernate que definimos.

Meditando el Codigo 

Si fuera un loco de seguro con esta parte del código podría disparar diferentes operaciones sobre la tabla con Hibernate 

Podría de seguro agregar un registro con un código similar a este.


public static void main( String[] args )
    {
        System.out.println("Maven + Hibernate + MySQL");
        Session session = HibernateUtil.getSessionFactory().openSession();
        session.beginTransaction();
        Curso curso = new Curso(); 
        curso.setId(4715);
        curso.setNombre("Diego Herrera"); 
        session.save(curso);
        session.getTransaction().commit();
    }

Podría buscar un registro y mapearlo a un objeto sin problema 


public static void main( String[] args )
    {

 Session session = HibernateUtil.getSessionFactory().openSession();
 session.beginTransaction();
 Curso curso = (Curso)session.load(Curso.class, 4715);
 session.getTransaction().commit();
        System.Out.Pringln(curso.getNombre());

    }

Ni hablar de que podría realizar una búsqueda y eliminación.


public static void main( String[] args )
    {

 Session session = HibernateUtil.getSessionFactory().openSession();
 session.beginTransaction();
 Curso curso = (Curso)session.load(Curso.class, 4715);
        session.delete(curso);
 session.getTransaction().commit();
    }

pero si realmente seria un loco !!!,  me pondría a generar implementaciones de estas operaciones CRUD para todas las clases de mi modelo de datos, dado que todas mis clases necesitan insertar, eliminar, actualizar , buscar. ..etc, de seguro alcanzaría una capa de negocio funcional después de haber picado muchas lineas para todo mi modelo, luego me miraría el código alguien que tenga un par de hola mundos hecho, me diría flaco y todo este codigo al pedo ? esto no se puede mantener de una manera facil, es repetitivo, no es escalable. Yo defendiendo las horas al pedo que dedique le diria, bueno si tienes una mejor forma de hacerlo tirame la data, el tipo pregunta  para que picaste tanto codigo, cuando todas estas generalidades se pueden propagar aplicando un par de cosas que juan java nos dejo para hacerlo.

Entendiendo Generic

En primera instancia existen momento en la vida de todo programadores que va necesitar la utilización de código que realice cierta función independiente del dato primitivo con el que tenga que hacerlo y aqui es donde llega GENERIC.

Ejemplo si definimos una clase Cajon, si observamos tiene una propiedad de tipo string con la cual trabaja en el get y set.

public class Cajón {
   private String t;
   public void set (String t){
   this.t = t;
   }
   public String get( ){
   return t;
   }
}

Ahora esto para usarlo, lo llamaríamos una cosa asi. 

Cajon micajon=new Cajon();
micajon.set("Prueba");
System.Out.println(micajon.get());

Pero ahora mi necesidad cambio y necesito que esta clase reciba no String sino un Integer y de seguro que si pensara que esto es magia intentaría 

Cajon micajon=new Cajon();
micajon.set(1979);
System.Out.println(micajon.get());

Nota: Estoy seguro la clase me dira, loco que haces me estas pasando un tipo de dato diferente al que puedo recibir.

Ahora lo que vamos a buscar es generalizar la clase realizando algunas modificaciones,  primero el tipo del objeto con el que trabajo sera generico permitiendo ser definido al momento de la definición.

public class Cajón {
   private T t;

   public void set (T t){
   this.t = t;
   }
   public T get( ){
   return t;
   }
}

Claroooo !! al momento de definir el tipo para un objeto, le estoy indicando el tipo con el que trabajara mi clase genérica logrando que no se enoje cuando le quiero meter el tipo de dato que se me cante. 

Cajon micajon=new Cajon();
micajon.set(1979);
System.Out.println(micajon.get());

Por convención, los nombres de los tipos de parámetros se escriben como una letra mayúscula, y los más habituales son :
  • T, de tipo
  • E, de elemento, muy usado con la Java Collection Framework
  • N, de número (class Number)
  • K, de key
  • V, de value, estas dos últimas utilizadas sobre todo en mapas (Map)

Pero ahora si yo estoy realizando operaciones numericas en mi implementacion y definiera un subtipo como string de seguro no le va gustar que quiera sumar 12 + "hola mundo" y para eso es posible en Generic definir una forma de limitar la definicion de los subtipos con los cuales podemos trabajar, es decir a una clase que realiza operaciones numericas podriamos limitarlo a que la parametrizacion solo se pueda realizar con tipos numericos.

public class Divisor <t extends number>{
  private T numero;  
....
....
....
...
}

Nota: cuando generemos la definición para instancia la clase por ejemplo 

Divisor<string> micalculadora= Divisor<string>();

Nota: Nos va indicar que no puedo parametrizar el tipo de dato que estoy intentando manipular.

Si quisiéramos obligar que el tipo implemente varias interfaces distintas, o que extienda una clase e implemente una o varias interfaces, tendríamos que separar estos tipos con el caracter &:
public class Divisor {
  private T numero;  
....
....
....
...
}

Otra cosa que podemos hacer es utilizar más de un parámetro de tipo, separando los tipos con comas. Supongamos por ejemplo que quisieramos crear una clase que imprimiera la suma de dos números. Escribiríamos algo como esto:

public class Sumador <t1 extends number, t2 extends number>{
  private T1 numero1;
  private T2 numero2;

 ....
}

Quiero aclarar que todo esto es aplicable a la definicion de Interfaces como podria ser el siguiente ejemplo 

public interface MiColeccion <T>{
  public void anyadir(T objeto);
  public T obtener();
  public void ordenar();
}

Cuales son los objetivos de GENERIC 

  • Reducir el numero de conversiones (cast) en el programa, así reduciremos el numero de errores en el programa.
  • Generamos un programa mucho ams seguro
  • Construcción de tipos abstractos reutilizables
  • Disminución de comprobación de tipos en tiempo de ejecución
  • Menos costoso ya que es mejor corregir errores mientras es compilado que mientras esta siendo ejecutado
  • Problemas de diseño, que anteriormente tenían que ser abordados de forma indirecta dando lugar a soluciones poco satisfactorias, puedan ser resueltos mediante soluciones de diseño simples y elegantes con el uso de la genericidad.

Construyendo Generic DAO en nuestro ejemplo

com.company.generic.GenericDao.java: definimos nuestra interfaz genérica 


package com.company.generic;

import java.io.Serializable;

public interface GenericDao<Entity, PK extends Serializable> {
     void Guardar(Entity t);
  void Actualizar(Entity t);
  Entity Buscar(PK id);
  void Eliminar(Entity t);
}

Nota: Como podemos observar definimos los métodos para Guardar, Actualizar , Buscar y Eliminar utilizando GENERIC donde en primera instancia definimos los subtipos, llamo Entity al subtipo que hara referencia a las clases de persistencia, por otro lado defino un subtipo PK para clave.

com.company.generic.GenericDaoImpl.java: es claro que sera la clase de nuestras implementaciones genéricas 


package com.company.generic;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;

import org.hibernate.HibernateException;
import org.hibernate.Session;

import com.company.exception.UnableToSaveException;



public class GenericDaoImpl<Entity, K extends Serializable> implements GenericDao<Entity, K> {

 public Class<Entity> domainClass = getDomainClass();
 private Session session;
 
 protected Class getDomainClass() {
   if (domainClass == null) {
   ParameterizedType thisType = (ParameterizedType) getClass()
     .getGenericSuperclass();
   domainClass = (Class) thisType.getActualTypeArguments()[0];
   }
  return domainClass;
 }
 
 private Session getHibernateTemplate() {
  session = com.company.util.HibernateUtil.getSessionFactory().openSession();
  session.beginTransaction();
  return session;
 }
 
 public Entity Buscar(K id) {
  Entity returnValue = (Entity) getHibernateTemplate().load(domainClass, id);
  session.getTransaction().commit();
  return returnValue;
 }
 
 public void Actualizar(Entity t) throws UnableToSaveException {
  try {
   getHibernateTemplate().update(t);
    session.getTransaction().commit();
   } catch (HibernateException e) {
    throw new UnableToSaveException(e);
  }
 }
 
 public void Guardar(Entity t) throws UnableToSaveException {
   try {
    getHibernateTemplate().save(t);
    session.getTransaction().commit();
   } catch (HibernateException e) {
    throw new UnableToSaveException(e);
   }
 }
 public void Eliminar(Entity t) {
   getHibernateTemplate().delete(t);
   session.getTransaction().commit();
 }
}

Esta es la clase donde nos vamos a dedicar un par de minutos por que en realidad tampoco hay grandes complicaciones.

primero tenemos que entender la cabecera 

public class GenericDaoImpl<Entity, K extends Serializable> implements GenericDao<Entity, K> {

Pero que es todo esto ? no es complicado primero definimos el subtipo de la clase propia para lo que lo indicamos GenericDaoImpl y por otro lado implementa los metodos de una interfaz  a la que debemos pasar los subtipos dependientes  que logicamente en nuestros caso seran los que reciba la clase por eso es que le pasamos los esperados por la clase GenericDao.

Aqui es la parte complicada donde el programador ve el codigo y dice no hay chance repito codigo a lo loco que voy a ser felizzz... 

Naaaa tranqui cuando definimos los subtipos para la clase, declaramos los parametros Entity y K  el primero nos indica subtipo para la clase de persistencia que utilizaremos (tendremos un modelo por ejemplo que trabaje en la persistencia de una clase persona, ventas , articulos , bueno creo que ya nos podemos dar cuenta que hacemos referencia a las clases que vamos a persistir) y el segundo define un subtipo que lo usaremos como clave de busqueda (logico por que las claves de busqueda no siempre tiene el mismo tipo de dato dependiendo de la tabla que vamos a mapear podriamos tener una clase donde la key es de tipo string o tener una clase donde la key es de tipo string).

La otra problemática que encontramos es que en algunos casos es necesario tener acceso al tipo de la clase genérica en el contexto de ejecución,  como entenderán solo tendremos esta información en tiempos de ejecución con la utilización de reflection.

Obtengo referencia a la clase parametrizada en tiempos de ejecucion donde utiliza un metodo para este fin.

 public Class domainClass = getDomainClass();

protected Class getDomainClass() {
if (domainClass == null) {
ParameterizedType thisType = (ParameterizedType) getClass()
  .getGenericSuperclass();
domainClass = (Class) thisType.getActualTypeArguments()[0];
}
return domainClass;
}


Primero obtengo la clase parametrizada  y la lista de subtipos

ParameterizedType thisType = (ParameterizedType) getClass()
  .getGenericSuperclass();

Me quedo con el primero por que se que es una clase simplemente por como defini la clase en la que estamos trabajando recuerdan primero Entity y luego K.

domainClass = (Class) thisType.getActualTypeArguments()[0];

El siguiente metodo es getHibernateTemplate() que por supuesto es uno de los métodos mas importantes dado que es el que me devuelve la session de hibernate e inicia la transacción.

private Session getHibernateTemplate() {
session = com.company.util.HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
return session;
}

El metodo Buscar generic define un tipo al parametro de entrada ID que en nuestro caso es para buscar por hibernate utilizando la clase subtipo utilizada en tiempo de ejecucion domainclass y la key.

public Entity Buscar(K id) {
Entity returnValue = (Entity) getHibernateTemplate().load(domainClass, id);
session.getTransaction().commit();
return returnValue;
}

Para los métodos Actualizar, Guardar y Eliminar la mecánica es similar, maneja las excepciones de manera personalizada y utiliza los métodos update, save, delete que proporciona hibernate recibiendo el tipo generico T como parametro, posterior realiza el commit de la transaccion si la operacion es exitosa.

Metodo Actualizar

public void Actualizar(Entity t) throws UnableToSaveException {
try {
getHibernateTemplate().update(t);
session.getTransaction().commit();
} catch (HibernateException e) {
throw new UnableToSaveException(e);
}
}

Método Guardar

public void Guardar(Entity t) throws UnableToSaveException {
try {
getHibernateTemplate().save(t);
session.getTransaction().commit();
} catch (HibernateException e) {
throw new UnableToSaveException(e);
}
}

Método Eliminar

public void Eliminar(Entity t) {
getHibernateTemplate().delete(t);
session.getTransaction().commit();
}

Trabajando nuestra Layer Dao

Aquí trabajaremos en nuestro negocio definiendo funcionalidades que no estan asociadas a CRUD dado que ya lo estoy solucionando con GENERIC.

com.company.dao.ICursoDAO.java: Definición de nuestra interfaz dao 


package com.company.dao;

import java.util.List;

import com.company.Modelo.Entity.Curso;
import com.company.generic.GenericDao;

public interface ICursoDAO extends GenericDao<Curso, Long> {
 public List<Curso> ListarCursos();
}

Nota: ahora lo que podemos observar que en la definición hereda de la interfaz genérica a la que le pasamos los subtipos el primero es clase Curso y el segundo el tipo Long.


com.company.dao.CursoDAO.java: La implementacion de nuestro Dao para cursos.


package com.company.dao;

import java.util.ArrayList;
import java.util.List;


import org.hibernate.Session;
import org.hibernate.Transaction;

import com.company.Modelo.Entity.Curso;
import com.company.generic.GenericDaoImpl;
import com.company.util.HibernateUtil;


public class CursoDAO extends GenericDaoImpl<Curso, Long> implements ICursoDAO {

 public List<Curso> ListarCursos() {
        List<Curso> users = new ArrayList<Curso>();
        Transaction trns = null;
        Session session = HibernateUtil.getSessionFactory().openSession();
        try {
            trns = session.beginTransaction();
            users = session.createQuery("from Curso").list();
        } catch (RuntimeException e) {
            e.printStackTrace();
        } finally {
            session.flush();
            session.close();
        }
        return users;
    }  

}

Nota: En el código podemos observar que en primera instancia le proporcionamos herencia sobre la clase dao generica  a la que logicamente le proporcionamos los 2 subtipos con los que queremos que trabaje, en primera instancia la clase Curso y como segundo subtipo el tipo Long, por otro lado le pedimos que implemente la interfaz CursoDao en la que definimos un nuevo metodo Listar Cursos que por supuesto presenta codigo el cual utiliza hibernate para obtener todo los cursos de la tabla. Que quiere decir que nuestra clase dao ahora tendra todos los metodos accesibles definimos en la dao generica mas lo que defina para mi dao.

De esta forma puedo dejar disponible la implementacion CRUD para todas las clases que pueda generar en mi modelo de datos.

Con esto logro poder concentrarme en implementaciones de codigo que nos son genericas por que las Crud las proporcionaremos a cada clase Dao por herencia.

Trabajando nuestro Layer Service

Aqui es donde trabajaremos en nuestra capa de servicio la cual utilizara 1 o N clases Dao de nuestro negocio para responder a una funcionalidad especifica.

com.company.service.ICursoService.java: defino la interfaz para mi clase de servicio.


package com.company.service;

import java.util.List;

import com.company.Modelo.Entity.Curso;



public interface ICursoService {
     public void GuardarCurso(Curso cursoacargar);
     public void ActualizarCurso(Curso cursoacargar);
     public void EliminarCurso(Curso micurso);
     public List<Curso> ListarCursos();
}

Nota: Defino solo los métodos para los cuales implementare codigo.

com.company.service.CursoService.java: Implementacion de la clase de servicio para curso.


package com.company.service;

import java.util.List;

import com.company.Modelo.Entity.Curso;
import com.company.dao.CursoDAO;
import com.company.dao.ICursoDAO;



public class CursoService implements ICursoService {

 private ICursoDAO CursosDao; 
 
 public CursoService() {
    CursosDao = new CursoDAO();
 }

 public void GuardarCurso(Curso cursoacargar) {
  CursosDao.Guardar(cursoacargar); 
 }

 public void ActualizarCurso(Curso cursoacargar) {
  CursosDao.Actualizar(cursoacargar); 
 }

 public void EliminarCurso(Curso micurso) {
  CursosDao.Eliminar(micurso);  
 }

 public List<Curso> ListarCursos() {
  return CursosDao.ListarCursos();
 }

 

}

Nota: a nivel código lo lógico es que esta clase de servicio utilizara uno o n dao disponibles en la capa de negocio para obtener las funcionalidades que se expondrán para ser consumidas.


Probando nuestro Trabajo

Vamos a explicar nuestra clase de prueba del proyecto.

com.company.test.CursoServiceTest.java:  Clase test del proyecto.


package com.company.test;

import com.company.Modelo.Entity.Curso;
import com.company.service.CursoService;


public class CursoServiceTest {

 /**
  * @param args
  */
 public static void main(String[] args) {
 
  
        //ICursoDAO objdao= new CursoDAO();
  CursoService objdao=new CursoService();
  
        Curso obj = new Curso();
        System.out.println("Guardando");
        obj.setNombre("Curso de Tecnologia J2EE + Hibernate + struts 2");
        objdao.GuardarCurso(obj);        
        System.out.println("Listando");
        for (Curso iter : objdao.ListarCursos()) {
            System.out.println(iter.getId().toString() + " - " + iter.getNombre());
        }
        System.out.println("Actualizando");
        obj.setNombre("Curso de Tecnologia J2EE MODIFICADO");
        objdao.ActualizarCurso(obj);
        System.out.println("Listando");
        for (Curso iter : objdao.ListarCursos()) {
            System.out.println(iter.getId().toString() + " - " + iter.getNombre());
        }
        System.out.println("Eliminando");
        objdao.EliminarCurso(obj);
        System.out.println("Listando");
        for (Curso iter : objdao.ListarCursos()) {
            System.out.println(iter.getId().toString() + " - " + iter.getNombre());
        }
        

 }

}

Nota: A nivel código solo instancia la clase servicio y comienzo la llamada de todos los métodos disponibles.

Resultado de la ejecución 

Realiza las operaciones CRUD + los nuevos métodos implementados en Dao.




Nota Final

De seguro si me preguntan hoy como solucionaría la generalidad dao en un proyecto y de seguro lo realizaría con spring con todo el soporte que nos proporciona para tal fin y con la potencia en integración con ORM algo que en próximas entregas abordaremos para contar con un punto de comparación. 

La idea no era mostrar Hibernate sino la utilización de Generic para generar código de alto rendimiento que evite tareas repetitivas.

El codigo del proyecto lo puede descargar desde aqui. codigofuente.zip