Skip to content

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

Propomos aqui retomar 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 figura acima, a camada [dao] havia sido implementada com o framework NHibernate. O framework Spring foi utilizado apenas para a integração entre as camadas. O framework Spring oferece classes utilitárias para trabalhar com o framework Nhibernate. O uso dessas classes torna o código da camada [dao] mais simples de escrever. A arquitetura anterior evolui da seguinte forma:

Devido à estrutura em camadas utilizada, a integração Spring / NHibernate implica a modificação apenas da camada [dao]. As camadas [presentation] (web / ASP.NET) e [metier] não precisarão ser modificadas. Essa é a principal vantagem das arquiteturas em camadas integradas pelo Spring.

A seguir, construiremos a camada [dao] com [Spring / NHibernate], comentando o código de uma solução funcional. Não pretendemos abordar todas as possibilidades de configuração ou utilização do framework [Spring / Nhibernate]. O leitor poderá adaptar a solução proposta aos seus próprios problemas com a ajuda da documentação do Spring.NET e do [http://www.springframework.net/documentation.html] (junho de 2010).

A abordagem seguida para construir as camadas [dao] e [metier] é a da versão 3, descrita no parágrafo 7. A abordagem seguida para a camada [présentation] é a da versão 7, descrita no parágrafo 11.

13.1. A camada [dao] de acesso aos dados

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

O projeto do Visual Studio da 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 arquivos [App.config] e [Dao.xml] configuram o framework Spring / NHibernate. Teremos que descrever o conteúdo desses dois arquivos.
  • No arquivo [2], as diferentes classes do projeto
    • na pasta [entites], encontramos as entidades NHibernate estudadas no projeto [pam-dao-nhibernate]
    • na pasta [service], encontramos a interface [IPamDao] e sua implementação com o framework Spring / NHibernate [PamDaoSpringNHibernate]. Teremos que escrever essa nova implementação da interface [IPamDao]
    • A pasta [tests] contém os mesmos testes que o projeto [pam-dao-nhibernate]. Eles testam a mesma interface [IPamdao].
  • No [3], as referências do projeto. A integração Spring / NHibernate requer dois novos arquivos: DLL, [Spring.Data] e [Spring.Data.NHibernate12]. Esses DLL estão disponíveis no framework Spring.Net. Eles foram adicionados à pasta [lib] dos DLL e [4]:

Nas referências [3] do projeto, encontram-se as seguintes DLL:

  • NHibernate: para o ORM NHibernate
  • MySql.Data: o driver ADO.NET do SGBD MySQL
  • Spring.Core: para o framework Spring, que garante a integração das camadas
  • log4net: uma biblioteca de logs
  • nunit.framework: uma biblioteca de testes unitários
  • Spring.Data e Spring.Data.NHibernate12: garantem o suporte ao Spring / NHibernate.

Essas referências foram extraídas da pasta [lib] [4]. É preciso garantir que todas essas referências tenham a propriedade “Cópia local” definida como “True” [5]:

13.1.2. A configuração do projeto C#

O projeto está configurado da seguinte maneira:

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

13.1.3. As entidades da camada [dao]

As entidades (objetos) necessárias para a camada [dao] foram reunidas na pasta [entites] [1] do projeto. Essas entidades são as do projeto [pam-dao-nhibernate], com uma única diferença nos arquivos de configuração NHibernate. Tomemos, por exemplo, o arquivo [Employe.hbm.xml]:

  • no [2], o arquivo está configurado para ser incorporado ao assembly do projeto

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 arquivo [Employe.hbm.xml] será encontrado no assembly [pam-dao-spring-nhibernate]

13.1.4. Configuração Spring / NHibernate

Voltemos ao projeto do Visual C#:

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

13.1.4.1. O arquivo [App.config]

O arquivo [App.config] é o seguinte:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!-- seções de configuração -->
    <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>


    <!-- configuração do Spring -->
    <spring>
        <parsers>
            <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
        </parsers>
        <context>
            <resource uri="Dao.xml" />
        </context>
    </spring>

    <!-- Esta seção contém as configurações do log4net -->
    <!-- NOTE IMPORTANTE: os logs não estão ativos por padrão. É necessário ativá-los programaticamente
    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>

        <!-- Defina o nível de registro padrão como DEBUG -->
        <root>
            <level value="DEBUG" />
            <appender-ref ref="ConsoleAppender" />
        </root>

        <!-- Configure o registro de logs para o Spring.  Os nomes dos loggers no Spring correspondem ao 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 arquivo [App.config] acima configura o Spring (linhas 5-9, 15-22), o log4net (linha 10, linhas 28-53), mas não o NHibernate. Os objetos do Spring não estão configurados no [App.config], mas sim no arquivo [Dao.xml] (linha 20). A configuração do Spring / NHibernate, que consiste em declarar objetos específicos do Spring, será, portanto, encontrada nesse arquivo.

13.1.4.2. O arquivo [Dao.xml]

O arquivo [Dao.xml], que reúne os objetos gerenciados pelo Spring, é o seguinte:


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

    <!-- Referenciado pelo arquivo de configuração do contexto da aplicação principal -->
    <description>
        Application Spring / NHibernate
    </description>

    <!-- Banco de dados e NHibernate Configuração -->
    <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>

    <!-- gerenciador de transações -->
    <object id="transactionManager"
        type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate12">
        <property name="DbProvider" ref="DbProvider"/>
        <property name="SessionFactory" ref="NHibernateSessionFactory"/>
    </object>

    <!-- Modelo do 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 acesso a dados -->
    <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 a 13 configuram a conexão com o banco de dados [dbpam_nhibernate]. Nele, encontramos:
    • o provedor ADO.NET necessário para a conexão; neste caso, o provedor do SGBD é o MySQL. Isso implica ter o DLL e o [Mysql.Data] nas referências do projeto.
    • a string de conexão ao banco de dados (servidor, nome do banco, proprietário da conexão, senha)
  • as linhas 15 a 29 configuram o SessionFactory a partir do NHibernate, o objeto utilizado para obter sessões NHibernate. Vale lembrar que toda operação no banco de dados é realizada dentro de uma sessão NHibernate. Na linha 15, pode-se observar que a SessionFactory é implementada pela classe Spring Spring.Data.NHibernate.LocalSessionFactoryObject, encontrada na DLL Spring.Data.NHibernate12.
  • linha 16: a propriedade DbProvider define os parâmetros de conexão com o banco de dados (provedor ADO.NET e string de conexão). Aqui, essa propriedade faz referência ao objeto DbProvider definido anteriormente nas linhas 11 a 13.
  • linhas 17-20: definem a lista de assemblies que contêm os arquivos [*.hbm.xml], os quais configuram entidades gerenciadas pelo NHibernate. A linha 19 indica que esses arquivos serão encontrados no assembly do projeto. Lembramos que esse nome se encontra nas propriedades do projeto C#. Lembramos também que todos os arquivos [*.hbm.xml] foram configurados para serem incorporados ao assembly do projeto.
  • linhas 22-27: propriedades específicas de NHibernate.
    • linha 24: o dialeto SQL utilizado será o de MySQL
    • linha 25: o SQL emitido pelo NHibernate não aparecerá nos logs do console. Definir essa propriedade como true permite identificar os comandos SQL emitidos por NHibernate. Isso pode ajudar a entender, por exemplo, por que um aplicativo fica lento ao acessar o banco de dados.
  • linha 28: a propriedade ExposeTransactionAwareSessionFactory definida como true fará com que o Spring gerencie as anotações de gerenciamento de transações encontradas no código C#. Voltaremos a esse assunto quando escrevermos a classe que implementa a camada [dao].
  • As linhas 32 a 36 definem o gerenciador de transações. Mais uma vez, esse gerenciador é uma classe Spring da DLL Spring.Data.NHibernate12. Esse gerenciador precisa conhecer os parâmetros de conexão com o banco de dados (linha 34), bem como a SessionFactory da NHibernate (linha 35).
  • As linhas 39 a 43 definem as propriedades da classe HibernateTemplate, que também é uma classe do Spring. Essa classe será utilizada como classe utilitária na classe que implementa a camada [dao]. Ela facilita as interações com os objetos NHibernate. Essa classe possui algumas propriedades que precisam ser inicializadas:
    • linha 40: o SessionFactory do NHibernate
    • linha 41: a propriedade TemplateFlushMode define o modo de sincronização do contexto de persistência NHibernate com o banco de dados. O modo Auto faz com que haja sincronização:
      • ao final de uma transação
      • antes de uma operação SELECT
    • linha 42: as consultas HQL (Hibernate Query Language) serão armazenadas em cache. Isso pode resultar em um ganho de desempenho.
  • as linhas 46-48 definem a classe de implementação da camada [dao]
    • linha 46: a camada [dao] será implementada pela classe [PamdaoSpringNHibernate] da DLL [pam-dao-spring-nhibernate]. Após a instanciação da classe, o método init da classe será executado imediatamente. Ao fechar o contêiner Spring, 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. Estrutura básica 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 {
        // Lista de todas as identidades dos funcionários 
        Employe[] GetAllIdentitesEmployes();
        // um funcionário específico com seus benefícios 
        Employe GetEmploye(string ss);
        // lista de todas as contribuições 
        Cotisations GetCotisations();
    }
}
  • linha 1: importa-se o espaço de nomes das entidades da camada [dao].
  • linha 3: a camada [dao] está no espaço de nomes [Pam.Dao.Service]. Os elementos do espaço de nomes [Pam.Dao.Entites] podem ser criados em várias instâncias. Os elementos do espaço de nomes [Pam.Dao.Service] são criados em uma única instância (singleton). Foi isso que justificou a escolha dos nomes dos espaços de nomes.
  • linha 4: a interface se chama [IPamDao]. Ela define três métodos:
    • linha 6, [GetAllIdentitesEmployes] retorna um array de objetos do tipo [Employe] que representa a lista de babás em um formato simplificado (sobrenome, nome, SS).
    • linha 8, [GetEmploye] retorna um objeto [Employe]: o funcionário cujo número de previdência social foi passado como parâmetro para o método, juntamente com os valores de indenização associados ao seu índice.
    • linha 10, [GetCotisations] retorna o objeto [Cotisations], que encapsula as alíquotas das diferentes contribuições sociais a serem deduzidas do salário bruto.

A estrutura da classe de implementação dessa interface com o suporte Spring / NHibernate poderia ser a 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 {
        // campos privados 
        private Cotisations cotisations;
        private Employe[] employes;

        // inicialização 
        [Transaction(ReadOnly = true)]
        public void init() {
...
        }

        // exclusão de objeto
        public void destroy() {
            if (HibernateTemplate.SessionFactory != null) {
                HibernateTemplate.SessionFactory.Close();
            }
        }

        // lista de todas as identidades dos funcionários
        public Employe[] GetAllIdentitesEmployes() {
            return employes;
        }


        // um funcionário específico com suas remunerações 
        [Transaction(ReadOnly = true)]
        public Employe GetEmploye(string ss) {
....
        }

        // lista de contribuições 
        public Cotisations GetCotisations() {
            return cotisations;
        }
    }
}
  • linha 9: a classe [PamDaoSpringNHibernate] implementa corretamente a interface da camada [dao] [IPamDao]. Ela também deriva da classe Spring [HibernateDaoSupport]. Essa classe possui uma propriedade [HibernateTemplate] que é inicializada pela configuração do 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] indica que os métodos init e destroy da classe [PamDaoSpringNHibernate] devem ser executados em momentos específicos. Esses dois métodos estão presentes na classe, nas linhas 16 e 21.
  • linhas 15, 34: anotações que fazem com que o método anotado seja executado em uma transação. O atributo ReadOnly=true indica que a transação é somente leitura. O método executado na transação pode lançar uma exceção. Nesse caso, o Spring realiza um Rollback automático da transação. Essa anotação elimina a necessidade de gerenciar uma transação dentro do método.
  • linha 16: o método init é executado pelo Spring imediatamente após a instanciação da classe. Veremos que seu objetivo é inicializar os campos privados das linhas 11 e 12. Ele será executado em 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] limita-se a retornar o atributo da linha 12 inicializado pelo método init.
  • linhas 40-42: o método [GetCotisations] limita-se a retornar o atributo da linha 11, inicializado pelo método init.

13.1.5.2. Métodos úteis da classe HibernateTemplate

Utilizaremos os seguintes métodos da classe HibernateTemplate:

IList<T> Find<T>(string requete_hql)
executa a consulta HQL e retorna uma lista de objetos do tipo T
IList<T> Find<T>(string requete_hql, object[])
executa uma consulta HQL parametrizada por ?. Os valores desses 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, mas que permitem recuperar, salvar, atualizar e excluir entidades:

T Load<T>(object id)
insere na sessão NHibernate a entidade do tipo T com a chave primária id.
void SaveOrUpdate(object entidade)
insere (INSERT) ou atualiza (UPDATE) o objeto entité, dependendo se este possui uma chave primária (UPDATE) ou não (INSERT). A ausência de chave primária pode ser configurada pelo atributo unsaved-values do arquivo de configuração da entidade. Após a operação SaveOrUpdate, o objeto entité está na sessão NHibernate.
void Delete(object entidade)
exclui o objeto entité da sessão 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 a instanciação da classe pelo Spring. Seu objetivo é armazenar em cache local as identidades simplificadas dos funcionários (sobrenome, nome, SS) e as alíquotas de contribuição. Seu código poderia ser o seguinte.


[Transaction(ReadOnly = true)]
        public void init() {
            try {
                // obtém-se a lista simplificada dos funcionários
                IList<object[]> lignes = HibernateTemplate.Find<object[]>("select e.SS,e.Nom,e.Prenom from Employe e");
                // coloca-se em uma tabela
                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++;
                }
                // colocamos as alíquotas de contribuição em um objeto 
                cotisations = (HibernateTemplate.LoadAll<Cotisations>())[0];
            } catch (Exception ex) {
                // converte-se a exceção 
                throw new PamException(string.Format("Erreur d'accès à la BD : [{0}]", ex.ToString()), 43);
            }
        }
  • linha 5: é executada uma consulta HQL. Ela solicita os campos SS, Sobrenome e Nome de todas as entidades Employé. Ela retorna uma lista de objetos. Se tivéssemos solicitado todos os dados do funcionário na forma “select e from Funcionário e”, teríamos obtido uma lista de objetos do tipo Employe.
  • linhas 7-12: essa lista de objetos é copiada para uma matriz de objetos do tipo Employe.
  • linha 14: solicita-se a lista de todas as entidades do tipo Cotisations. Sabe-se que essa lista possui apenas um elemento. Portanto, recupera-se o primeiro elemento da lista para obter as alíquotas de contribuição.
  • As linhas 7 e 14 inicializam os dois campos privados da classe.

13.1.5.4. Implementação do método GetEmploye

O método GetEmploye deve retornar a entidade Funcionário com um determinado número SS. Seu código poderia ser o seguinte:


[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) {
                // transformamos a exceção 
                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);
            }
            // conseguimos recuperar um funcionário? 
            if (employés.Count == 0) {
                // está-se relatando o fato 
                throw new PamException(string.Format("L'employé de n° ss [{0}] n'existe pas", ss), 42);
            } else {
                return employés[0];
            }
        }
  • linha 6: obtém a lista de funcionários com um determinado número SS
  • linha 12: normalmente, se o funcionário existir, deve-se obter uma lista com um único elemento
  • linha 14: caso contrário, lança-se uma exceção
  • linha 16: se for o caso, retorna-se o primeiro funcionário da lista

13.1.5.5. Conclusion

Se compararmos o código da camada [dao] no caso de uso

  1. apenas do framework NHibernate
  2. do framework Spring / NHibernate

percebe-se que a segunda solução permitiu escrever um código mais simples.

13.2. Testes da camada [dao]

13.2.1. O projeto do Visual Studio

O projeto do Visual Studio já foi apresentado. Vamos relembrá-lo:

  • em [1], o projeto como um todo
  • em [2], as diferentes classes do projeto. A pasta [tests] contém um teste de console [Main.cs] e um teste unitário [NUnit.cs].
  • em [3], o programa [Main.cs] é compilado.
  • no [4], o arquivo [NUnit.cs] não é gerado.
  • O projeto é um aplicativo de console. A classe executada é aquela especificada em [5], a classe do arquivo [Main.cs].

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

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

Ele é responsável por testar os métodos da interface [IPamDao]. Um exemplo básico poderia 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 {
                // instanciação da camada [dao]
                IPamDao pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
                // lista das identidades dos funcionários 
                foreach (Employe Employe in pamDao.GetAllIdentitesEmployes()) {
                    Console.WriteLine(Employe.ToString());
                }
                // um funcionário com suas indenizações 
                Console.WriteLine("------------------------------------");
                Console.WriteLine(pamDao.GetEmploye("254104940426058"));
                Console.WriteLine("------------------------------------");
                // lista de contribuições 
                Cotisations cotisations = pamDao.GetCotisations();
                Console.WriteLine(cotisations.ToString());
            } catch (Exception ex) {
                // exibição de exceção 
                Console.WriteLine(ex.ToString());
            }
            //pausa 
            Console.ReadLine();
        }
    }
}
  • linha 11: solicita-se ao Spring uma referência na camada [dao].
  • linhas 13-15: teste do método [GetAllIdentitesEmployes] da interface [IPamDao]
  • linha 18: teste do método [GetEmploye] da interface [IPamDao]
  • linha 21: teste do método [GetCotisations] da interface [IPamDao]

Spring, NHibernate e log4net são configurados pelo arquivo [App.config] , analisado no parágrafo 13.1.4.1.

A execução realizada com o banco de dados descrito no parágrafo 6.2 produz o seguinte resultado no terminal:

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 dois funcionários do tipo [Employe], com as seguintes informações: [SS, Nom, Prenom]
  • linha 4: o funcionário do tipo [Employe] com o nº de previdência social [254104940426058]
  • linha 5: as alíquotas de contribuição

13.2.3. Testes unitários com NUnit

Passamos agora a um teste unitário com NUnit. O projeto do Visual Studio da camada [dao] será alterado da seguinte forma:

  • para [1]; o programa de teste [NUnit.cs]
  • para [2,3]; o projeto irá gerar um DLL denominado [pam-dao-spring-nhibernate.dll]
  • em [4], a referência ao 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 {
        // a camada [dao] a ser testada 
        private IPamDao pamDao = null;

        // construtor 
        public NunitPamDao() {
            // instanciação da camada [dao]
            pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
        }

        // inicialização 
        [SetUp]
        public void Init() {

        }

        [Test]
        public void GetAllIdentitesEmployes() {
            // verificação do número de funcionários 
            Expect(2, EqualTo(pamDao.GetAllIdentitesEmployes().Length));
        }

        [Test]
        public void GetCotisations() {
            // verificação da alíquota de contribuições 
            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() {
            // verificação de pessoas físicas 
            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() {
            // verificação de indivíduo inexistente 
            bool erreur = false;
            try {
                Employe employe1 = pamDao.GetEmploye("xx");
            } catch {
                erreur = true;
            }
            Expect(erreur, True);
        }
    }
}

Essa classe já foi abordada no parágrafo 7.3.4.

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

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

Image

Acima, os testes foram bem-sucedidos.

Trabalho prático:


  • executar na máquina os testes da classe [PamDaoSpringNHibernate].
  • utilizar diferentes arquivos de configuração [Dao.xml] para utilizar outros SGBD (Firebird, MySQL, Postgres, SQL Server)

13.2.4. Geração d e da DLL a partir da camada [dao]

Após a criação e o teste da classe [PamDaoNHibernate], geraremos a DLL a partir da camada [dao] da seguinte maneira:

  • [1]; os programas de teste são excluídos da compilação do projeto
  • [2,3], configuração do projeto
  • [4], geração do projeto
  • o DLL é gerado na pasta [bin/Release] [5]. Nós o adicionamos aos arquivos DLL já presentes na pasta [lib] [6]:

13.3. A camada de negócios

Voltemos à arquitetura geral do aplicativo [SimuPaie]:

Consideramos agora que a camada [dao] está implementada e que foi encapsulada na DLL [pam-dao-spring-nhibernate.dll]. Agora, vamos nos concentrar na camada [metier]. É ela que implementa as regras de negócio, neste caso, as regras de cálculo de um salário.

O projeto do Visual Studio da camada de negócios poderia se parecer com o seguinte:

  • em [1], todo o projeto configurado pelos arquivos [App.config] e [Dao.xml]. O arquivo [App.config] é idêntico ao que era no projeto da camada [dao] [pam-dao-spring-nhibernate]. O mesmo se aplica ao arquivo [Dao.xml], exceto que ele declara um objeto Spring adicional com o ID pammetier. A declaração deste último é idêntica à que constava no arquivo [App.config] do projeto [pam-metier-dao-nhibernate].
  • No [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. Observe-se o DLL e o [pam-dao-spring-nhibernate] da camada [dao] analisada anteriormente.

Questão: construir o projeto [pam-metier-dao-spring-nhibernate] acima. Ele será testado separadamente:

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

  • pelo teste unitário [NUnit.cs] executado pelo framework 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 ser alterados.


Após os testes, será gerado o DLL a partir da camada [metier], que será denominado [pam-metier-dao-spring-nhibernate]:

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

Adicionaremos o DLL da camada [metier] aos DLL já presentes na pasta [lib] [3]:

13.4. A camada [web]

Voltemos à arquitetura geral do aplicativo [SimuPaie]:

Consideramos que as camadas [dao] e [métier] já estão implementadas e encapsuladas nas camadas DLL e [pam-dao-spring-nhibernate, pam-metier-dao-spring-nhibernate]. Passaremos agora a descrever a camada web.

O projeto Visual Web Developer da camada [web] é obtido, em primeiro lugar, por meio da simples cópia da pasta do projeto web [pam-v7-3tier-nhibernate-multivues-multipages]. Em seguida, o projeto é renomeado como [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] nos seguintes pontos:

  • no [1], ele é configurado pelos arquivos [Dao.xml] e [Web.config]. O [Dao.xml] não existia no [pam-v7], e o arquivo [Web.config] deve integrar a configuração Spring / NHibernate, enquanto que no [pam-v7], ele configurava apenas o NHibernate.
  • No [2], os DLL das camadas [dao] e [metier] são aqueles que acabamos de construir.

O arquivo [Dao.xml] é aquele utilizado na construção da camada [metier]. O arquivo [Web.config] é o de [pam-v7] ao qual se adiciona a configuração Spring / NHibernate que se encontrava nos arquivos [App.config] das camadas [dao] e [metier]. O arquivo [Web.config] de [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>

  <!-- configuração do 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]

Nas linhas 7-11 e 16-23, encontramos a configuração do Spring que tínhamos nos arquivos [App.config] das camadas [dao] e [metier] criadas anteriormente, com uma única diferença: nos arquivos [App.config], a linha 17 estava escrita da seguinte forma:


      <resource uri="Dao.xml" />

Com a seguinte configuração:

o arquivo [Dao.xml] é copiado para a pasta [bin] da pasta do projeto web. Com a sintaxe


      <resource uri="Dao.xml" />

o arquivo [Dao.xml] será procurado na pasta atual do processo que está executando o aplicativo web. Acontece que essa pasta não é a pasta [bin] da pasta do projeto web em execução. É preciso escrever:


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

para que o arquivo [Dao.xml] seja procurado na pasta [bin] da pasta do projeto web em execução.


Pergunta: implementar essa aplicação web em uma máquina.


13.5. Conclusion

Passamos da arquitetura:

na arquitetura:

O objetivo era implementar a camada [dao], aproveitando os recursos oferecidos pela integração do NHibernate pelo Spring.

Pudemos constatar que isso:

  • afetava a camada [dao]. Essa camada foi mais simples de escrever, mas exigiu uma configuração mais complexa do Spring.
  • afetava, de forma marginal, as camadas [metier] e [web]

Tivemos aqui mais um exemplo da vantagem das arquiteturas em camadas.