11.27.08
ORM + NHibernate
ORM
El mapeo entre el modelo de objetos y el modelo relacional es una solución para conseguir la persistencia de datos en nuestros programas orientados a objetos, permitiendo que se puedan almacenar las instancias de los objetos en una base de datos normal y que luego puedan ser recuperados a su forma usual programática.
Ahora bien, un acercamiento quizás más clásico para esto es el trasladar los datos de los objetos a estructuras de datos simples que puedan ser almacenadas en la base de datos tras un proceso de conversiones, pero esto implica que los datos no son limpiamente almacenados, sino que deben guardarse de otras maneras con algún procedimiento intermedio. Los paquetes ORM automatizan este proceso haciendo las operaciones de conversión transparentes para el programador.
Con un paquete de ORM el programador simplemente indica que quiere guardar un objeto y este se almacena automáticamente en la base de datos relacional, luego el programador puede lanzar un procedimiento que recupere instancias del objeto mediante el paquete de mapeo y obtendría estas instancias limpiamente sin enterarse de las conversiones que tuvieron lugar detrás de bastidores.
Uno de estos paquetes es Hibernate para Java, un nombre bastante apropiado ya que nos refiere a la hibernación, como una analogía a lo que le ocurre a los objetos cuando estos pasan por el mapeo y se convierten en objetos persistentes que luego pueden ser “despertados” para regresar a la actividad en el programa. Esto implica determinados cambios en nuestro enfoque de programación pero serán mínimos y en realidad muy económicamente bajos si hablamos de mantener el modelo de objetos limpio.
Algunos de estos cambios son por ejemplo el contar con una idea básica de características únicas de nuestras instancias que podrían servir por ejemplo como llaves primarias al momento de mapear, además de esto sería necesario el esquematizar varios archivos de configuración que especifiquen como realizar este mapeo, luego el paquete tomará los archivos de configuración y convertirá en cadena nuestras instancias a las tablas.
Ventajas y usos
Esto es claramente útil ya que cada objeto puede guardarse fácilmente, por ejemplo si tuviésemos un juego de computadora donde se trabaja con una gran cantidad de personajes, digamos cada uno un objeto del tipo personaje y cada uno con sus respectivas propiedades como stamina, energía vital, experiencia, etc. Todo iría bien mientras estuviéramos corriendo el juego, cada personaje tendría sus características en tiempo de corrida, pero… ¿Qué pasa si queremos guardar el juego? Sería algo normal pensar en hacer una tabla con las propiedades de los personajes y guardarlos con un ciclo que recorra todos nuestros personajes actuales, pero sería ideal si se pudieran guardar de alguna manera estos objetos, tal como son actualmente en la memoria ram en la ejecución, para que rápida y limpiamente pudieran ser restaurados al reiniciar el juego con un simple “recuperar_personaje()” y esto es precisamente lo que se consigue con ORM.
El tiempo de ejecución por una de estas operaciones podría ser similar al tiempo que se haría con un while una apertura de conexión y el query de almacenamiento, pues es relativamente lo que hace el ORM pero si es una ventaja en eficiencia a la larga ya que estos paquetes están optimizados para esto, pero quizás más importante todavía, proveen un estándar para cada una de las operaciones de mapeo que realizaremos, es decir, no haremos las cosas de un modo y de otro para distintos objetos, evitando confusiones y posibles ciclos de más.
Al estar las funcionalidades de ORM convenientemente encapsuladas en las clases de los diversos frameworks, se facilita además mucho el aplicarles otras técnicas, como un singleton para crear un generador de conversiones orm único que ahorrará la inversión en tiempo procesador y en accesos.
Donde quizás se perciben mejor sus bondades es al lidiar con problemas complejos, como las relaciones entre objetos, como de herencia y otras, que precisamente son planificadas y mapeadas gracias al framework ORM que hayamos escogido.
NHIbernate
En el caso particular de .Net podemos emplear Nhibernate, que es un port del framework Hibernate con las clases transformadas para ejecutarse en este framework superior, la mecánica para emplear NHibernate lleva estos pasos básicos:
-
Copiar los archivos esquemáticos nhibernate-configuration.xsd y nhibernate-mapping.xsd a la carpeta de esquemas de .Net
-
Incluir las librerías de Nhibernate en nuestro proyecto.
-
Planificar nuestros objetos de tal manera que puedan ser mapeados al modelo relacional.
-
Escribir un archivo xml para cada clase donde planteamos como se mapeará el objeto al modelo relacional, incluyendo por ejemplo que campos del objeto formarán la llave primaria en la tabla.
-
Programar funciones que faciliten el trabajo con los objetos como procedimientos de igualdad o almacenadores y recuperadores de objetos en masa.
Ejemplo
Un ejemplo empleado bastante práctico es el de la creación de objetos de tipo producto y luego facturas que pueden tener muchos objetos, con un modelo de objetos como este:

Esto quedaría mapeado a las siguientes tablas, programables en SQL y generadas por el paquete ORM

Luego, cada una de estas clases tiene que contar con su respectivo archivo de configuración xml donde se indicará como será el mapeo, por ejemplo, para la clase factura el archivo de configuración sería este:
<?xml version=”1.0″ encoding=”utf-8″ ?>
<hibernate-mapping xmlns=”urn:nhibernate-mapping-2.2″ assembly=”Proy.NH01″ namespace=”Proy.NH01.Entidades”>
<class name=”LineaFactura”>
<id name=”Id” column =”IdLineaFactura” type=”int” unsaved-value=”0″>
<generator class=”identity”/>
</id>
<property name=”Cantidad” type=”int” />
<property name=”Precio” type =”Decimal”/>
<many-to-one name=”Factura” column=”IdFactura” />
<many-to-one name=”Producto” column=”IdProducto” />
</class>
</hibernate-mapping>
Aquí notamos como se indica que partes de la clase serán identificadores, que tipos tendrán los campos y las relaciones que se darán en las diversas columnas.
Finalmente se tiene que configurar un archivo de preferencias generales de Nhibernate, el isession factory donde se definen parámetros generales como la base de datos relacional a usar, la cadena de conexión y otros.
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2" >
<session-factory name="NH01">
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
<property name="connection.connection_string">Data Source=localhost\SQLEXPRESS;Initial Catalog=NH01;Integrated Security=True</property>
<property name="show_sql">true</property>
<mapping assembly="Proy.NH01" />
</session-factory>
</hibernate-configuration>
En el programa definiremos un objeto sesión que usará estas características y donde ya usaremos operaciones simples para persistir los objetos, tales como:
sesion.Save(Objeto1);
Para este ejemplo me base en el excelente blog de uno de los colaboradores de NHIbernate, http://darioquintana.com.ar/articles/tutorial-de-nhibernate-primeros-pasos si les interesa NHibernate ese blog les vendrá como anillo al dedo.