Sunteți pe pagina 1din 27

Introducción 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:

En esta arquitectura, NHibernate provee lo que es el control de transacciones


(utilizando Transactions creadas por una TransactionFactory) y control de conexiones
ADO.NET que no están expuestas a la aplicación, sin embargo pueden ser extendidas o
implementadas por los desarrolladores.
En resumen, NHibernate es un Framework que pretende abstraer por completo al
desarrollador de lo que es el manejo de persistencia en las aplicaciones. Al utilizar
NHibernate el desarrollador sólo trabajará con objetos capaces de almacenar y recuperar
estados, con todo lo que ello significa (manejo de relaciones entre objetos, jerarquía de
objetos, etc.).

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

Las siguientes librerías son de utilidad en los proyectos que implementen el


framework Nhibernate.
• log4net.dll: Permite controlar un log de los eventos ocurridos durante la
ejecución de una aplicación. Este log puede almacenarse en un archivo plano o
en una base de datos.
• Iesi.Collections.dll: Implementa algunas colecciones, como Sets, que no están
disponibles en el .NET Framework.

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.

<?xml version=”1.0” encoding=”utf-8”?>


<configuration>
<configSections>
<section name=”nhibernate”
type=”System.Configuration.NameValueSectionHandler, System,
Version=”1.0.5000.0,Culture=neutral,
PublicKeyToken=b77a5c561934e089”
/>
</configSections>
<nhibernate>
<!-- configuración de nhibernate -->
</nhibernate>
</configuration>

En la zona de configuración es posible agregar las configuraciones específicas


para cada aplicación utilizada en la solución actual. En este caso es necesario configurar
las opciones de NHibernate para que acceda a la base de datos utilizada. A continuación
un ejemplo de la configuración de NHibernate para acceder a una base de datos.

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

Microsoft SQL Server 2005


• XXX: MsSql2005Dialect
• YYY: SqlClientDriver
• ZZZ: “Server=Server;Database=EjemplosNHibernate;User
Id=usuario;Password=pwd”

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.

• SQL Server 2000-2005: System.Data.SqlClient en System.Data.dll (VS2005).


• MySQL: http://dev.mysql.com/downloads/connector/net/1.0.html
• Oracle: System.Data.OracleClient.dll (en VS2005).
• PostgreSQL: http://pgfoundry.org/projects/npgsql/

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.

public class NHibernateManager()


{
private static IsessionFactory nhFactory;
private static Configuration nhConfig;

static void NHibernateManager()


{
nhConfig = new Configuration();
nhConfig.AddAssembly(“NombreAssembly”);
nhFactory = nhConfig.BuildSessionFactory();
}

public static Isession NHSession


{
get { return nhFactory.OpenSession(); }
}

public static void CloseSessionFactory()


{
If ( nhFactory != null )
nhFactory.Close();
}
}

Esta clase nos permitirá crear una nueva conexión a la base de datos y asociar
una sesión a dicha conexión.

Antes de ver los ejemplos básicos de almacenamiento de objetos persistentes y


su correspondiente mapeo objeto-relacional vamos a necesitar implementar el método
Save en cada objeto persistente. El contenido de este método se muestra a continuación.

Introducción a NHibernate 9
public void Save()
{
NhibernateManager nhm = new NHibernateManager();

ISession session = NHibernateManager.NHSession;

session.Save(this);
session.Close();

NHibernateManager.CloseSessionFactory();
}

Existe la posibilidad de manejar el acceso a la base de datos en base a


transacciones, es decir los cambios en la base de datos se realizan de manera atómica
controlados por una transacción. En caso de suceder alguna anomalía durante una
transacción abierta, se puede indicar a la aplicación que aborte la transacción y esto
anulará cualquier posible cambio en la base de datos. Para utilizar transacciones en el
método Save de los objetos persistentes se puede utilizar el siguiente código de ejemplo.

public void Save()


{
NhibernateManager nhm = new NHibernateManager();
ISession session = NHibernateManager.NHSession;
ITransaction tx = session.Transaction;

tx.Begin();

try {
session.Save(this);
tx.Commit();
} catch {
tx.RollBack();
}

session.Close();
NHibernateManager.CloseSessionFactory();
}

Otro requerimiento para el mapeo Objeto-Relacional es el poseer un archivo de


mapeo, este archivo está en formato xml y su nombre por convención es
clasePersistente.hbm.xml. Todo archivo de mapeo debe contener como nodo raíz lo
siguiente:

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

Donde se hace referencia al assembly donde estará contenida la clase mapeada y


su archivo de mapeo. Además se referencia el namespace de la clase mapeada.
NHibernate 1.2 considera por defecto la opción default-lazy=”true”, lo que requiere
que todas las propiedades públicas de las clases persistentes sean virtuales, si queremos
evitar esta opción seteamos el atributo a false.

4.1 Mapeo de clases sencillo.

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 class Cat


{
private string id;
private string nombre;

public string Id
{
get { return id; }
set { id = value; }
}

public string Nombre


{
get { return nombre; }
set { nombre = value; }
}

public void Save()


{
/* implementacion */
}
}

Donde el identificador del gato será un string de tipo hash (32


caracteres), entonces nuestra tabla SQL debe estar definida como
sigue:

CREATE TABLE gato(


idCat varchar(32) NOT NULL,
name varchar(30) NOT NULL
);

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:

<class name="Cat" table="gato">


<id name=”Id”>
<column name=”idCat” sql-type=”char(32)”
not-null=”true”/>
<generator class=”uuid.hex”/>
</id>
<property name="Nombre" column="name"/>
</class>

Teniendo todo esto listo, guardar un objeto Cat en la base de datos es algo tan
simple como:

Cat tom = new Cat();


tom.Nombre = “Tom”;
tom.Save();

4.2 Mapeo con herencia.

NHibernate permite 3 estrategias para manejo de persistencia de jerarquías de


clases. Las estrategias permitidas son:
• table-per-class-hierarchy
• table-per-subclass
• table-per-concrete-class (polimorfismo implícito)

Para efectos de este documento sólo se verán las estrategias table-per-class-


hierarchy y table-per-subclass.
Supongamos que tenemos el siguiente ejemplo. Una empresa necesita manejar la
información personal tanto de sus empleados como de sus clientes, por lo tanto genera
un modelo jerárquico, donde existe una clase Persona que contiene los datos personales
y subclases Empleado y Cliente que contienen los detalles requeridos para empleados y
clientes.
La definición de clases se muestra a continuación.

public class Persona


{
private string id;
private string nombre;
private int edad;

Introducción a NHibernate 12
public string Id
{
get { return id; }
set { id = value;}
}

public string Nombre


{
get { return nombre; }
set { nombre = value; }
}

public int Edad


{
get { return edad; }
set { edad = value; }
}

Public void Save()


{
/* implementacion */
}
}

public class Empleado : Persona


{
private int sueldo;
public int Sueldo{
get { return sueldo; }
set { sueldo = value; }
}
}

public class Cliente : Persona


{
private string tipoCliente;
public string TipoCliente{
get { return tipoCliente; }
set { tipoCliente = value; }
}
}

La primera estrategia es llamada table-per-class-hierarchy, es decir, en el


modelo de datos se tiene sólo una tabla por jerarquía de datos, lo que significa que en la
base de datos sólo habrá una tabla llamada, por ejemplo, tblPersona. Esta tabla
contendrá los campos necesarios para almacenar a la clase persona y todos los campos
necesarios para mapear los atributos de toda las subclases de persona, en este caso se
requiere que tblPersona agregue los campos sueldo y tipoCliente a su definición. Un
campo extra es requerido para manejar polimorfismo, este campo se conocerá como

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á:

CREATE TABLE tblPersona(


idPersona varchar(32) NOT NULL,
name varchar(30) NOT NULL,
edad int,
TipoPersona varchar(255),
sueldo int,
tipocliente varchar(30)
);

En este caso el discriminador será la columna TipoPersona y será de tipo string


que caracterice a la clase a la cual pertenece la fila correspondiente en la tabla. El
discriminador puede ser de cualquier tipo NHibernate.

<class name="Persona" table="tblPersona"


discriminator-value=”PERSONA”>
<id name=”Id”>
<column name=”idPersona” sql-type=”char(32)”
not-null=”true”/>
<generator class=”uuid.hex”/>
</id>
<discriminator column="TipoPersona" type="String"/>
<property name="Nombre" column="name"/>
<property name="Edad" column="edad"/>

<subclass name="Cliente" discriminator-value="CLIENTE">


<property name="TipoCliente" column="tipocliente"/>
</subclass>

<subclass name="Empleado" discriminator-value="EMPL">


< property name="Sueldo" column="sueldo"/>
</subclass>
</class>

La segunda estrategia es llamada table-per-subclass que, tal como lo indica,


utiliza una tabla por cada clase de la jerarquía. En este caso tendremos las tablas
Persona, Cliente y Empleado, las cuales se definen a continuación.

CREATE TABLE Persona(


idPersona varchar(32) NOT NULL,
name varchar(30) NOT NULL,
edad int
);

CREATE TABLE Cliente(

Introducción a NHibernate 14
personaId varchar(32) NOT NULL,
tipocliente varchar(30)
);

CREATE TABLE Empleado(


personaId varchar(32) NOT NULL,
sueldo int
);

Para manejar la relación de jerarquía entre las clases, es necesario agregar un


campo a las tablas Cliente y Empleado que será una referencia a la fila en la tabla
Persona donde estarán sus datos personales. Este campo extra puede ser una clave
foránea en la base de datos, pero eso es tarea del DBA, por lo que no se detallará sobre
ese tema en esta sección.
Ahora, el mapeo de las clases pertenecientes a esta jerarquía se muestra a
continuación:

<class name="Persona" table="Persona">


<id name=”Id”>
<column name=”idPersona” sql-type=”char(32)”
not-null=”true”/>
<generator class=”uuid.hex”/>
</id>
<property name="Nombre" column="name"/>
<property name="Edad" column="edad"/>

<joined-subclass name="Cliente" table="tblCliente">


<key column="PersonaId"/>
<property name="TipoCliente" column="tipocliente"/>
</joined-subclass>
<joined-subclass name="Empleado" table="tblEmpleado">
<key column="PersonaId"/>
<property name="Sueldo" column="sueldo"/>
</joined-subclass>
</class>

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:

Persona p = new Persona();


p.Nombre = “Rosa”;
p.Edad = 35;
p.Save();

Introducción a NHibernate 15
Empleado e = new Empleado();
e.Nombre = “Paulina Mondaca”;
e.Edad = 24;
e.Sueldo = 120000;
((Persona)e).Save();

Empleado e = new Empleado();


c.Nombre = “Jorge Garay”;
c.Edad = 34;
c.TipoCliente = “basico”;
((Persona)c).Save();

4.3 Mapeo de composiciones.

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.

Es posible representar esta composición en una tabla SQL agregando los


atributos de la clase Dirección a la tabla que corresponde a la clase Persona. Haciendo
esto, nuestra tabla tblPersona quedaría de la siguiente manera:

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.

public class Direccion


{
private string region;
private string ciudad;
private string calle;
private int numero;

public string Region


{
get { return region; }
set { region = value;}
}

public string Ciudad


{
get { return ciudad; }
set { ciudad = value;}
}

public string Calle


{
get { return calle; }
set { calle = value;}
}

public int Numero


{
get { return numero; }
set { numero = value;}
}
}

Para representar una composición de Dirección en la clase Persona, debemos


agregar un atributo a Persona que sea de tipo Dirección como se muestra a
continuación.

public class Persona

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;

/* Implementacion de propiedades públicas */

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:

<class name="Persona" table="tblPersona">


<id name=”Id”>
<column name=”PersId” sql-type=”char(32)”
not-null=”true”/>
<generator class=”uuid.hex”/>
</id>
<property name="Rut" column="rut"/>
<property name="Nombre" column="nombre"/>
<property name="Apellido" column="apellido"/>
...

<component name=”dir” class=”Direccion” >


<property name=”Region” column=”DIR_REGION”/>
<property name=”Ciudad” column=”DIR_CIUDAD”/>
<property name=”Calle” column=”DIR_CALLE”/>
<property name=”Numero” column=”DIR_NUMERO”/>
</component>
</class>
Un ejemplo de cómo crear una nueva persona es el siguiente:

Persona p = new Persona();


p.Rut = “15000000-0”;
p.Nombre = “Nombre Persona”;
p.Apellido = “Apellido”;
p.Edad = 41;
p.Sexo = “M”;
p.Dir = new Direccion();
p.Dir.Region = “Metropolitana”;
p.Dir.Ciudad = “Santiago”;
p.Dir.Calle = “Agustinas”;
p.Dir.Numero = 123;

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

Es posible implementar una agregación de dos maneras, dependiendo de las


propiedades de la agregación que se requieran.

• 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).

Dadas las mismas definiciones de clases vistas para el caso de composición,


entonces necesitamos crear los archivos Persona.hbm.xml y Direccion.hbm.xml. El
contenido del archivo de mapeo de Direccion debe ser un mapeo de una clase simple, es
decir:

<class name="Direccion" table="tblDireccion">


<id name=”IdDireccion”>
<column name=”IdDireccion” sql-type=”char(32)”
not-null=”true”/>
<generator class=”uuid.hex”/>
</id>
<property name=”Region” column=”region”/>
<property name=”Ciudad” column=”ciudad”/>
<property name=”Calle” column=”calle”/>
<property name=”Numero” column=”numero”/>
</class>

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" />

4.4 Mapeo de relaciones uno a muchos.

El siguiente es un ejemplo de una clase que contiene una colección de objetos de


otra clase y la relación es uno a muchos. Un Jefe de Proyecto puede manejar uno o más
proyectos a la vez, y un proyecto debe estar asociado a un Jefe de Proyecto.

A continuación se expone un ejemplo del código asociado al modelo de clases:

public class Proyecto


{
private string idProyecto;
private string descripcion;

public string IdProyecto


{
get { return idProyecto; }
set { idProyecto = value; }
}

public string Descripcion


{
get { return descripcion; }
set { descripcion = value; }
}
}

Introducción a NHibernate 20
Imports Iesi.Collections;

public class JefeProyecto


{
private string idJefeProyecto;
private ISet proyectos; //requiere Iesi.Collections

public string IdJefeProyecto


{
get { return idProyecto; }
set { idProyecto = value; }
}

public ISet Proyectos


{
get { return proyectos; }
set { proyectos = value; }
}
}

Las colecciones pueden ser implementadas como Listas, Arreglos, Diccionarios


o cualquier clase que implemente la interfaz System.Collections.IDictionary,
Systems.Collections.IList o Iesi.Collections.ISet. Esta última se encuentra en la librería
Iesi.Collections, que se distribuye junto con NHibernate, por lo tanto es necesario
agregar la referencia correspondiente a nuestro proyecto.
Existe un detalle al implementar una colección en una clase y es que en la
propiedad la colección debe definirse del tipo de la interfaz correspondiente a la clase de
colección, es decir una propiedad puede ser de tipo IList, IDictionary o ISet pero no, por
ejemplo, de tipo Set.
Teniendo idea de las posibles definiciones en el código de la clase para los
distintos tipos de colecciones ahora es necesario conocer cómo realizar el mapeo entre
nuestra clase con colección y nuestra base de datos. Para esto, primero es necesario
modelar la base de datos, pero esto no es tarea difícil. En el caso que estudiamos, una
relación uno a muchos puede ser representada por una tabla tblJefeProyecto que
contenga el id del jefe de proyecto y varias tablas tblProyecto que hagan referencia al id
del jefe del proyecto como clave foránea. En pocas palabras un esquema relacional
puede ser el siguiente:

Luego, el mapeo de la clase Proyecto se realiza de manera simple, como se


muestra en el siguiente código.

<class name="Proyecto" table="tblProyecto">


<id name=”IdProyecto”>
<column name=”IdProyecto” sql-type=”char(32)”

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.

<class name="JefeProyecto" table="tblJefeProyecto">


<id name=”IdJefeProyecto”>
<column name=”IdJefeProyecto” sql-type=”char(32)”
not-null=”true”/>
<generator class=”uuid.hex”/>
</id>
<set name="Proyectos">
<key column="idJefeProyecto" />
<one-to-many class="Proyecto" />
</set>
</class>

Lo que se indica en la etiqueta <set> es el nombre de la propiedad de la


colección de proyectos. Luego se indica que la relación entre proyectos y jefe de
proyecto se hará mediante la columna idJefeProyecto de la tabla asociada a la clase
Proyecto.
Con esta configuración es posible crear proyectos y asociarlos a un jefe de
proyecto en particular. Un ejemplo de cómo realizar esta acción es el siguiente;

JefeProyecto jp = new JefeProyecto();


Jp.Proyectos = new Iesi.Collections.HashedSet();

Proyecto p1 = new Proyecto();


p1.Descripcion = “proyecto 1”;
Proyecto p2 = new Proyecto();
p2.Descripcion = “proyecto 2”;
Proyecto p1 = new Proyecto();
p1.Descripcion = “proyecto 3”;
p1.Save();
p2.Save();
p3.Save();

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. */

Eventualmente, la clase Proyecto puede tener el otro extremo de la relación uno-


a-muchos, es decir tendrá un elemento de relación muchos-a-uno (como el visto en
agregación). Esto implica agregar una propiedad de tipo JefeProyecto en la clase
Proyecto y luego definir el siguiente archivo de mapeo de la clase Proyecto.

<class name="Proyecto" table="tblProyecto">


<id name=”IdProyecto”>
<column name=”IdProyecto” sql-type=”char(32)”
not-null=”true”/>
<generator class=”uuid.hex”/>
</id>
<property name=”Descripcion” column=”descripcion”/>
<many-to-one name=”jpId” class=”JefeProyecto”
column=”IdJefeProyecto”/>
</class>

En este ejemplo, la clase Proyecto tiene un atributo llamado jpId de tipo


JefeProyecto, el cual será una referencia al contenido de la columna IdJefeProyecto de
la tabla asociada a la clase 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.

También es posible utilizar las etiquetas <array> y <primitive-array> pero no


se ha encontrando información sobre estas etiquetas.
Una propiedad importante del mapeo de colecciones de objetos es conocida
como inicialización perezosa o carga perezosa (lazy loading, lazy initialization). Esta
estrategia indica que si un objeto X tiene una colección de objetos Y, entonces al
recuperar X desde la base de datos, sólo se recuperarán las propiedades atómicas de X
(tipo String, Int, Char, Boolean, etc) pero no las colecciones de objetos Y asociados a X
sino hasta que realmente se quiera utilizar la colección de objetos Y. En el caso de
JefeProyecto sólo se recupera el identificador del jefe de proyecto, y luego, si en algún
momento se requiere recorrer la colección de proyectos cargo de este jefe de proyecto,
sólo entonces se recuperará la colección de proyectos desde la base de datos. Esta

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

4.5 Mapeo de relaciones muchos a muchos.

El mapeo muchos a muchos consiste en que dadas dos clases, A y B, entonces A


tiene una colección de objetos de tipo B y a la vez, la clase B tiene una colección de
objetos de tipo A.
Esta relación entre A y B se puede llevar al modelo de BD utilizando una tabla
intermedia que relacione los códigos de los elementos de A con los elementos de B. Esta
tabla intermedia sólo estará constituida por claves foráneas de las claves principales de
las tablas mapeadas de A y B.
En el siguiente caso tenemos que un banco tiene una cartera de clientes, y a su
vez, lo clientes pueden ser clientes de varios bancos. El modelo de este caso se vería
algo como sigue:

Esta relación muchos a muchos debe ser representada en un modelo relacional


añadiendo una tercera tabla que haga de NUB entre Banco y Cliente, es decir esta
tercera tabla relacionará a los clientes con los bancos, teniendo plena libertad de
relacionar cualquier cliente con cualquier banco (claro sin permitir repetición).

Teniendo este modelo de clases y su respectivo modelo relacional podemos


realizar el mapeo OR cosa de poder persistir nuestras clases.

<class name=”Banco” table=”Banco”>


<id name=”ID”>
<generator class=”identity”/>
</id>
<set name=”Clientes” lazy=”true”

Introducción a NHibernate 24
table=”Banco_Cliente”>
<key column=”BancoID”/>
<many-to-many class=”Cliente”
column=”ClienteID”/>
</set>
</class>

<class name=”Cliente” table=”Cliente”>


<id name=”ID”>
<generator class=”identity”/>
</id>
<property name=”Nombre” column=”Nombre” />
<property name=”Apellido” column=”Apellido” />
</class>

Para explicar cómo se realiza el mapeo muchos-a-muchos veremos en detalle la


etiqueta de la colección:

<set name=”Clientes” lazy=”true” table=”Banco_Cliente”>


<key column=”BancoID”/>
<many-to-many class=”Cliente” column=”ClienteID”/>
</set>

La relación entre Banco y Cliente se realiza buscando una instancia en la tabla


Banco_Cliente que contenga en la columna BancoID el identificador del banco y en la
columna ClienteID el identificador asociado a la clase Cliente.

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.

5.1 Recuperando un objeto de 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.

Cat tom = (Cat) session.Load(typeof(Cat), idBuscado);

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.

Cat tom = new Cat();


Session.Load(tom, idBuscado);

El método Load lanza una NHibernate.UnresolvableObjectException en caso


de no existir el objeto asociado al identificador dado como parámetro.
Una alternativa al uso del método Load es utilizar el método Get de la clase
session. Este método devuelve el objeto indicado y en caso de no encontrarlo retorna
null, por lo que es más indicado para comprobar la existencia de un objeto. También
tiene la opción de invocarlo determinando el bloqueo que se quiera obtener para el
objeto recuperado.

Cat tom = (Cat) session.Get(typeof(Cat), idBuscado);


Cat perkins = (Cat) session.Get(typeof(Cat), id2, bloqueo);

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.

Es común que no conozcamos con anterioridad el identificador del o los objetos


que queramos recuperar de la base de datos. Frente a estas situaciones los métodos
Load y Get no son útiles y es necesario utilizar otra estrategia.
Se pueden recuperar objetos desde la base de datos utilizando una consulta en
lenguaje HQL (Hibernate Quero Language), el cual se verá en profundidad más
adelante.
A continuación se presentan algunos ejemplos de consultas a la base de datos

IQuery q1 = session.createQuery(“from Cat as cat”);


IList lista = q1.List();

IQuery q2 = session.createQuery(“from Cat as cat ” +


“where cat.Nombre=:nombreGato”);
q2.SetParameter(“nombreGato”,valor);
IList lista2 = q2.List();
Cat primerGato = q2.List()[0]

Podemos realizar consultas que obtengan más de un campo como resultado de la


siguiente manera:

INumerable resultado = session.Enumerable(


“select mc.b, mc.c, count(mc) from miClass mc” +
“group by mc.a” );

foreach ( object[] filaActual in resultado ){


/* tipo1 y tipo2 son los tipos de las columnas 1 y 2
de la consulta que hemos realizado */
tipo1 col1 = (tipo1) filaActual[0];
tipo2 col2 = (tipo2) filaActual[1];
int total = (int) filaActual[2];
}

Introducción a NHibernate 27

S-ar putea să vă placă și