Está en la página 1de 27

Introduccin a NHibernate

Francisco Daines O. 20/01/2007

1.- Que es NHibernate y para qu sirve.........................................................................3 2.- Instalacin de NHibernate.........................................................................................6 3.- Configuracin 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.- Manipulacin de datos persistentes........................................................................26 5.1 Recuperando un objeto de la base de datos. .........................................................26 5.2 Realizando consultas simples................................................................................27

Introduccin a NHibernate

1.- Que es NHibernate y para qu sirve.


El desarrollo de aplicaciones utilizando el paradigma OO es cada vez ms 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 anloga, 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 abstraccin 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 cdigo escrito en una gran cantidad de lneas de, quizs, una gran cantidad de clases. Por lo que la prdida 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 programacin tales como C++, Java y la plataforma MS.NET. Adems de ser escasas, la mayora de estas bases de datos estn en versiones de testing, por lo que implementar estas bases de datos significa un riesgo que muchas empresas no estn dispuestas a correr. Hasta hace un tiempo la tarea de comunicar OO con BDR era difcil, luego, con la creacin del modelo DAO (Data Access Object) se simplific el asunto, definiendo que seran slo algunos objetos los que se comunicaran con la BDR, disminuyendo notoriamente el impacto de realizar cambios en la Base de Datos. Pero todava no era posible obtener independencia entre el cdigo de las clases y el acceso a la base de datos. Hace un par de aos se liber la primera versin de un Framework de persistencia llamado NHibernate, una implementacin para el Microsoft .Net FrameWork de Hibernate (implementado originalmente slo para Java). Bueno pero Qu es precisamente NHibernate? y Para qu utilizar NHibernate? son algunas de las preguntas que tratar de responder a continuacin. 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 motivacin 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 slo trabaja con objetos, los cuales puede guardar en una base de datos utilizando mtodos de los mismos objetos, pero nunca escribir ni analizar una sentencia SQL en su cdigo. La arquitectura de NHibernate se puede representar de una manera simple como el siguiente diagrama:

Introduccin a NHibernate

Podemos notar que la aplicacin trabajar con objetos persistentes, pero sin comunicarse directamente con la base de datos. En su lugar, la comunicacin ser con el framework Nhibernate, el cual se compone de una seccin de configuracin (puede ser archivo App.config o Web.config segn 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 (insercin, actualizacin, borrado, seleccin). Entrando un poco ms en detalle, una arquitectura ligera de NHibernate es cuando la aplicacin proporciona sus propias conexiones ADO.NET y maneja lo que son transacciones, entonces la arquitectura ser:

Introduccin a NHibernate

Los objetos persistentes requieren almacenar estados, para esto es necesario utilizar una sesin (canal de comunicacin entre la aplicacin y la base de datos). La sesin de comunicacin 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 cdigo o configurando los archivos App.config o Web.config. Ahora, si deseamos utilizar todas las caractersticas que provee NHibernate, podemos utilizar una arquitectura como la que se muestra a continuacin:

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 estn expuestas a la aplicacin, 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 slo trabajar con objetos capaces de almacenar y recuperar estados, con todo lo que ello significa (manejo de relaciones entre objetos, jerarqua de objetos, etc.).

Introduccin a NHibernate

2.- Instalacin de NHibernate.


NHibernate es una librera (.dll) que se utiliza con Microsoft Visual Studio .NET. Actualmente la versin 1.0.3 es stable y soporta las caractersticas del .Net Framework 1.1 (VS 2003). Tambin existe la versin 1.2.0 beta, desarrollada con el objetivo de brindar soporte a todas las caractersticas del .Net Framework 2.0 (VS 2005). Ambas versiones de NHibernate se pueden obtener desde la pgina oficial del proyecto (http://nhibernate.sourceforge.net). Existe un paquete de extensiones llamado Nhibernate Contrib disponible para la versin 1.0.2 de NHibernate, que ofrece caractersticas extra a las ofrecidas en el paquete original de NHibernate. Hoy en da slo existe el paquete Contrib para la versin 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 algn directorio del disco duro. Para trabajar con NHibernate desde un proyecto de VS 2003 o 2005 slo es necesario agregar referencias a las siguientes libreras de NHibernate: NHibernate.dll Las siguientes libreras son de utilidad en los proyectos que implementen el framework Nhibernate. log4net.dll: Permite controlar un log de los eventos ocurridos durante la ejecucin de una aplicacin. Este log puede almacenarse en un archivo plano o en una base de datos. Iesi.Collections.dll: Implementa algunas colecciones, como Sets, que no estn disponibles en el .NET Framework. En este momento nuestro proyecto est listo para poder utilizar las caractersticas del Framework NHibernate.

Introduccin a NHibernate

3.- Configuracin de NHibernate.


Como bien sabemos, la idea de utilizar NHibernate es facilitar la interaccin entre objetos y tablas en un modelo de datos relacional. Si bien, el desarrollador no interacta directamente con la base de datos, Nhibernate s lo hace, por lo que es necesario indicar en un archivo de configuracin cul ser la base de datos a utilizar y cmo conectarse con dicha base de datos. Existen varias formas de configurar la comunicacin entre NHibernate y la base de datos, sin embargo la ms recomendable es utilizar un archivo App.config (configuracin de proyecto) ya que permite cambiar la configuracin de acceso sin cambiar el cdigo de la aplicacin en s. Un archivo de configuracin de aplicaciones es un archivo XML que permite configurar algunas opciones especficas de la aplicacin que desarrollamos. La estructura bsica del archivo de configuracin 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> <!-- configuracin de nhibernate --> </nhibernate> </configuration>

En la zona de configuracin es posible agregar las configuraciones especficas para cada aplicacin utilizada en la solucin actual. En este caso es necesario configurar las opciones de NHibernate para que acceda a la base de datos utilizada. A continuacin un ejemplo de la configuracin 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 segn sea la base de datos que estemos utilizando para almacenar los datos de nuestra aplicacin. La versin 1.2 de Nhibernate soporta las bases de datos ms utilizadas hoy en da. A continuacin se listan algunas de las bases de datos soportadas por Nhibernate 1.2 y los valores de configuracin de las variables XXX, YYY y ZZZ del ejemplo anterior.

Introduccin a NHibernate

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 configuracin del archivo App.config definida, slo nos queda obtener el driver necesario para la conexin 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/

Introduccin a NHibernate

4.- Ejemplos sencillos de mapeo OR.


NHibernate permite la implementacin de varios modelos de diseo para la persistencia de objetos, por tanto se puede optar por utilizar objetos DAO, patrn AbstractFactory, entre otros. Se recomienda crear una clase que controle la creacin de sesiones y acceso a la base de datos, luego, segn 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 conexin a la base de datos, esta clase manejar la creacin de sesiones para poder realizar las operaciones de bsqueda, insercin, actualizacin y eliminacin. A continuacin se presenta un ejemplo del cdigo de la clase NHibernateManager que utilizaremos en los ejemplos de esta seccin. 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 conexin a la base de datos y asociar una sesin a dicha conexin. Antes de ver los ejemplos bsicos de almacenamiento de objetos persistentes y su correspondiente mapeo objeto-relacional vamos a necesitar implementar el mtodo Save en cada objeto persistente. El contenido de este mtodo se muestra a continuacin.

Introduccin a NHibernate

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 atmica controlados por una transaccin. En caso de suceder alguna anomala durante una transaccin abierta, se puede indicar a la aplicacin que aborte la transaccin y esto anular cualquier posible cambio en la base de datos. Para utilizar transacciones en el mtodo Save de los objetos persistentes se puede utilizar el siguiente cdigo 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 convencin es clasePersistente.hbm.xml. Todo archivo de mapeo debe contener como nodo raz lo siguiente: <hibernate-mapping xmlns=urn:nhibernate-mapping-2.2 namespace=miNamespace assembly=miAssembly default-lazy=false > <class . . . > <!-- mapeo de la clase --> </class> Introduccin a NHibernate 10

</hibernate-mapping> Donde se hace referencia al assembly donde estar contenida la clase mapeada y su archivo de mapeo. Adems se referencia el namespace de la clase mapeada. NHibernate 1.2 considera por defecto la opcin default-lazy=true, lo que requiere que todas las propiedades pblicas de las clases persistentes sean virtuales, si queremos evitar esta opcin 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 ningn tipo (asociacin, agregacin, etc.) con otra clase. Nuestra clase de ejemplo seguir siendo Cat, la cual ya posee el mtodo 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 );

Introduccin 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 continuacin: <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 jerarquas de clases. Las estrategias permitidas son: table-per-class-hierarchy table-per-subclass table-per-concrete-class (polimorfismo implcito) Para efectos de este documento slo se vern las estrategias table-per-classhierarchy y table-per-subclass. Supongamos que tenemos el siguiente ejemplo. Una empresa necesita manejar la informacin personal tanto de sus empleados como de sus clientes, por lo tanto genera un modelo jerrquico, 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 definicin de clases se muestra a continuacin. public class Persona { private string id; private string nombre; private int edad;

Introduccin 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 slo una tabla por jerarqua de datos, lo que significa que en la base de datos slo 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 definicin. Un campo extra es requerido para manejar polimorfismo, este campo se conocer como

Introduccin a NHibernate

13

discriminador e indicar a NHibernate a qu clase corresponde una fila particular de la tabla tblPersona. Un ejemplo de la definicin de la tabla tblPersona para la estrategia table-perclass-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 jerarqua. En este caso tendremos las tablas Persona, Cliente y Empleado, las cuales se definen a continuacin. CREATE TABLE Persona( idPersona varchar(32) NOT NULL, name varchar(30) NOT NULL, edad int ); CREATE TABLE Cliente( Introduccin a NHibernate 14

personaId varchar(32) NOT NULL, tipocliente varchar(30) ); CREATE TABLE Empleado( personaId varchar(32) NOT NULL, sueldo int ); Para manejar la relacin de jerarqua 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 estarn sus datos personales. Este campo extra puede ser una clave fornea en la base de datos, pero eso es tarea del DBA, por lo que no se detallar sobre ese tema en esta seccin. Ahora, el mapeo de las clases pertenecientes a esta jerarqua se muestra a continuacin: <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 sealar 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 restriccin de la OO. En cambio, si optamos por la estrategia table-per-concrete-class tendremos que slo realizaremos mapeos de las clases concretas (no abstractas). Realizado el mapeo OR de la jerarqua Persona, utilizar las clases persistentes es tan simple como: Persona p = new Persona(); p.Nombre = Rosa; p.Edad = 35; p.Save();

Introduccin 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 composicin corresponde a una relacin 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 relacin entre clases se ve materializada como una agregacin de columnas en la tabla principal de los atributos de la clase contenida. A continuacin se presenta un ejemplo de una composicin de clases.

Es posible representar esta composicin en una tabla SQL agregando los atributos de la clase Direccin a la tabla que corresponde a la clase Persona. Haciendo esto, nuestra tabla tblPersona quedara de la siguiente manera:

Introduccin 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 relacin de equivalencia directa entre modelos, este mapeo tiene sentido ya que al eliminar una instancia de Persona efectivamente se eliminar la instancia Direccin asociada a dicha Persona. Ahora veremos como llevar a la prctica el mapeo del ejemplo recin expuesto. Para este caso primero debemos crear una clase Direccin, la cual se define a continuacin. 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 composicin de Direccin en la clase Persona, debemos agregar un atributo a Persona que sea de tipo Direccin como se muestra a continuacin. public class Persona Introduccin a NHibernate 17

{ private private private private private private private string id; string rut; string nombre; string apellido; int edad; char sexo; Direccion dir;

/* Implementacion de propiedades pblicas */ } Teniendo estas definiciones para ambas clases ahora slo falta configurar correctamente el archivo Persona.hbm.xml para mapear la clase Persona y su relacin con la clase Direccin. Para indicarle a NHibernate que esta es una relacin de Composicin 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 cmo 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;

Introduccin a NHibernate

18

. . . // guardar objeto p. p.Save();

4.3.1 Agregacin.
Tambin 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 relacin en OO se conoce como Agregacin. El siguiente ejemplo representa la relacin entre Persona y Direccin, ahora desde el punto de vista de agregacin

Es posible implementar una agregacin de dos maneras, dependiendo de las propiedades de la agregacin que se requieran. Una relacin 1 a 1 entre las clases: En este caso el objeto agregado slo estar ligado al objeto contenedor, pero al borrar el objeto contenedor no es necesario borrar el objeto agregado. Una relacin n a 1 entre las clases: En este caso el objeto agregado podr estar ligado a cero o ms objetos contenedores. Tambin es posible determinar si al eliminar el objeto agregado se debe eliminar todo objeto que lo contenga (eliminacin en cascada).

Dadas las mismas definiciones de clases vistas para el caso de composicin, 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>

Introduccin 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 relacin entre las tablas (a nivel lgico). Debemos agregar una directiva que indique la relacin que queramos construir entre Persona y Direccin. Para esto mapeamos todos los atributos de manera simple (salvo el atributo direccin) y agregamos el mapeo de Direccin dependiendo del tipo de relacin que queramos construir: Relacin 1 a 1: Agregamos.
<one-to-one name="Dir" class="Direccion" column="IdDireccion" />

Relacin 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 coleccin de objetos de otra clase y la relacin es uno a muchos. Un Jefe de Proyecto puede manejar uno o ms proyectos a la vez, y un proyecto debe estar asociado a un Jefe de Proyecto.

A continuacin se expone un ejemplo del cdigo 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; } } }

Introduccin 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 librera 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 coleccin en una clase y es que en la propiedad la coleccin debe definirse del tipo de la interfaz correspondiente a la clase de coleccin, 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 cdigo de la clase para los distintos tipos de colecciones ahora es necesario conocer cmo realizar el mapeo entre nuestra clase con coleccin y nuestra base de datos. Para esto, primero es necesario modelar la base de datos, pero esto no es tarea difcil. En el caso que estudiamos, una relacin 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 fornea. 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 cdigo. <class name="Proyecto" table="tblProyecto"> <id name=IdProyecto>

Introduccin a NHibernate

21

<column name=IdProyecto sql-type=char(32) 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 relacin entre un jefe de proyecto con varios proyectos. Esta etiqueta depender del tipo de coleccin que se haya implementado. A continuacin el mapeo para la clase JefeProyecto que implementa una coleccin 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 coleccin de proyectos. Luego se indica que la relacin entre proyectos y jefe de proyecto se har mediante la columna idJefeProyecto de la tabla asociada a la clase Proyecto. Con esta configuracin es posible crear proyectos y asociarlos a un jefe de proyecto en particular. Un ejemplo de cmo realizar esta accin 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();

Introduccin a NHibernate

22

/* Se puede implementar que en el mtodo Save de JefeProyecto se guarden primero los proyectos y luego el objeto JefeProyecto. */ Eventualmente, la clase Proyecto puede tener el otro extremo de la relacin unoa-muchos, es decir tendr un elemento de relacin muchos-a-uno (como el visto en agregacin). 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.
<set>, y <bag>. Cada una de estas etiquetas est orientada a soportar un tipo de coleccin en particular. A continuacin se listan las principales caractersticas de cada uno de estos elementos: <list>, <map>

Las colecciones de objetos pueden ser mapeadas utilizando las etiquetas

<set>: Coleccin desordenada sin duplicados. Compatible con la interfaz Iesi.Collections.ISet. <list>: Coleccin ordenada, requiere una columna ndice. Compatible con la interfaz System.Collections.IList. <map>: Coleccin que almacena pares de elementos. Compatible con la interfaz System.Collections.IDictionary. <bag>: Coleccin desordenada que permite elementos duplicados. Sirve para representar listas no ordenadas. Tambin es posible utilizar las etiquetas <array> y <primitive-array> pero no se ha encontrando informacin sobre estas etiquetas. Una propiedad importante del mapeo de colecciones de objetos es conocida como inicializacin perezosa o carga perezosa (lazy loading, lazy initialization) . Esta estrategia indica que si un objeto X tiene una coleccin de objetos Y, entonces al recuperar X desde la base de datos, slo se recuperarn las propiedades atmicas 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 coleccin de objetos Y. En el caso de JefeProyecto slo se recupera el identificador del jefe de proyecto, y luego, si en algn momento se requiere recorrer la coleccin de proyectos cargo de este jefe de proyecto, slo entonces se recuperar la coleccin de proyectos desde la base de datos. Esta

Introduccin a NHibernate

23

caracterstica se puede definir en el archivo de mapeo indicando elementos <set>, <list>, <map> o <bag>.

lazy=true

en los

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 coleccin de objetos de tipo B y a la vez, la clase B tiene una coleccin de objetos de tipo A. Esta relacin entre A y B se puede llevar al modelo de BD utilizando una tabla intermedia que relacione los cdigos de los elementos de A con los elementos de B. Esta tabla intermedia slo estar constituida por claves forneas 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 vera algo como sigue:

Esta relacin muchos a muchos debe ser representada en un modelo relacional aadiendo 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 repeticin).

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

Introduccin 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 cmo se realiza el mapeo muchos-a-muchos veremos en detalle la etiqueta de la coleccin: <set name=Clientes lazy=true table=Banco_Cliente> <key column=BancoID/> <many-to-many class=Cliente column=ClienteID/> </set> La relacin 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 definicin son las mismas (atributos de clase definidos como de tipo de la interfaz correspondiente (ISet, IList, etc.).

Introduccin a NHibernate

25

5.- Manipulacin de datos persistentes.


En la seccin anterior, vimos ejemplos bsicos de cmo crear objetos persistentes y cmo registrar esta persistencia en la base de datos. Ahora veremos cmo 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 mtodo Load retorna un objeto, por lo que es necesario hacer el cast al objeto requerido. Como parmetros debemos indicar el tipo del objeto requerido y el id que estamos buscando. Cat tom = (Cat) session.Load(typeof(Cat), idBuscado); Otra opcin es crear el objeto y luego llamar al mtodo Load, ahora como primer parmetro indicamos el objeto donde ser almacenado el resultado de la carga y como segundo parmetro el identificador del objeto a recuperar. Cat tom = new Cat(); Session.Load(tom, idBuscado); El mtodo Load lanza una NHibernate.UnresolvableObjectException en caso de no existir el objeto asociado al identificador dado como parmetro. Una alternativa al uso del mtodo Load es utilizar el mtodo Get de la clase session. Este mtodo devuelve el objeto indicado y en caso de no encontrarlo retorna null, por lo que es ms indicado para comprobar la existencia de un objeto. Tambin tiene la opcin 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 opcin interesante es refrescar el contenido de un objeto (en caso que haya sido actualizado desde otro sistema o sesin). El mtodo a utilizar es Refresh, al cual se le debe indicar el objeto a refrescar. session.Refresh(cat);

Introduccin a NHibernate

26

5.2 Realizando consultas simples.


Es comn que no conozcamos con anterioridad el identificador del o los objetos que queramos recuperar de la base de datos. Frente a estas situaciones los mtodos 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 ms adelante. A continuacin 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 ms 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]; }

Introduccin a NHibernate

27

También podría gustarte