13 de mayo de 2009

HIbernate - Parte 2: Persistiendo Objetos Simples usando Anotaciones (metadatos)

En el tutorial anterior vimos como crear una pequeña aplicación con Hibernate usando mapeos en archivos XML. Aunque esto es bastante útil (en algunas situaciones) puede ser bastante engorroso, especialmente en aplicaciones muy grandes.

Afortunadamente existe otra forma de realizar estos mapeos, y es usando anotaciones o metadatos, los cuales se agregan directamente al código fuente y, por lo tanto, es más fácil estar conscientes de estos datos de mapeos al estar editando el código fuente. Además usan muchos valores por default, ya no será necesario que escribamos toda la información para los mapeos.

En esta ocasión mostraré como realizar el mismo ejemplo que en el tutorial anterior, pero haciendo uso de anotaciones en lugar de archivos de mapeo XML.

Para este ejemplo usaremos MySQL 5.1 como base de datos. Si no la tienen pueden descargarla desde aquí. También deben bajar el conector para Java versión 5.1.7 desde aquí. Además usaremos la biblioteca de Hibernate que creamos también en el tutorial anterior.

Hibernate proporciona un API especial para trabajar usando anotaciones llamada "Hibernate Annotations" que puede ser descargada desde aquí. Actualmente la última versión es la 3.4.0 GA y es la que usaremos para este tutorial.

Una vez descargado el archivo y descomprimido, veremos que dentro del directorio de "hibernate-annotations" hay un archivo llamado "hibernate-annotations.jar". Este jar contiene las clases principales de Hibernate annotations. Dentro del directorio "lib" encontrarán algunos jars de soporte.

Crearemos una biblioteca que contendrá estos archivos para que sea fácil agregarlos cada vez que queramos trabajar con anotaciones de Hibernate.

Abrimos el NetBeans y vamos al menú "Tools -> Libraries":




En la ventana que se abre seleccionamos la opción "New Library..." con lo que se abre otra ventana. En esta ventana le damos a la biblioteca el nombre "HibernateAnotaciones" (sin espacios), de tipo "Class Libraries":



Presionamos el botón "OK" y ya tendremos creada nuestra biblioteca, ahora hay que agregar los jars que la conformarán. Presionamos el botón "Add JAR/Folder" y seleccionamos, del directorio de "hibernate-annotations" los siguientes archivos:

  • hibernate-annotations.jar

y del directorio lib:

  • ejb3-persistence.jar
  • hibernate-commons-annotations.jar

Nuestra biblioteca debe verse así:



Ahora cada vez que queramos usar hibernate con anotaciones deberemos agregar la biblioteca "Hibernate" (que creamos en el tutorial anterior) y la nueva biblioteca "HibernateAnotaciones".

Ahora creamos un nuevo proyecto desde el menú "File -> New Project... -> Java -> Java Application". Llamamos a la aplicación "HibernateAnotaciones" y nos aseguramos que las opciones "Create Main Class" y "Set as Main Project" estén seleccionadas. Presionamos el botón "Finish" y veremos la clase "Main" en nuestro editor.

Agregamos las bibliotecas de Hibernate a nuestro proyecto. Hacemos clic derecho sobre el nodo "Libraries" del proyecto y agregamos las dos bibliotecas de Hibernate que creamos ("Hibernate" y "HibernateAnotaciones"), seleccionando la opción "Add Library..." del menú contextual que se abre. Aprovechamos para agregar el driver de la base de datos, el archivo "mysql-connector-java-5.1.7-bin.jar" usando la opción "Add JAR/Folder..." de ese mismo menú contextual. Al final el nodo "Libraries" debe verse así:



También es buen momento para crear la base de datos "pruebahibernate" si no lo han hecho. Igual que en el tutorial anterior no se preocupen por crear tablas, ya que será el propio Hibernate quien lo haga.

Creamos una clase simple que servirá como Entidad. De hecho reutilizaremos la clase "Contacto" del tutorial anterior, cuyo código quedó de esta forma:

public class Contacto implements Serializable
{
    private long id;
    private String nombre;
    private String email;
    private String telefono;

    public Contacto()
    {
    }

    public Contacto(String nombre, String email, String telefono)
    {
        this.nombre = nombre;
        this.email = email;
        this.telefono = telefono;
    }

    public String getEmail()
    {
        return email;
    }

    public void setEmail(String email)
    {
        this.email = email;
    }

    public long getId()
    {
        return id;
    }

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

    public String getNombre()
    {
        return nombre;
    }

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

    public String getTelefono()
    {
        return telefono;
    }

    public void setTelefono(String telefono)
    {
        this.telefono = telefono;
    }
}


En el caso anterior habíamos dejado la clase así, y el archivo de mapeo se encargaba del resto. En este caso agregaremos en la clase las anotaciones para realizar el mapeo.

Antes de comenzar debo decir que en realidad Hibernate no trabaja solo cuando usamos anotaciones. Si recuerdan, cuando creamos la biblioteca de "HibernateAnotaciones" entre los archivos que agregamos para conformarla se encontraba "ejb3-persistence.jar". Este archivo contiene las clases de un Framework llamado "JPA" o "Java Persistence API", la cual es el API "oficial" de persistencia para aplicaciones Java. Trabaja utilizando lo que se conoce como un "proveedor de persistencia" que es quien se encarga de hacer el verdadero trabajo con la base de datos. En este caso Hibernate será el proveedor de persistencia. Las anotaciones que usaremos si son de JPA. Hibernate también proporciona algunas anotaciones, pero son por si queremos agregar funcionalidad extra, lo cual escapa al objetivo de este tutorial.

No usaremos al 100% JPA ya que no realizaremos la configuración como debe ser (en un archivo llamado "persistence.xml") sino que seguiremos usando Hibernate para configurar todo.

Ahora si, pasemos a anotar nuestra clase. La anotación más importante es "javax.persistence.Entity", la cual indica que la clase es una "Entidad", por lo que la anotación se coloca a nivel de clase.

@Entity
public class Contacto implements Serializable
{
}


Cuando Hibernate genere la tabla para esta entidad lo hará en base al nombre de la misma. En este caso creará una tabla llamada "contacto". Pero, en lo personal, prefiero que los nombres de las tablas estén el plural. Así que usaré la anotación "javax.persistence.Table" para indicar cuál será el nombre de la tabla generada (o si ya tenemos una base de datos creada, cuál es el nombre de la tabla en la que se almacenarán estas entidades):

@Entity 
@Table(name="contactos")
public class Contacto implements Serializable 
{ 
}


Después debemos indicar cuál atributo de la clase será el identificador. Esto lo hacemos con la anotación "javax.persistence.Id". Este atributo podemos colocarlo en dos lugares: en el getter del atributo identificador, o directamente en el atributo.

Cuando colocamos la anotación en el atributo decimos que se realiza un acceso por "atributo" (field access), o sea que Hibernate lee y establece los valores directamente de los atributos. Cuando la colocamos en el getter decimos que se realiza un acceso por "propiedad" (property access), o sea que Hibernate accesa a los valores mediante los setters y getters. No pueden mezclarse los tipos de acceso, así que elijan con cuidado. Pueden encontrar un poco más de información acerca de esto aquí (es un tutorial de JPA)

En este caso colocaré la anotación en el atributo:

@Id
private long id;


Podemos personalizar la forma en la que se generará el valor del identificador usando la anotación "javax.persistence.GeneratedValue" e indicando la "estrategia" que se usará para generarlo. Existen solo 4 estrategias en JPA (si usamos las anotaciones de Hibernate podemos usar todos los generadores de Hibernate):

  • AUTO
  • IDENTITY
  • SEQUENCE
  • TABLE

Las más usadas son "AUTO" e "IDENTITY". La primera usa la estrategia por default de la base de datos, mientras que la segunda genera los identificadores en orden (1, 2, 3, etc.). Pueden encontrar más información aquí.

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;


Esto es todo lo que es necesario hacerle a la clase, el resto de las propiedades que se persistirán serán obtenidas mediante reflexión, la explicación es la misma que en el tutorial anterior. Todos los atributos que no estén marcados como "transient" o con la anotación "javax.persistence.Transient" serán persistidas.

Si queremos personalizar la columna de la tabla que se generará de un atributo, podemos hacerlo con la anotación "javax.persistence.Column". Por ejemplo, yo no quiero que la columna generada del atributo "email" sea "email", sino que sea "e_mail", por lo que agrego la anotación de la siguiente forma:

@Column(name="e_mail")
private String email;


Al final la clase "Contacto" queda de la siguiente forma (omitiendo los imports):

@Entity
@Table(name="contactos")
public class Contacto implements Serializable
{
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;
    private String nombre;
    
    @Column(name="e_mail")
    private String email;
    private String telefono;

    public Contacto()
    {
    }

    public Contacto(String nombre, String email, String telefono)
    {
        this.nombre = nombre;
        this.email = email;
        this.telefono = telefono;
    }

    public String getEmail()
    {
        return email;
    }

    public void setEmail(String email)
    {
        this.email = email;
    }

    public long getId()
    {
        return id;
    }

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

    public String getNombre()
    {
        return nombre;
    }

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

    public String getTelefono()
    {
        return telefono;
    }

    public void setTelefono(String telefono)
    {
        this.telefono = telefono;
    }
}


Y esto nos ahorrará el tener que crear el archivo de mapeo para esta clase ^-^. Ahora configuraremos Hibernate. Como expliqué anteriormente: La configuración de Hibernate puede hacerse en tres lugares:

  • Un archivo de propiedades llamado "hibernate.properties".
  • Un archivo XML llamado "hibernate.cfg.xml".
  • En código dentro de la misma aplicación.

Yo usaré el archivo "hibernate.cfg.xml". Así que crearemos este archivo en nuestro proyecto. Hacemos clic derecho en el nodo "Source Packages" del proyecto. En el menú contextual que se abre seleccionamos "New -> XML Document...":



Nombramos al archivo "hibernate.cfg", el IDE se encargará de colocarle la extensión ".xml". Ubicamos el archivo en el directorio "src":



Con esto lograremos que el archivo quede en la raíz del classpath de la aplicación, también conocido como el paquete default. Presionamos el botón "Next >" y en la pantalla siguiente indicamos que queremos crear un documento XML bien formado (como en el caso anterior). Presionamos el botón "Finish" para generar el archivo. Si nos fijamos en el archivo generado, este debe encontrarse en un paquete llamado "<default package>":



Borramos todo el contenido del archivo generado y colocamos las siguientes líneas:

<?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>

</hibernate-configuration>


El DTD nos ayudará para que el IDE autocomplete las etiquetas.

Como podemos ver, el elemento raíz del archivo de configuración es "<hibernate-configuration>" y, por lo tanto, todos los elementos los colocaremos entre estas dos etiquetas.

Lo primero que debemos hacer es configurar un "session-factory", que básicamente es lo que le dice a Hibernate cómo conectarse y manejar la conexión a la base de datos. Podemos tener más de un "session-factory" en el archivo de configuración (por si quisiéramos conectarnos a más de una base de datos), pero por lo regular solo ponemos uno:

<hibernate-configuration>
    <session-factory>

    </session-factory>
</hibernate-configuration>


Lo siguiente es configurar la conexión a nuestra base de datos. En este archivo se configuran los parámetros básicos y típicos para una conexión (la URL, nombre de usuario, contraseña, driver, etc.). Cada uno de estos parámetros se configura dentro de una etiqueta "<property>" (al igual que casi todos los elementos del archivo de configuración). Como dije antes, usaré una base de datos MySQL para este ejemplo, así que mi configuración queda de la siguiente forma:

<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost/pruebahibernate</property>
<property name="connection.username">usuario</property>
<property name="connection.password">password</property>


Después, configuramos el pool de conexiones de Hibernate. En este caso como es un ejemplo muy simple, solo nos interesa tener una conexión en el pool, por lo que colocamos la propiedad "connection.pool_size" con un valor de "1":

<property name="connection.pool_size">1</property>


*Nota: Este pool de conexiones es solo para efectos de pruebas en desarrollo. Cuando nuestra aplicación para a producción se recomienda usar un pool de conexiones distinto, como C3P0.

El siguiente paso es muy importante. Debemos indicar el "dialecto" que usará Hibernate para comunicarse con la base de datos. Este dialecto es la variante de SQL que usa la base de datos para ejecutar queries. Indicamos el dialecto con el "fully qualified class name", o el nombre completo de la clase incluyendo el paquete. En el caso de MySQL 5 usamos "org.hibernate.dialect.MySQL5Dialect". En esta página pueden encontrar una lista más o menos completa de los dialectos soportados por Hibernate, pero siempre es mejor revisar la documentación de la versión que estén usando para estar seguros:

<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>


Otras dos propiedades importantes que podemos configurar son: "show_sql", que indica si queremos que las consultas SQL generadas sean mostradas en el stdout (normalmente la consola), y "hbm2ddl.auto", que indica si queremos que se genere automáticamente el esquema de la base de datos (las tablas). "show_sql" puede tomar valores de "true" o "false", yo lo colocaré en "true" (lo que puede ser bueno mientras estamos en etapas de desarrollo o pruebas, pero querrán cambiar su valor cuando su aplicación pase a producción). Por otro lado "hbm2ddl.auto" puede tomar los valores, según la documentación oficial, de "validate", "update", "create", y "create-drop" (falta "none") (aunque no todos los valores funcionan para todas las bases de datos). Yo los colocaré de la siguiente forma:

<property name="show_sql">true</property> 
<property name="hbm2ddl.auto">create-drop</property>


Con el valor "create-drop" hacemos que cada vez que se ejecute la aplicación Hibernate elimine las tablas de la base de datos y las vuelva a crear, cuando su sistema pase a producción sería bueno que quitaran esta propiedad.

Para terminar la configuración, debemos indicar dónde están las clases entidades. Cuando usamos Hibernate con archivos de mapeo usamos el atributo "resource" del elemento "<mapping>", en el que indicábamos en dónde se encontraba dicho archivo de mapeo. Cuando usamos anotaciones (y por lo tanto no hay archivos de mapeo) debemos indicar las clases anotadas usando su atributo "class", de la siguiente forma:

<mapping class="hibernateanotaciones.Contacto" />


Tal vez encuentren que "<mapping>" tiene otro atributo que es "package". No se confundan, este atributo NO sirve para indicar un paquete en el que se encuentren las clases entidades anotadas y lograr que Hibernate encuentre automáticamente estas clases para ahorrarnos el trabajo de indicar cada clase. De hecho este atributo se usa cuando tenemos paquetes anotados. No estoy seguro de para qué pueden usarse, pero solo no se confundan ^-^!.

El archivo de configuración queda de la siguiente forma:

<?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="connection.driver_class">com.mysql.jdbc.Driver</property>         
        <property name="connection.url">jdbc:mysql://localhost/pruebahibernate</property>     
        <property name="connection.username">usuario</property>       
        <property name="connection.password">password</property>     
        <property name="connection.pool_size">1</property>         
        <property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>     
        <property name="show_sql">true</property>     
        <property name="hbm2ddl.auto">create-drop</property>      
        <mapping class="hibernateanotaciones.Contacto" />     
    </session-factory>
</hibernate-configuration>




Esto es todo lo que debemos poner en el archivo de configuración. Ahora veremos cómo crear el código de acceso a la base de datos. Al igual que en el tutorial anterior crearemos una clase de utilidad llamada "HibernateUtil", que se hará cargo de inicializar y hacer el acceso al "SessionFactory". La clase será casi idéntica, con una excepción.

Creamos una nueva clase llamada "HibernateUtil" y dentro de esta clase declaramos un atributo static de tipo "SessionFactory", así nos aseguraremos de que solo existe una instancia en la aplicación. Además lo declararemos como final para que la referencia no pueda ser cambiada después de que la hayamos asignado.

private static final SessionFactory sessionFactory;


Después usamos un bloque de inicialización estático para inicializar esta variable en el momento en el que la clase sea cargada en la JVM.

La última vez usamos una instancia de la clase "org.hibernate.cfg.Configuration" para cargar la configuración. Cuando usamos anotaciones usamos una instancia de "org.hibernate.cfg.AnnotationConfiguration". Si usamos el método "configure()" que no recibe parámetros entonces Hibernate busca el archivo "hibernate.cfg.xml" que creamos anteriormente. Una vez que tenemos este objeto, entonces podemos inicializar la instancia de "SessionFactory" con su método "buildSessionFactory". Además como este proceso puede lanzar "HibernateException" (que extiende de "RuntimeException") la cachamos y lanzamos como un "ExceptionInInitializarError" (que es lo único que puede lanzarse desde un bloque de inicialización). El bloque de inicialización queda de la siguiente forma:

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


Finalmente creamos un método static para recuperar la instancia de la "SessionFactory":

public static SessionFactory getSessionFactory()
{ 
    return sessionFactory;
} 


La clase "HibernateUtil" queda de la siguiente forma:

import org.hibernate.HibernateException;
import org.hibernate.SessionFactory; 
import org.hibernate.cfg.AnnotationConfiguration;

public class HibernateUtil
{  
    private static final SessionFactory sessionFactory;   

    static 
    { 
        try 
        { 
            sessionFactory = new AnnotationConfiguration().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; 
    } 
}




Ahora escribiremos una clase DAO (nuevamente no seguiremos el patrón al pie de la letra, es solo para mostrar cómo trabajar con Hibernate) que nos permitirá realizar operaciones de base de datos. De hecho es la misma clase "ContactosDAO" del tutorial anterior. Copiaré los pasos y la explicación para que no tengan que regresar a revisarla

Creamos una clase llamada "ContactosDAO" y agregamos dos atributos, uno llamado "sesion" de tipo "org.hibernate.Session", y otro llamado "tx" de tipo "org.hibernate.Transaction".

private Session sesion;
private Transaction tx;


Estos atributos nos servirán para mantener la referencia a la sesión a base de datos, y a la transacción actual, respectivamente. Ahora agregaremos dos métodos de utilidad. El primero nos ayudará a iniciar una sesión y una transacción en la base de datos. Llamaremos a este método "iniciaOperacion", y la implementación es la siguiente:

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


En el método anterior obtenemos una referencia a "SessionFactory" usando nuestra clase de utilidad "HibernateUtil". Una vez que tenemos la "SessionFactory" creamos una conexión a la base de datos e iniciamos una nueva sesión con el método "openSession()". Una vez teniendo la sesión iniciamos una nueva transacción y obtenemos una referencia a ella con "beginTransaction()".

Ahora el segundo método de utilidad (llamado "manejaExcepcion") nos ayudará a manejar las cosas en caso de que ocurra una excepción. Si esto pasa queremos que la transacción que estamos ejecutando se deshaga y se relance la excepción (o podríamos lanzar una propia). Por lo que el método queda así:

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


Ahora crearemos los métodos que nos permitirán realizar las tareas de persistencia de una entidad "Contacto", conocidas en lenguaje de base de datos como CRUD: guardarla, actualizarla, eliminarla, buscar un entidad "Contacto" y obtener todas los contactos que existen en la base de datos, así que comencemos.

Afortunadamente Hibernate hace que esto sea fácil ya que proporciona métodos para cada una de estas tareas. Primero veamos como guardar un objeto "Contacto". Para esto Hibernate proporciona el método "save" en nuestro objeto de tipo "org.hibernate.Session", que se encarga de generar el "INSERT" apropiado para la entidad que estamos tratando de guardar. El método "guardaContacto" queda de la siguiente forma:

public long guardaContacto(Contacto contacto)
{ 
    long id = 0;  

    try 
    { 
        iniciaOperacion(); 
        id = (Long)sesion.save(contacto); 
        tx.commit(); 
    }catch(HibernateException he) 
    { 
        manejaExcepcion(he);
        throw he; 
    }finally 
    { 
        sesion.close(); 
    }  
    return id; 
}


Regresamos el "id" generado al guardar el "Contacto" solo por si queremos usarlo más adelante en el proceso (como lo haremos nosotros), o si queremos mostrarle al usuario, por alguna razón, el identificador del contacto.

Ahora veremos cómo actualizar un "Contacto". Para eso usamos el método "update", de nuestro objeto "session", en nuestro método "actualizaContacto":

public void actualizaContacto(Contacto contacto) throws HibernateException 
{ 
    try 
    { 
        iniciaOperacion(); 
        sesion.update(contacto); 
        tx.commit(); 
    }catch (HibernateException he) 
    { 
        manejaExcepcion(he); 
        throw he; 
    }finally 
    { 
        sesion.close(); 
    } 
}


Como podemos ver, el método para actualizar es muy similar al método para guardar la entidad. Lo mismo ocurre con el método para eliminarla, "eliminaContacto":

public void eliminaContacto(Contacto contacto) throws HibernateException 
{ 
    try 
    { 
        iniciaOperacion(); 
        sesion.delete(contacto); 
        tx.commit(); 
    } catch (HibernateException he) 
    { 
        manejaExcepcion(he); 
        throw he; 
    }finally 
    { 
        sesion.close(); 
    } 
}


Ahora veremos unos métodos un poco más interesantes.

Cuando queremos buscar una entidad podemos usar varios criterios. La forma más fácil es buscar una entidad particular usando su "id". El objeto "org.hibernate.Session" proporciona dos métodos para esto: "load" y "get". Los dos hacen prácticamente lo mismo. En base al identificador y tipo de la entidad recuperan la entidad indicada, con la diferencia de que "load" lanza una excepción en caso de que la entidad indicada no sea encontrada en la base de datos, mientras que "get" simplemente regresa "null". Pueden usar el que prefieran, en lo personal me gusta más "get", así que lo usaré para el método "obtenContacto":

public Contacto obtenContacto(long idContacto) throws HibernateException
{ 
    Contacto contacto = null;  

    try 
    { 
        iniciaOperacion(); 
        contacto = (Contacto) sesion.get(Contacto.class, idContacto); 
    } finally 
    { 
        sesion.close(); 
    }  
    return contacto; 
}


Finalmente veremos el método "obtenListaContactos" que recupera todos los Contactos que estén guardados en la base de datos. Como en este caso regresaremos una lista de elementos deberemos crear una consulta. Nuevamente usaremos HQL para generar dicha consulta:

public List<Contacto> obtenListaContactos() throws HibernateException 
{ 
    List<Contacto> listaContactos = null;  
    
    try 
    { 
        iniciaOperacion(); 
        listaContactos = sesion.createQuery("from Contacto").list(); 
    }finally 
    { 
        sesion.close(); 
    }  

    return listaContactos; 
}


Eso es todo, nuestra consulta para recuperar todos los Contactos que tenemos en la base de datos solo debemos usar la clausula "from Contacto". Si queren una referencia más amplia de HQL pueden encontrarla en esta página.

Bien, eso es todo. La clase "ContactosDAO" queda de la siguiente forma (omitiendo los imports):

public class ContactosDAO
{  
    private Session sesion; 
    private Transaction tx;  

    public long guardaContacto(Contacto contacto) throws HibernateException 
    { 
        long id = 0;  

        try 
        { 
            iniciaOperacion(); 
            id = (Long) sesion.save(contacto); 
            tx.commit(); 
        } catch (HibernateException he) 
        { 
            manejaExcepcion(he); 
            throw he; 
        } finally 
        { 
            sesion.close(); 
        }  

        return id; 
    }  

    public void actualizaContacto(Contacto contacto) throws HibernateException 
    { 
        try 
        { 
            iniciaOperacion(); 
            sesion.update(contacto); 
            tx.commit(); 
        } catch (HibernateException he) 
        { 
            manejaExcepcion(he); 
            throw he; 
        } finally 
        { 
            sesion.close(); 
        } 
    }  

    public void eliminaContacto(Contacto contacto) throws HibernateException 
    { 
        try 
        { 
            iniciaOperacion(); 
            sesion.delete(contacto); 
            tx.commit(); 
        } catch (HibernateException he) 
        { 
            manejaExcepcion(he); 
            throw he; 
        } finally 
        { 
            sesion.close(); 
        } 
    }  

    public Contacto obtenContacto(long idContacto) throws HibernateException 
    { 
        Contacto contacto = null;  
        try 
        { 
            iniciaOperacion(); 
            contacto = (Contacto) sesion.get(Contacto.class, idContacto); 
        } finally 
        { 
            sesion.close(); 
        }  

        return contacto; 
    }  

    public List<Contacto> obtenListaContactos() throws HibernateException 
    { 
        List<Contacto> listaContactos = null;  

        try 
        { 
            iniciaOperacion(); 
            listaContactos = sesion.createQuery("from Contacto").list(); 
        } finally 
        { 
            sesion.close(); 
        }  

        return listaContactos; 
    }  

    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); 
    } 
}


Ahora, y para terminar el ejemplo, haremos uso de "ContactosDAO" en la clase "Main" para crear y recuperar algunas instancias de "Contactos". Como la clase es la misma que en el tutorial anterior no daré una explicación. La clase "Main" queda de la siguiente forma:

public class Main
{ 
    public static void main(String[] args) 
    { 
        ContactosDAO contactosDAO = new ContactosDAO(); 
        Contacto contactoRecuperado = null;  
        long idAEliminar = 0;  

        //Creamos tes instancias de Contacto 
        Contacto contacto1 = new Contacto("Contacto 1", "contacto1@contacto.com", "12345678"); 
        Contacto contacto2 = new Contacto("Contacto 2", "contacto2@contacto.com", "87654321"); 
        Contacto contacto3 = new Contacto("Contacto 3", "contacto3@contacto.com", "45612378");  

        //Guardamos las tres instancias, guardamos el id del contacto1 para usarlo posteriormente 
        idAEliminar = contactosDAO.guardaContacto(contacto1); 
        contactosDAO.guardaContacto(contacto2); 
        contactosDAO.guardaContacto(contacto3);  

        //Modificamos el contacto 2 y lo actualizamos 
        contacto2.setNombre("Nuevo Contacto 2"); 
        contactosDAO.actualizaContacto(contacto2);  

        //Recuperamos el contacto1 de la base de datos 
        contactoRecuperado = contactosDAO.obtenContacto(idAEliminar); 
        System.out.println("Recuperamos a " + contactoRecuperado.getNombre());  

        //Eliminamos al contactoRecuperado (que es el contacto3) 
        contactosDAO.eliminaContacto(contactoRecuperado);  

        //Obtenemos la lista de contactos que quedan en la base de datos y la mostramos 
        List<Contacto> listaContactos = contactosDAO.obtenListaContactos();  
        System.out.println("Hay " + listaContactos.size() + "contactos en la base de datos");  

        for(Contacto c : listaContactos) 
        { 
            System.out.println("-> " + c.getNombre()); 
        } 
    } 
}




En el momento que ejecutemos la aplicación Hibernate creará la tabla correspondiente en la base de datos (llamada "contactos", como lo indicamos) y con los siguientes campos:



Como podemos ver la tabla se ha generado en base a las indicaciones que hemos hecho con las anotaciones.

Para terminar veamos los datos de la tabla. Según la salida en la consola del NetBeans, debe haber dos contactos en la base de datos "Nuevo Contacto 2" y "Contacto 3":



Si vemos los datos en la base comprabos que, efectivamente, esto es correcto:



Con lo que comprobamos que el ejemplo funciona!!! ^- ^.

Bien, pues en esta ocasión vimos cómo usar Hibernate sin hacer uso de archivos de mapeo en XML, indicando esos datos en el código fuente de la clase.

Esto tiene sus ventajas y desventajas, así que debe usarse con cuidado. Por ejemplo, como la información está en el código fuente, cualquier cambio necesitará que recompilemos la aplicación. Además que nuestra entidad ahora ha quedado atada al API de JPA.

Esto puede no ser problema en muchos casos, pero habrá unos en los que si lo sea, como cuando no tenemos el código fuente de las entidades, o no queremos atarlas a JPA, porque las usaremos posteriormente en otra aplicación que no usará JPA. Pero, nuevamente, esto depende de la aplicación que estén desarrollando.

Además podemos mezclar ambos métodos en donde, si existe un mapeo en los dos lugares para una misma clase, el mapero del archivo XML toma precedencia.

Espero que este tutorial les haya sido de utilidad. En los próximos tutoriales mostraré como trabajar con Hibernate para hacer cosas más interesantes y mostraré como realizar la configuración de las dos formas, con anotaciones y archivos de mapeo.

No olviden dejar sus dudas, comentarios y sugerencias.

Saludos.

Descarga los archivos de este tutorial desde aquí:

Entradas Relacionadas: