Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Introduccion A NHibernate
Introduccion A NHibernate
Francisco Daines O.
20/01/2007
1.- Que es NHibernate y para qué sirve.........................................................................3
2.- Instalación de NHibernate.........................................................................................6
3.- Configuración de NHibernate...................................................................................7
4.- Ejemplos sencillos de mapeo OR..............................................................................9
4.1 Mapeo de clases sencillo.......................................................................................11
4.2 Mapeo con herencia...............................................................................................12
4.3 Mapeo de composiciones.......................................................................................16
4.4 Mapeo de relaciones uno a muchos.......................................................................20
4.5 Mapeo de relaciones muchos a muchos................................................................24
5.- Manipulación de datos persistentes........................................................................26
5.1 Recuperando un objeto de la base de datos. .........................................................26
5.2 Realizando consultas simples................................................................................27
Introducción a NHibernate 2
1.- Que es NHibernate y para qué sirve.
El desarrollo de aplicaciones utilizando el paradigma OO es cada vez más
adoptado por empresas de desarrollo de software, ya que provee una gran cantidad de
ventajas que otros paradigmas de desarrollo no permiten de forma sencilla. De manera
análoga, nanie puede negar el dominio absoluto del modelo relacional en el mundo de
las bases de datos.
El problema angular es que al utilizar OO con BDR se pierde gran parte de la
abstracción que provee la POO. Claramente el impacto puede variar dependiendo de la
estrategia que se utilice para comunicar nuestras clases con las tablas de un modelo
relacional. En el peor de los casos, cada clase del sistema se comunicará con la BDR,
creando una dependencia directa entre el modelo OO y el modelo relacional. Esto
significa que si realizo un simple cambio, como cambiar el nombre a una tabla, esto
afecta directamente el código escrito en una gran cantidad de líneas de, quizás, una gran
cantidad de clases. Por lo que la pérdida de tiempo es notable.
Existen alternativas al modelo relacional, son las emergentes Bases de Datos
Orientadas a Objetos, las cuales trabajan directamente con objetos y en muchos casos
proveen accesos para una gran cantidad de lenguajes de programación tales como C++,
Java y la plataforma MS.NET. Además de ser escasas, la mayoría de estas bases de
datos están en versiones de testing, por lo que implementar estas bases de datos
significa un riesgo que muchas empresas no están dispuestas a correr.
Hasta hace un tiempo la tarea de comunicar OO con BDR era difícil, luego, con
la creación del modelo DAO (Data Access Object) se simplificó el asunto, definiendo
que serían sólo algunos objetos los que se comunicarían con la BDR, disminuyendo
notoriamente el impacto de realizar cambios en la Base de Datos. Pero todavía no era
posible obtener independencia entre el código de las clases y el acceso a la base de
datos.
Hace un par de años se liberó la primera versión de un Framework de
persistencia llamado NHibernate, una implementación para el Microsoft .Net
FrameWork de Hibernate (implementado originalmente sólo para Java). Bueno pero
¿Qué es precisamente NHibernate? y ¿Para qué utilizar NHibernate? son algunas de las
preguntas que trataré de responder a continuación.
NHibernate es un Framework de persistencia, es decir provee herramientas que
facilitan la tarea de persistir objetos (i.e. almacenar el estado de un objeto con el fin de
recuperarlo en el futuro). La motivación principal de NHibernate es abstraer por
completo al desarrollador de la base de datos asociada al proyecto en desarrollo, es
decir, el desarrollador debe pensar que sólo trabaja con objetos, los cuales puede
guardar en una base de datos utilizando métodos de los mismos objetos, pero nunca
escribir ni analizar una sentencia SQL en su código.
La arquitectura de NHibernate se puede representar de una manera simple como
el siguiente diagrama:
Introducción a NHibernate 3
Podemos notar que la aplicación trabajará con objetos persistentes, pero sin
comunicarse directamente con la base de datos. En su lugar, la comunicación será con el
framework Nhibernate, el cual se compone de una sección de configuración (puede ser
archivo App.config o Web.config según nuestro proyecto sea Windows Forms o Web) y
un conjunto de mapeos Objeto-Relacionales. Utilizando estos elementos, Nhibernate se
comunicará con la base de datos y realizará las acciones requeridas por los objetos
persistentes (inserción, actualización, borrado, selección).
Entrando un poco más en detalle, una arquitectura “ligera” de NHibernate es
cuando la aplicación proporciona sus propias conexiones ADO.NET y maneja lo que
son transacciones, entonces la arquitectura será:
Introducción a NHibernate 4
Los objetos persistentes requieren almacenar estados, para esto es necesario
utilizar una sesión (canal de comunicación entre la aplicación y la base de datos). La
sesión de comunicación será creada por una SessionFactory, que es un caché de los
mapeos de una base de datos en particular. La SessionFactory puede ser configurada
utilizando código o configurando los archivos App.config o Web.config.
Ahora, si deseamos utilizar todas las características que provee NHibernate,
podemos utilizar una arquitectura como la que se muestra a continuación:
Introducción a NHibernate 5
2.- Instalación de NHibernate.
NHibernate es una librería (.dll) que se utiliza con Microsoft Visual Studio .NET.
Actualmente la versión 1.0.3 es stable y soporta las características del .Net Framework
1.1 (VS 2003). También existe la versión 1.2.0 beta, desarrollada con el objetivo de
brindar soporte a todas las características del .Net Framework 2.0 (VS 2005). Ambas
versiones de NHibernate se pueden obtener desde la página oficial del proyecto
(http://nhibernate.sourceforge.net).
Existe un paquete de extensiones llamado Nhibernate Contrib disponible para la
versión 1.0.2 de NHibernate, que ofrece características extra a las ofrecidas en el
paquete original de NHibernate. Hoy en día sólo existe el paquete Contrib para la
versión 1.0.2 de NHibernate.
Bueno, una vez obtenido el paquete de NHibernate deseado (viene en un archivo
.zip) es necesario descomprimir el archivo en algún directorio del disco duro. Para
trabajar con NHibernate desde un proyecto de VS 2003 o 2005 sólo es necesario
agregar referencias a las siguientes librerías de NHibernate:
• NHibernate.dll
En este momento nuestro proyecto está listo para poder utilizar las
características del Framework NHibernate.
Introducción a NHibernate 6
3.- Configuración de NHibernate.
Como bien sabemos, la idea de utilizar NHibernate es facilitar la interacción
entre objetos y tablas en un modelo de datos relacional. Si bien, el desarrollador no
interactúa directamente con la base de datos, Nhibernate sí lo hace, por lo que es
necesario indicar en un archivo de configuración cuál será la base de datos a utilizar y
cómo conectarse con dicha base de datos.
Existen varias formas de configurar la comunicación entre NHibernate y la base
de datos, sin embargo la más recomendable es utilizar un archivo App.config
(configuración de proyecto) ya que permite cambiar la configuración de acceso sin
cambiar el código de la aplicación en sí.
Un archivo de configuración de aplicaciones es un archivo XML que permite
configurar algunas opciones específicas de la aplicación que desarrollamos. La
estructura básica del archivo de configuración App.config es la siguiente.
<nhibernate>
<add key=”hibernate.connection.provider”
value=”Nhibernate.Connection.DriverConnectionProvider”/>
<add key=”hibernate.dialect” value=”Nhibernate.Dialect.XXX” />
<add key=”hibernate.connection.driver_class”
value=”Nhibernate.Driver.YYY”/>
<add key=”hibernate.connection.connection_string” value=”ZZZ” />
</nhibernate>
Donde se debe reemplazar los valores XXX, YYY y ZZZ según sea la base de
datos que estemos utilizando para almacenar los datos de nuestra aplicación.
La versión 1.2 de Nhibernate soporta las bases de datos más utilizadas hoy en
día. A continuación se listan algunas de las bases de datos soportadas por Nhibernate
1.2 y los valores de configuración de las variables XXX, YYY y ZZZ del ejemplo
anterior.
Introducción a NHibernate 7
Microsoft SQL Server 2000
• XXX: MsSql2000Dialect
• YYY: SqlClientDriver
• ZZZ: “Server=dbServer;Initial catalog=db;Integrated Security=SSPI”
MySQL
• XXX: MySQLDialect o MySQL5Dialect (MySQL 5)
• YYY: MySqlDataDriver
• ZZZ: “Server=server;Database=database;User ID=user; Password=password”
Oracle
• XXX: OracleDialect / Oracle9Dialect
• YYY: OracleClientDriver
• ZZZ: “Data Source=fuente;User Id=user;Password=pwd;”
PostgreSQL
• XXX: PostgreSQLDialect
• YYY: NpgslDriver
• ZZZ: “Server=Server;Database=db;User Id=user;Password=pwd;
Encoding=UNICODE”
Teniendo la configuración del archivo App.config definida, sólo nos queda obtener
el driver necesario para la conexión y agregar una referencia a él en nuestro proyecto.
Introducción a NHibernate 8
4.- Ejemplos sencillos de mapeo OR.
NHibernate permite la implementación de varios modelos de diseño para la
persistencia de objetos, por tanto se puede optar por utilizar objetos DAO, patrón
AbstractFactory, entre otros.
Se recomienda crear una clase que controle la creación de sesiones y acceso a la
base de datos, luego, según sea el modelo de objetos que utilicemos, llamaremos a esta
clase desde los objetos Save(), Update(), etc. de cada objeto que maneje persistencia en
la base de datos.
Es recomendable construir una clase manager de la conexión a la base de datos,
esta clase manejará la creación de sesiones para poder realizar las operaciones de
búsqueda, inserción, actualización y eliminación.
A continuación se presenta un ejemplo del código de la clase
NHibernateManager que utilizaremos en los ejemplos de esta sección.
Esta clase nos permitirá crear una nueva conexión a la base de datos y asociar
una sesión a dicha conexión.
Introducción a NHibernate 9
public void Save()
{
NhibernateManager nhm = new NHibernateManager();
session.Save(this);
session.Close();
NHibernateManager.CloseSessionFactory();
}
tx.Begin();
try {
session.Save(this);
tx.Commit();
} catch {
tx.RollBack();
}
session.Close();
NHibernateManager.CloseSessionFactory();
}
<hibernate-mapping xmlns=”urn:nhibernate-mapping-2.2”
namespace=”miNamespace” assembly=”miAssembly”
default-lazy=”false” >
<class . . . >
<!-- mapeo de la clase -->
</class>
Introducción a NHibernate 10
</hibernate-mapping>
El primer ejemplo que veremos será crear una clase persistente simple, es decir,
esta clase no tendrá relaciones de ningún tipo (asociación, agregación, etc.) con otra
clase.
Nuestra clase de ejemplo seguirá siendo Cat, la cual ya posee el método Save
definido recientemente. Si nuestra clase Cat está definida como sigue:
public string Id
{
get { return id; }
set { id = value; }
}
Introducción a NHibernate 11
Teniendo ambas partes del sistema, es decir, la clase persistente y la tabla sql
correspondiente a dicha clase, ahora debemos realizar el mapeo de atributos de la clase
a columnas de la tabla. Para esto debemos crear un archivo llamado Cat.hbm.xml, que
será el archivo por el cual NHibernate pregunte cuando se requiera guardar un nuevo
objeto Cat en la base de datos.
Un ejemplo del archivo Cat.hbm.xml para mapear la clase Cat a la base de datos
es el que se muestra a continuación:
Teniendo todo esto listo, guardar un objeto Cat en la base de datos es algo tan
simple como:
Introducción a NHibernate 12
public string Id
{
get { return id; }
set { id = value;}
}
Introducción a NHibernate 13
discriminador e indicará a NHibernate a qué clase corresponde una fila particular de la
tabla tblPersona.
Un ejemplo de la definición de la tabla tblPersona para la estrategia table-per-
class-hierarchy será:
Introducción a NHibernate 14
personaId varchar(32) NOT NULL,
tipocliente varchar(30)
);
Cabe señalar que, por ejemplo, si Persona fuese una clase abstracta, ambas
estrategias no sufren cambios, el único cambio será que no podremos instanciar un
objeto de tipo Persona, pero esta es una restricción de la OO. En cambio, si optamos por
la estrategia table-per-concrete-class tendremos que sólo realizaremos mapeos de las
clases concretas (no abstractas).
Realizado el mapeo OR de la jerarquía Persona, utilizar las clases persistentes es
tan simple como:
Introducción a NHibernate 15
Empleado e = new Empleado();
e.Nombre = “Paulina Mondaca”;
e.Edad = 24;
e.Sueldo = 120000;
((Persona)e).Save();
Una composición corresponde a una relación uno-a-uno entre dos clases donde
al eliminar la instancia de la clase contenedora se elimina la instancia de la clase
compuesta. La equivalencia en modelo relacional de esta relación entre clases se ve
materializada como una agregación de columnas en la tabla principal de los atributos de
la clase contenida. A continuación se presenta un ejemplo de una composición de
clases.
Introducción a NHibernate 16
Lo que pretende este mapeo es que si una clase contiene a otra, entonces la tabla
SQL debe contener un campo de un tipo de dato complejo, lo que se representa como un
conjunto de columnas que en realidad corresponden a una misma columna, en este caso
las columnas DIR_* corresponden a un tipo de datos complejo (tipo de datos Direccion)
y deben tratarse como un todo. Si bien se pierde la relación de equivalencia directa entre
modelos, este mapeo tiene sentido ya que al eliminar una instancia de Persona
efectivamente se eliminará la instancia Dirección asociada a dicha Persona.
Ahora veremos como llevar a la práctica el mapeo del ejemplo recién expuesto.
Para este caso primero debemos crear una clase Dirección, la cual se define a
continuación.
Introducción a NHibernate 17
{
private string id;
private string rut;
private string nombre;
private string apellido;
private int edad;
private char sexo;
private Direccion dir;
Teniendo estas definiciones para ambas clases ahora sólo falta configurar
correctamente el archivo Persona.hbm.xml para mapear la clase Persona y su relación
con la clase Dirección. Para indicarle a NHibernate que esta es una relación de
Composición el contenido de Persona.hbm.xml debe ser similar al siguiente:
Introducción a NHibernate 18
. . .
// guardar objeto p.
p.Save();
4.3.1 Agregación.
También es posible encontrarse con que una clase A contiene un objeto de una
clase B, pero si elimino un objeto de la clase A no se debe borrar el objeto de la clase B
asociado al objeto eliminado. Esta relación en OO se conoce como Agregación.
El siguiente ejemplo representa la relación entre Persona y Dirección, ahora
desde el punto de vista de agregación
• Una relación 1 a 1 entre las clases: En este caso el objeto “agregado” sólo
estará ligado al objeto contenedor, pero al borrar el objeto contenedor no es
necesario borrar el objeto agregado.
• Una relación n a 1 entre las clases: En este caso el objeto agregado podrá estar
ligado a cero o más objetos contenedores. También es posible determinar si al
eliminar el objeto agregado se debe eliminar todo objeto que lo contenga
(eliminación en cascada).
Introducción a NHibernate 19
Para el caso del mapeo de Persona, en la tabla SQL debemos agregar un campo
que sea del mismo tipo que el campo IdDireccion de la tabla tblDireccion, que manejará
la relación entre las tablas (a nivel lógico). Debemos agregar una directiva que indique
la relación que queramos construir entre Persona y Dirección. Para esto mapeamos
todos los atributos de manera simple (salvo el atributo dirección) y agregamos el mapeo
de Dirección dependiendo del tipo de relación que queramos construir:
• Relación 1 a 1: Agregamos.
<one-to-one name="Dir" class="Direccion"
column="IdDireccion" />
• Relación n a 1: Agregamos.
<many-to-one name="Dir" class="Direccion"
column="IdDireccion" />
Introducción a NHibernate 20
Imports Iesi.Collections;
Introducción a NHibernate 21
not-null=”true”/>
<generator class=”uuid.hex”/>
</id>
<property name=”Descripcion” column=”descripcion”/>
</class>
Por otro lado, el mapeo de la clase JefeProyecto requiere de una etiqueta extra
que haga referencia a la relación entre un jefe de proyecto con varios proyectos. Esta
etiqueta dependerá del tipo de colección que se haya implementado. A continuación el
mapeo para la clase JefeProyecto que implementa una colección de tipo ISet.
jp.Proyectos.Add(p1);
jp.Proyectos.Add(p2);
jp.Proyectos.Add(p3);
jp.Save();
Introducción a NHibernate 22
/* Se puede implementar que en el método Save de
JefeProyecto se guarden primero los proyectos y luego el
objeto JefeProyecto. */
Las colecciones de objetos pueden ser mapeadas utilizando las etiquetas <set>,
<list>, <map> y <bag>. Cada una de estas etiquetas está orientada a soportar un tipo de colección
en particular. A continuación se listan las principales características de cada uno de estos
elementos:
• <set>: Colección desordenada sin duplicados. Compatible con la interfaz
Iesi.Collections.ISet.
• <list>: Colección ordenada, requiere una columna índice. Compatible con la
interfaz System.Collections.IList.
• <map>: Colección que almacena pares de elementos. Compatible con la interfaz
System.Collections.IDictionary.
• <bag>: Colección desordenada que permite elementos duplicados. Sirve para
representar listas no ordenadas.
Introducción a NHibernate 23
característica se puede definir en el archivo de mapeo indicando lazy=”true” en los
elementos <set>, <list>, <map> o <bag>.
Introducción a NHibernate 24
table=”Banco_Cliente”>
<key column=”BancoID”/>
<many-to-many class=”Cliente”
column=”ClienteID”/>
</set>
</class>
Las colecciones soportadas por las relaciones muchos-a-muchos son las mismas
que para el caso de uno-a-muchos y las reglas de definición son las mismas (atributos de
clase definidos como de tipo de la interfaz correspondiente (ISet, IList, etc.).
Introducción a NHibernate 25
5.- Manipulación de datos persistentes.
En la sección anterior, vimos ejemplos básicos de cómo crear objetos
persistentes y cómo registrar esta persistencia en la base de datos. Ahora veremos cómo
recuperar objetos de la base de datos para poder modificar sus estados y guardar estos
nuevos estados en la base de datos.
Hay dos modos de recuperar objetos desde la base de datos, ambos basados en el
Id del objeto (clave primaria de la tabla asociada). El primero de ellos se invoca cuando
creamos el objeto (en este caso tom). El método Load retorna un objeto, por lo que es
necesario hacer el cast al objeto requerido. Como parámetros debemos indicar el tipo
del objeto requerido y el id que estamos buscando.
Otra opción es crear el objeto y luego llamar al método Load, ahora como primer
parámetro indicamos el objeto donde será almacenado el resultado de la carga y como
segundo parámetro el identificador del objeto a recuperar.
Una opción interesante es refrescar el contenido de un objeto (en caso que haya
sido actualizado desde otro sistema o sesión). El método a utilizar es Refresh, al cual se
le debe indicar el objeto a refrescar.
session.Refresh(cat);
Introducción a NHibernate 26
5.2 Realizando consultas simples.
Introducción a NHibernate 27