Fecha: 29/Septiembre/2008
Hala a todos!!! (hahaha típico saludo de blood)
En esta ocasión les traigo un “mini tutorial” del manejo de la persistencia de objetos con Hibernate (hahaha también conocido de manera incorrecta como Hiberné, Hibernet ó el muy gracioso Hiberante; si no me creen busquen en Google :p).
Este tutorial he decidido dividirlo en 3 partes, en particular esta entrada se centra en conceptos básicos y en la configuración general de Hibernate. Los post siguientes tratarán de cuestiones que exijen un poco más de trabajo en la configuración y en la programación así como algunos errores comunes que se cometen de manera involuntaria, asi que empecemos por lo esencial.
¿Qué es persistencia?
La persistencia es la capacidad de un lenguaje de programación o entorno de desarrollo para almacenar y recuperar el estado de los objetos de forma que sobrevivan a los procesos que las manipulan. Dicho de otra manera: es conseguir que los datos de un objeto sobrevivan a la ejecución de un proceso padre (en donde fueron creados), para que puedan ser reutilizados por otros procesos.
Existen diversas técnicas para hacer uso de la persistencia, siendo las principales: La Serialización (mediante la interfaz java.io.Serializable de Java), el uso de un motor de persistencia(como Hibernate) o el uso de un manejador de base de datos orientada a objetos.
Pero como dice nuestro título, veremos la persistencia mediante el uso de Hibernate.
¿Qué es un motor de persistencia?

Traducción del Motor de Persistencia
Es un componente de software que realiza la traducción de registos de base de datos relacional a objetos y viceversa. Esta traduccíon es conocida como mapeo o mapping que precisamente es una técnica de programación para convertir el sistema de tipos utilizado en un lenguaje de programación orientado a objetos y el utilizado en la base de datos relacional.
Las principales ventajas del uso de un motor de persistencia son que por una parte, el programa solo ve que puede guardar y recuperar objetos, como si estuviera programado para una base de datos orientada a objetos, y a su vez la base de datos sólo ve que guarda registros y recupera registros como si el programa estuviera dirigiendose a ella de forma relacional. Y tal vez la principal: es posible programar con orientación a objetos, aprovechando las ventajas de flexibilidad, mantenimiento y reusabilidad.
Estos conceptos son necesarios para poder entender el funcionamiento de Hibernate, así que vamos a la acción.
Trabajando con Hibernate
******************************************************************************************************
Para poder usarlo hay que bajar las librerías de Hibernate Core aquí y claro, añadirlas al proyecto
******************************************************************************************************
Es un framework de java, el cual facilita el mapeo de atributos mediante archivos XML que permiten establecer relaciones entre una base de datos relacional y objetos Java, creando un entorno Objeto-Relacional.
Para lograr esto es necesario detallar cómo es el modelo de datos, qué relaciones existen y qué forma tienen. Con esta información Hibernate le permite a la aplicación manipular los datos de la base operando sobre objetos, con todas las características de la programación orientada a objetos. Hibernate convierte los datos entre los tipos utilizados por Java y los definidos por SQL.
Hibernate genera las sentencias SQL y libera al programador del manejo manual de los datos que resultan de la ejecución de dichas sentencias, manteniendo la portabilidad entre todas (o por lo menos la mayoría) de las bases de datos con un ligero incremento en el tiempo de ejecución. Lo que es una ventaja importe, nunca se esta atado a usar siempre un mismo manejador de base de datos.
Hibernate está diseñado para ser flexible en cuanto al esquema de tablas utilizado, para poder adaptarse a su uso sobre una base de datos ya existente. También ofrece un lenguaje de consulta de datos llamado HQL (Hibernate Query Language), al mismo tiempo que una API para construir las consultas programáticamente (conocida como “criteria”).
Hablando un poco de la composición de Hibernate, pueden destacar 3 módulos:
Proveedor de Conexiones: Este administrador provee un manejo eficiente de las conexiones a las base de datos. Las conexiones son la parte más costosa al momento de interactuar con la base de datos pues requieren de muchos recursos al momento de abrir o cerrar la conexión. Sin embargo, los desarrolladores de Hibernate recomiendan no usar este administrador en un ambiente de producción, por lo cual, en la siguiente parte hablaremos un poco de C3P0, un framework de Java que lleva a cabo esta tarea.
Fabrica de Transacciones: Este servicio provee al usuario la habilidad de ejecutar más de una sentencia a la base de datos a la vez.
Fábrica de Sesiones (Session Factory): La Fábrica de Sesiones es aquella que se encarga de decir al sistema, donde se encuentran todos los ficheros de mapeo de Hibernate, el dialecto de Hibernate a utilizar (varía según el gestor de base de datos). De esta componente se generan los objetos Session de Hibernate, los cuales son los encargados de realizar todas las operaciones sobre la base de datos.
Ahora bien, empecemos con la verdadera acción, para poder definir nuestro modelo de datos desde el paradigma orientado a objetos es necesario crear clases llamadas POJO (Plain Old Java Object). Estas clases representan nuestras tablas de la base de datos pero claro, como objetos. Lo único que necesitan estas clases son métodos SET y GET por cada uno de los atributos de la tabla, como su nombre lo indica, set es el método que asigna el valor a la variable, y get el que lo obtiene. Ejemplo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| public class Contacto{
prívate int idContacto;
private String nombreContacto;
private String telefonoContacto;
private String direccionContacto;
public Contacto(){}
public void setIdContacto(int valor){
this.idContacto=valor;
}
public int getIdContacto(){
return idContacto;
}
public void setNombreContacto(String valor){
this.nombreContacto=valor;
}
public String getNombreContacto(){
return nombreContacto;
}
public void setTelefonoContacto(String valor){
this.telefonoContacto=valor;
}
public String getTelefonoContacto(){
return telefonoContacto;
}
public void setDireccionContacto(String valor){
this.direccionContacto=valor;
}
public String getDireccionContacto(){
return direccionContacto;
}
} |
Lo siguiente será definir la estructura de nuestra base de datos en un archivo XML llamado nombre_del_archivo.hbm.xml (OJO (u¬¬) nombre_del_archivo es cualquiera que deseen). Este archivo es conocido como archivo de mapeo (o mapping), este archivo contiene (tan detallado como se necesite) la estructura de la base de datos y a su vez a que clase POJO va asociada cada tabla.
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="mipaquete.Contacto" table="Contacto">
<id name="idContacto" type="integer" column="idContacto" >
<generator class="assigned"/>
</id>
<property name="nombreContacto" column="nombreContacto" type="string"/>
<property name="telefonoContacto" column="telefonoContacto" type="string"/>
<property name="direccionContacto" column="direccionContacto" type="string"/>
</class>
</hibernate-mapping>
Vamos a entender la estrucura del archivo de mapeo; a primera instancia notese que inicia con la etiqueta hibernate-mapping :p, a partir de aquí pueden ser agregadas cuantas Tablas(Clases) deseemos mapear, haciendo uso de la etiqueta class y de algunas de sus propiedades:
name: Se debe indicar la clase POJO que se utilizará para el mapeo.
table: Es el nombre de la tabla en la base de datos.
mutable: Especifica si las instancias de esa clase pueden sufrir cambios.
y algunos otros…
Lo siguiente es describir las propiedades de la clase, lo primero es definir la llave primaria (o id). La llave puede ser especificada de muchas formas distintas, si es asignada, si se obtiene por medio de alguna secuencia(sequence), algoritmos hi/lo, etc.; estas formas dependen del SMBD, pero las más comunes siempre serán asignarlas manualmente o por medio de una secuencia.
La etiqueta id, tiene como principales atributos: name: en cual especifica el atributo de la clase POJO que corresponde, type: el tipo de dato a utilizar; column: el nombre del campo de la tabla de la base de datos. Y para la definición del tipo de llave primaria hace uso del miembro generator, mediante su atributo class es como le indicamos a Hibernate como es nuestra PK.
Lo siguiente será enlistar todas las propiedades restantes mediante la etiqueta property. Property tiene como principales attributos name (nombre de del atributo de la clase POJO), type(tipo de dato), column(nombre de la columna de la tabla de BD), length(longitud del campo) y not-null(nulo??), pero claro tiene algunas más.
Como vemos no resulta muy difícil generar este archivo, pero sí llega a ser tedioso cuando tenemos una cantidad considerable de tablas que deseamos mapear.
Teniendo definida la estructura de la base de datos es momento de configurar Hibernate, claro, creando un archivo XML llamado hibernate.cfg.xml.
<?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>
<!--1--><property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver
</property>
<!--2--><property name="hibernate.connection.url">jdbc:oracle:thin:@xxx.xxx.xxx.xxx:1521:XX
</property>
<!--3--><property name="hibernate.connection.username">nombre_usuario
</property>
<!--4--><property name="hibernate.connection.password">password
</property>
<!--5--><property name="show_sql">true
</property>
<!--6--><property name="dialect">org.hibernate.dialect.OracleDialect
</property>
<!-- Archivos de Mapeo -->
<mapping resource="contacto.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Hibernate proporciona un gran número de propiedades para poder realizar su configuración, pero de momento solo nos centraremos en las necesarias para lograr un buen funcionamiento, básico, pero bueno. Observemos el ejemplo, la primera propiedad hace referencia al driver de conexión para la BD que utilizaremos, en el ejemplo se hace para Oracle. En la segunda propiedad se especifica la cadena de conexión que se utilizará. Las propiedades 3 y 4 son datos de autenticación del usuario o esquema de la BD. La 5ta propiedad es opcional, y para fines de ejemplo la colocamos; su función es imprimir en consola la sentencia HQL o SQL ejecutada por Hibernate. En la 6ta propiedad se debe indicar el diálecto de la base de datos que utilizaremos, es necesaria para que Hibernate puede realizar la traducción correcta de los tipos de datos y sintaxis de las consultas.
En el atributo resources de mapping indicamos la ubicación del archivo de mapeo creado anteriormente. Y con esto estamos listos para empezar a realizar operaciones sobre la base de datos. Ejemplo
....
private void guardarObjeto(MiObjeto obj) {
try {
1) SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
2) Session session = sessionFactory.openSession();
3) Transaction tx = session.beginTransaction();
4) session.save(obj);
5) tx.commit();
6) session.close();
} catch (HibernateException e) {
throw new RuntimeException(e.getMessage());
}
}
....
A continuación se describe el ejemplo anterior:

1)Se inicializa la Fábrica de Sesiones configurándola a partir del archivo hibernate.cfg.xml.

2)Se obtiene una sesión de la Fábrica de Sesiones con la que realizarán las operaciones.
3)Se inicia una transacción con la base de datos, esta transacción es la encargada de mantener el acceso a los datos y de realizar un commit para terminar con la operación o un rollback en caso de haberse generado un error.

4)Se ejecuta la operación deseada, en el ejemplo se usa el método save del objeto session, que es lo equivalente a realizar un insert en la base de datos. Si se quisiera realizar una búsqueda bastaría con usar load(clase.del.objeto, id_a_buscar). Una actualización se realiza con update(objeto) o con saveOrUpdate(objeto), la diferencia radica en que saveOrUpdate inserta el registro en caso que éste no exista. Y para eliminar bastará con usar delete(objeto).
5)Se cierra la transacción con un commit, Este paso es muy importante, pues al no realizar el commit, ninguna de los cambios de guardará en la base de datos.
6)Se concluye la operación al cerrar la sesión y libera todos sus recursos.
Aquí concluye esta primera parte, se ha visto solamente la parte básica del funcionamiento de hibernate; en los próximos días estaré publicando la segunda parte con un ejemplo más completo, con las operaciones tipicas de una aplicación CRUD (Create, Retrieve, Update and Delete) en una sola clase y algunos detalles finos en el manejo del objeto Session y del archivo de mapeo.
Como comentario final a este post, los errores más comunes en esta parte surgen en las clases POJO y en el archivo de mapeo, ya sea por errores de dedo o porque falta algun método set o get para un atributo, Así que cuidado, igualmente no esta de más revisar los propiedades del hibernate.cfg.xml
.
Que les aproveche, saludos!!!
Ja na!!
ミゲエル