miércoles, 4 de septiembre de 2013

Hibernate: Primeros Pasos. Ejemplo

Hibernate es un framework open source que nos facilitara mucho la tarea de realizar una capa de persistencia de datos en nuestra aplicacion mediante el mapeo de clases de una base de datos relacional.

En este tutorial mostraremos un mix entre realizar el esqueleto mediante el asistente hibernate tool y la creacion de ciertos componentes a mano, evidentemente cada uno puede utilizar luego un metodo u otro, pero en este caso, y a mi criterio, considero que esta es la mejor manera de realizar el desarrollo.

En primer lugar vamos a bajarnos hibernate tool en nuestro eclipse, en mi caso se trata de una versión juno, por lo que deberemos ir a la pestaña Help --> Eclipse marketplace y en el buscador poner Hibernate.
Hibernate tool forma parte de el paquete de herramientas JBoss en su versión para eclipse juno por lo que seleccionaremos este.


Una vez se despleguen todos los paquetes seleccionamos el que nos interesa


Nos pedirá reiniciar eclipse y ya tendremos nuestro asistente de Hibernate instalado.

El ejemplo lo desarrollaremos en un proyecto para J2EE, por lo que  crearemos uno "Dinamic Web Project" de ejemplo.

Una vez creado añadiremos los jars necesarios para hibernate, una vez descargado el zip con todos los jars de Hibernate veremos que los jars necesarios los encontraremos en lib/requiered. Todos ellos los añadiremos a WEB-INF/lib y luego al classpath del proyecto.

Antes de continuar necesitamos tener el jar para la conexion a la BD y añadirlo a nuestro classpath. En mi caso voy a conectarme a una BD Sql Server remota y voy a utilizar  JTDS.



Seguidamente vamos a crear el archivo de configuración de Hibernate con el asistente que acabamos de bajar, para ello, click derecho sobre nuestro proyecto New --> Hibernate configuration File


Seleccionamos que se guarde en la raíz de la carpeta src de nuestro proyecto, y seguidamente rellenamos los campos necesarios, en este caso se puede ver en la imagen como los he rellenado para mi proyecto (recordemos que utilizo una BD SQL server en remoto para este ejemplo). Seleccionamos tambien Create a console configuration

En la siguiente pantalla se puede configurar todo lo relacionado con nuestro hibernate (version, conexión a base de datos...) yo lo dejo todo por defecto pero me aseguro que en la ventana del classpath este incluido mi jar jstd, si no estuviese, fallaría al intentar conectarse.

Una vez terminado, veremos como se nos ha generado el archivo de configuración de hibernate, si queremos curiosear veremos que se trata de una estructura clásica de xml con todos los datos necesarios.

En el siguiente apartado vamos a mapear una tabla de la base de datos, esto se puede hacer a mano o con el asistente, en mi caso voy a proceder a mano a partir de aquí porque quiero tener un control total sobre el código que se genere, cada uno puede escoger la opción que mejor le convenga.

La tabla a mapear es una llamada Telefons que tiene la siguiente estructura :




En primer lugar, y por comodidad de tenerlo todo ordenado, creo un package llamado mapeos donde pondré todas las clases mapeadas, a continuación creo una clase y la escribo usando la convención de nombres Java Beans para getters y setters y visibilidad privada para sus atributos. No es obligatorio hacerlo así pero si recomendable.






De esta manera, la clase queda así:

public class Telefons implements Serializable{

private int id;
private String movil;
private String nom;
private String descripcio;
private String dataAlta;
private String dataModificacio;
private boolean bsms;

public Telefons() {

}

public Telefons(String movil, String nom, String descripcio,

String dataAlta, String dataModificacio, boolean bsms) {
this.movil = movil;
this.nom = nom;
this.descripcio = descripcio;
this.dataAlta = dataAlta;

this.dataModificacio = dataModificacio;
this.bsms = bsms;
}

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

private void setId(int id) {
this.id = id;
}

public String getMovil() {
return this.movil;
}

public void setMovil(String movil) {
this.movil = movil;
}


public String getNom() {
return this.nom;
}

public void setNom(String nom) {
this.nom = nom;
}

public String getDescripcio() {
return this.descripcio;
}

public void setDescripcio(String descripcio) {
this.descripcio = descripcio;
}

public String getDataAlta() {

return this.dataAlta;

}
public void setDataAlta(String dataAlta) {
this.dataAlta = dataAlta;
}

public String getDataModificacio() {
return this.dataModificacio;
}

public void setDataModificacio(String dataModificacio) {
this.dataModificacio = dataModificacio;
}

public boolean isBsms() {
return this.bsms;
}

public void setBsms(boolean bsms) {
this.bsms = bsms;
}
}


A destacar tenemos que se debe crear un constructor sin argumentos, esto es obligatorio ya que Hibernate creará instancias de esta clase por reflexión.

A continuación vamos a crear el archivo de mapeo, para ello click derecho new--> othres--> y crearemos un Hibernate XML Mapping file asociado a nuestra clase Telefons. El archivo debe quedar de esta manera :

<hibernate-mapping>
    <class name="mapeo.Telefons" table="TELEFONS">
        <id name="id" type="int">
            <column name="ID" />
            <generator class="increment" />
        </id>
        <property name="movil" type="java.lang.String">
            <column name="MOVIL" />
        </property>
        <property name="nom" type="java.lang.String">
            <column name="NOM" />
        </property>
        <property name="descripcio" type="java.lang.String">
            <column name="DESCRIPCIO" />
        </property>
        <property name="dataAlta" type="java.lang.String">
            <column name="DATAALTA" />
        </property>
        <property name="dataModificacio" type="java.lang.String">
            <column name="DATAMODIFICACIO" />
        </property>
        <property name="bsms" type="boolean">
            <column name="BSMS" />
        </property>
    </class>
</hibernate-mapping>


El tag <generator> como increment hará que Hibernate nos genere los id de forma automática e incremental. Si la base de datos tiene el campo id definido como clave primaria, ella será la encargada de generar esos id para el campo si es que vamos a insertar un nuevo objeto. Por lo tanto, en vez de poner "incremental" en el campo "generator", pondremos "native" para que podamos realizar los inserts


Ahora nos dirigimos a nuestro archivo de configuración .cfg y le decimos que hemos creado un nuevo mapeo, esto se hace colocando el tag :


<mapping resource="mapeo/Telefons.hbm.xml"/>


Dentro de la etiqueta : <session-factory>


Otra cosa que puede ser interesante indicar en este archivo de propiedades es el pool de conexiones, con el tag <property name="connection.pool_size">1</property>, pero de momento no es imprescindible.


Hasta aquí ya tenemos el trabajo hecho, evidentemente esto se puede hacer extensible a tantas tablas como queramos, hasta tener la base de datos completamente mapeada.


Ahora vamos a empezar con el manejo de los objetos Telefons con nuestra base de datos.


Antes de nada, debemos incorporar al classpath de nuestro proyecto los jars de hibernate, que es algo que todavía no habíamos hecho, para ello nos dirigimos a su página y descargamos el zip de la última versión, a día de hoy es este. Abrimos el zip y buscamos en la carpeta lib una llamada required, cogemos todos estos jars y los añadimos al classpath de nuestro proyecto.


Ahroa ya podemos realizar la implementación de nuestra interficie DAO entre nuestra aplicación y la base de datos.

En primer lugar, crearemos una clase común a todos nuestras DAO's(en este caso solo uno) que llamaremos HibernateUtil, tendrá esta pinta :


public class HibernateUtil {
private static final SessionFactory sessionFactory;   

    static 
    { 
        try { 
          sessionFactory = new Configuration().configure().buildSessionFactory(); 
        } catch (HibernateException he) { 
           System.err.println("Ocurrió un error en la inicialización de la SessionFactory: " + he); 
           throw new ExceptionInInitializerError(he); 
        } 
    }  

    public static SessionFactory getSessionFactory() 
    { 
        return sessionFactory; 
    } 

}


Apunte : en la versión 4 de hibernate, el método .buildSessionFactory() esta deprecated, la alternativa la podemos obtener de StackOverFlow


Una vez hecho esto, creamos otra llamada TelefonsDAO, que sera la encargada de realizar las acciones propiamente dichas. En este caso vamos a hacer métodos para guardar, actualizar, borrar y obtener uno a todos los elementos de la tabla. La clase quedará así :



public class TelefonsDAO {

private Session sesion;
private Transaction tx;

private void iniciaOperacion() throws HibernateException{
   sesion = HibernateUtil.getSessionFactory().openSession();
   tx = sesion.beginTransaction();
}

private void manejaExcepcion(HibernateException he) throws HibernateException{
   tx.rollback();
   throw new HibernateException("Ocurrió un error en la capa de acceso a datos", he);
}

public int guardaTelefono(Telefons telefons){ 
   int id = 0;  
   try { 
       iniciaOperacion(); 
       id = (Integer)sesion.save(telefons); 
       tx.commit(); 
   }catch(HibernateException he) { 
       manejaExcepcion(he);
       throw he; 
   }finally { 
       sesion.close(); 
   }  
   return id; 
}

public void actualizaTelefono(Telefons telefons) throws HibernateException { 
   try{ 
       iniciaOperacion(); 
       sesion.update(telefons); 
       tx.commit(); 
   }catch (HibernateException he) { 
       manejaExcepcion(he); 
       throw he; 
   }finally { 
       sesion.close(); 
   } 
}

public void eliminaTelefons(Telefons telefons) throws HibernateException { 
   try { 
       iniciaOperacion(); 
       sesion.delete(telefons); 
       tx.commit(); 
   } catch (HibernateException he){ 
       manejaExcepcion(he); 
       throw he; 
   }finally { 
       sesion.close(); 
   } 
}

public Telefons obtenTelefonsPorID(int idTelefon) throws HibernateException{ 
Telefons tlf = null;  
   try { 
       iniciaOperacion(); 
       tlf = (Telefons) sesion.get(Telefons.class, idTelefon); 
   } finally { 
       sesion.close(); 
   }  
   return tlf; 
}

public List<Telefons> obtenTodosTelefons() throws HibernateException { 
   List<Telefons> listaTelefons = null;  
   try { 
       iniciaOperacion(); 
       listaTelefons = sesion.createQuery("from Telefons").list(); 
   }finally { 
       sesion.close(); 
   }  

   return listaTelefons; 
}
}

El package de mi proyecto queda de esta manera :





Con esto ya tendremos preparada nuestra capa de acceso a los datos. Ahora solo hemos deinstanciar objetos Telefons y pasárselo a los métodos de la clase TelefonsDAO






En próximos posts iré ampliando mas información sobre Hibernate