Hacia un NHibernate 2.0

NHibernate encuentra en la versión 1.2.0 GA, asimismo se está preparando para la versión 2.0 que va a contar con muchos ports disponibles en la versión de Hibernate 3.2.5 GA para Java.

Se hicieron en este lapso de tiempo modificaciones significativas en el Core de NHibernate, grandes reestructuraciones para poder introducir las nuevas caracteristicas, en este post pueden ver el resultado de dichas reestructuraciones.

NHibernate está tomando mucho color ahora, algunas de las features que se van a poder usar en la nueva versión son:

StatelessSession: IStatelessSession es la interfaz que nos va a permitir manejarnos con sessiones sin caché de primer nivel. Creada especialmente para poder hacer guardados/actualizaciones/borrados en modo masivo. IStatelessSession posee una semántica similar a ISession. Ahora, ISessionFactory tiene responsabilidad de la factoría de ISession como así también de IStatelessSession.

EventListeners: Son clases que nos permiten interceptar las acciones que NHibernate realiza antes o despues de que las haga. Con la versión actual de NHibernate podiamos realizar intercepciones, por medio de las interfaces ILifecycle e IInterceptors. Ahora podemos hablar con NHibernate a un nivel muy bajo, antes de que se haga el guardado en la base de datos, o después que lo hizo por medio de EventListeners.

EntityName: es una característica que nos va a permitir tratar a las clases por un nombre único. También vamos a poder tratar a 1 clase con 2 entity names diferentes (imposible hasta ahora). Algo que va a desaparecer en NHibernate son las consultas de un tipo haciendo uso de typeof(<Nombre Del Tipo>), van a permanecer solamente llamadas con Entity Name y métodos genéricos. Por ejemplo:

  • Session.Get<Foo>(id);
  • Session.Get("TheFooEntity",id);

Ambas serían consultas equivalentes, en la 2da es un método que retorna System.Object y usando EntityName. Un preview de esto lo pueden ver en IStatelessSession, que ya tiene los métodos estructurados de esta manera. Como lo pueden ver, esto va a provocar un breaking change en la estructura de ISession.

DetachedQuery: este es el hermano de DetachedCriteria pero para HQL, con esto vamos a poder crear consultas separadas de la Session, eso ayuda a jugar un poco en nuestras DAOs. Era una opción faltante en NHibernate debido a que hay cosas que no se pueden hacer solamente usando Criteria. Esta implementación ya estuvo disponible desde hace tiempo en uNHAddIns.

Stadistics: Con esta característica vamos a poder hacer estadisticas de nuestras consultas principalmente. Un ejemplo que seguramente a muchos les va a gustar, nos dará las herramientas para poder calcular el hit-ratio cache, es decir, la tasa de asiertos en la caché de primer nivel.

Todo entre Transacciones: este es otro breaking change para tener igual  comportamiento que en Hibernate. Cuando querramos hacer un Save/Update/Delete en la base de datos, va a tener que estar encerrado en transacciones. Actualmente, es posible hacerlo sin ellas.

uNHAddIns new release available - Revision 64

By internal policies uNHAddins not have yet a release number o release version name, by now we are using the revision number at SVN for this purpose. Probably will come a branch when NHibernate 2.0 release appear, and that will be the time.

Download here

Features of new version:

  • Added UserType: UpperString. Allow save/get strings against the database in upper case.
  • Few features at pagination.
  • Documentation enhanced.
  • Cloner helper for make objects clone via binary serialization.
  • Build files for compiler using NAnt.

For more resources visit:

[In Spanish]

Por politicas internas uNHAddIns todavía no tiene asignado numero o version de Release, por ahora estamos usando el número de revision para esto. Ya vendrá seguramente un branch para usar NHibernate 2.0 cuando sea release, y ese será el momento.

Descarga aquí

En la nueva versión se incluyen:

  • Nuevo UpperString User Type. Permite guardar/obtener los string en mayúsculas (upper case).
  • Algunas caracteristicas nuevas en la paginación (pocas)
  • Se ha mejorado la documentación.
  • Un Cloner para clonar objetos mediante serialización binaria. Simple helper.
  • Soporta compilación usando NAnt.

Para más recursos visitá:

Named Queries en NHibernate y por qué debemos usarlas

En chat con Fabio Maulo, me estaba contando de las bondades de usar Named Queries en NHibernate, las cuales justifican esta entrada en el blog.

Que es una named query? Es una query que se define en los archivos de mapeos y posee un nombre, puede ser HQL o SQL nativo.  Una HQL query puede ser escrita así:

  <query name=”Foo-by-name” cacheable=”false” read-only=”true”>
    <![CDATA[
        from Foo where Name like :value
    ]]>
  </query>

Y la named query equivalente en Sql Nativo se puede escribir de esta manera: 

  <sql-query name=”Foo-by-name” cacheable=”false” read-only=”true”>
    <return alias=”foo” class=”Foo”/>
    <![CDATA[
    SELECT
    {foo}.ID AS {foo.Id},
    {foo}.NAME AS {foo.Name},
    {foo}.MISC AS {foo.Misc}
    FROM Foo {foo}
    WHERE {foo}.Name LIKE :value
    ]]>
  </sql-query>

El código que lo ejecuta: 

    Foo f1 = new Foo();
    f1.Misc = “misc 1″;
    f1.Name = “foo 1″;
    session.Save(f1);

    Foo f2 = new Foo();
    f2.Misc = “misc 2″;
    f2.Name = “foo 2″;
    session.Save(f2);

    session.Flush();

    IQuery q = session.GetNamedQuery(“Foo-by-name”);
    q.SetString(“value”, “f%”);

    Debug.Assert(q.List().Count == 2);

Si se dan cuenta, ambas Named Queries se ejecutan con el mismo código ;)

Ventajas:

  • Si queremos cambiar de motor de base de datos, no tenemos que cambiar la query nativa en el código, sino en el mapping.
  • Si poseemos queries escritas en HQL y queremos usar alguna feature especial del motor, solo debemos escribir una named query equivalente en SQL nativo y usarla.
  • Muy importante! Y practicamente La Razón de este post. Las named queries se parsean 1 sola vez, cuando se llama al .BuildSessionFactory(); de modo que si escribimos mal la query (por que nos equivocamos en el nombre de una property por ejemplo), NHibernate nos va a avisar con un error. Las otras queries se parsean TODAS las veces que van a hacer ejecutadas, a menos que estén en una caché destinada a ellas. Esta es una caché MRU (Most Recently Used) de 128 posiciones.
  • El código queda más limpio !
  • Se evita la construcción de los queries artesanalmente y variables, haciendo uso y abuso de String.Format( … ), gastando posiciones en la caché MRU para estas queries.

Gracias Fabio por la información.

Movimientos en el SVN de NHibernate - parte 2

Ayer hice este post mostrando una captura de los movimientos del trunk de NHibernate. Hoy hago otro update del SVN y me encuentro con:

  • 18 agregados
  • 10 borrados
  • 95 modificados

Como hacés Fabio? Dormí un poco :)

Movimientos en el SVN de NHibernate

Recientemente en el grupo fueron varias las personas que preguntaron: NHibernate está muerto?, cuál es su futuro? Hay alguién programando para NHibernate?

No voy a adelantar algunas posibles features, todo se dará a su tiempo (pero son buenas…confien :) ). Por ahora me atrevo a decir que NHibernate, tiene futuro y mucho. Hay gente programando para NHibernate y por ahora estan portando muchas cosas de Hibernate como lo veniamos mostrando en posts anteriores.

Cada vez que hago un update del SVN de NHibernate siempre se actualizan muchísimos archivos, se agregan algunos, como así también los tests de las clases. Ahora bien, hace 2 días hice un update y hubo bastante movimientos, hoy realizo otro update y me encuentro que se modificaron 44 archivos y se agregaron 21 archivos !!!

Esto es para que saquen sus conclusiones sobre si NHibernate está estancado o no:

Muchachos…programen tranquilos, hay NHibernate para rato !

NHibernate Stadistics - Go Fabio Go

Fabio Maulo, figura importatísima en el NHibernate team, siempre estuvo contribuyendo con parches y features, y ahora está portando muchas features de Hibernate 3.2.5 GA -ya lo veiamos aquí y aquí. Y ahora está por va tras Hibernate Statistics que podemos verlo en acción en este post.

La mejor parte del post es cuando realiza el calculo del caché hit-ratio: 

double queryCacheHitCount  = stats.getQueryCacheHitCount();
double queryCacheMissCount = stats.getQueryCacheMissCount();
double queryCacheHitRatio = queryCacheHitCount / (queryCacheHitCount + queryCacheMissCount);

Les gustó ?

NHibernate EventListeners

English post

En este post comentábamos sobre StatelessSession como nueva feature de NHibernate, ahora bien, otra de las features disponibles en el trunk de NHibernate (portada de Hibernate) son los EventListeners. Con ellos podemos interceptar acciones llevadas a cabo por NHibernate antes o después de que se realicen como por ejemplo un Save, Update, Load, Flush entre otras.

En este ejemplo vamos a realizar acciones antes y después de que se ejecute el comando Save(object).

Definimos el EventListener:

public class MySaveEventListener : NHibernate.Event.Default.DefaultSaveOrUpdateEventListener
{
    protected override void CascadeBeforeSave(IEventSource source, IEntityPersister persister,
                                              object entity, object anything)
    {
        Console.WriteLine(“Before Save the entity “ + entity);
        base.CascadeBeforeSave(source,persister,entity,anything);
    }

    protected override void CascadeAfterSave(IEventSource source, IEntityPersister persister,
                                             object entity, object anything)
    {
        Console.WriteLine(“After Save the entity “ + entity);
        base.CascadeAfterSave(source,persister,entity,anything);
    }
}

Y para configurarlo programaticamente con el SessionFactory hacemos: 

Configuration cfg = new Configuration();

cfg.Configure(“hibernate.cfg.xml”);

cfg.SetListener(ListenerType.Save, new MySaveEventListener());

Ahora realizamos un Save: 

using (ISessionFactory sf = cfg.BuildSessionFactory())
{
    using (ISession session = sf.OpenSession())
    {
        Foo f1 = new Foo();
        f1.Misc = “m1″;
        f1.Name = “n1″;

        session.Save(f1);

        session.Flush();
    }
}

Y este es el resultado de la consola, incluyendo el SQL que NHibernate generó: 

Before Save the entity Entity Foo:0:n1
NHibernate: INSERT INTO Foo (Name, Misc) VALUES (@p0, @p1); select SCOPE_IDENTIT
Y(); @p0 = 'n1', @p1 = 'm1'
After Save the entity Entity Foo:1:n1

Foo tiene un object id como Integer e Identity. Antes de guardarse la property Id tiene como valor 0, despues del Save tiene como valor 1 (debido a que es el primer objeto en la base).

NHibernate StatelessSession: sin caché de primer nivel

En la próxima release de NHibernate estará disponible esta feature portada desde Hibernate por Fabio Maulo, los que deseen usarla en este momento deben hacerlo desde el trunk.

Qué hace? IStatelessSession es la interfaz que debemos usar para conseguir sesiones de NHibernate que no posean caché de primer nivel. Debemos recordar que la caché de primer nivel en ISession es obligatoria y siempre la usamos cuando trabajamos con NHibernate. Trabajar sin ella era imposible hasta ahora.

Para que sirve? Especialmente hecho para realizar operaciones en masa contra la base de datos.

Miremos este código: 

        using (ISessionFactory sf = cfg.BuildSessionFactory())
            {
                using (IStatelessSession session = sf.OpenStatelessSession())
                {
                    using (ITransaction tx = session.BeginTransaction())
                    {
                        Foo f = new Foo();

                        f.Id = 1;
                        f.Misc = “m1″;
                        f.Name = “n1″;

                        session.Insert(f);

                        f.Id = 2;

                        session.Insert(f);

                        tx.Commit();

                        Debug.Assert(session.CreateQuery(“from Foo”)
                                                    .List().Count == 2);
                    }
                }
            }

Como verán el objeto f fué creado 1 vez y mandado a persistir dos veces con el Id cambiado, primero con ‘1′ y luego con ‘2′. En un escenario común con caché de primer nivel esto no funcionaría, ya que NHibernate “recordaría” al objeto f desde que fué guardado por primera vez. Y como resultado en la base habrán 2 objetos guardados.

Notese también que al guardar el objeto realizamos un Insert en vez de un Save. Las inferzaces ISession e IStatelessSession no se relacionan para nada.

Para que este ejemplo funcione con cache de primer nivel es obligatorio hacer que NHibernate se “olvide” del objeto que acabamos de guardar llamando al metodo Evit(object) -método no disponible en IStatelessSession como podrán intuir. Y se vería así: 

            using (ISessionFactory sf = cfg.BuildSessionFactory())
            {
                using (ISession session = sf.OpenSession())
                {
                    using (ITransaction tx = session.BeginTransaction())
                    {
                        Foo f1 = new Foo();

                        f1.Id = 1;
                        f1.Misc = “m1″;
                        f1.Name = “n1″;

                        session.Save(f1);

                        //Eliminamos el objeto de la cache
                        session.Evict(f1); 

                        f1.Id = 2;

                        session.Save(f1);

                        tx.Commit();

                        Debug.Assert(session.
                             CreateQuery(“from Foo”).List().Count == 2);

                    }
                }
            }

Recursos:

Sintaxis en Rhino Mocks

Rhino Mocks es un excelente framework para realizar mocking en nuestras pruebas unitarias. Una de las buenas razones para usarlo es que no hace uso de strings para hardcodear los nombres de tipos, metodos, properties, etc, reduciendo entonces la posibilidad de error en tiempo de ejecución.

Despues de la versión 3.0.5 liberada hace un tiempo, la sintaxis para realizar mocking quedó un poco más fácil de leer y con menos posibilidades de error.

Al mirar este screencastintroductorio a Rhino Mocks te darás cuenta como se hacía antes. También salta a la vista que cualquiera puede olvidar de llamar al método VerifyCall() dejando libre la posibilidad de errores.  

        [Test]
        public void SigmoidalFunctionDerivative()
        {
            INet net = Mock<INet>();

            ITransferFunction tf = new SigmoidalTransferFuncion();

            Expect
                    .Call(net.Value)
                    .Return(5.0);

            Mocks.ReplayAll();

                //f’(net) = y * ( 1 - y )
                Assert.Equal(0.006648057, Math.Round(tf.Derivate(net), 9));

            Mocks.VerifyAll();
        }

Una forma más clara de hacerlo es:

        [Test]
        public void SigmoidalFunctionDerivative()
        {
            INet net = Mock<INet>();

            ITransferFunction tf = new SigmoidalTransferFuncion();

            using (Mocks.Record())
            {
                Expect
                    .Call(net.Value)
                    .Return(5.0);
            }

            using (Mocks.Playback())
            {
                //f’(net) = y * ( 1 - y )
                Assert.Equal(0.006648057, Math.Round(tf.Derivate(net), 9));
            }
        }

En estos ejemplos estoy usando un helper para el metodo Mock<T>() y la property MockRepository Mocks {get; set; }. Nada de otro mundo, y se pueda mejorar: 

public class TestFixtureBase
    {
        private MockRepository mocks;

        public MockRepository Mocks
        {
            get
            {
                if (mocks == null)
                { mocks = new MockRepository(); }

                return mocks;
            }
        }

        public T Mock<T>()
        {
            return Mocks.CreateMock<T>();
        }
    }

Existe aún así otra manera de hacer lo mismo, en vez de using, hacemos uso de delegados y la clase With (no es el de VB pero creo que por ahí vino la idea): 

        [Test]
        public void SigmoidalFunctionDerivative()
        {
            INet net = Mock<INet>();

            ITransferFunction tf = new SigmoidalTransferFuncion();

            With
                .Mocks(Mocks)
                .Expecting(delegate
                               {
                                   Expect
                                       .Call(net.Value)
                                       .Return(5.0);
                               })
                .Verify(delegate
                            {
                                //f’(net) = y * ( 1 - y )
                                Assert.Equal(0.006648057,
                                    Math.Round(tf.Derivate(net), 9));
                            });
        }

Particulamente elijo la opción 2, el usar la sintaxis con using.

xUnit.Net - Excepciones Esperadas

Para los que no estaban enterados, el autor de NUnit saca a la luz este nuevo Framework de unit testing. La intención de un nuevo Framework de Testing para .Net parecería querer reinventar la rueda, pero no lo es. Muchas practicas que se hacían en NUnit (y muchas de estas heredadas de JUnit) no van a estar disponibles -para bien de nuestro código según argumenta James Newkirk.

Uno de los cambios: No va más el ExpectedException. Y el argumento de su desaparición es válido: Puede que una linea de codigo arroje esa excepción, y no sea la linea de codigo que estamos esperando a que lo haga. Ahora debemos hacerlos así: 

        [Test]
        public void NeuronNotConnected()
        {
            INeuron n = new SigmoidalNeuron();

            Assert.Throws<BpnException>(
                delegate
                {
                    double foo = n.Net.Value;
                });
        }

Esta es la pagina del proyecto http://www.codeplex.com/xunit.

Y para correr los test con Visual Studio pueden usar http://www.testdriven.net/