Generación de Scripts SQL con NHibernate
Uno de los problemas más frecuentes con lo que nos encontramos a la hora de utilizar NHibernate, son las diferencias entre el modelo de domino y el modelo relacional de la base de datos, de forma que a veces es muy difícil relacionar algunas entidades del modelo de dominio con las tablas de la base de datos. Estas diferencias, se convierten con mucha facilidad en problemas de rendimiento, ya que las consultas que se generan no son todo lo optimas que deberían ser. En muchas ocasiones estas diferencias se vuelven insalvables, y se pierde mucho tiempo (dinero) en intentar solucionar algo que sencillamente no tiene arreglo.
Mi recomendación, siempre es la misma, primero se debe construir el dominio, siguiendo siempre los principios del Domain Driven Design, y luego a partir del dominio generar la base de datos, de forma que esas diferencias entre el domino la base de datos sean lo menor posible.
En este aspecto, NHibernate nos puede ayudar a generar la base de datos a partir de los ficheros de mapeo de la siguiente forma:
var configuration = new Configuration();
configuration.Configure(fileConfiguration);
using (var sessionFactory = configuration.BuildSessionFactory())
using (var session = sessionFactory.OpenSession())
{
var generateSchemaCreationScript = configuration.GenerateSchemaCreationScript(new MsSql2008Dialect());
foreach (var script in generateSchemaCreationScript)
{
var sqlQuery = session.CreateSQLQuery(script);
sqlQuery.ExecuteUpdate();
}
}
Por otro lado, durante el ciclo de vida de un proyecto, el modelo de dominio va evolucionando en el tiempo y el espacio, y normalmente resulta bastante tedioso la creación de los scripts SQL de creación y modificación de la base de datos. Con NHibernate también podemos generar los scripts diferenciales entre la base de datos y el modelo de dominio de la siguiente forma:
var configuration = new Configuration();
configuration.Configure(fileConfiguration);
using (var sessionFactory = configuration.BuildSessionFactory())
using (var session = sessionFactory.OpenSession())
{
var meta = new DatabaseMetadata((DbConnection)session.Connection, new MsSql2008Dialect());
var generateSchemaCreationScript = configuration.GenerateSchemaUpdateScript(new MsSql2008Dialect(), meta);
foreach (var script in generateSchemaCreationScript)
{
var sqlQuery = session.CreateSQLQuery(script);
sqlQuery.ExecuteUpdate();
}
}
Si nos encontramos que tenemos un problema de diferencia entre el modelo de domino y la base de datos, es un buen ejercicio generar los scripts diferenciales, para poder ver que lejos (o cerca) estamos de lo que deberíamos tener. Si las diferencias son muy grandes, es muy posible que este script no nos sea de mucha ayuda.
La implementación que uso para la generación del esquema de la base de datos es la siguiente:
using System;
using System.Collections.Generic;
using System.Data.Common;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Tool.hbm2ddl;
public class NHibernateSchemaHelper : INHibernateSchemaHelper
{
private readonly ISessionFactory _sessionFactory;
private readonly ISession _session;
private readonly Configuration _configuration;
public NHibernateSchemaHelper()
{
_configuration = new Configuration();
_configuration.Configure();
_sessionFactory = _configuration.BuildSessionFactory();
_session = _sessionFactory.OpenSession();
}
public NHibernateSchemaHelper(string fileConfiguration)
{
_configuration = new Configuration();
_configuration.Configure(fileConfiguration);
_sessionFactory = _configuration.BuildSessionFactory();
_session = _sessionFactory.OpenSession();
}
public void Create()
{
Drop();
var generateSchemaCreationScript = _configuration.GenerateSchemaCreationScript(new MsSql2008Dialect());
ExecuteSchemaScript(generateSchemaCreationScript);
}
public void Update()
{
var meta = new DatabaseMetadata((DbConnection)_session.Connection, new MsSql2008Dialect());
var generateSchemaCreationScript = _configuration.GenerateSchemaUpdateScript(new MsSql2008Dialect(), meta);
ExecuteSchemaScript(generateSchemaCreationScript);
}
public void Drop()
{
var generateSchemaCreationScript = _configuration.GenerateDropSchemaScript(new MsSql2008Dialect());
ExecuteSchemaScript(generateSchemaCreationScript);
}
private void ExecuteSchemaScript(IEnumerable generateSchemaCreationScript)
{
foreach (var script in generateSchemaCreationScript)
{
var sqlQuery = _session.CreateSQLQuery(script);
sqlQuery.ExecuteUpdate();
}
}
public void Dispose()
{
GC.SuppressFinalize(this);
Dispose(true);
}
private void Dispose(bool disposing)
{
if (disposing)
{
_session.Dispose();
_sessionFactory.Dispose();
}
}
}
public interface INHibernateSchemaHelper : IDisposable
{
void Create();
void Update();
void Drop();
}
Como podéis ver, Nhibernate nos ayuda a generar el schema de la base de datos, y a que las diferencias entre el dominio y la base de datos sean las menores posibles, lo que significa que nuestro dominio está replicado en al base de datos, y que los problemas ocasionados por los intentos de adaptación del dominio a la base de datos no los vamos a tener.
Esto no nos va a eliminar los problemas de rendimiento ocasionados por un mal dominio, esos lamentablemente los seguiremos teniendo, NHibernate no es tan listo, como decía Botines: “La programasión es un arte,… sino programarían los roboses”.