Skip to content

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

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

En la versión anterior, la capa [DAO] se implementó utilizando el marco NHibernate. El marco Spring se utilizó únicamente para integrar las capas entre sí. El marco Spring proporciona clases de utilidad para trabajar con el marco NHibernate. El uso de estas clases facilita la escritura del código en la capa [DAO]. La arquitectura anterior evoluciona de la siguiente manera:

Debido a la estructura en capas utilizada, la implementación de la integración Spring/NHibernate requiere modificar únicamente la capa [DAO]. No será necesario modificar las capas [Presentación] (web/ASP.NET) y [Negocio]. Esta es la principal ventaja de las arquitecturas en capas integradas con Spring.

En las siguientes secciones, construiremos la capa [DAO] con [Spring/NHibernate] comentando el código de una solución funcional. No intentaremos abarcar todas las opciones de configuración ni todos los usos del marco [Spring/NHibernate]. Los lectores pueden adaptar la solución propuesta a sus propias necesidades utilizando la documentación de Spring.NET [http://www.springframework.net/documentation.html] (junio de 2010).

El enfoque seguido para construir las capas [DAO] y [business] es el de la versión 3 descrito en el párrafo 7. El enfoque seguido para la capa [presentation] es el de la versión 7 descrito en el párrafo 11.

13.1. La capa de acceso a datos [DAO]

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

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

  • en [1], el proyecto en su conjunto
    • la carpeta [pam] contiene las clases del proyecto, así como la configuración de las entidades de 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 distintas clases del proyecto
    • En la carpeta [entities], encontramos las entidades NHibernate estudiadas en el proyecto [pam-dao-nhibernate]
    • En la carpeta [service], encontramos la interfaz [IPamDao] y su implementación utilizando 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 prueban 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]. Estas DLL están disponibles en el marco Spring.Net. Se han añadido a la carpeta [lib] de las DLL [4]:

En las referencias del proyecto [3], encontrará los siguientes archivos DLL:

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

Estas referencias se han extraído de la carpeta [lib] [4]. Asegúrese de que la propiedad «Copia local» de todas estas referencias esté establecida en «Verdadero» [5]:

13.1.2. Configuración del proyecto C#

El proyecto se configura 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. Entidades en la capa [dao]

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

  • En [2], el archivo está configurado para incluirse en el 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 [Employee.hbm.xml] se encuentra 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>
    <!-- configuration sections -->
    <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>
 
 
    <!-- spring configuration -->
    <spring>
        <parsers>
            <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
        </parsers>
        <context>
            <resource uri="Dao.xml" />
        </context>
    </spring>
 
    <!-- This section contains the log4net configuration settings -->
    <!-- NOTE IMPORTANTE: logs are not active by default. They must be activated by program
    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>
 
        <!-- Set default logging level to DEBUG -->
        <root>
            <level value="DEBUG" />
            <appender-ref ref="ConsoleAppender" />
        </root>
 
        <!-- Set logging for Spring.  Logger names in Spring correspond to the namespace -->
        <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) y 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 encuentra en este archivo.

13.1.4.2. El archivo [Dao.xml]

El archivo [Dao.xml], que contiene los objetos gestionados 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">
 
    <!-- Referenced by main application context configuration file -->
    <description>
        Application Spring / NHibernate
    </description>
 
    <!-- Database and NHibernate Configuration -->
    <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>
 
    <!-- transaction manager -->
    <object id="transactionManager"
        type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate12">
        <property name="DbProvider" ref="DbProvider"/>
        <property name="SessionFactory" ref="NHibernateSessionFactory"/>
    </object>
 
    <!-- Hibernate Template -->
    <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>
 
    <!-- Data Access Objects -->
    <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–13 configuran la conexión a la base de datos [dbpam_nhibernate]. Incluyen:
    • el proveedor ADO.NET necesario para la conexión, en este caso el proveedor del SGBD MySQL. Para ello, es necesario incluir el archivo DLL [Mysql.Data] en las referencias del proyecto.
    • la cadena de conexión a la base de datos (servidor, nombre de la base de datos, propietario de la conexión y contraseña)
  • Las líneas 15-29 configuran la SessionFactory de NHibernate, el objeto que se utiliza para obtener sesiones de NHibernate. Tenga en cuenta que todas las operaciones de base de datos se realizan dentro de una sesión de NHibernate. En la línea 15, podemos ver que la SessionFactory está implementada por la clase Spring.Data.NHibernate.LocalSessionFactoryObject, que se encuentra en el archivo 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–13.
  • Líneas 17-20: Especifican la lista de ensamblados que contienen archivos [*.hbm.xml] que configuran las entidades gestionadas por NHibernate. La línea 19 indica que estos archivos se encontrarán en el ensamblado del proyecto. Tenga en cuenta que este nombre se encuentra en las propiedades del proyecto C#. Tenga en cuenta también que todos los archivos [*.hbm.xml] se han configurado para incluirse en el ensamblado del proyecto.
  • Líneas 22-27: Propiedades específicas de NHibernate.
    • Línea 24: El dialecto SQL utilizado será MySQL
    • Línea 25: El SQL generado por NHibernate no aparecerá en los registros de la consola. Establecer esta propiedad en true permite ver las sentencias SQL generadas por NHibernate. Esto puede ayudarle a comprender, por ejemplo, por qué una aplicación es lenta al acceder a la base de datos.
  • Línea 28: Establecer la propiedad ExposeTransactionAwareSessionFactory en «true» hará que Spring gestione las anotaciones de gestión de transacciones que se encuentran en el código C#. Volveremos a esto cuando escribamos la clase que implementa la capa [DAO].
  • Las líneas 32–36 definen el gestor de transacciones. Una vez más, este gestor es una clase de Spring procedente del archivo DLL Spring.Data.NHibernate12. Este gestor requiere los parámetros de conexión a la base de datos (línea 34), así como el 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 implemente la capa [DAO]. Facilita las interacciones con los objetos de NHibernate. Esta clase tiene ciertas propiedades que deben inicializarse:
    • línea 40: la SessionFactory de NHibernate
    • línea 41: la propiedad TemplateFlushMode establece el modo de sincronización del contexto de persistencia de NHibernate con la base de datos. El modo Auto garantiza que la sincronización se produzca:
      • 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 suponer una mejora en el rendimiento.
  • Las líneas 46-48 definen la clase de implementación para la capa [dao]
    • línea 46: la capa [dao] será implementada por la clase [PamdaoSpringNHibernate] en el DLL [pam-dao-spring-nhibernate]. Una vez instanciada la clase, se ejecutará inmediatamente el método init de la clase. Cuando se cierre 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 {
        // list of all employee identities 
        Employe[] GetAllIdentitesEmployes();
        // an individual employee with benefits 
        Employe GetEmploye(string ss);
        // list of all contributions 
        Cotisations GetCotisations();
    }
}
  • Línea 1: Importamos el espacio de nombres para 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.Entities] pueden crearse en múltiples instancias. Los elementos del espacio de nombres [Pam.Dao.Service] se crean como una única instancia (singleton). Esto es lo que justificó la elección de los nombres de los espacios de nombres.
  • Línea 4: La interfaz se denomina [IPamDao]. Define tres métodos:
    • Línea 6: [GetAllIdentitesEmployes] devuelve una matriz de objetos de tipo [Employe] que representa la lista de cuidadores infantiles en un formato simplificado (apellidos, nombre, número de la Seguridad Social).
    • Línea 8: [GetEmploye] devuelve un objeto [Employe]: el empleado con el número de la Seguridad Social pasado como parámetro al método, junto con las prestaciones asociadas a su categoría salarial.
    • Línea 10: [GetCotisations] devuelve el objeto [Cotisations], que encapsula los tipos de las distintas cotizaciones a la Seguridad Social que se deducirán del salario bruto.

El esqueleto de la clase de implementación para esta interfaz utilizando 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 {
        // private fields 
        private Cotisations cotisations;
        private Employe[] employes;
 
        // init 
        [Transaction(ReadOnly = true)]
        public void init() {
...
        }
 
        // delete object
        public void destroy() {
            if (HibernateTemplate.SessionFactory != null) {
                HibernateTemplate.SessionFactory.Close();
            }
        }
 
        // list of all employee identities
        public Employe[] GetAllIdentitesEmployes() {
            return employes;
        }
 
 
        // an individual employee with benefits 
        [Transaction(ReadOnly = true)]
        public Employe GetEmploye(string ss) {
....
        }
 
        // list of contributions 
        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 de Spring [HibernateDaoSupport]. Esta clase tiene una propiedad [HibernateTemplate] que se inicializa mediante la configuración de Spring que se ha establecido (línea 2 más abajo):

    <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, vemos que la definición del objeto [pamdao] especifica que los métodos init y destroy de la clase [PamDaoSpringNHibernate] deben ejecutarse en momentos específicos. Estos dos métodos están efectivamente presentes en la clase, en las líneas 16 y 21.
  • Líneas 15 y 34: anotaciones que garantizan que el método anotado se ejecutará dentro de una transacción. El atributo ReadOnly=true indica que la transacción es de solo lectura. El método ejecutado dentro de la transacción puede lanzar una excepción. En este caso, Spring revierte automáticamente 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 instanciar 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, que fue inicializado por el método init.
  • Líneas 40-42: El método [GetCotisations] simplemente devuelve el atributo de la línea 11, que fue 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 hql_query)
ejecuta la consulta HQL y devuelve una lista de objetos de tipo T
IList<T> Find<T>(string hql_query, object[])
ejecuta una consulta HQL parametrizada por ?. Los valores de estos parámetros se proporcionan mediante la matriz de objetos.
IList<T> LoadAll<T>()
devuelve todas las entidades de tipo T
  

Hay otros métodos útiles que no tendremos ocasión de utilizar, pero que permiten recuperar, guardar, actualizar y eliminar entidades:

T Load<T>(object id)
Añade la entidad de tipo T con la clave principal id a la sesión de NHibernate.
void SaveOrUpdate(object entity)
Inserta (INSERT) o actualiza (UPDATE) el objeto entidad dependiendo de si tiene una clave principal (UPDATE) o no (INSERT). La ausencia de una clave principal se puede configurar mediante el atributo `unsaved-values` en el archivo de configuración de la entidad. Tras la operación `SaveOrUpdate`, el objeto entidad se encuentra en la sesión de NHibernate.
void Delete(object entity)
Elimina el objeto de entidad de la sesión de 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 propósito es almacenar en caché localmente las identidades simplificadas de los empleados (apellido, nombre, número de la seguridad social) y las tasas de cotización. Su código podría ser el siguiente.


[Transaction(ReadOnly = true)]
        public void init() {
            try {
                // on récupère la liste simplifiée des employés
                IList<object[]> lignes = HibernateTemplate.Find<object[]>("select e.SS,e.Nom,e.Prenom from Employe e");
                // on la met dans un tableau
                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++;
                }
                // on met les taux de cotisations dans un objet 
                cotisations = (HibernateTemplate.LoadAll<Cotisations>())[0];
            } catch (Exception ex) {
                // on transforme l'exception 
                throw new PamException(string.Format("Erreur d'accès à la BD : [{0}]", ex.ToString()), 43);
            }
        }
  • línea 5: Se ejecuta una consulta HQL. Recupera los campos SS, LastName y FirstName de todas las entidades Employee. Devuelve una lista de objetos. Si hubiéramos solicitado el registro completo de Employee utilizando «select e from Employee e», habríamos recuperado una lista de objetos de tipo Employee.
  • Líneas 7–12: Esta lista de objetos se copia en una matriz de objetos de tipo Employee.
  • Línea 14: Recuperamos la lista de todas las entidades de tipo Contributions. Sabemos que esta lista solo tiene un elemento. Por lo tanto, recuperamos el primer elemento de la lista para obtener las tasas de contribución.
  • Las líneas 7 y 14 inicializan los dos campos privados de la clase.

13.1.5.4. Implementación del método GetEmployee

El método GetEmployee debe devolver la entidad Employee con un SSN dado. Su código podría ser el siguiente:


[Transaction(ReadOnly = true)]
        public Employe GetEmploye(string ss) {
            IList<Employe> employés = null;
            try {
                // requête
                employés = HibernateTemplate.Find<Employe>("select e from Employe e where e.SS=?", new object[]{ss});
            } catch (Exception ex) {
                // on transforme l'exception 
                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);
            }
            // a-t-on récupéré un employé ? 
            if (employés.Count == 0) {
                // on signale le fait 
                throw new PamException(string.Format("L'employé de n° ss [{0}] n'existe pas", ss), 42);
            } else {
                return employés[0];
            }
        }
  • Línea 6: Recupera la lista de empleados con un número de la Seguridad Social determinado
  • línea 12: normalmente, si el empleado existe, deberíamos obtener una lista con un elemento
  • línea 14: si no es así, lanza una excepción
  • línea 16: si ese es el caso, devuelve el primer empleado de la lista

13.1.5.5. Conclusión

Si comparamos el código de la capa [DAO] al utilizar

  1. el marco Spring / NHibernate
  2. el marco Spring / NHibernate

nos damos cuenta de que la segunda solución permitió 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. A modo de recordatorio:

  • en [1], el proyecto en su conjunto
  • en [2], las distintas 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 {
                // layer instantiation [dao]
                IPamDao pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
                // list of employee identities 
                foreach (Employe Employe in pamDao.GetAllIdentitesEmployes()) {
                    Console.WriteLine(Employe.ToString());
                }
                // an employee with benefits 
                Console.WriteLine("------------------------------------");
                Console.WriteLine(pamDao.GetEmploye("254104940426058"));
                Console.WriteLine("------------------------------------");
                // list of contributions 
                Cotisations cotisations = pamDao.GetCotisations();
                Console.WriteLine(cotisations.ToString());
            } catch (Exception ex) {
                // exception display 
                Console.WriteLine(ex.ToString());
            }
            //break 
            Console.ReadLine();
        }
    }
}
  • Línea 11: Solicitamos a Spring una referencia a la capa [dao].
  • líneas 13–15: probamos el método [GetAllEmployeeIDs] de la interfaz [IPamDao]
  • Línea 18: probamos el método [GetEmployee] de la interfaz [IPamDao]
  • línea 21: prueba del método [GetCotisations] de la interfaz [IPamDao]

Spring, NHibernate y log4net se configuran a través del archivo [ App.config] descrito en la sección 13.1.4.1.

Al ejecutar el código con la base de datos descrita en la sección 6.2, se obtiene la siguiente salida 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 [Employee] con solo la información [SS, Apellido, Nombre]
  • línea 4: el empleado de tipo [Employee] con número de la Seguridad Social [254104940426058]
  • línea 5: tipos de cotización

13.2.3. Pruebas unitarias con NUnit

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

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

La clase de prueba de 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 {
        // the [dao] layer to be tested 
        private IPamDao pamDao = null;
 
        // manufacturer 
        public NunitPamDao() {
            // layer instantiation [dao]
            pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
        }
 
        // init 
        [SetUp]
        public void Init() {
 
        }
 
        [Test]
        public void GetAllIdentitesEmployes() {
            // audit no. of employees 
            Expect(2, EqualTo(pamDao.GetAllIdentitesEmployes().Length));
        }
 
        [Test]
        public void GetCotisations() {
            // checking contribution rates 
            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() {
            // individual verification 
            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() {
            // non-existent individual verification 
            bool erreur = false;
            try {
                Employe employe1 = pamDao.GetEmploye("xx");
            } catch {
                erreur = true;
            }
            Expect(erreur, True);
        }
    }
}

Esta clase ya se ha tratado en la sección 7.3.4.

La generación del proyecto crea el archivo DLL [pam-dao-spring-nhibernate.dll] en la carpeta [bin/Release].

Cargamos el archivo DLL [pam-dao-spring-nhibernate.dll] utilizando la herramienta [NUnit-Gui], versión 2.4.6, y ejecutamos las pruebas:

Image

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

Ejercicio práctico:


  • Implementa las pruebas para la clase [PamDaoSpringNHibernate] en un equipo.
  • Utilice diferentes archivos de configuración [Dao.xml] para utilizar otros sistemas de gestión de bases de datos (Firebird, MySQL, Postgres, SQL Server).

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

Una vez que se haya escrito y probado la clase [PamDaoNHibernate], se generará la DLL de la capa [dao] de la siguiente manera:

  • [1], los programas de prueba se excluyen del ensamblado del proyecto
  • [2,3], configuración del proyecto
  • [4], compilación del proyecto
  • La DLL se genera en la carpeta [bin/Release] [5]. La añadimos a las DLL que ya se encuentran en la carpeta [lib] [6]:

13.3. La capa de negocio

Repasemos la arquitectura general de la aplicación [SimuPaie]:

Suponemos ahora que la capa [dao] está completa y se ha encapsulado en el archivo DLL [pam-dao-spring-nhibernate.dll]. Nos centraremos ahora en la capa [business]. Esta es la capa que implementa las reglas de negocio, en este caso las reglas para calcular un salario.

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

  • En [1], todo el proyecto se configura mediante los archivos [App.config] y [Dao.xml]. El archivo [App.config] es idéntico al del proyecto de la capa [dao] [pam-dao-spring-nhibernate]. Lo mismo se aplica al archivo [Dao.xml], salvo que declara un objeto Spring adicional con el ID pammetier. La declaración de este objeto es idéntica a la del archivo [App.config] del proyecto [pam-metier-dao-nhibernate].
  • En [2], la carpeta [pam] es idéntica a la de la capa [metier] del proyecto [pam-metier-dao-nhibernate]
  • En [3], las referencias utilizadas por el proyecto. Obsérvese el archivo DLL [pam-dao-spring-nhibernate] de la capa [dao] comentada anteriormente.

Pregunta: Compile 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 NUnit

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


Tras las pruebas, generaremos la DLL de la capa [business], a la que llamaremos [pam-metier-dao-spring-nhibernate]:

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

Añadiremos la DLL de la capa [business] a las DLL que ya se encuentran en la carpeta [lib] [3]:

13.4. La capa [web]

Repasemos la arquitectura general de la aplicación [SimuPaie]:

Suponemos que las capas [dao] y [business] están completas y encapsuladas en las DLL [pam-dao-spring-nhibernate, pam-business-dao-spring-nhibernate]. A continuación, describiremos la capa web.

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

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

  • En [1], la configuración se realiza mediante los archivos [Dao.xml] y [Web.config]. El archivo [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 se configuraba NHibernate.
  • En [2], las DLL para las capas [dao] y [business] son las que acabamos de compilar.

El archivo [Dao.xml] es el que se utiliza para crear la capa [business]. El archivo [Web.config] es el de [pam-v7], al que añadimos la configuración de Spring/NHibernate que se encuentra en los archivos [App.config] de las capas [dao] y [business]. El archivo [Web.config] para [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>
 
  <!-- spring configuration -->
  <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]

Las líneas 7–11 y 16–23 contienen la configuración de Spring que se encontraba en los archivos [App.config] de las capas [dao] y [business] 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] dentro de la carpeta del proyecto web. Con la sintaxis


      <resource uri="Dao.xml" />

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


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

para que el archivo [Dao.xml] se busque en la carpeta [bin] del proyecto web ejecutado.


Pregunta: Implemente esta aplicación web en un equipo.


13.5. Conclusión

Hemos pasado de la arquitectura:

a la arquitectura:

El objetivo era implementar la capa [DAO] aprovechando las capacidades que ofrece la integración de Spring con NHibernate.

Observamos que esto:

  • afectaba a la capa [DAO]. Esta capa era más sencilla de escribir, pero requería una configuración de Spring más compleja.
  • afectaba ligeramente a las capas [business] y [web]

Esto proporcionó otro ejemplo de las ventajas de las arquitecturas en capas.