sábado, 7 de diciembre de 2013

- Hibernate 4 sin maven

En este tutorial describiré los pasos necesarios para realizar un proyecto Hibernate sin la herramienta de gestión y construcción MAVEN, lo cual quiere decir que obtendremos nosotros mismos las librerías necesarias para nuestros proyecto así como configuraremos nuestro entorno de desarrollo para que todos los componentes sean tomadas correctamente en nuestra aplicación.

Introducción a Hibernate

Hibernate es un framework del tipo ORM lo cual quiere decir Object Relational Mapping que en castellano significa Mapeo Objeto Relacional; esto quiere decir que Hibernate nos permite en pocas palabras convertir nuestras tablas de base de datos en objetos JAVA (para este caso J2EE) para así poder modificarlas y usarlas a nuestro antojo (persistencia de datos). Los componentes están definidos de la siguiente manera:

Figura 1.1






  1. Session Interface.- Esta es la interface básica para una aplicación en hibernate. Las instancias que se generan de esta interface no son nada costosas y muy ligeras a la hora de crear y destruir en la aplicación
  2. SessionFactory Interface.- Esta interface nos devuelve el objeto session para usarla en la aplicación. Normalmente es necesario un objeto de este tipo para toda la aplicación la cual sera compartida en todos los entornos de nuestra aplicación.
  3. Configuration Interface.- Esta interface es usada para configurar y arrancar Hibernate en nuestra aplicación. La instancia de este objeto es usada en la aplicación para especificar los parámetros y mapeos respectivos.
  4. Transaction Interface.- Esta es una interface opcional que básicamente abstrae el código para cualquier tipo de transacción como puede ser JDBC o JTA.
  5. Query y Criteria Interface.- Nos permite desarrollar queries y tomar el control del flujo al ejecutar la query.

Proyecto en Hibernate

Las herramientas con las cuales trabajaremos son:
  1. Eclipse Kepler
  2. Apache Tomcat 7.0
  3. MySQL Server 5.0
  4. Librerias Hibernate
  5. Mysql-connector-java
Como indiqué anteriormente no explicaré como se realiza la instalación del Eclipse con su respectivo JDK ni el Tomcat, ni la Base de datos MySQL por lo que se asume que ya tenéis el entorno instalado.

Antes que nada crearemos en MySQL nuestra base de datos así como la tabla necesaria para poder trabajar con ella:

CREATE DATABASE BD_TUTORIAL;
USE BD_TUTORIAL;
grant all on BD_TUTORIAL.* to 'admin'@'localhost' identified by 'test';

CREATE TABLE COMPONENTE
(   id        INT PRIMARY KEY AUTO_INCREMENT,
    nombre    VARCHAR(30),
    version   VARCHAR(30),
    tipo      VARCHAR(15),
    extension VARCHAR(30),
    creado    TIMESTAMP DEFAULT NOW()
);

Primero que todo creamos un proyecto JAVA en el eclipse: File -> New -> Java Project
y el siguiente nombre al proyecto ProyectoHibernateTutorial y a continuación Finish para crear el proyecto tal como podemos verlo en la imagen:

Figura 1.2


A continuación creamos en ProyectoHibernateTutorial/src los siguientes paquetes:
  1. com.victor.elliott.humala.aplicacion
  2. com.victor.elliott.humala.dao
  3. com.victor.elliott.humala.formulario
  4. com.victor.elliott.humala.servicios
  5. com.victor.elliott.humala.util
Luego creamos las siguientes clases en los paquetes respectivos
  1. com.victor.elliott.humala.aplicacion  ->  Aplicacion.java
  2. com.victor.elliott.humala.dao  ->  ComponenteDAO.java
  3. com.victor.elliott.humala.dao-> ComponenteDAOImpl.java
  4. com.victor.elliott.humala.formulario -> ComponenteForm.java
  5. com.victor.elliott.humala.servicios -> ComponenteService.java
  6. com.victor.elliott.humala.servicios -> ComponenteServiceImpl.java
  7. com.victor.elliott.humala.util -> HibernateUtil.java
Una vez creadas las clases creamos el xml respectivo en 

ProyectoHibernateTutorial/src

.
  1. hibernate.cfg.xml
Una vez creadas nuestras clases y componentes que usaremos, la estructura de proyecto debe de quedar de la siguiente manera:

Figura 1.3


Ahora que ya tenemos casi todos los elementos necesarios para empezar a desarrollar nuestro proyecto haremos la configuración con Hibernate para empezar a usar el framework sin errores y para esto primero bajemos las librerías necesarias que os la podéis descargar desde el siguiente link:

  1. Librerias Hibernate
Una vez descargado lo descomprimimos y lo añadiremos a nuestro proyecto de la siguiente manera:
Click derecho en ProyectoHibernateTutorial -> Properties -> Java Build Path -> Libraries -> Add Library -> User Library -> Next -> User Libraries -> New  y a continuación escriben Hibernate y OK que sera el nombre de la librería, inmediatamente sin salir de la ventana presionamos Add External JARs vamos a la carpeta donde descomprimimos las librerías, seleccionamos todos los jars presionamos Abrir y finalmente OK. Saliendo de esa ventana pondremos Finish y ya tendremos nuestras librerías de Hibernate agregadas al proyecto tal como se muestra

Figura 1.4


Una vez agregadas las librerías Hibernate agregamos nuestro conector que este caso es del MySql 
ProyectoHibernateTutorial -> Properties -> Java Build Path -> Libraries -> Add Library -> Add External JAR's y agregamos en este caso: mysql-connector-java-5.0.8-bin.jar como se muestra en la figura:

Figura 1.5



Ahora si podemos empezar a codificar nuestro proyecto. 

Antes que nada modificamos nuestro fichero de configuración para Hibernate que es:
/ProyectoHibernateTutorial/src/hibernate.cfg.xml de la siguiente manera:


<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
 "-//Hibernate/Hibernate Configuration DTD//EN"
 "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
 <session-factory>
        <!-- Database connection settings -->
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost/BD_TUTORIAL</property>
        <property name="connection.username">admin</property>
        <property name="connection.password">test</property>

        <!-- JDBC connection pool (use the built-in) -->
        <property name="connection.pool_size">1</property>

        <!-- SQL dialect -->
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>

        <!-- Enable Hibernate's automatic session context management -->
        <property name="current_session_context_class">thread</property>

        <!-- Disable the second-level cache -->
        <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>

        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>

        <!-- Drop and re-create the database schema on startup -->
        <property name="hbm2ddl.auto">update</property> 
        
   <mapping class="com.victor.elliott.humala.formulario.ComponenteForm" />
 </session-factory>

</hibernate-configuration>

En esta configuración lo que estamos definiendo principalmente es:

  1. El driver (conector) que usaremos: com.mysql.jdbc.Driver
  2. La base de datos: jdbc:mysql://localhost/BD_TUTORIAL
  3. El usuario y contraseña que dimos permisos al inicio del tutorial:(admin, test)
  4. El dialecto que usaremos que en este caso es del MySQL: org.hibernate.dialect.MySQLDialect
  5. La clase entidad que representará las tabla de la base de datos que definimos al inicio de la configuración: com.victor.elliott.humala.formulario.ComponenteForm

Una vez desarrollado nuestro fichero de configuración modificamos com.victor.elliott.humala.util.HibernateUtil.java tal como sigue:

package com.victor.elliott.humala.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;
    }
}

Esta clase simplemente nos instanciará la SessionFactory que es la encargada de ejecutar las querys necesarias de una base de datos respectiva, si tuvieramos que usar mas de una Base de datos tendriamos que definir otra SessionFactory pero en este caso solo necesitamos una.

Ahora modificamos com.victor.elliott.humala.formulario.ComponenteForm.java de la siguiente manera:

package com.victor.elliott.humala.formulario;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="componente")
public class ComponenteForm {
 
 @Id
 @Column(name="id")
 @GeneratedValue
 private Integer id;
 
 @Column(name="nombre")
 private String nombre;
 
 @Column(name="version")
 private String version;
 
 @Column(name="tipo")
 private String tipo;
 
 @Column(name="extension")
 private String extension;
 
 public Integer getId() {
  return id;
 }
 public void setId(Integer id) {
  this.id = id;
 }
 public String getNombre() {
  return nombre;
 }
 public void setNombre(String nombre) {
  this.nombre = nombre;
 }
 public String getVersion() {
  return version;
 }
 public void setVersion(String version) {
  this.version = version;
 }
 public String getTipo() {
  return tipo;
 }
 public void setTipo(String tipo) {
  this.tipo = tipo;
 }
 public String getExtension() {
  return extension;
 }
 public void setExtension(String extension) {
  this.extension = extension;
 }
 
 public String toString(){
  
  return "Componente: "+this.id+", "+this.nombre+", "+this.version+", "+this.extension;
 }
}

Ahora describimos rápidamente el formulario:

  1. Los import son las clases necesarias para mapear nuestra tabla Componente con la clase ComponenteForm. Tener en cuenta que esto lo hacemos para ahorrarnos el tener que crear un xml nuevo que se llame: componente.hbm.xml y mapearlo en ese xml. Esto se llama Hibernate con anotaciones los cuales nos facilitará la configuración del mapeo para tenerlo ya en nuestra clase respectiva y poder controlarla en el entorno del objeto.
  2. @Entity nos indica que nuestra clase es una entidad que se encargará de mapear con una tabla de la base de datos.
  3. @Table(name="componente") nos indica que la clase representará nuestra tabla componente
  4. @Id, @Column(name="id"), @GeneratedValue nos da las características del atributo id.
  5. Finalmente @Column(name="xxxxx") nos indica como estarán representadas los atributos con las respectivas columnas en la Tabla.
El siguiente paso es modificar com.victor.elliott.humala.dao.ComponenteDAO.java tal como sigue:

package com.victor.elliott.humala.dao;

import java.util.List;
import com.victor.elliott.humala.formulario.ComponenteForm;

public interface ComponenteDAO {
  public void agregarComponente(ComponenteForm componente);
  public List<ComponenteForm> mostrarComponentes();
  public void eliminarComponente(Integer id);
  public void actualizarComponente(ComponenteForm componente);
  public ComponenteForm mostrarComponente(int id);
}

Esta clase es una interface que nos indica las acciones que realizaremos en nuestra tabla componentes a partir de su entidad mapeada respectiva que este caso es ComponenteForm. En consiguiente implementamos la interface modificando la siguiente clase:
com.victor.elliott.humala.dao.ComponenteDAOImpl.java tal como sigue:

package com.victor.elliott.humala.dao;

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

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.victor.elliott.humala.formulario.ComponenteForm;
import com.victor.elliott.humala.util.HibernateUtil;


public class ComponenteDAOImpl implements ComponenteDAO{
 public void agregarComponente(ComponenteForm componente) {
  
        Transaction trns = null;
        Session session = HibernateUtil.getSessionFactory().openSession();
        session.save(componente);
        
        try {
            trns = session.beginTransaction();
            session.save(componente);
            session.getTransaction().commit();
        } catch (RuntimeException e) {
            if (trns != null) {
                trns.rollback();
            }
            e.printStackTrace();
        } finally {
            session.flush();
            session.close();
        }
    }
 public List<ComponenteForm> mostrarComponentes() {
        List<ComponenteForm> componentes = new ArrayList<ComponenteForm>();
        Transaction trns = null;
        Session session = HibernateUtil.getSessionFactory().openSession();
        try {
            trns = session.beginTransaction();
            componentes = session.createQuery("from ComponenteForm").list();
        } catch (RuntimeException e) {
            e.printStackTrace();
        } finally {
            session.flush();
            session.close();
        }
        
        return componentes;  
 }
 public void eliminarComponente(Integer id) {
        Transaction trns = null;
        Session session = HibernateUtil.getSessionFactory().openSession();
        try {
            trns = session.beginTransaction();
            ComponenteForm componente = (ComponenteForm) session.load(ComponenteForm.class, new Integer(id));
            session.delete(componente);
            session.getTransaction().commit();
        } catch (RuntimeException e) {
            if (trns != null) {
                trns.rollback();
            }
            e.printStackTrace();
        } finally {
            session.flush();
            session.close();
        }
 }
 
    public void actualizarComponente(ComponenteForm componente) {
        Transaction trns = null;
        Session session = HibernateUtil.getSessionFactory().openSession();
        try {
            trns = session.beginTransaction();
            session.update(componente);
            session.getTransaction().commit();
        } catch (RuntimeException e) {
            if (trns != null) {
                trns.rollback();
            }
            e.printStackTrace();
        } finally {
            session.flush();
            session.close();
        }
    } 
    
    public ComponenteForm mostrarComponente(int id) {
     ComponenteForm componente = null;
        Transaction trns = null;
        Session session = HibernateUtil.getSessionFactory().openSession();
        try {
            trns = session.beginTransaction();
            String queryString = "from ComponenteForm where id = :id";
            Query query = session.createQuery(queryString);
            query.setInteger("id", id);
            componente = (ComponenteForm) query.uniqueResult();
        } catch (RuntimeException e) {
            e.printStackTrace();
        } finally {
            session.flush();
            session.close();
        }
        return componente;
    }    
}


  1. En esta implementación básicamente desarrollamos el CRUD de la tabla 'componente' .
  2. Si os percatáis lo mas importante aquí es tener claro que es necesario abrir y cerrar la sesión (el uso exclusivo de la tabla) antes de cualquier acción:  openSession() y session.close() respectivamente.
  3. Posteriormente las acciones de agregar, actualizar, eliminar están definidas en los métodos del objeto session de la siguiente forma respectivamente: session.save(componente);  session.update(componente); session.delete(componente);
  4. Hay que percatarnos de que en las querys que realicemos no tenemos que realizarlas contra las tablas de Base de datos si no contra la entidad que la representa en este caso la tabla componente esta representada por la entidad ComponenteForm por ello las querys están dadas de la siguiente forma: "from ComponenteForm" o "from ComponenteForm where id = :id", los atributos tambien serán usados con el mismo criterio es decir las querys se realizarán con los atributos de la entidad que representan las filas de la tabla.
  5. Finalmente para obtener un fila o todas en nuestro caso tambien se usa un metodo del objeto session que es session.createQuery(queryString), donde queryString viene a ser la cadena con los datos de la restriccion para nuestra busqueda ya sea de uno o mas elementos. Cuando vamos a obtener un solo elemento es necesario especificar query.uniqueResult().
Ahora desarrollamos los servicios modificandolos de la siguiente manera:
/ProyectoHibernateTutorial/src/com/victor/elliott/humala/servicios/ComponenteService.java

package com.victor.elliott.humala.servicios;

import java.util.List;
import com.victor.elliott.humala.formulario.ComponenteForm;

public interface ComponenteService {
  public void agregarComponente(ComponenteForm componente);
  public List<ComponenteForm> mostrarComponentes();
  public void eliminarComponente(Integer id);
  public void actualizarComponente(ComponenteForm componente);
  public ComponenteForm mostrarComponente(int id);
}

Y su respectiva implementación:
/ProyectoHibernateTutorial/src/com/victor/elliott/humala/servicios/ComponenteServiceImpl.java

package com.victor.elliott.humala.servicios;

import java.util.List;

import com.victor.elliott.humala.dao.ComponenteDAO;
import com.victor.elliott.humala.dao.ComponenteDAOImpl;
import com.victor.elliott.humala.formulario.ComponenteForm;



public class ComponenteServiceImpl implements ComponenteService{
 
 private ComponenteDAO componenteDAO = new ComponenteDAOImpl();

 public void agregarComponente(ComponenteForm componente) {
  // TODO Auto-generated method stub
  componenteDAO.agregarComponente(componente);
 }

 public List<ComponenteForm> mostrarComponentes() {
  // TODO Auto-generated method stub
  return componenteDAO.mostrarComponentes();
 }

 public void eliminarComponente(Integer id) {
  // TODO Auto-generated method stub
  componenteDAO.eliminarComponente(id);
 }

 public void actualizarComponente(ComponenteForm componente) {
  // TODO Auto-generated method stub
  componenteDAO.actualizarComponente(componente);
 }

 public ComponenteForm mostrarComponente(int id) {
  // TODO Auto-generated method stub
  return componenteDAO.mostrarComponente(id);
 }
}

Cuya única función sera la de ser instanciada e invocada sus métodos por nuestra aplicación y a su vez invocar los metodos del DAO que necesitemos. En resumen esta será nuestra capa de servicios.
Ahora codificamos nuestra aplicación:
/ProyectoHibernateTutorial/src/com/victor/elliott/humala/aplicacion/Aplicacion.java

package com.victor.elliott.humala.aplicacion;

import com.victor.elliott.humala.formulario.ComponenteForm;
import com.victor.elliott.humala.servicios.ComponenteService;
import com.victor.elliott.humala.servicios.ComponenteServiceImpl;
import java.util.List;

public class Aplicacion {
 
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  ComponenteService componenteService=new ComponenteServiceImpl();
  List<ComponenteForm> componentesLista;
  ComponenteForm componente=new ComponenteForm();
  
  componente.setNombre("Eclipse");
  componente.setVersion("4.3.0");
  componente.setTipo("IDE");
  componente.setExtension("exe");
  componenteService.agregarComponente(componente);//id=1
  componente.setNombre("Hibernate");
  componente.setVersion("4.2.7");
  componente.setTipo("Framework");
  componente.setExtension("zip");
  componenteService.agregarComponente(componente);//id=2
  componente.setNombre("Spring");
  componente.setVersion("3.1.0");
  componente.setTipo("Framework");
  componente.setExtension("zip");
  componenteService.agregarComponente(componente);//id=3
  componente.setNombre("MySQL");
  componente.setVersion("5.0");
  componente.setTipo("BD");
  componente.setExtension("msi");
  componenteService.agregarComponente(componente);//id=4
  componentesLista = componenteService.mostrarComponentes();
  System.out.println(componentesLista.toString());
  componente.setId(1);
  componente.setNombre("Eclipse");
  componente.setVersion("4.3.1");
  componente.setTipo("IDE");
  componente.setExtension("exe");
  componenteService.actualizarComponente(componente);
  System.out.println(componenteService.mostrarComponente(1).toString());
  componenteService.eliminarComponente(4);
  componentesLista = componenteService.mostrarComponentes();
  System.out.println(componentesLista.toString());
 }
 
}

Que realizará las siguientes tareas:

  1. Crear cuatro componentes en la base de datos con id auto-generables: 1,2,3 y 4 respectivamente.
  2. Mostrar los cuatro componentes mediante una lista.
  3. Modificar el primer componente id=1
  4. Mostrar el primer componente
  5. Eliminar el cuarto componente id=4
  6. Mostrar los 3 componentes de la base de datos que quedan.
Ahora finalmente podemos ejecutar nuestra aplicación click derecho en Aplicacion.java -> Run as-> Java Application
Si todo es correcto, no hay errores de compilación ni de ejecución veran el siguiente resultado:

Hibernate: insert into componente (extension, nombre, tipo, version) values (?, ?, ?, ?)
Hibernate: insert into componente (extension, nombre, tipo, version) values (?, ?, ?, ?)
Hibernate: insert into componente (extension, nombre, tipo, version) values (?, ?, ?, ?)
Hibernate: insert into componente (extension, nombre, tipo, version) values (?, ?, ?, ?)
Hibernate: select componente0_.id as id1_0_, componente0_.extension as extensio2_0_, componente0_.nombre as nombre3_0_, componente0_.tipo as tipo4_0_, componente0_.version as version5_0_ from componente componente0_
[Componente: 1, Eclipse, 4.3.0, exe, Componente: 2, Hibernate, 4.2.7, zip, Componente: 3, Spring, 3.1.0, zip, Componente: 4, MySQL, 5.0, msi]
Hibernate: update componente set extension=?, nombre=?, tipo=?, version=? where id=?
Hibernate: select componente0_.id as id1_0_, componente0_.extension as extensio2_0_, componente0_.nombre as nombre3_0_, componente0_.tipo as tipo4_0_, componente0_.version as version5_0_ from componente componente0_ where componente0_.id=?
Componente: 1, Eclipse, 4.3.1, exe
Hibernate: select componente0_.id as id1_0_0_, componente0_.extension as extensio2_0_0_, componente0_.nombre as nombre3_0_0_, componente0_.tipo as tipo4_0_0_, componente0_.version as version5_0_0_ from componente componente0_ where componente0_.id=?
Hibernate: delete from componente where id=?
Hibernate: select componente0_.id as id1_0_, componente0_.extension as extensio2_0_, componente0_.nombre as nombre3_0_, componente0_.tipo as tipo4_0_, componente0_.version as version5_0_ from componente componente0_
[Componente: 1, Eclipse, 4.3.1, exe, Componente: 2, Hibernate, 4.2.7, zip, Componente: 3, Spring, 3.1.0, zip]

Debemos tener en cuenta que antes de volver a ejecutar nuestro proyecto debemos volver a eliminar nuestra data en la Base de datos y reiniciar el contador del id ejecutando en la consola de la BD los siguientes comandos:

DELETE FROM componente;
ALTER TABLE componente AUTO_INCREMENT=1;

caso contrario habrá un error de ejecución al intentar borrar el id=4 ya que en la anterior ejecución lo borramos y ya no lo encontrará. El error sera asi:

org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [com.victor.elliott.humala.formulario.ComponenteForm#4]

El código del proyecto os lo podéis bajar del siguiente link:

ProyectoHibernateTutorial

6 comentarios:

  1. Interesante ... muy bueno amigo Victor ... quizá para evitar el error de las pruebas que al final mencionas, podrías incluir para tus pruebas JUnit que utilice algunas de las Base de Datos ligeras como Derby (JavaDB) o HSQLDB en el formato "embebido" en memoria. Podrias incluir un tutorial acerca de JUnit. Saludos.

    ResponderEliminar
    Respuestas
    1. Gracias Moises, a ver si cuando termine los tutoriales de Maven y Struts empiezo con lo de JUnit ;)

      Eliminar
  2. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  3. Realize tu ejercicio en eclipse kepler y tengo un problema:
    sigo paso a paso las instrucciones, importo las librerias pero al llegar a
    return new Configuration().configure().buildSessionFactory();
    el programa genera una excepcion y me muestra información sobre configuration.class, el cual no encontro y esto genera un Nullpointer.
    Si hago lo mismo con un proyecto maven el compila normal o netbeans compila. pero eclipse kepler solo con libreras hibernate, no deja compilar

    ResponderEliminar
  4. En la figura 1.4 puedes ver que hay una pestaña de order an export, verifica que cambiando el orden de algunos items obtengas el resultado deseado. Por otro lado si presionas Ctrl - Shift - T, y escribes Configuration podras ver que librería contiene esta clase para y la pones como primera en el orden que te indiqué.

    Saludos.

    ResponderEliminar
  5. Hola. Estoy usando Hibernate 5.2.9 y me dice que la clase QUERY está deprecada. No me compila el proyecto. Será por esto? Cómo puedo reemplazar la clase QUERY? Saludos.

    ResponderEliminar