Skip to content

13. A aplicação [SimuPaie] – versão 9 – integração Spring/NHibernate

Aqui, propomos revisitar a aplicação ASP.NET de três camadas da versão 7 [pam-v7-3tier-nhibernate-multivues-multipages]. A arquitetura em camadas da aplicação era a seguinte:

Na versão acima, a camada [DAO] foi implementada utilizando o framework NHibernate. O framework Spring foi utilizado apenas para integrar as camadas entre si. O framework Spring fornece classes utilitárias para trabalhar com o framework NHibernate. A utilização destas classes facilita a escrita do código na camada [DAO]. A arquitetura anterior evolui da seguinte forma:

Devido à estrutura em camadas utilizada, a implementação da integração Spring/NHibernate requer apenas a modificação da camada [DAO]. As camadas [Presentation] (web/ASP.NET) e [Business] não precisarão de ser modificadas. Esta é a principal vantagem das arquiteturas em camadas integradas com o Spring.

Nas secções seguintes, iremos construir a camada [DAO] com [Spring/NHibernate], comentando o código de uma solução funcional. Não tentaremos abordar todas as opções de configuração ou utilizações do framework [Spring/NHibernate]. Os leitores podem adaptar a solução proposta às suas próprias necessidades utilizando a documentação do Spring.NET [http://www.springframework.net/documentation.html] (junho de 2010).

A abordagem seguida para construir as camadas [DAO] e [business] é a da versão 3 descrita no parágrafo 7. A abordagem seguida para a camada [presentation] é a da versão 7 descrita no parágrafo 11.

13.1. A camada de acesso a dados [DAO]

13.1.1. O projeto C# do Visual Studio para a camada [dao]

O projeto do Visual Studio para a camada [dao] é o seguinte:

  • em [1], o projeto como um todo
    • a pasta [pam] contém as classes do projeto, bem como a configuração das entidades NHibernate
    • os ficheiros [App.config] e [Dao.xml] configuram a estrutura Spring/NHibernate. Teremos de descrever o conteúdo destes dois ficheiros.
  • em [2], as várias classes do projeto
    • na pasta [entities], encontramos as entidades NHibernate estudadas no projeto [pam-dao-nhibernate]
    • Na pasta [service], encontramos a interface [IPamDao] e a sua implementação utilizando a estrutura Spring/NHibernate [PamDaoSpringNHibernate]. Teremos de escrever esta nova implementação da interface [IPamDao]
    • A pasta [tests] contém os mesmos testes que o projeto [pam-dao-nhibernate]. Testam a mesma interface [IPamDao].
  • Em [3], as referências do projeto. A integração Spring/NHibernate requer duas novas DLLs: [Spring.Data] e [Spring.Data.NHibernate12]. Estas DLLs estão disponíveis na estrutura Spring.Net. Foram adicionadas à pasta [lib] das DLLs [4]:

Nas referências do projeto [3], encontrará as seguintes DLLs:

  • NHibernate: para o ORM NHibernate
  • MySql.Data: o controlador ADO.NET para o SGBD MySQL
  • Spring.Core: para o framework Spring, que lida com a integração de camadas
  • log4net: uma biblioteca de registo
  • nunit.framework: uma biblioteca de testes unitários
  • Spring.Data e Spring.Data.NHibernate12: fornecem suporte para Spring/NHibernate.

Estas referências foram retiradas da pasta [lib] [4]. Certifique-se de que a propriedade «Cópia local» para todas estas referências está definida como «True» [5]:

13.1.2. Configurar o projeto C#

O projeto está configurado da seguinte forma:

  • Em [1], o nome do assembly do projeto é [pam-dao-spring-nhibernate]. Este nome aparece em vários ficheiros de configuração do projeto.

13.1.3. Entidades na camada [dao]

As entidades (objetos) necessárias para a camada [dao] foram reunidas na pasta [entities] [1] do projeto. Estas entidades são as mesmas que as do projeto [pam-dao-nhibernate], com uma exceção nos ficheiros de configuração do NHibernate. Veja, por exemplo, o ficheiro [Employee.hbm.xml]:

  • Em [2], o ficheiro está configurado para ser incluído na compilação do projeto

O seu conteúdo é o seguinte:


<?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>
  • Linha 2: O atributo assembly indica que o ficheiro [Employee.hbm.xml] se encontra no assembly [pam-dao-spring-nhibernate]

13.1.4. Configuração do Spring / NHibernate

Voltemos ao projeto Visual C#:

  • Em [1], os ficheiros [App.config] e [Dao.xml] configuram a integração Spring / NHibernate

13.1.4.1. O ficheiro [ App.config]

O ficheiro [App.config] é o seguinte:


<?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>

O ficheiro [App.config] acima configura o Spring (linhas 5–9, 15–22) e o log4net (linha 10, linhas 28–53), mas não o NHibernate. Os objetos Spring não são configurados no [App.config], mas sim no ficheiro [Dao.xml] (linha 20). A configuração do Spring/NHibernate, que consiste na declaração de objetos Spring específicos, será, portanto, encontrada neste ficheiro.

13.1.4.2. O ficheiro [Dao.xml]

O ficheiro [Dao.xml], que contém os objetos geridos pelo Spring, é o seguinte:


<?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>
  • As linhas 11–13 configuram a ligação à base de dados [dbpam_nhibernate]. Incluem:
    • o fornecedor ADO.NET necessário para a ligação, neste caso o fornecedor do SGBD MySQL. Isto requer que a DLL [Mysql.Data] seja incluída nas referências do projeto.
    • a cadeia de ligação à base de dados (servidor, nome da base de dados, proprietário da ligação e palavra-passe)
  • As linhas 15–29 configuram a SessionFactory do NHibernate, o objeto utilizado para obter sessões do NHibernate. Note-se que todas as operações de base de dados são realizadas no âmbito de uma sessão do NHibernate. Na linha 15, podemos ver que a SessionFactory é implementada pela classe Spring.Data.NHibernate.LocalSessionFactoryObject, que se encontra na DLL Spring.Data.NHibernate12.
  • Linha 16: A propriedade DbProvider define os parâmetros de ligação à base de dados (provedor ADO.NET e cadeia de ligação). Aqui, esta propriedade faz referência ao objeto DbProvider definido anteriormente nas linhas 11–13.
  • Linhas 17–20: Especifique a lista de assemblies que contêm ficheiros [*.hbm.xml] que configuram entidades geridas pelo NHibernate. A linha 19 indica que estes ficheiros se encontram no assembly do projeto. Note que este nome se encontra nas propriedades do projeto C#. Note também que todos os ficheiros [*.hbm.xml] foram configurados para serem incluídos no assembly do projeto.
  • Linhas 22–27: Propriedades específicas do NHibernate.
    • Linha 24: O dialeto SQL utilizado será o MySQL
    • Linha 25: O SQL gerado pelo NHibernate não aparecerá nos registos da consola. Definir esta propriedade como true permite-lhe ver as instruções SQL geradas pelo NHibernate. Isto pode ajudá-lo a compreender, por exemplo, por que razão uma aplicação é lenta ao aceder à base de dados.
  • Linha 28: Definir a propriedade ExposeTransactionAwareSessionFactory como true fará com que o Spring gerencie as anotações de gerenciamento de transações encontradas no código C#. Voltaremos a este ponto quando escrevermos a classe que implementa a camada [DAO].
  • As linhas 32–36 definem o gestor de transações. Mais uma vez, este gestor é uma classe Spring da DLL Spring.Data.NHibernate12. Este gestor requer os parâmetros de ligação à base de dados (linha 34), bem como o SessionFactory do NHibernate (linha 35).
  • As linhas 39–43 definem as propriedades da classe HibernateTemplate, que também é uma classe Spring. Esta classe será utilizada como uma classe utilitária na classe que implementa a camada [DAO]. Ela facilita as interações com objetos NHibernate. Esta classe possui certas propriedades que devem ser inicializadas:
    • linha 40: o NHibernate SessionFactory
    • linha 41: a propriedade TemplateFlushMode define o modo de sincronização do contexto de persistência do NHibernate com a base de dados. O modo Auto garante que a sincronização ocorra:
      • no final de uma transação
      • antes de uma operação SELECT
    • linha 42: as consultas HQL (Hibernate Query Language) serão armazenadas em cache. Isto pode resultar num ganho de desempenho.
  • As linhas 46–48 definem a classe de implementação para a camada [dao]
    • linha 46: a camada [dao] será implementada pela classe [PamdaoSpringNHibernate] na DLL [pam-dao-spring-nhibernate]. Após a instância da classe, o método init da classe será executado imediatamente. Quando o contentor Spring for fechado, o método destroy da classe será executado.
    • Linha 47: A classe [PamDaoSpringNHibernate] terá uma propriedade HibernateTemplate que será inicializada com a propriedade HibernateTemplate da linha 39.

13.1.5. Implementação da camada [dao]

13.1.5.1. O esqueleto da classe de implementação

A interface [IPamDao] é a mesma do projeto [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();
    }
}
  • Linha 1: Importamos o namespace para entidades na camada [dao].
  • Linha 3: A camada [dao] encontra-se no namespace [Pam.Dao.Service]. Os elementos no namespace [Pam.Dao.Entities] podem ser criados em múltiplas instâncias. Os elementos no namespace [Pam.Dao.Service] são criados como uma única instância (singleton). Foi isto que justificou a escolha dos nomes dos namespaces.
  • Linha 4: A interface é denominada [IPamDao]. Define três métodos:
    • Linha 6: [GetAllIdentitesEmployes] devolve uma matriz de objetos do tipo [Employe] que representa a lista de prestadores de cuidados infantis num formato simplificado (apelido, nome próprio, SS).
    • Linha 8: [GetEmploye] devolve um objeto [Employe]: o funcionário com o número de segurança social passado como parâmetro ao método, juntamente com os benefícios associados à sua categoria salarial.
    • Linha 10: [GetCotisations] devolve o objeto [Cotisations], que encapsula as taxas das várias contribuições para a segurança social a deduzir do salário bruto.

O esboço da classe de implementação para esta interface utilizando Spring/NHibernate poderia ser o seguinte:


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;
        }
    }
}
  • Linha 9: A classe [PamDaoSpringNHibernate] implementa corretamente a interface da camada [dao] [IPamDao]. Também deriva da classe Spring [HibernateDaoSupport]. Esta classe possui uma propriedade [HibernateTemplate] que é inicializada pela configuração Spring que foi definida (linha 2 abaixo):

    <object id="pamdao" type="Pam.Dao.Service.PamDaoSpringNHibernate, pam-dao-spring-nhibernate" init-method="init" destroy-method="destroy">
        <property name="HibernateTemplate" ref="HibernateTemplate"/>
</object>
  • Na linha 1 acima, vemos que a definição do objeto [pamdao] especifica que os métodos init e destroy da classe [PamDaoSpringNHibernate] devem ser executados em momentos específicos. Estes dois métodos estão, de facto, presentes na classe nas linhas 16 e 21.
  • Linhas 15, 34: anotações que garantem que o método anotado será executado dentro de uma transação. O atributo ReadOnly=true indica que a transação é de leitura apenas. O método executado dentro da transação pode lançar uma exceção. Neste caso, o Spring reverte automaticamente a transação. Esta anotação elimina a necessidade de gerir uma transação dentro do método.
  • Linha 16: O método init é executado pelo Spring imediatamente após a instância da classe. Veremos que o seu objetivo é inicializar os campos privados nas linhas 11 e 12. Será executado dentro de uma transação (linha 15).
  • Os métodos da interface [IPamDao] são implementados nas linhas 28, 35 e 40.
  • Linhas 28–30: O método [GetAllIdentitesEmployes] simplesmente devolve o atributo da linha 12, que foi inicializado pelo método init.
  • Linhas 40–42: O método [GetCotisations] simplesmente devolve o atributo da linha 11, que foi inicializado pelo método init.

13.1.5.2. Métodos úteis da classe HibernateTemplate

Iremos utilizar os seguintes métodos da classe HibernateTemplate:

IList<T> Find<T>(string hql_query)
executa a consulta HQL e devolve uma lista de objetos do tipo T
IList<T> Find<T>(string hql_query, object[])
executa uma consulta HQL parametrizada por ?. Os valores destes parâmetros são fornecidos pela matriz de objetos.
IList<T> LoadAll<T>()
retorna todas as entidades do tipo T
  

Existem outros métodos úteis que não teremos oportunidade de utilizar, que permitem recuperar, guardar, atualizar e eliminar entidades:

T Load<T>(object id)
Adiciona a entidade do tipo T com a chave primária id à sessão do NHibernate.
void SaveOrUpdate(object entity)
Inserir (INSERT) ou atualizar (UPDATE) o objeto entidade, dependendo de este possuir uma chave primária (UPDATE) ou não (INSERT). A ausência de uma chave primária pode ser configurada através do atributo `unsaved-values` no ficheiro de configuração da entidade. Após a operação `SaveOrUpdate`, o objeto entidade encontra-se na sessão do NHibernate.
void Delete(object entity)
Remove o objeto entidade da sessão do NHibernate.

13.1.5.3. Implementação do método init

O método init da classe [PamDaoSpringNHibernate] é, por configuração, o método executado após o Spring instanciar a classe. O seu objetivo é armazenar em cache as identidades simplificadas dos funcionários (apelido, nome próprio, SSN) e as taxas de contribuição localmente. O seu código poderia ser o seguinte.


[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);
            }
        }
  • linha 5: É executada uma consulta HQL. Esta recupera os campos SS, LastName e FirstName de todas as entidades Employee. Retorna uma lista de objetos. Se tivéssemos solicitado o registo completo de Employee utilizando "select e from Employee e", teríamos recuperado uma lista de objetos do tipo Employee.
  • Linhas 7–12: Esta lista de objetos é copiada para uma matriz de objetos do tipo Employee.
  • Linha 14: Recuperamos a lista de todas as entidades do tipo Contributions. Sabemos que esta lista tem apenas um elemento. Por isso, recuperamos o primeiro elemento da lista para obter as taxas de contribuição.
  • As linhas 7 e 14 inicializam os dois campos privados da classe.

13.1.5.4. Implementação do método GetEmployee

O método GetEmployee deve devolver a entidade Employee com um determinado SSN. O seu código poderia ser o seguinte:


[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];
            }
        }
  • Linha 6: Recupera a lista de funcionários com um determinado SSN
  • linha 12: normalmente, se o funcionário existir, devemos obter uma lista com um elemento
  • linha 14: caso contrário, lança uma exceção
  • linha 16: se for esse o caso, devolve o primeiro funcionário da lista

13.1.5.5. Conclusão

Se compararmos o código da camada [DAO] ao utilizar

  1. a estrutura Spring / NHibernate
  2. a estrutura Spring / NHibernate

percebemos que a segunda solução permitiu um código mais simples.

13.2. Testar a camada [DAO]

13.2.1. O projeto do Visual Studio

O projeto do Visual Studio já foi apresentado. Como lembrete:

  • em [1], o projeto como um todo
  • em [2], as várias classes do projeto. A pasta [tests] contém um teste de consola [Main.cs] e um teste unitário [NUnit.cs].
  • em [3], o programa [Main.cs] é compilado.
  • em [4], o ficheiro [NUnit.cs] não é gerado.
  • O projeto é uma aplicação de consola. A classe que está a ser executada é a especificada em [5], a classe no ficheiro [Main.cs].

13.2.2. O programa de teste de consola [Main.cs]

O programa de teste [Main.cs] é executado na seguinte arquitetura:

É responsável por testar os métodos da interface [IPamDao]. Um exemplo básico pode ser o seguinte:


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();
        }
    }
}
  • Linha 11: Solicitamos ao Spring uma referência à camada [dao].
  • linhas 13–15: teste do método [GetAllEmployeeIDs] da interface [IPamDao]
  • linha 18: teste do método [GetEmploye] da interface [IPamDao]
  • linha 21: teste do método [GetCotisations] da interface [IPamDao]

O Spring, o NHibernate e o log4net são configurados através do ficheiro [ App.config] discutido na Secção 13.1.4.1.

A execução do código com a base de dados descrita na Secção 6.2 produz a seguinte saída na 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]
  • linhas 1-2: os 2 funcionários do tipo [Funcionário] com apenas as informações [SS, Apelido, Nome]
  • linha 4: o funcionário do tipo [Employee] com o número de segurança social [254104940426058]
  • linha 5: taxas de contribuição

13.2.3. Testes unitários com o NUnit

Passaremos agora a um teste unitário com o NUnit. O projeto do Visual Studio para a camada [dao] evoluirá da seguinte forma:

  • em [1], o programa de teste [NUnit.cs]
  • em [2,3], o projeto irá gerar uma DLL denominada [pam-dao-spring-nhibernate.dll]
  • em [4], a referência à DLL do framework NUnit: [nunit.framework.dll]
  • em [5], a classe [Main.cs] não será incluída na DLL [pam-dao-spring-nhibernate]
  • em [6], a classe [NUnit.cs] será incluída na DLL [pam-dao-spring-nhibernate]

A classe de teste NUnit é a seguinte:


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 classe já foi abordada na Secção 7.3.4.

A geração do projeto cria a DLL [pam-dao-spring-nhibernate.dll] na pasta [bin/Release].

Carregamos a DLL [pam-dao-spring-nhibernate.dll] utilizando a ferramenta [NUnit-Gui], versão 2.4.6, e executamos os testes:

Image

Acima, os testes foram bem-sucedidos.

Exercício prático:


  • Implemente os testes para a classe [PamDaoSpringNHibernate] numa máquina.
  • Utilize diferentes ficheiros de configuração [Dao.xml] para utilizar outros SGBDs (Firebird, MySQL, Postgres, SQL Server)

13.2.4. Geração da DLL da camada [dao]

Depois de a classe [PamDaoNHibernate] ter sido escrita e testada, a DLL da camada [dao] será gerada da seguinte forma:

  • [1], os programas de teste são excluídos da compilação do projeto
  • [2,3], configuração do projeto
  • [4], compilação do projeto
  • A DLL é gerada na pasta [bin/Release] [5]. Adicionamo-la às DLLs já presentes na pasta [lib] [6]:

13.3. A camada de negócios

Vamos rever a arquitetura geral da aplicação [SimuPaie]:

Partimos do princípio de que a camada [dao] está concluída e foi encapsulada na DLL [pam-dao-spring-nhibernate.dll]. Vamos agora concentrar-nos na camada [business]. Esta é a camada que implementa as regras de negócio, neste caso as regras para o cálculo de um salário.

O projeto do Visual Studio para a camada de negócios pode ter o seguinte aspeto:

  • Em [1], todo o projeto é configurado pelos ficheiros [App.config] e [Dao.xml]. O ficheiro [App.config] é idêntico ao que existia no projeto da camada [dao] [pam-dao-spring-nhibernate]. O mesmo se aplica ao ficheiro [Dao.xml], exceto que este declara um objeto Spring adicional com o ID pammetier. A declaração deste objeto é idêntica à do ficheiro [App.config] do projeto [pam-metier-dao-nhibernate].
  • Em [2], a pasta [pam] é idêntica à que existia na camada [metier] do projeto [pam-metier-dao-nhibernate]
  • Em [3], as referências utilizadas pelo projeto. Repare na DLL [pam-dao-spring-nhibernate] da camada [dao] discutida anteriormente.

Pergunta: Compile o projeto [pam-metier-dao-spring-nhibernate] acima. Ele será testado separadamente:

  • no modo de consola pelo programa de consola [Main.cs]

  • pelo teste unitário [NUnit.cs] executado pela estrutura NUnit

O novo projeto [pam-metier-dao-spring-nhibernate] pode ser compilado simplesmente copiando o projeto [pam-metier-dao-nhibernate] e, em seguida, modificando os elementos que precisam de ser alterados.


Após o teste, iremos gerar a DLL da camada [business], à qual daremos o nome de [pam-metier-dao-spring-nhibernate]:

  • em [1], o teste NUnit bem-sucedido
  • em [2], a DLL gerada pelo projeto

Iremos adicionar a DLL da camada [business] às DLLs já presentes na pasta [lib] [3]:

13.4. A camada [web]

Vamos rever a arquitetura geral da aplicação [SimuPaie]:

Partimos do princípio de que as camadas [dao] e [business] estão completas e encapsuladas nas DLLs [pam-dao-spring-nhibernate, pam-business-dao-spring-nhibernate]. Vamos agora descrever a camada web.

O projeto do Visual Web Developer para a camada [web] é obtido, em primeiro lugar, simplesmente copiando a pasta do projeto web [pam-v7-3tier-nhibernate-multivues-multipages]. O projeto é então renomeado para [pam-v9-3tier-spring-nhibernate-multivues-multipages]:

O novo projeto web [pam-v9-3tier-spring-nhibernate-multivues-multipages] difere do projeto [pam-v7-3tier-nhibernate-multivues-multipages] das seguintes formas:

  • Em [1], a configuração é feita pelos ficheiros [Dao.xml] e [Web.config]. O ficheiro [Dao.xml] não existia no [pam-v7], e o ficheiro [Web.config] deve incluir a configuração do Spring/NHibernate, enquanto que no [pam-v7] apenas configurava o NHibernate.
  • Em [2], as DLLs para as camadas [dao] e [business] são as que acabámos de compilar.

O ficheiro [Dao.xml] é o utilizado na construção da camada [business]. O ficheiro [Web.config] é o do [pam-v7], ao qual adicionamos a configuração Spring/NHibernate encontrada nos ficheiros [App.config] das camadas [dao] e [business]. O ficheiro [Web.config] para o [pam-v9] é o seguinte:


<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]

As linhas 7–11 e 16–23 contêm a configuração Spring que estava nos ficheiros [App.config] das camadas [dao] e [business] criadas anteriormente, com uma diferença: nos ficheiros [App.config], a linha 17 estava escrita da seguinte forma:


      <resource uri="Dao.xml" />

Com a seguinte configuração:

O ficheiro [Dao.xml] é copiado para a pasta [bin] dentro da pasta do projeto web. Com a sintaxe


      <resource uri="Dao.xml" />

o ficheiro [Dao.xml] será procurado no diretório atual do processo que está a executar a aplicação web. Acontece que este diretório não é a pasta [bin] do projeto web que está a ser executado. Deve escrever:


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

para que o ficheiro [Dao.xml] seja procurado na pasta [bin] do projeto web executado.


Pergunta: Implemente esta aplicação web numa máquina.


13.5. Conclusão

Passámos da arquitetura:

para a arquitetura:

O objetivo era implementar a camada [DAO] tirando partido das capacidades oferecidas pela integração do Spring com o NHibernate.

Constatámos que isto:

  • afetava a camada [DAO]. Esta camada era mais simples de escrever, mas exigia uma configuração mais complexa do Spring.
  • afetou marginalmente as camadas [business] e [web]

Isto constituiu mais um exemplo das vantagens das arquiteturas em camadas.