Dentro de Hexacta, el uso de nHibernate dentro de la población .Net se ha popularizado. El hecho de tener gente con experiencia en Hibernate ayudó también a la inclución de nHibernate en nuestro stack tecnológico.
UPDATE: la siguiente parte de este post está disponible aquí
Por la misma razón, algunas prácticas importadas de nuestros primos de Java continuan en forma casi indiscutida, una de ellas, la configuración de mapeos mediante XML y eventualmente la misma configuración de la factory por XML. Si bien XML es una opción válida, vale la pena mirar opciones.
Una alternativa es la de usar atributos para el mapeo, está disponible desde las primeras versiones de nHibernate pero no cuenta con el apoyo popular de aquellos más puristas que no desean ver el su modelo de dominio con datos foráneos como es la persistencia.
Otra opción, la que ocupa este post, es la de Fluent nHibernate. Esta aproximación a la configuración de mapeos, tiene sus pilares sobre ciertas ideas:
El objetivo de este post es el de ver casos muy sencillos de mapeo y ver los beneficios que puede aportar el uso de Fluent nHibernate frente al XML tradicional, para ello voy a utilizar un pequeño modelo muy simplificado de un blog. Este modelo no está diseñado para ver las características más avanzadas de nHibernate, sino para hacer una pequeña comparación en un caso muy simple. En futuras iteraciones espero poder mirar las características más avanzadas y ver si Fluent nHibernate muestra sus limitaciones que solo puedan superarse en forma razonable con otra técnica de mapeo.

Un Blog tiene Posts, estos Posts están escritos por Autores que pertenecen al Blog y se encuentran en un estado (EstadoPostEnum) que puede ser publicado o como draft. Los Posts pueden tener Categorías y Etiquetas que son globales al Blog. Estos Posts pueden también tener Comentarios y estos Comentarios pueden tener respuestas (en su otra forma de Comentario).
Para probar el mapeo de una entidad sencilla, se puede usar la entidad Autor. En C# está definida de la siguiente forma:
public class Autor { public virtual int Id { get; private set; } public virtual string Nombre { get; set; } public virtual string Apellido { get; set; } public virtual string Email { get; set;} public virtual string NombreUsuario { get;set;} public virtual string PasswordUsuario { get;set;} }
Esta entidad, sería mapeada con XML de la siguiente manera:
<class name="Autor"> <id name="Id"> <generator class="identity"/> </id> <property name="Nombre"/> <property name="Apellido"/> <property name="NombreUsuario"/> <property name="PasswordUsuario"/> <property name="Email"/> </class>
Debe quedar claro que este es el absolutamente mínimo mapeo necesario para esta clase. nHibernate va a ser responsable de mapear los tipos Net y SQL en base al uso de reflection y del provider de la base de datos, estamos asumiendo claramente que no existe una base de datos previa o que si existe se ajusta exactamente o nuestro modelo.
Para comparar, podemos ver la versión Fluent. En este caso, no hay XML, sino que se generan clases de mapeo. Para nuestra entidad Autor, la clase de mapeo se vería como:
public class Autor : ClassMap<Model.Autor> { public Autor() { Id(x => x.Id).GeneratedBy.Identity(); Map(x => x.Nombre); Map(x => x.Apellido); Map(x => x.NombreUsuario); Map(x => x.PasswordUsuario); Map(x => x.Email); } }
Fluent nHibernate (usando ClassMap<>) va a colaborar con nHibernate para generar nuestro mapeo. Una ventaja clara de esta aproximación es no tener más cadenas mágicas en nuestro código, si decido cambiar el atributo Descripcion por AutorEmail, solo debo renombrarlo y el mapeo será actualizado. El caso con XML no es tan sencillo, voy a tener que actualizarlo haciendo referencia a la nueva propiedad.
Si en cambio decido modificar el esquema de la base de datos y llamar al campo Email como AutorEmail, no voy a tener otra salida más que modificar el mapeo.
Con XML:
<property name="Email" column="AutorEmail" />
Con Fluent:
Map(x => x.Email).Column("AutorEmail");
Lamentablemente ya apareció una cadena mágica (“AutorEmail”) pero al menos está haciendo referencia a un campo de una tabla y no un nombre que pertenece exclusivamente a nuestro modelo de objetos.
Un mapeo levemente más complicado es el de Post. Sus dos versiones:
Con XML:
<class name="Post"> <id name="Id"> <generator class="identity"/> </id> <property name="Titulo" /> <property name="Texto" /> <property name="FechaUltimoCambio" /> <property name="Estado" type="EstadoPostEnum" /> <many-to-one name="Autor" class="Autor" /> <set name="Comentarios"> <key column="Post_id"></key> <one-to-many class="Comentario"/> </set> <set name="Etiquetas"> <key column="Post_Id"/> <many-to-many class="Etiqueta" column="Etiqueta_Id"/> </set> <set name="Categorias"> <key column="Post_Id"/> <many-to-many class="Categoria" column="Categoria_Id"/> </set> </class>
Con Fluent:
public class Post : ClassMap<Model.Post> { public Post() { Id(x => x.Id).GeneratedBy.Identity(); Map(x => x.Titulo); Map(x => x.Texto); Map(x => x.FechaUltimoCambio); Map(x => x.Estado); HasMany(x => x.Comentarios); HasManyToMany(x => x.Categorias); HasManyToMany(x => x.Etiquetas); References(x => x.Autor); } }
En este caso, es más que claro que Fluent nHibernate tiene un resultado más limpio. Con respecto a los key que no se definen en el mapeo Fluent pero si son generados (Blog_id en Post, Post_id en Comentario, etc), este framework hace un uso extensivo de convenciones (modificables) de nombres y mapeos. Visualmente el resultado de generar el esquema desde nHibernate:
Con XML:

Con Fluent:

Personalmente encuentro más fácil de leer lo generado por Fluent, especialmente el nombre de las tablas de las relaciones many-to-many. Aunque por otro lado:
De cualquier forma, espero que este pequeño ejemplo haya servido para mostrar las ventajas de Fluent. Adjunto código fuente que crea el ddl para este modelo en un archivo de texto (el dialecto es Sql Server 2008).
Muy groso!
Estaría bueno que alguien haga lo mismo para el mundo java (tal vez usando groovy…)
Saludos!
Muy bueno lo de eliminar las cadenas mágicas! Cuando cambias cosas en la base no te queda otra, pero por lo menos te queda atado a modificaciones manuales solo de un lado. El modelo no se mancha.
My spanish is a bit rusty but the examples are clear enough. Keep it up guys. I love your country.
[...] This post was Twitted by cristianelopez [...]
Otro framework similar al que creo vale la pena pegarle una mirada es ConfORM http://code.google.com/p/codeconform/
Muy post, bastante explicito. pero me gustaria hacer una pregunta, como puedo crear una tabla que contenga una llave primaria compuesta y que no sean producto de la relacion con otras dos tablas, sencillamente quiero crear una tabla que tenga como id la comninacion de dos