jueves 26 de enero de 2012

Repositorio vs DAO

Una de las partes más importantes de una aplicación, tanto en Java como en .Net, es el acceso a datos. Las dos estrategias más famosas son el DAO y el Repositorio, aunque algunos los confunden, son estrategias diferentes que dan solución al mismo problema.

Patrón DAO
DAO son las siglas de Data Access Object, pero para entenderlo mejor vayamos a las fuentes J2EE Core Paterns:
"Use a Data Access Object (DAO) to abstract and encapsulate all access to the data source. The DAO manages the connection with the data source to obtain and store data. (...)
The DAO implements the access mechanism required to work with the data source.(...) 
The DAO completely hides the data source implementation details from its clients."

En román paladino: el DAO es el responsable de componer los datos de sistemas externos, en caso de una base de datos, será el responsable de componer y ejecutar las consultas SQL necesarias para hacer las operaciones. Por ejemplo:
    interface IFacturaDAO
    {
        Factura Get(int id);
        void Save(Factura factura);
        void Delete(Factura factura);

        ICollection<Factura> getFacturasSinCobrar();
    }
Más o menos este es el ejemplo clásico, un interfaz que nos permite realizar como mínimo las operaciones de un CRUD.

Patrón Repositorio
Si para el patrón DAO consultamos J2EE Core Patterns, para la definición del repositorio debemos acudir a Martin Fowler:
"Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects"

Aunque la definción de los colaboradores de Fowler (Edward Hieatt y Rob Mee) es breve y clara, creo que deja muchos interrogantes abiertos. Únicamente indica que se debe implementar un interfaz similar al de una colección y que por debajo ya se encargará acceder a los datos.
    interface IFacturaRepositoy : ICollection<Factura>
    {
        ICollection<Factura> GetFacturasSinCobrar();
    }

En este ejemplo, además del interfaz de ICollection, el respositorio de facturas implementa una consulta que recupera las facturas sin cobrar. Pero al implementar ICollection hay que sopesar si se debe implementar el método Clear(), yo personalmente no lo haría ;)

Las ¿consultas son reglas de negocio? ¿o es parte del acceso a datos?
Si os fijáis en los ejemplos anteriores, tanto el DAO como el repositorio de factura exponen Este es un debate muy importante que condiciona el desarrollo de la capa de acceso a datos y la implementación de las reglas de negocio.
Utilices DAOs o Repositorios, si crees que las consultas forman parte de las reglas de negocio y quieres mantener la independencia entre negocio y acceso a datos, ¿como puedo escribir las querys, sin usar SQL en la capa de negocio? La solución nos la ofrece otra vez Fowler con el patrón Query Object: "An object that represents a database query"
Si crees que las consultas son solo consultas o que programar una implementación de query object es un esfuerzo innecesario, siempre puedes escribir las consultas dentro de tu DAO (incluso dentro del repositorio, estirando un poco la interpretación del patrón).
Algunos de vosotros pensareis, este Query Object se parece mucho a un criteria de Hibernate. Sí, tenéis razón, se podría decir que el api de criteria es una implementación de Query Object. ¿Y por qué no usarlo en la capa de negocio? Porque si usas criteria fuera de la capa de acceso a datos, estas añadiendo una referencia a Hibernate (a un mecanismo de acceso a datos) en tu capa de negocio. Pero de esto hablaremos más tarde en este post (la tercera vía)
En .Net Linq nos permite hacer implementaciones limpias del patrón repositorio. Linq nos permite hacer consultas sobre colecciones IQueriables y está soportado por el framework. Pero ¿exponer IQueriable fuera de nuestro acceso a datos nos permite mantener a nuestras entidades ignorantes de los mecanismos de persistencia? Sí y no.
En teoría sí ya que Linq forma parte del framework, pero en la realidad es que, de esta forma, se limita el número de opciones a disposición del programador: NHibernate, Entity Framework, o algun otro sistema que soporte Linq. Porque el coste de implementar IQueriable en un sistema que utiliza Ado.Net es altísimo y no creo que merezca la pena.
En definitiva se podría decir que se aplican las reglas de negocio en dos momentos de la aplicación:
  1. al aplicar condiciones para recuperar los datos (recuperar las facturas no pagadas) 
  2. Antes de persistir los cambios en la base de datos.
Con la palabra "Antes" ya queda claro que es en la capa de negocio donde se deben aplicar las validaciones, cambios de estado, etc. la duda está a la hora de recuperar los datos ¿dentro o fuera del acceso a datos?

Tercera vía: los ORM ya nos abstraen de la base de datos.
La gran ventaja de los patrones DAO y repositorio es que abstraen al resto del código de la base de datos, mediante estos mecanismos, si cambia la base de datos no tenemos que tocar la implementación de las reglas de negocio.
Al utilizar ORMs como Hibernate o Entity Framework la abstracción con la base de datos está asegurada. Si ya tenemos una capa de abstracción ¿por qué añadir otra? evitar perder funcionalidad que ofrece el ORM directamente. De esta forma el código es más seco (DRY), incluso se podría decir que cumple el principio de responsabilidad única, ya que el encargado de guardar el objeto es el ORM. Por último, al no tener DAOs ya no tienes la duda de ¿la consulta es una regla de negocio o forma parte del acceso a datos? al usar el ORM directamente también se usan directamente sus mecanismos de consulta.


Reflexiones finales
En una aplicación empresarial, donde es importante tener organizado el código, donde muchas cosas pueden cambiar a petición del cliente y además el equipo estará formado por varias personas, intentaría mantener la capa de acceso a datos completamente aislada, es decir que usaría DAO o Repositorio.

En .Net dudaría si implementar repositorios exponiendo IQueriables ya que muchos controles del framework, paginan y ordenan estas colecciones.
En Java utilizaría DAOs sin lugar a dudas, no implementaría Query Objects, dejando las consultas en el DAO.
Si estas en una empresa con personas que programan en Java y .Net un DAO con hibernate creo que es la mejor solución.

El uso directo de un ORM lo dejaría en proyectos pequeños o medianos, con una persona desarrollando y que requieran cambios rápidos. Aunque eso es un riesgo en un entorno empresarial, ya que desaparece esa persona, y lo que para unos es orden, para otros es caos.

¿Y tu qué opinas? ¿cómo plantearías la estrategia de acceso a datos de una aplicación?

jueves 19 de enero de 2012

RWJ. Keep it simple!!

"La belleza de lo simple"

boolean orderByAsc = false;
if (orden.equals("asc")) {
    orderByAsc = true;
}

El proyecto que estoy sufriendo está lleno de cosas así. ¿El programador no podría haber hecho algo tan fácil como... ?

boolean orderByAsc = "asc".equals(orden);

Y además, no petaría si orden fuese null.

martes 17 de enero de 2012

La base de datos ¿Fin o Medio?

Cuando se plantea un desarrollo a medida, una de las primeras preguntas que se hace es: ¿Qué base de datos se va a utilizar?
Aunque creáis que no, tomar la base de datos como un medio o como un fin, condiciona en gran medida el desarrollo del sistema.

Base de datos como Fin
Si consideramos que la base de datos es la parte más importante de la aplicación, nos preocuparemos de que los datos sean fácilmente legibles. En primer lugar diseñaremos un modelo de datos, que posteriormente se traducirá a clases.
La base de datos será el centro del negocio, otras aplicaciones podrían atacar directamente a nuestra base de datos.

Base de datos como Medio
Desde este punto de vista, la base de datos se considera como un mero sistema para persistir los objetos de negocio de nuestra aplicación
Son los objetos de nuestra aplicación los que realmente ofrecen valor, la consulta de datos de nuestro sistema se debe realizar mediante una capa de servicios que desarrollaremos nosotros, el acceso directo a base de datos podrías dar como resultado lecturas incorrectas.
Adaptaremos el modelo de datos a las necesidades de nuestro código, primando el diagrama de clases sobre modelando los datos.

En los últimos años, estamos asistiendo al proliferación de las bases de datos documentales. Esta movimiento se podría considerar como el punto extremo del uso de la base de datos como medio. Ya que de esta forma abandonamos toda la experiencia y el conocimiento que ya existe en las bases de datos relacionales, por un sistema menos estándar.
Lo cierto es que esta moda me recuerda a las bases de datos orientadas a objetos, de las que tanto se hablaba en los 90, mientras que hoy en día parece que nadie se acuerda de ellas.

Aunque personalmente considero las bases de datos como medio y no como fin, ¿por qué?
Los objetos son los que contienen la información relevante, los objetos no son nadie sin las reglas de negocio a aplicar, que las tengo en las clases, no en la base de datos (nada de procedimientos almacenados, funciones de base datos o trigers). Si alguien quiere acceder a mis datos que me lo pida a través de una capa de integración. Mis datos los toco yo, nadie más.
Eso no significa que no use características de las base de datos, quizás la más importante sea la transaccionalidad, delego en ella la generación de ids.

Aunque esta aproximación no siempre se puede aplicar, por ejemplo en grandes empresas con bases de datos enormes que alimentan a varias aplicaciones y con un departamento de administración de base de datos. Muchos consideran que trabajar con DBAs es un suplicio por las restricciones que imponen, pero en modelos realmente grandes, con millones de registros, que alguien conozca como se comporta la base de datos y que nos indique como diseñar las tablas para evitar problemas de rendimiento es una ventaja.

¿Y tu qué opinas? ¿Modelas primero la base de datos y en ella basas tus objetos? ¿Comienzas por el diagrama de clases? ¿o eres de los aventureros que se adentra en las bases de datos documentales?

martes 10 de enero de 2012

Abstracción, aplicaciones con pies de barro

Cuando desarrollamos aplicaciones, utilizamos varios mecanismos de abstracción que nos permiten, entre otras cosas, centrarnos en problemas funcionales, abstrayendonos de ciertos problemas técnicos. Por ejemplo con java o con .net un desarrollador no suele preocuparse por la gestión de memoria porque el recolector de basura de cada plataforma se encarga de eso.

Los propios lenguajes de programación no mas que abstracciones que permiten comunicarnos de forma sencilla con las máquinas.

La abstracción ha permitido que más desarrolladores puedan trabajar, ya que el nivel de conocimiento necesario para hacer el trabajo ha disminuido. Si tuviésemos que trabajar en ensamblador ¿cuanta gente sería capaz de software de calidad?

La abstracción bien entendida
Estamos viviendo una época que pasará a los anales de la historia, seguramente en unos años el final del siglo XX y el principio del XXI se conocerá como La revolución informática. Esta revolución no sería posible sin la abstracción, que permite que mucha gente pueda usar los ordenadores de diferente manera y cada vez con menos conocimiento.

Pero nunca debemos olvidar qué es lo que estamos usando o de lo qué nos estamos abstrayendo. A pesar de utilizar lenguajes que nos abstraen del comportamiento del micro procesador o del concepto de memoria, no podemos olvidarnos que estos elementos condicionan de forma muy importante el resultado.

Creo que un desarrollador debe conocer con un cierto nivel de detalle el comportamiento de la última capa de abstracción. Por ejemplo, un desarrollador web debe conocer http, qué pasa cuando se hace un post, qué es una petición ajax, etc.

Aplicaciones con pies de barro
Es muy importante tener afianzados conocimientos básicos sobre la tecnología que usamos, porque podremos evitar muchos problemas conociendo el contexto en el que trabajamos.
Microsoft creó Asp.Net WebForms como una forma de acercar de forma casi transparente a los desarrolladores de Winforms al entorno web. Con un Visual Studio no necesitas saber http, ni html, ni nada, tan solo arrastrando cajitas y con los asistentes incluidos en el IDE, puedes llegar a hacer aplicaciones de forma relativamente sencilla y rápida.
Por ejemplo al dibujar un botón, tan solo hay que hacer doble click sobre él para abrir una ventana donde pueda condificar la operación necesaria. Esta magia oculta el submit del form, el código funciona y todos felices y contentos.
Creo que ASP.Net WebForms es un gran framework de desarrollo web (a pesar de lo que muchos opinan), pero esta abstracción puede llegar a ser muy peligrosa en malas manos, ya que el desconocimiento de http puede provocar que se comentan errores, provocando problemas de rendimiento y de otra índole.

En el mundo java el uso de Hibernate es casi el estándar de acceso a datos. Este ORM oculta las comunicaciones de la base de datos, pero el desarrollador nunca debería olvidar que al final lo que se ejecuta son instrucciones SQL. Problemas muy comunes en los ORMs, como el n+1 select, vienen provocados por el desconocimiento de la abstracción.
El acceso a datos es una de las partes más importantes de la aplicación, un error en este punto puede provocar problemas catastróficos muy difíciles de solucionar.
Un ORM no es una tecnología trivial, su uso en cada aplicación debería ser estudiado con mucho cuidado y no usarse a la ligera. Como es un estándar de facto, muchos desarrolladores lo usan sin ser conscientes de lo que hacen.
  • Conocer la técnica de ORM es fundamental, no es una opción.
  • Nunca olviden que todo termina en una base de datos relacional; empezar con objetos y pensar OO no significa que tengamos que olvidarnos que el data base existe.
Estas dos frases han sido extraídas de un post de Fabio Maulo, donde da consejos a los nuevo usuarios de NHibernate y que pueden aplicarse a los desarrolladores de java.

Debemos evitar hacer aplicaciones con pies de barro, que se basan en algo que desconocemos, que se usa por costumbre, o porque a otros les ha funcionado, pero que no sabemos lo que hace y que en cualquier momento se puede desmoronar. 

Resumiendo, no debemos basarnos nunca en conocimiento casual, en algo que hace magia pero que no tenemos ni idea de cómo lo hace.

miércoles 4 de enero de 2012

Hibernate y NHibernate (II) Objetos y sesión

En entradas anteriores de NoCompila.com:
  •  Estudiamos como Hibernate y NHibernate es capaz de decidir si debe hacer un Insert o un Update de un objeto. Esta decisión la toma en base al id. Después de escribir el post y revisando la documentación, he descubierto (N)Hibernate también puede utilizar una propiedad version para decidir entre Insert y Update.
  • Vimos que Hibernate y NHibernate se comportan igual, o prácticamente igual, a la hora de generar insert o updates.
Después de unas cuantas charlas con compañeros he decido ampliar el post y hablar sobre la sesión y los objetos, creo que es un tema fundamental para entender el funcionamiento del ORM.
Como lo que vamos a ver son cosas básicas que son iguales en java y en .net solo voy a poner el código en c#, además voy a utilizar unas versiones antiguas (c# 2.0 y NHibernate 2.1) para utilizar métodos y sintaxis lo más comunes y facilite la compresión.
Además vamos a utilizar la clase y el mapeo del post anterior, os los recuerdo:
    class Entidad
    {
        private int _id;
        private string _nombre;

        virtual public int Id
        {
            get { return _id; }
            set { _id = value; }
        }

        virtual public string Nombre
        {
            get {return _nombre;}
            set { _nombre = value; }
        }

        public Entidad()
        {
        }
        public Entidad(string nombre)
        {
            Nombre = nombre;
        }
    }

  
    
      
    
    
  



La sesión
La sesión de (N)Hibernate es, según la documentación del ORM, una implementación del patrón Unit of Work. La sesión controla todos los cambios que se realizan a los objetos que tiene asociados y sabe si tiene que hacer un delete, update o insert.
Además funciona a modo de caché, ya que al hacer las peticiones de base de datos a través de ella esta puede saber si el objeto ya está cargado en memoria sin necesidad de lanzar una select.
¡¡¡OJO!!! a la sesión no se pueden asociar dos objetos del mismo tipo con el mismo id. de la misma forma que una base de datos no puedes insertar dos registros con la misma pk en la misma tabla.

Estado de los Objetos
Los objetos en (N)Hibernate pueden estar en tres estados:
Transient: es un obejto que no está asociado a una sesión de (N)Hibernate. Es decir cuando creamos un objeto con un new
Persistent: es un objeto que está asociado a una sesión. Al hacer flush() en la sesión, el estado del objeto se envia a base de datos.
Detached: es un objeto asociado a una sesión que ya no existe.
para más información cosultad la documentación oficial


Asociando objetos a la sesión
Hacer que un objeto sea Persistent es relativamente sencillo, como es de imaginar el objeto session nos ofrece varios métodos:
  • save: se utiliza cuando se envía un objeto nuevo a la base de datos. Por ejemplo cuando creamos un objeto que no existe en la base de datos. Lo que hace save es ponerle un id al objeto, según la política que esté definida en el mapeo.
  • update: sirve para asociar un objeto que ya sabemos que existe en base de datos en la sesión. Es decir, a diferencia de save, update no le asigna id al objeto.
  • SaveOrUpdate: lo vimos en el post anterior, asocial al objeto a la sesión, decidiendo si usar save o update
Veamos un ejemplo:
            Configuration cfg = new Configuration();
            cfg.Configure();
            ISessionFactory sf = cfg.BuildSessionFactory();
            ISession session = sf.OpenSession();

            Entidad e1 = new Entidad("UNO"); //creamos un objeto transient
            session.Save(e1); //el objeto se vuelve Persistent

            session.Flush();//Se lanza un Insert en la base de datos
            session.Close(); //e1 pasa a ser detached 

            session = sf.OpenSession();

            Entidad e2 = new Entidad(); //se crea el objeto transient
            e2.Id = e1.Id;
            e2.Nombre = "DOS";

            session.Update(e2); // e2 se vuelve Persistent

            e2.Nombre = "TRES";

            session.Flush ();
            session.Close(); //e2 pasa a ser detached

            sf.Close();

¿Cuantas querys se lanzan contra la base de datos?¿y en qué momento?
Se lanzan tan solo dos querys contra la base de datos, un insert y un update:
    INSERT 
    INTO
        Entidades
        (Nombre) 
    VALUES
        (@p0);
    select
        SCOPE_IDENTITY();
    @p0 = 'UNO'

    UPDATE
        Entidades 
    SET
        Nombre = @p0 
    WHERE
        Id = @p1;
    @p0 = 'TRES', @p1 = 1032

El insert se lanza en la primera sesión que creamos, en la línea 7 al hacer el save. Lo hace aqui porque el generador de id que está definido en el mapeo es native, esto significa que le tiene que pedir a la base de datos el id. Únicamente por esta razón lanza el insert en esa línea. Si subiésemos definido el generador como guid el ORM le asignaría como id un new System.Guid() y no ejecutaría el insert en ese momento.

El update se lanza en la linea 22, al hacer el flush de la segunda sesión.
Es muy importante no confundir session.save y session.update con operaciones de base de datos. Os recuerdo que la sesión es la encargada de realizar las comunicaciones con la base de datos, save y update solo pasan a persistent un objeto. Fijaos en el update del log, el valor de nombre es TRES, pero en el código se hizo update con el nombre valiendo DOS. Esto es porque update solo asoció el objeto a la sesión. Es la sesión al hacer flush la que envía los cambios que tiene controlados a la base de datos.

La sesión tiene una propiedad llamada flushMode donde se define la política de flush. Yo recomendaría hacer un flush manual antes de cerrar la sesión para evitarnos sorpresas inesperadas.

  • Lock: solo se debe usar con objetos detached y sirve para atachearlos a la sesión. ¿entonces cual es la diferencia con update? Cuando se hace update de un objeto detached hibernate siempre lanzará un update a la base de datos (salvo que en mapping se le marque con select-before-update, entonces lanzará una select antes para saber si debe actualizar). Es decir si no sabemos si el objeto ha cambiado desde que se cerró la sesión podemos usar Update, si sabemos que no cambio podemos usar lock.
            Configuration cfg = new Configuration();
            cfg.Configure();
            ISessionFactory sf = cfg.BuildSessionFactory();
            ISession session = sf.OpenSession();

            Entidad e1 = new Entidad("UNO"); 
            Entidad e2 = new Entidad("ALFA");
            session.Save(e1); 
            session.Save(e2);

            session.Flush();
            session.Close();  

            session = sf.OpenSession();

            e2.Nombre = "BETA";

            session.Update(e1); 
            session.Lock(e2, LockMode.None);

            session.Flush ();
            session.Close();

            session = sf.OpenSession();

            session.Lock(e2, LockMode.None);
            e2.Nombre = "GAMMA";

            session.Flush();
            session.Close();

            sf.Close();
En las líneas 7 y 8 lanza los inserts correspondientes para cada objeto.
En el flush de la linea 21 lanza un update para actualizar el objeto e1, esta operación no sería necesaria porque el objeto no cambió, pero al reasociarlo con update se lanza una actualización contra la base de datos. Fijaos que en la linea 16 hemos actualizado el nombre de la e2, pero al estar detached ninguna sessión capturó el cambio, así que en la linea 21 no se nos lanza un update para este objeto.
En el flush de la linea 29 se lanza un update de la e2 ya que esta vez sí que hemos cambiado el nombre del objeto cuando estaba asociado a la sesión.
Aquí tenéis el log con las querys:
    INSERT 
    INTO
        Entidades
        (Nombre) 
    VALUES
        (@p0);
    select
        SCOPE_IDENTITY();
    @p0 = 'UNO'

    INSERT 
    INTO
        Entidades
        (Nombre) 
    VALUES
        (@p0);
    select
        SCOPE_IDENTITY();
    @p0 = 'ALFA'

--Flush Línea 21
    UPDATE
        Entidades 
    SET
        Nombre = @p0 
    WHERE
        Id = @p1;
    @p0 = 'UNO', @p1 = 1047

 --Flush Línea 29
    UPDATE
        Entidades 
    SET
        Nombre = @p0 
    WHERE
        Id = @p1;
    @p0 = 'GAMMA', @p1 = 1048

  • Merge: Quizás lo describía mejor su antiguo nombre SaveOrUpdateCopy, es decir, busca en la sesión y en base de datos el objeto, si existe lo machaca con el que lo pasamos y devuelve una instancia del objeto asociado a la sesión. Con este método evitamos que la sesión lance un error al intentar asociar un objeto con un id ya asociado a la sesión.
No sé si ha quedado claro, veamos un ejemplo
            Configuration cfg = new Configuration();
            cfg.Configure();
            ISessionFactory sf = cfg.BuildSessionFactory();
            ISession session = sf.OpenSession();

            Entidad e1 = new Entidad("UNO"); 
            Entidad e2 = new Entidad("ALFA");
            e1 = (Entidad)session.Merge(e1);
            e2.Id = e1.Id; 
            e2 = (Entidad)session.Merge(e2);

            session.Flush();
            session.Close();  

            session = sf.OpenSession();

            e2.Nombre = "BETA";

            session.Merge(e2); 
            
            session.Flush ();
            session.Close();

            sf.Close();

En este caso el merge de la línea 8 provoca un insert, ya que como pasamos a la sesión un objeto sin Id (N)Hibernate va a la base de datos para calcularla. En la línea 10 se machaca el objeto de sesión e1, con el e2 (porque tienen el mismo id) pero no se lanza ninguna query. En la línea 12 en el flush de la primera sesión se realiza un update que cambia el nombre de UNO a ALFA en la base de datos. En la línea 19 en el merge de una nueva sesión se lanza una select, ya que el objeto que se le pasa no está en la sesión y tiene que ir a la base de datos a buscarlo. En la línea 21, el flush provoca un update actualizando el Nombre a BETA
--Línea 8, Merge
    INSERT 
    INTO
        Entidades
        (Nombre) 
    VALUES
        (@p0);
    select
        SCOPE_IDENTITY();
    @p0 = 'UNO'

--Línea 12, Flush
    UPDATE
        Entidades 
    SET
        Nombre = @p0 
    WHERE
        Id = @p1;
    @p0 = 'ALFA', @p1 = 1053

--Línea 19, Merge
    SELECT
        entidad0_.Id as Id0_0_,
        entidad0_.Nombre as Nombre0_0_ 
    FROM
        Entidades entidad0_ 
    WHERE
        entidad0_.Id=@p0;
    @p0 = 1053

--Línea 21, Flush
    UPDATE
        Entidades 
    SET
        Nombre = @p0 
    WHERE
        Id = @p1;
    @p0 = 'BETA', @p1 = 1053

Espero que con esto quede claro como se comporta NHibernate, está claro que esto no cubre todos los casos, pero creo que es interesante y necesario conocer como se comportan los objetos con la sesión y como se pasan de transient a persistent y a detached.

lunes 2 de enero de 2012

Hibernate y NHibernate

El otro día en la oficina estaba hablando con un compañero sobre Hibernate. Este compañero me comentaba que debía asociar a la sesión objetos antes de guardarlos. Según me explicaba, al hacer un new de un objeto y guardarlo directamente con Hibernate, el ORM daba fallos en algunos escenarios pudiendo lanzar inserts cuando debería hacer updates o al revés (no me acuerdo).
Desgraciadamente para mí, nunca he podido utilizar NHibernate en un proyecto real de producción, pero estaba seguro que el comportamiento que me describía no se daba en .Net y siendo proyectos hermanos, supuse que el comportamiento sería igual en Hibernate.
Este compañero de la oficina es difícil de convencer, así que programé una mini aplicación de consola en c# para demostrarle como se comportaba el ORM.

Primero hice una clase Entidad:

    class Entidad
    {
        private int _id;
        private string _nombre;

        virtual public int Id
        {
            get { return _id; }
            set { _id = value; }
        }

        virtual public string Nombre
        {
            get {return _nombre;}
            set { _nombre = value; }
        }

        public Entidad()
        {
        }
        public Entidad(string nombre)
        {
            Nombre = nombre;
        }
 
    }

Depues añadí el Mapping de NHibernate

  
    
      
    
    
  

Y por último el código del programa. Tened en cuenta que es una aplicación de NHibernate donde se trabaja directamente con session, es decir, que este código no es una buena referencia para escribir una aplicación profesional.

    class Program
    {
        static void Main(string[] args)
        {
            Configuration cfg = new Configuration();
            cfg.Configure();
            ISessionFactory sf = cfg.BuildSessionFactory();
            ISession session = sf.OpenSession();

            Entidad entidad = new Entidad("PRUEBA");
            session.SaveOrUpdate(entidad);

            session.Flush();
            session.Close();

            session = sf.OpenSession();
            Entidad entidad2 = new Entidad();
            entidad2.Id = entidad.Id;
            entidad2.Nombre = "MODIFICADO";

            session.SaveOrUpdate(entidad2);

            session.Flush();
            session.Close();

            sf.Close();      
        }
    } 

Como podéis ver, el código es muy simple. Se crea una entidad y se llama directamente al método SaveOrUpdate y cerramos la sesión. Si os fijáis en el mapeo de Entidad podéis ver que el generador del id es native, esto significa que la base de datos es la responsable de asignarle un ID. En mi caso (un SQL Server) la columna Id de la tabla Entidades está definida como identity (autoincremental). Mirando la consulta de base de datos que ejecuta NHibernate podemos ver lo siguiente:

INSERT 
    INTO
        Entidades
        (Nombre) 
    VALUES
        (@p0);
    select
        SCOPE_IDENTITY();
    @p0 = 'PRUEBA'

Podemos ver claramente como NHibernate no envía el Id y deja a la base de datos asignarle un valor, despés de insertar el registro, recupera el id haciendo select SCOPE_IDENTITY
Después de eso se cierra la sesión y se crea otra nueva, además se crea una nueva entidad que copia las propiedades Id y Nombre del objeto anterior y se vuelve a guardar. Esta vez nos encontramos con un objeto creado fuera de la sesión de NHibernate, pero que representa a un registro existente en base de datos. ¿Qué hará el ORM cuando tenga que almacenarlo en la base de datos? ¿Sabrá hacerlo? Esto es lo que hace NHibernate:

UPDATE
        Entidades 
    SET
        Nombre = @p0 
    WHERE
        Id = @p1;
    @p0 = 'MODIFICADO', @p1 = 1

NHibernate ha sabido que tenía que hacer un update aunque el objeto no llegó de la sesión. ¿Cómo lo hace? Muy sencillo, en el mapeo se indica que el tipo de generador de id es native, lo que significa que será la base de datos la responsable de asignar id. En el primer caso, como el objeto no tenía id, hizo un insert, en el segundo como ya tenía id hizo un update.

Como comenté anteriormente, mi compañero no es fácil de convencer, y no le gustó mi explicación. Así que intentó ponerla en duda. Me pidió que quitase el identity a la base de datos y que asignase el id de forma manual.
No voy a volver a copiar y pegar todo el código, porque es el mismo, solo os indicaré que puse esta línea antes del primer SaveOrUpdate() esto:
    entidad.Id = 1024;
además modifiqué el mapping
  
    
      
    
    
  

Esta vez, al asignar de forma manual el id, NHibernate no sabe si tiene que lanzar un update o un insert ¿Cómo pensais que se comportaría al guardar el primer objeto?
    SELECT
        entidad_.Id,
        entidad_.Nombre as Nombre0_ 
    FROM
        Entidades entidad_ 
    WHERE
        entidad_.Id=@p0;
    @p0 = 1024

    INSERT 
    INTO
        Entidades
        (Nombre, Id) 
    VALUES
        (@p0, @p1);
    @p0 = 'PRUEBA', @p1 = 1024

Como podemos ver NHibernate no hace magia, lanza un select contra el Id para saber si el objeto existe en base de datos. Como no existe lanza un insert. Con el segundo objeto hace lo mismo:
    SELECT
        entidad_.Id,
        entidad_.Nombre as Nombre0_ 
    FROM
        Entidades entidad_ 
    WHERE
        entidad_.Id=@p0;
    @p0 = 1024

    UPDATE
        Entidades 
    SET
        Nombre = @p0 
    WHERE
        Id = @p1;
    @p0 = 'MODIFICADO', @p1 = 1024

Esta vez, después de comprobar que el objeto existe en base de datos mediante otro select, el ORM decide lanzar un update.
Después de todo esto pesaréis que la discusión quedó zanjada, y que mi compañero confía en mi explicación... pues de eso nada. Mi compañero ahora sostiene que Hibernate y NHibernate se comportan de forma diferente, lo que me obliga a hacer el mismo programa en java para demostrarle que Hibernate y NHibernate (al menos en este caso) se comportan de la misma forma.
Así que volví a crear la clase entidad (perdonad si me cargo alguna convención de java, al final, el 90% de mi tiempo lo paso picando vb.net):
public class Entidad {
 private Integer id;
 private String Nombre;
 public void setId(Integer id) {
  this.id = id;
 }
 public Integer getId() {
  return id;
 }
 public void setNombre(String nombre) {
  Nombre = nombre;
 }
 public String getNombre() {
  return Nombre;
 }
}
El mapeo de Hibernate es prácticamente igual que el de NHibernate y este post ya es demasiado largo, así que os copio el código del programa de consola:
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  Configuration conf = new Configuration().configure();
  SessionFactory sf = conf.buildSessionFactory();
  Session session = sf.openSession();
  
  Entidad entidad = new Entidad();
  entidad.setNombre("Prueba");
   
          session.saveOrUpdate(entidad);

          session.flush();
          session.close();
         
          session = sf.openSession();
          Entidad entidad2 = new Entidad();
          entidad2.setId(entidad.getId());
          entidad2.setNombre("MODIFICADO");

          session.saveOrUpdate(entidad2);

          session.flush();
          session.close();

          sf.close();
  
 }

Como podeis ver en java he hecho lo mismo que en c#. La primera vez que se ejecuta el código la base de datos tiene configurado el campo id con identity y las consultas que lanza Hibernate son:
insert into Entidades (Nombre) values (?)
update Entidades set Nombre=? where Id=?

Y al quitar el identity y poner assigned en el mapeo la aplicación genera estas consultas:
select entidad_.Id, entidad_.Nombre as Nombre0_ from Entidades entidad_ where entidad_.Id=?
insert into Entidades (Nombre, Id) values (?, ?)
select entidad_.Id, entidad_.Nombre as Nombre0_ from Entidades entidad_ where entidad_.Id=?
update Entidades set Nombre=? where Id=?

En resumen, y obviando la diferencia de formatos de logs entre Hibernate y su hermano de .NET, los dos ORMs se comportan de la misma forma. (N)Hibernate utiliza el ID para saber si un objeto está en base de datos y saber si tiene que lanzar un update o un insert.
Como nota curiosa he de mencionar que para el ejemplo de java he usado Hibernate 4 y me he llevado una sorpresa al descubrir que buildSessionFactory() está deprecado pero esto no afectó a mi prueba de concepto.

martes 20 de diciembre de 2011

RWJ. La memoria en la JVM

Aunque muchas veces se nos olvida, la JVM es un proceso del sistema operativo como otro cualquiera. Si, si. Lo que leéis. Y como en el resto de procesos, la memoria está dividida en tres regiones :  el stack, el heap y el code segment 

  • el stack es la zona de memoria donde se almacenan los parámetros que se pasan a una función en una llamada, las variables locales e información de retorno. Cada vez que se llama a una función desde dentro de otra, se apilan los parámetros de la nueva función y sus variables hasta que se haga return.
  • el heap es el área de memoria en el que "se hacen los malloc() " . En él se guardan las estructuras de datos permanentes en memoria. Y en los lenguajes orientados a objetos, se crean los objetos. Y si se usa mucho, acaba por llenarse.
  • el code segment es la zona de memoria donde reside el código del proceso. En el caso de la JVM, es la parte del código que no está programado en Java. Es decir, el core nativo de la JVM.
Como comentaba, la JVM es un proceso como otro cualquiera. Y la gran idea que tuvo Sun fue hacer un proceso que ejecutase procesos. Y esos pseudoprocesos están escritos en bytecode, una especie de código máquina para JVM, independiente de la plataforma sobre la que se ejecute la JVM.

Como tuvieron tiempo para pensárselo, hicieron las cosas a conciencia: los programas Java se compilan a  bytecode común y se ejecutan en la JVM -específica por sistema operativo- . La orientación a objetos  está muy presente en todo el diseño del lenguaje y, como no, en el de la propia JVM. La máquina virtual ofrece mecanismos para liberar la memoria de los objetos obsoletos, llamado garbage collector. Y el propio código de los programas Java forma parte del paradigma objetual. 

El área de memoria de los procesos de la JVM tiene un diseño propio, muy parecido al de los sistemas operativos, pero diseñado para facilitar el garbage collecting: 
  • el stack, con el mismo funcionamiento comentado antes
  • el heap, en el que se crean todos los objetos utilizados, y sobre los que posteriormente se hará recolección de basura, con dos partes :
    • young generation: en el que se crean los nuevos objetos (eden) y con dos survival spaces por los que van pasando los objetos antes de llegar a la
    • tenured (old) generation, en el que acaban los objetos persistentes a los gc.
  • el permanent generation space , equivalente al code segment. En él se va cargando la información de las clases y métodos utilizados. La memoria usada en este espacio no se libera jamás, y hará falta más espacio cuantas más clases utilice el proceso.
A nivel de implementación de sistema operativo, tanto el heap como el permgen space se guardan en el heap del proceso JVM, aunque cuando hablamos de Java, solemos decir que el permgen space no es parte del heap.

Por cuestiones de diseño y seguridad, la memoria máxima que el proceso de la JVM va a utilizar se define en el momento de arrancarla, y no es posible aumentarla. ¿Cómo? Con las siguientes opciones :
  • -Xms64m , que indica que el tamaño del heap reservado al arrancar la JVM será de 64 megas.
  • -Xmx1024m, que limita a 1024 megas el tamaño máximo de heap reservable.
  • -XXMaxPermSize=128m , limita a 128 megas el tamaño máximo de la permanent generation.
La memoria máxima reservada por defecto para la JVM de Sun es -Xmx64m . Pero estos y otros muchos parámetros son configurables .

Leído todo esto, es posible que empecéis a distinguir los diferentes errores de memoria de la JVM
  • java.lang.StackOverflowError . Error de desbordamiento de pila, que ocurre cuando se apilan tantas llamadas a funciones que se salen de su espacio de memoria ( normalmente con llamadas recursivas infinitas ) 
  • java.lang.OutOfMemoryError : heap space . Ocurre cuando el heap se llena por completo y el gc no es capaz de liberar espacio. Puede deberse a memory leaks o a que nuestro programa necesita más memoria.
  • java.lang.OutOfMemoryError : PermGen space failure . Cuando el permgen space se llena a tope. Puede deberse a que no hay memoria suficiente para cargar todas las clases que llamamos (¿¿nos cargamos todo apache commons?? ) o a memory leaks en classloaders que recargan clases en caliente pero no liberan bien la memoria de las anteriores.
Y en cuanto al tema del garbage collector, yo creo que se merece una entrada propia. ¿Se echa de menos algún dibujo en el artículo?











martes 13 de diciembre de 2011

Comprando chollos

Soy de los que les gustan los libros. Me parece mucho más fácil aprender de un libro que de blogs o de una plataforma de formación online. Pero lo malo es que los libros técnicos cuestan una pasta. 

Yo creo que a día de hoy ya sabemos todos que los libros salen muuuy baratos en amazon (sobre todo en el de US). Lo malo son los sablazos que nos pegan por los portes. 


Pues resulta que play.com no sólo vende juegos y camisetas. También vende libros, y aunque no es la repera como amazon, lo que si que tiene son chollazos en la parte de segunda mano - libros muy baratos y portes gratis - . 
La semana pasada me compré estos libros y ... ya los tengo en casa: 
Y todo con portes gratis y enviados en paquetes separados para evitar grandes males. Por eso os aconsejo consultar de vez en cuando los libros de programación ordenados por precio .

miércoles 2 de noviembre de 2011

Consejos de Coco

Hola niños, soy Coco y hoy voy a enseñaros la diferencia entre servidor y cliente en aplicaciones web.

El cliente es un navegador, y hay de muuuuuuuuuuchas clases. Algunos tienen una E azul muy gorda y todo el mundo habla mal de ellos. Otros tienen un zorro enroscado. Dicen que los hackers usan uno con una O roja. Google también tiene el suyo y puede hacer que se esconda debajo de la E azul.
Pero todos hacen dos cosas pintar HTML y ejecutar Javascript.


El servidor es un ordenador muy grande que hace muchas cosas, es muy potente y puede hacer casi de todo: poner pelis y música, imprimir, hacer ficheros, leer bases de datos,… el límite lo pone tu imaginación.
Pero el pobre servidor está solito, no tiene usuarios que jueguen con él, así que cuando programes para él haz que hable con los navegadores, que tenga algo de compañía.

Si pones al servidor a hacer cosas solito se pone muy triste. Lo peor que puedes hacerle es obligarle a mostrar un cuadro de diálogo, en esos casos el servidor esperará a que alguien venga y le responda, pero lo triste es que nadie llegará. Algunas veces la depresión es tan grande que el servidor dejará todo esperando la respuesta.

Para hablar con la gente el servidor puede mandar html y javascript o incluso ficheros (eso los muy listos) al navegador, donde lo mostrará todo muy bonito (en unos más que en otros), preguntarle cosas con cuadros de dialogo o guadar pdfs...


Ya sabeis niños, el amigo del servidor es el navegador, haz que hable con él, y no lo pongas a hablar solo, que se deprime.


¡¡¡Y COMO VUELVA A VER UN MSGBOX EN UNA DLL COMIENZO A CORTAR MANOS!!!   

lunes 31 de octubre de 2011

Grep en Windows

Esta tarde hablando con un compañero le pregunté si se podía crear un logger para log4net en el que solo sacase los WARN de una clase. Me comentó que él haría un grep y que eso era una de las razones por las cuales le gustaban más los sistemas tipo Linux/UNIX. Entonces recordé que en windows tambien podemos hacer cosas de este tipo y le comenté que con findstring se podía hacerse. He de reconocer que no suelo pensar en línea de comandos. Hace años que dejé eso (desde que no tengo que editar autoexec.bat y el config.sys para ejecutar según que juegos).

Me gustan las ventanas y no lo niego
Creo que los sistemas de ventanas son muy cómodos, no solo a nivel usuario. Para realizar tareas de administración me gusta tener una ventana donde vea todas las opciones y poder editar. Me resulta infinitamente más cómodo que tener que leerme un fichero de script y esperar que las opciones que necesite y que no estén configuradas vengan en una linea comentada.
Aunque reconozco que para ciertas operaciones por lotes la linea de comandos está muy bien y que debería pensar más en eso de lo que suelo hacer.

Linea de comandos en Windows.
Aunque parezca mentira hay linea de comandos en windows, y a fata de una hay dos: El command (para mi MS-DOS aunque no lo sea) y el Powershell.
Powershell es mucho más pontente que la consola de MS-DOS. Fue desarrollado por Mircrosoft para contentar a los administradores de windows que echaban de menos una linea de comando ponente, como la de UNIX. Se lanzó en el 2003 y actualmente se distribuye con los sistemas operativos, tanto de servidor (Server 2008) como de escritorio (Windows 7).
La nueva política de Microsoft intenta que todos sus productos tengan soporte powershell para que los administradores puedan hacer las mismas operaciones tanto desde ventanas como desde linea de comandos, así que sqlserver trae sus scripts de powershell, exchange server también, etc.
Por ejemplo si instalas el Server Core de Windows 2008 Server, se teiene que hacer todo desde la línea de comandos.

Punto negro de powershell
Powershell tiene una carencia importante: No hay una instruccion equivalente a tail -f en UNIX. Lo más parecido es get-content -wait, pero no funciona todo lo bien que sería necesario. Afortunadamente la comunidad de ususarios han generado herramientas para suplir esta carencia. Esperoemos que los chicos de Redmond añadan soporte oficial en próximas actualizaciones.

Ya está bien de tanto coñazo ¿y lo del grep como va?
Para filtrar lineas de fichero en MS-DOS existe la instrucción findstr y para volcar a un fichero cualquier salida de comando utilizamos > así que para hacer el filtro que necesitaba en el log solo tenía que hacer:
findstr WARN aplicacion.log > logfiltrado.log

En powershell se haría concatenando tres comandos: get-content, select-string y out-file, los comandos en powershell se concatenan con pipes:
get-content aplicacion.log | select-string WARN | out-file logfiltrado.log