Skip to content

13. La aplicación [SimuPaie] – versión 9 – integración Spring / NHibernate

Aquí nos proponemos retomar la aplicación ASP.NET de tres capas de la versión 7 [pam-v7-3tier-nhibernate-multivues-multipages]. La arquitectura en capas de la aplicación era la siguiente:

En el ejemplo anterior, la capa [dao] se había implementado con el marco NHibernate. El marco Spring solo se había utilizado para la integración de las capas entre sí. El marco Spring ofrece clases de utilidad para trabajar con el marco Nhibernate. El uso de estas clases facilita la escritura del código de la capa [dao]. La arquitectura anterior evoluciona de la siguiente manera:

Debido a la estructura en capas utilizada, la integración de Spring con NHibernate implica la modificación únicamente de la capa [dao]. Las capas [presentation] (web / ASP.NET) y [metier] no tendrán que modificarse. Esta es la principal ventaja de las arquitecturas en capas integradas por Spring.

A continuación, construiremos la capa [dao] junto con [Spring / NHibernate], comentando el código de una solución funcional. No pretenderemos abarcar todas las posibilidades de configuración o uso del marco [Spring / Nhibernate]. El lector podrá adaptar la solución propuesta a sus propios problemas con la ayuda de la documentación de Spring.NET y [http://www.springframework.net/documentation.html] (junio de 2010).

El procedimiento seguido para construir las capas [dao] y [metier] es el de la versión 3 descrito en el párrafo 7. El procedimiento seguido para la capa [présentation] es el de la versión 7 descrito en el párrafo 11.

13.1. La capa [dao] de acceso a los datos

13.1.1. El proyecto de Visual Studio C# de la capa [dao]

El proyecto de Visual Studio de la capa [dao] es el siguiente:

  • en [1], el proyecto en su totalidad
    • la carpeta [pam] contiene las clases del proyecto, así como la configuración de las entidades NHibernate
    • los archivos [App.config] y [Dao.xml] configuran el marco Spring / NHibernate. Tendremos que describir el contenido de estos dos archivos.
  • En [2], las diferentes clases del proyecto
    • en la carpeta [entites] encontramos las entidades NHibernate estudiadas en el proyecto [pam-dao-nhibernate]
    • en la carpeta [service], encontramos la interfaz [IPamDao] y su implementación con el marco Spring / NHibernate [PamDaoSpringNHibernate]. Tendremos que escribir esta nueva implementación de la interfaz [IPamDao]
    • La carpeta [tests] contiene las mismas pruebas que el proyecto [pam-dao-nhibernate]. Estas pruebas evalúan la misma interfaz [IPamdao].
  • En [3], las referencias del proyecto. La integración de Spring / NHibernate requiere dos nuevas: DLL, [Spring.Data] y [Spring.Data.NHibernate12]. Estos DLL están disponibles en el marco Spring.Net. Se han agregado a la carpeta [lib] de los DLL y [4]:

En las referencias [3] del proyecto, se encuentran las siguientes DLL:

  • NHibernate: para el ORM NHibernate
  • MySql.Data: el controlador ADO.NET del SGBD MySQL
  • Spring.Core: para el marco Spring, que garantiza la integración de las capas
  • log4net: una biblioteca de registros
  • nunit.framework: una biblioteca de pruebas unitarias
  • Spring.Data y Spring.Data.NHibernate12: garantizan la compatibilidad con Spring / NHibernate.

Estas referencias se han tomado de la carpeta [lib] [4]. Se debe asegurar que todas estas referencias tengan la propiedad «Copia local» establecida en «True» [5]:

13.1.2. La configuración del proyecto C#

El proyecto está configurado de la siguiente manera:

  • En [1], el nombre del ensamblado del proyecto es [pam-dao-spring-nhibernate]. Este nombre aparece en varios archivos de configuración del proyecto.

13.1.3. Las entidades de la capa [dao]

Las entidades (objetos) necesarias para la capa [dao] se han reunido en la carpeta [entites] [1] del proyecto. Estas entidades son las mismas que las del proyecto [pam-dao-nhibernate], con una sola diferencia en los archivos de configuración NHibernate. Tomemos, por ejemplo, el archivo [Employe.hbm.xml]:

  • en [2], el archivo está configurado para ser incorporado al ensamblado del proyecto

Su contenido es el siguiente:


<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="Pam.Dao.Entites" assembly="pam-dao-spring-nhibernate">
    <class name="Employe" table="EMPLOYES">
        <id name="Id" column="ID">
            <generator class="native" />
        </id>
        <version name="Version" column="VERSION"/>
        <property name="SS" column="SS" length="15" not-null="true" unique="true"/>
        <property name="Nom" column="NOM" length="30" not-null="true"/>
        <property name="Prenom" column="PRENOM" length="20" not-null="true"/>
        <property name="Adresse" column="ADRESSE" length="50" not-null="true" />
        <property name="Ville" column="VILLE" length="30" not-null="true"/>
        <property name="CodePostal" column="CP" length="5" not-null="true"/>
        <many-to-one name="Indemnites" column="INDEMNITE_ID" cascade="all" lazy="false"/>
    </class>
</hibernate-mapping>
  • línea 2: el atributo assembly indica que el archivo [Employe.hbm.xml] se encontrará en el ensamblado [pam-dao-spring-nhibernate]

13.1.4. Configuración de Spring / NHibernate

Volvamos al proyecto de Visual C#:

  • En [1], los archivos [App.config] y [Dao.xml] configuran la integración de Spring / NHibernate

13.1.4.1. El archivo [App.config]

El archivo [App.config] es el siguiente:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!-- secciones de configuración -->
    <configSections>
        <sectionGroup name="spring">
            <section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core" />
            <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
            <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
        </sectionGroup>
        <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
    </configSections>


    <!-- Configuración de Spring -->
    <spring>
        <parsers>
            <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
        </parsers>
        <context>
            <resource uri="Dao.xml" />
        </context>
    </spring>

    <!-- Esta sección contiene los ajustes de configuración de log4net -->
    <!-- NOTE IMPORTANTE: los registros no están activos de forma predeterminada. Deben activarse mediante programación
    avec l'instruction log4net.Config.XmlConfigurator.Configure();
    ! -->
    <log4net>
        <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
            <layout type="log4net.Layout.PatternLayout">
                <conversionPattern value="%-5level %logger - %message%newline" />
            </layout>
        </appender>

        <!-- Establece el nivel de registro predeterminado en DEBUG -->
        <root>
            <level value="DEBUG" />
            <appender-ref ref="ConsoleAppender" />
        </root>

        <!-- Configurar el registro para Spring.  Los nombres de los registradores en Spring corresponden al espacio de nombres -->
        <logger name="Spring">
            <level value="INFO" />
        </logger>

        <logger name="Spring.Data">
            <level value="DEBUG" />
        </logger>

        <logger name="NHibernate">
            <level value="DEBUG" />
        </logger>
    </log4net>

</configuration>

El archivo [App.config] anterior configura Spring (líneas 5-9, 15-22), log4net (línea 10, líneas 28-53), pero no NHibernate. Los objetos de Spring no se configuran en [App.config], sino en el archivo [Dao.xml] (línea 20). Por lo tanto, la configuración de Spring / NHibernate, que consiste en declarar objetos específicos de Spring, se encontrará en este archivo.

13.1.4.2. El archivo [Dao.xml]

El archivo [Dao.xml], que reúne los objetos administrados por Spring, es el siguiente:


<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net"
         xmlns:db="http://www.springframework.net/database">

    <!-- Referenciado por el archivo de configuración del contexto de la aplicación principal -->
    <description>
        Application Spring / NHibernate
    </description>

    <!-- Configuración de la base de datos y NHibernate -->
    <db:provider id="DbProvider"
                   provider="MySql.Data.MySqlClient"
                   connectionString="Server=localhost;Database=dbpam_nhibernate;Uid=root;Pwd=;"/>

    <object id="NHibernateSessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate12">
        <property name="DbProvider" ref="DbProvider"/>
        <property name="MappingAssemblies">
            <list>
                <value>pam-dao-spring-nhibernate</value>
            </list>
        </property>
        <property name="HibernateProperties">
            <dictionary>
                <entry key="hibernate.dialect" value="NHibernate.Dialect.MySQLDialect"/>
                <entry key="hibernate.show_sql" value="false"/>
            </dictionary>
        </property>
        <property name="ExposeTransactionAwareSessionFactory" value="true" />
    </object>

    <!-- gestor de transacciones -->
    <object id="transactionManager"
        type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate12">
        <property name="DbProvider" ref="DbProvider"/>
        <property name="SessionFactory" ref="NHibernateSessionFactory"/>
    </object>

    <!-- Plantilla de Hibernate -->
    <object id="HibernateTemplate" type="Spring.Data.NHibernate.Generic.HibernateTemplate">
        <property name="SessionFactory" ref="NHibernateSessionFactory" />
        <property name="TemplateFlushMode" value="Auto" />
        <property name="CacheQueries" value="true" />
    </object>

    <!-- Objetos de acceso a datos -->
    <object id="pamdao" type="Pam.Dao.Service.PamDaoSpringNHibernate, pam-dao-spring-nhibernate" init-method="init" destroy-method="destroy">
        <property name="HibernateTemplate" ref="HibernateTemplate"/>
    </object>
</objects>
  • Las líneas 11 a 13 configuran la conexión a la base de datos [dbpam_nhibernate]. En ellas se encuentra:
    • el proveedor ADO.NET necesario para la conexión; en este caso, el proveedor de SGBD es MySQL. Esto implica tener DLL y [Mysql.Data] en las referencias del proyecto.
    • la cadena de conexión a la base de datos (servidor, nombre de la base, propietario de la conexión, su contraseña)
  • las líneas 15-29 configuran el SessionFactory de NHibernate, el objeto que sirve para obtener sesiones NHibernate. Cabe recordar que toda operación en la base de datos se realiza dentro de una sesión NHibernate. En la línea 15, se puede observar que SessionFactory está implementada por la clase Spring Spring.Data.NHibernate.LocalSessionFactoryObject, que se encuentra en DLL Spring.Data.NHibernate12.
  • Línea 16: la propiedad DbProvider establece los parámetros de conexión a la base de datos (proveedor ADO.NET y cadena de conexión). Aquí, esta propiedad hace referencia al objeto DbProvider definido anteriormente en las líneas 11 a 13.
  • Líneas 17-20: establecen la lista de ensamblados que contienen los archivos [*.hbm.xml] que configuran las entidades administradas por NHibernate. La línea 19 indica que estos archivos se encontrarán en el ensamblado del proyecto. Recordamos que este nombre se encuentra en las propiedades del proyecto C#. También recordamos que todos los archivos [*.hbm.xml] se han configurado para ser incorporados al ensamblado del proyecto.
  • líneas 22-27: propiedades específicas de NHibernate.
    • línea 24: el dialecto SQL que se utilizará será el de MySQL
    • línea 25: el SQL generado por NHibernate no aparecerá en los registros de la consola. Establecer esta propiedad en true permite conocer las órdenes SQL emitidas por NHibernate. Esto puede ayudar a comprender, por ejemplo, por qué una aplicación se vuelve lenta al acceder a la base de datos.
  • Línea 28: la propiedad ExposeTransactionAwareSessionFactory en true hará que Spring administre las anotaciones de gestión de transacciones que se encuentren en el código C#. Volveremos a esto cuando escribamos la clase que implemente la capa [dao].
  • Las líneas 32 a 36 definen el administrador de transacciones. Una vez más, este administrador es una clase de Spring de la clase DLL Spring.Data.NHibernate12. Este administrador necesita conocer los parámetros de conexión a la base de datos (línea 34), así como la clase SessionFactory de NHibernate (línea 35).
  • Las líneas 39-43 definen las propiedades de la clase HibernateTemplate, que también es una clase de Spring. Esta clase se utilizará como clase de utilidades en la clase que implementa la capa [dao]. Facilita las interacciones con los objetos NHibernate. Esta clase tiene ciertas propiedades que deben inicializarse:
    • línea 40: SessionFactory de NHibernate
    • línea 41: la propiedad TemplateFlushMode establece el modo de sincronización del contexto de persistencia NHibernate con la base de datos. El modo Auto hace que haya sincronización:
      • al final de una transacción
      • antes de una operación SELECT
    • línea 42: las consultas HQL (Hibernate Query Language) se almacenarán en caché. Esto puede generar una mejora en el rendimiento.
  • las líneas 46-48 definen la clase de implementación de la capa [dao]
    • línea 46: la capa [dao] será implementada por la clase [PamdaoSpringNHibernate] de DLL [pam-dao-spring-nhibernate]. Tras la instanciación de la clase, se ejecutará inmediatamente el método init de la clase. Al cerrarse el contenedor Spring, se ejecutará el método destroy de la clase.
    • línea 47: la clase [PamDaoSpringNHibernate] tendrá una propiedad HibernateTemplate que se inicializará con la propiedad HibernateTemplate de la línea 39.

13.1.5. Implementación de la capa [dao]

13.1.5.1. El esqueleto de la clase de implementación

La interfaz [IPamDao] es la misma que en el proyecto [pam-dao-nhibernate]:


using Pam.Dao.Entites;

namespace Pam.Dao.Service {
    public interface IPamDao {
        // Lista de todas las identidades de los empleados 
        Employe[] GetAllIdentitesEmployes();
        // un empleado específico con sus prestaciones 
        Employe GetEmploye(string ss);
        // Lista de todas las cotizaciones 
        Cotisations GetCotisations();
    }
}
  • línea 1: se importa el espacio de nombres de las entidades de la capa [dao].
  • línea 3: la capa [dao] se encuentra en el espacio de nombres [Pam.Dao.Service]. Los elementos del espacio de nombres [Pam.Dao.Entites] pueden crearse en varias instancias. Los elementos del espacio de nombres [Pam.Dao.Service] se crean en una sola instancia (singleton). Esto es lo que justificó la elección de los nombres de los espacios de nombres.
  • línea 4: la interfaz se llama [IPamDao]. Define tres métodos:
    • línea 6: [GetAllIdentitesEmployes] devuelve un array de objetos de tipo [Employe] que representa la lista de cuidadoras infantiles en un formato simplificado (apellido, nombre, SS).
    • línea 8, [GetEmploye] devuelve un objeto [Employe]: el empleado cuyo número de seguro social se pasó como parámetro al método, junto con las prestaciones relacionadas con su índice.
    • Línea 10: [GetCotisations] devuelve el objeto [Cotisations], que encapsula las tasas de las diferentes cotizaciones sociales que se deben deducir del salario bruto.

El esqueleto de la clase de implementación de esta interfaz con el soporte de Spring / NHibernate podría ser el siguiente:


using System;
using System.Collections;
using System.Collections.Generic;
using Pam.Dao.Entites;
using Spring.Data.NHibernate.Generic.Support;
using Spring.Transaction.Interceptor;

namespace Pam.Dao.Service {
    public class PamDaoSpringNHibernate : HibernateDaoSupport, IPamDao {
        // campos privados 
        private Cotisations cotisations;
        private Employe[] employes;

        // inicialización 
        [Transaction(ReadOnly = true)]
        public void init() {
...
        }

        // eliminación de objeto
        public void destroy() {
            if (HibernateTemplate.SessionFactory != null) {
                HibernateTemplate.SessionFactory.Close();
            }
        }

        // lista de todas las identidades de los empleados
        public Employe[] GetAllIdentitesEmployes() {
            return employes;
        }


        // un empleado específico con sus prestaciones 
        [Transaction(ReadOnly = true)]
        public Employe GetEmploye(string ss) {
....
        }

        // lista de cotizaciones 
        public Cotisations GetCotisations() {
            return cotisations;
        }
    }
}
  • línea 9: la clase [PamDaoSpringNHibernate] implementa correctamente la interfaz de la capa [dao] [IPamDao]. También deriva de la clase Spring [HibernateDaoSupport]. Esta clase tiene una propiedad [HibernateTemplate] que se inicializa mediante la configuración de Spring que se ha realizado (línea 2 a continuación):

    <object id="pamdao" type="Pam.Dao.Service.PamDaoSpringNHibernate, pam-dao-spring-nhibernate" init-method="init" destroy-method="destroy">
        <property name="HibernateTemplate" ref="HibernateTemplate"/>
</object>
  • En la línea 1 anterior, se observa que la definición del objeto [pamdao] indica que los métodos init y destroy de la clase [PamDaoSpringNHibernate] deben ejecutarse en momentos específicos. Estos dos métodos se encuentran en la clase, en las líneas 16 y 21.
  • líneas 15, 34: anotaciones que hacen que el método anotado se ejecute dentro de una transacción. El atributo ReadOnly=true indica que la transacción es de solo lectura. El método que se ejecuta dentro de la transacción puede lanzar una excepción. En ese caso, Spring realiza un Rollback automático de la transacción. Esta anotación elimina la necesidad de gestionar una transacción dentro del método.
  • Línea 16: Spring ejecuta el método init inmediatamente después de la instanciación de la clase. Veremos que su propósito es inicializar los campos privados de las líneas 11 y 12. Se ejecutará dentro de una transacción (línea 15).
  • Los métodos de la interfaz [IPamDao] se implementan en las líneas 28, 35 y 40.
  • Líneas 28-30: el método [GetAllIdentitesEmployes] simplemente devuelve el atributo de la línea 12 inicializado por el método init.
  • líneas 40-42: el método [GetCotisations] simplemente devuelve el atributo de la línea 11 inicializado por el método init.

13.1.5.2. Métodos útiles de la clase HibernateTemplate

Utilizaremos los siguientes métodos de la clase HibernateTemplate:

IList<T> Find<T>(string requete_hql)
ejecuta la consulta HQL y devuelve una lista de objetos de tipo T
IList<T> Find<T>(string requete_hql, object[])
ejecuta una consulta HQL parametrizada con ?. Los valores de estos parámetros son proporcionados por la matriz de objetos.
IList<T> LoadAll<T>()
devuelve todas las entidades de tipo T
  

Existen otros métodos útiles que no tendremos oportunidad de utilizar y que permiten recuperar, guardar, actualizar y eliminar entidades:

T Load<T>(object id)
inserta en la sesión NHibernate la entidad de tipo T que tiene la clave primaria id.
void SaveOrUpdate(object entidad)
inserta (INSERT) o actualiza (UPDATE) el objeto entité, dependiendo de si este tiene una clave primaria (UPDATE) o no (INSERT). La ausencia de clave primaria se puede configurar mediante el atributo unsaved-values del archivo de configuración de la entidad. Después de la operación SaveOrUpdate, el objeto entité se encuentra en la sesión NHibernate.
void Delete(object entidad)
elimina el objeto entité de la sesión NHibernate.

13.1.5.3. Implementación del método init

El método init de la clase [PamDaoSpringNHibernate] es, por configuración, el método que se ejecuta después de que Spring instancie la clase. Su objetivo es almacenar en caché local las identidades simplificadas de los empleados (nombre, apellido, SS) y las tasas de cotización. Su código podría ser el siguiente.


[Transaction(ReadOnly = true)]
        public void init() {
            try {
                // se obtiene la lista simplificada de empleados
                IList<object[]> lignes = HibernateTemplate.Find<object[]>("select e.SS,e.Nom,e.Prenom from Employe e");
                // se coloca en una tabla
                employes = new Employe[lignes.Count];
                int i = 0;
                foreach (object[] ligne in lignes) {
                    employes[i] = new Employe() { SS = ligne[0].ToString(), Nom = ligne[1].ToString(), Prenom = ligne[2].ToString() };
                    i++;
                }
                // se colocan las tasas de cotización en un objeto 
                cotisations = (HibernateTemplate.LoadAll<Cotisations>())[0];
            } catch (Exception ex) {
                // se transforma la excepción 
                throw new PamException(string.Format("Erreur d'accès à la BD : [{0}]", ex.ToString()), 43);
            }
        }
  • línea 5: se ejecuta una consulta HQL. Solicita los campos SS, Apellido y Nombre de todas las entidades Employé. Devuelve una lista de objetos. Si se hubiera solicitado toda la información del empleado en la forma «select e from Empleado e», se habría obtenido una lista de objetos de tipo Employe.
  • líneas 7-12: esta lista de objetos se copia en un array de objetos de tipo Employe.
  • línea 14: se solicita la lista de todas las entidades de tipo Cotisations. Sabemos que esta lista solo tiene un elemento. Por lo tanto, recuperamos el primer elemento de la lista para obtener las tasas de cotización.
  • Las líneas 7 y 14 inicializan los dos campos privados de la clase.

13.1.5.4. Implementación del método GetEmploye

El método GetEmploye debe devolver la entidad «Empleado» con un número SS dado. Su código podría ser el siguiente:


[Transaction(ReadOnly = true)]
        public Employe GetEmploye(string ss) {
            IList<Employe> employés = null;
            try {
                // consulta
                employés = HibernateTemplate.Find<Employe>("select e from Employe e where e.SS=?", new object[]{ss});
            } catch (Exception ex) {
                // se transforma la excepción 
                throw new PamException(string.Format("Erreur d'accès à la BD lors de la demande de l'employé de n° ss [{0}] : [{1}]", ss, ex.ToString()), 41);
            }
            // ¿Se ha recuperado a un empleado? 
            if (employés.Count == 0) {
                // se reporta el hecho 
                throw new PamException(string.Format("L'employé de n° ss [{0}] n'existe pas", ss), 42);
            } else {
                return employés[0];
            }
        }
  • línea 6: obtiene la lista de empleados con un número SS dado
  • línea 12: normalmente, si el empleado existe, se debe obtener una lista con un solo elemento
  • línea 14: si no es así, se lanza una excepción
  • línea 16: si es así, se devuelve el primer empleado de la lista

13.1.5.5. Conclusion

Si comparamos el código de la capa [dao] en el caso de un uso

  1. solo del marco NHibernate
  2. del marco Spring / NHibernate

nos damos cuenta de que la segunda solución permitió escribir un código más sencillo.

13.2. Pruebas de la capa [dao]

13.2.1. El proyecto de Visual Studio

El proyecto de Visual Studio ya se ha presentado. Recordemos:

  • en [1], el proyecto en su totalidad
  • en [2], las diferentes clases del proyecto. La carpeta [tests] contiene una prueba de consola [Main.cs] y una prueba unitaria [NUnit.cs].
  • en [3], se compila el programa [Main.cs].
  • En [4], no se genera el archivo [NUnit.cs].
  • El proyecto es una aplicación de consola. La clase que se ejecuta es la especificada en [5], la clase del archivo [Main.cs].

13.2.2. El programa de prueba de consola [Main.cs]

El programa de prueba [Main.cs] se ejecuta en la siguiente arquitectura:

Se encarga de probar los métodos de la interfaz [IPamDao]. Un ejemplo básico podría ser el siguiente:


using System;
using Pam.Dao.Entites;
using Pam.Dao.Service;
using Spring.Context.Support;

namespace Pam.Dao.Tests {
    public class MainPamDaoTests {
        public static void Main() {
            try {
                // instanciación de la capa [dao]
                IPamDao pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
                // lista de identidades de los empleados 
                foreach (Employe Employe in pamDao.GetAllIdentitesEmployes()) {
                    Console.WriteLine(Employe.ToString());
                }
                // un empleado con sus prestaciones 
                Console.WriteLine("------------------------------------");
                Console.WriteLine(pamDao.GetEmploye("254104940426058"));
                Console.WriteLine("------------------------------------");
                // lista de cotizaciones 
                Cotisations cotisations = pamDao.GetCotisations();
                Console.WriteLine(cotisations.ToString());
            } catch (Exception ex) {
                // visualización de excepción 
                Console.WriteLine(ex.ToString());
            }
            //pausa 
            Console.ReadLine();
        }
    }
}
  • línea 11: se le solicita a Spring una referencia en la capa [dao].
  • líneas 13-15: prueba del método [GetAllIdentitesEmployes] de la interfaz [IPamDao]
  • línea 18: prueba del método [GetEmploye] de la interfaz [IPamDao]
  • línea 21: prueba del método [GetCotisations] de la interfaz [IPamDao]

Spring, NHibernate y log4net se configuran mediante el archivo [App.config] , analizado en el párrafo 13.1.4.1.

La ejecución realizada con la base de datos descrita en el párrafo 6.2 arroja el siguiente resultado en la consola:

1
2
3
4
5
6
[254104940426058,Jouveinal,Marie,,,,]
[260124402111742,Laverti,Justine,,,,]
------------------------------------
[254104940426058,Jouveinal,Marie,5 rue des oiseaux,St Corentin,49203,[2, 2,1, 2,1, 3,1, 15]]
------------------------------------
[3,49,6,15,9,39,7,88]
  • líneas 1-2: los 2 empleados de tipo [Employe] con la única información de [SS, Nom, Prenom]
  • línea 4: el empleado de tipo [Employe] con el número de seguro social [254104940426058]
  • línea 5: las tasas de cotización

13.2.3. Pruebas unitarias con NUnit

Ahora pasamos a una prueba unitaria con NUnit. El proyecto de Visual Studio de la capa [dao] evolucionará de la siguiente manera:

  • a [1], el programa de prueba [NUnit.cs]
  • a [2,3]; el proyecto generará un archivo DLL denominado [pam-dao-spring-nhibernate.dll]
  • en [4], la referencia a DLL del marco NUnit: [nunit.framework.dll]
  • en [5], la clase [Main.cs] no se incluirá en DLL [pam-dao-spring-nhibernate]
  • en [6], la clase [NUnit.cs] se incluirá en la clase DLL [pam-dao-spring-nhibernate]

La clase de prueba NUnit es la siguiente:


using System.Collections;
using NUnit.Framework;
using Pam.Dao.Service;
using Pam.Dao.Entites;
using Spring.Objects.Factory.Xml;
using Spring.Core.IO;
using Spring.Context.Support;

namespace Pam.Dao.Tests {

    [TestFixture]
    public class NunitPamDao : AssertionHelper {
        // la capa [dao] a probar 
        private IPamDao pamDao = null;

        // constructor 
        public NunitPamDao() {
            // instanciación de la capa [dao]
            pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
        }

        // inicialización 
        [SetUp]
        public void Init() {

        }

        [Test]
        public void GetAllIdentitesEmployes() {
            // verificación del número de empleados 
            Expect(2, EqualTo(pamDao.GetAllIdentitesEmployes().Length));
        }

        [Test]
        public void GetCotisations() {
            // verificación de la tasa de cotizaciones 
            Cotisations cotisations = pamDao.GetCotisations();
            Expect(3.49, EqualTo(cotisations.CsgRds).Within(1E-06));
            Expect(6.15, EqualTo(cotisations.Csgd).Within(1E-06));
            Expect(9.39, EqualTo(cotisations.Secu).Within(1E-06));
            Expect(7.88, EqualTo(cotisations.Retraite).Within(1E-06));
        }

        [Test]
        public void GetEmployeIdemnites() {
            // verificación de personas 
            Employe employe1 = pamDao.GetEmploye("254104940426058");
            Employe employe2 = pamDao.GetEmploye("260124402111742");
            Expect("Jouveinal", EqualTo(employe1.Nom));
            Expect(2.1, EqualTo(employe1.Indemnites.BaseHeure).Within(1E-06));
            Expect("Laverti", EqualTo(employe2.Nom));
            Expect(1.93, EqualTo(employe2.Indemnites.BaseHeure).Within(1E-06));
        }

        [Test]
        public void GetEmployeIdemnites2() {
            // verificación de persona inexistente 
            bool erreur = false;
            try {
                Employe employe1 = pamDao.GetEmploye("xx");
            } catch {
                erreur = true;
            }
            Expect(erreur, True);
        }
    }
}

Esta clase ya se ha analizado en el párrafo 7.3.4.

Al generar el proyecto, se crean los archivos DLL y [pam-dao-spring-nhibernate.dll] en la carpeta [bin/Release].

Se cargan los archivos DLL y [pam-dao-spring-nhibernate.dll] con la herramienta [NUnit-Gui], versión 2.4.6, y se ejecutan las pruebas:

Image

En el ejemplo anterior, las pruebas se realizaron con éxito.

Trabajo práctico:


  • ejecutar en la máquina las pruebas de la clase [PamDaoSpringNHibernate].
  • utilizar diferentes archivos de configuración [Dao.xml] para poder utilizar otros SGBD (Firebird, MySQL, Postgres, SQL Server)

13.2.4. Generación de l e la DLL de la capa [dao]

Una vez escrita y probada la clase [PamDaoNHibernate], se generará la clase DLL a partir de la capa [dao] de la siguiente manera:

  • [1], los programas de prueba se excluyen del ensamblaje del proyecto
  • [2,3], configuración del proyecto
  • [4], generación del proyecto
  • el archivo DLL se genera en la carpeta [bin/Release] [5]. Lo agregamos a los archivos DLL que ya se encuentran en la carpeta [lib] [6]:

13.3. La capa de negocio

Volvamos a la arquitectura general de la aplicación [SimuPaie]:

Ahora damos por hecho que la capa [dao] está lista y que ha sido encapsulada en DLL [pam-dao-spring-nhibernate.dll]. Ahora nos enfocamos en la capa [metier]. Es esta la que implementa las reglas de negocio, en este caso, las reglas para calcular un salario.

El proyecto de Visual Studio de la capa de negocio podría tener el siguiente aspecto:

  • en [1], el proyecto completo está configurado por los archivos [App.config] y [Dao.xml]. El archivo [App.config] es idéntico a lo que era en el proyecto de la capa [dao] [pam-dao-spring-nhibernate]. Lo mismo ocurre con el archivo [Dao.xml], salvo que declara un objeto Spring adicional con el identificador pammetier. La declaración de este último es idéntica a la que aparecía en el archivo [App.config] del proyecto [pam-metier-dao-nhibernate].
  • En [2], la carpeta [pam] es idéntica a la que había en la capa [metier] del proyecto [pam-metier-dao-nhibernate]
  • en [3] las referencias utilizadas por el proyecto. Cabe destacar las referencias DLL y [pam-dao-spring-nhibernate] de la capa [dao] analizada anteriormente.

Pregunta: compila el proyecto [pam-metier-dao-spring-nhibernate] anterior. Se probará por separado:

  • en modo consola mediante el programa de consola [Main.cs]

  • mediante la prueba unitaria [NUnit.cs] ejecutada por el marco de trabajo NUnit

El nuevo proyecto [pam-metier-dao-spring-nhibernate] se puede compilar simplemente copiando el proyecto [pam-metier-dao-nhibernate] y luego modificando los elementos que deben cambiarse.


Después de las pruebas, se generará el DLL a partir de la capa [metier], al que se le dará el nombre de [pam-metier-dao-spring-nhibernate]:

  • en [1], la prueba NUnit superada
  • en [2], la DLL generada por el proyecto

Se agregará el archivo DLL de la capa [metier] a los archivos DLL que ya se encuentran en la carpeta [lib] [3]:

13.4. La capa [web]

Volvamos a la arquitectura general de la aplicación [SimuPaie]:

Consideramos que las capas [dao] y [métier] ya están implementadas y encapsuladas en las capas DLL y [pam-dao-spring-nhibernate, pam-metier-dao-spring-nhibernate]. A continuación, describimos la capa web.

El proyecto Visual Web Developer de la capa [web] se obtiene, en primer lugar, simplemente copiando la carpeta del proyecto web [pam-v7-3tier-nhibernate-multivues-multipages]. A continuación, el proyecto se renombra como [pam-v9-3tier-spring-nhibernate-multivues-multipages]:

El nuevo proyecto web [pam-v9-3tier-spring-nhibernate-multivues-multipages] se diferencia del proyecto [pam-v7-3tier-nhibernate-multivues-multipages] en los siguientes aspectos:

  • en [1], está configurado por los archivos [Dao.xml] y [Web.config]. [Dao.xml] no existía en [pam-v7] y el archivo [Web.config] debe incluir la configuración de Spring / NHibernate, mientras que en [pam-v7], solo configuraba NHibernate.
  • En [2], los DLL de las capas [dao] y [metier] son los que acabamos de construir.

El archivo [Dao.xml] es el que se utilizó en la construcción de la capa [metier]. El archivo [Web.config] es el de [pam-v7] al que se le agrega la configuración de Spring / NHibernate que se encontraba en los archivos [App.config] de las capas [dao] y [metier]. El archivo [Web.config] de [pam-v9] es el siguiente:


<configuration>

  <configSections>
    <sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
........
    </sectionGroup>
    <sectionGroup name="spring">
      <section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core" />
      <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
      <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
    </sectionGroup>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
  </configSections>

  <!-- configuración de Spring -->
  <spring>
    <parsers>
      <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
    </parsers>
    <context>
      <resource uri="~/Dao.xml" />
    </context>
  </spring>
............. le reste est identique au fichier [Web.config] de [pam-v7]

En las líneas 7-11 y 16-23 se encuentra la configuración de Spring que teníamos en los archivos [App.config] de las capas [dao] y [metier] creadas anteriormente, con una diferencia: en los archivos [App.config], la línea 17 estaba escrita de la siguiente manera:


      <resource uri="Dao.xml" />

Con la siguiente configuración:

el archivo [Dao.xml] se copia en la carpeta [bin] de la carpeta del proyecto web. Con la sintaxis


      <resource uri="Dao.xml" />

el archivo [Dao.xml] se buscará en la carpeta actual del proceso que ejecuta la aplicación web. Resulta que esta carpeta no es la carpeta [bin] de la carpeta del proyecto web que se está ejecutando. Hay que escribir:


      <resource uri="~/Dao.xml" />

para que el archivo [Dao.xml] se busque en la carpeta [bin] de la carpeta del proyecto web que se está ejecutando.


Pregunta: implementar esta aplicación web en una máquina.


13.5. Conclusion

Hemos pasado de la arquitectura:

en arquitectura:

Se trataba de implementar la capa [dao] aprovechando las facilidades que ofrece la integración de NHibernate por parte de Spring.

Pudimos observar que esto:

  • afectaba a la capa [dao]. Esta fue más sencilla de escribir, pero requirió una configuración de Spring más compleja.
  • afectaba de manera marginal a las capas [metier] y [web]

Ahí tuvimos otro ejemplo de la importancia de las arquitecturas en capas.