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





8 comentarios:

  1. Muy buena tu interpretación reutilizando lo generico de DAOs-Generic, Entidades con hibernate o jpa, Saludos !!!

    ResponderEliminar
  2. buenas tardes, antes que nada muchas gracias por compartir tus conocimientos esta genial tus tutos, debo decirte que me estoy iniciando y aun no entiendo algunas cosas, tengo una duda con respecto al método buscar, porque solo lo declaras en generic y en la capa de servicios no lo consumes? y como lo consumes y desde donde?, gracias

    ResponderEliminar
  3. implemente tu modelo en un proyecto y me faltaba esa parte, ya lo resolvi, lo que pasa que en la capa de servicios no la estaba implementado correctamente, lo único que tenia que hacer era definir la clase a la que pertenece para que me devuelva el objeto encontrado, jiji no se si me explique correctamente.

    ResponderEliminar
  4. Solo esto, en la implementación del generic y hablando del método buecar, tuve que cambiar el método load por get ya que no funciono y quedo asi mi código.
    returnValue = (Entity) getHibernateTemplate().get(domainclass, Id);

    ResponderEliminar
  5. BUen post, y gracias, pero ya no está disponible el proyecto para descargar

    ResponderEliminar