13. Aplikacja [SimuPaie] – wersja 9 – integracja Spring / NHibernate
W tym miejscu proponujemy przerobić trójwarstwową aplikację ASP.NET na podstawie wersji 7 [pam-v7-3tier-nhibernate-multivues-multipages]. Architektura warstwowa aplikacji wyglądała następująco:
![]() |
W powyższym przykładzie warstwa [dao] została zaimplementowana przy użyciu frameworka NHibernate. Framework Spring został wykorzystany wyłącznie do integracji poszczególnych warstw. Framework Spring udostępnia klasy pomocnicze do pracy z frameworkiem Nhibernate. Wykorzystanie tych klas ułatwia pisanie kodu warstwy [dao]. Poprzednia architektura ewoluuje w następujący sposób:
![]() |
Ze względu na zastosowaną strukturę warstwową wykorzystanie integracji Spring / NHibernate powoduje zmianę wyłącznie w warstwie [dao]. Warstwy [presentation] (web / ASP.NET) oraz [metier] nie będą wymagały modyfikacji. Jest to główna zaleta architektur warstwowych zintegrowanych przez Spring.
W dalszej części zbudujemy warstwę [dao] wraz z [Spring / NHibernate], omawiając kod działającego rozwiązania. Nie będziemy starać się przedstawić wszystkich możliwości konfiguracji ani zastosowania frameworka [Spring / Nhibernate]. Czytelnik będzie mógł dostosować proponowane rozwiązanie do własnych potrzeb, korzystając z dokumentacji Spring.NET i [http://www.springframework.net/documentation.html] (czerwiec 2010 r.).
Podejście zastosowane przy tworzeniu warstw [dao] i [metier] jest zgodne z wersją 3 opisaną w paragrafie 7. Podejście zastosowane w przypadku warstwy [présentation] jest zgodne z wersją 7 opisaną w paragrafie 11.
13.1. Warstwa [dao] zapewniająca dostęp do danych
![]() |
13.1.1. Projekt Visual Studio C# warstwy [dao]
Projekt Visual Studio warstwy [dao] wygląda następująco:
![]() |
- w [1] znajduje się cały projekt
- folder [pam] zawiera klasy projektu oraz konfigurację encji NHibernate
- pliki [App.config] i [Dao.xml] konfigurują framework Spring / NHibernate. Będziemy musieli opisać zawartość tych dwóch plików.
- W pliku [2] znajdują się różne klasy projektu
- w folderze [entites] znajdują się encje NHibernate analizowane w projekcie [pam-dao-nhibernate]
- w folderze [service] znajdujemy interfejs [IPamDao] oraz jego implementację przy użyciu frameworka Spring / NHibernate [PamDaoSpringNHibernate]. Będziemy musieli napisać tę nową implementację interfejsu [IPamDao]
- Katalog [tests] zawiera te same testy, co projekt [pam-dao-nhibernate]. Testują one ten sam interfejs [IPamdao].
- W [3] znajdują się odniesienia do projektu. Integracja Spring / NHibernate wymaga dwóch nowych plików: DLL, [Spring.Data] oraz [Spring.Data.NHibernate12]. Te pliki DLL są dostępne w frameworku Spring.Net. Zostały one dodane do folderu [lib] w ramach DLL i [4]:
![]() |
W odniesieniach [3] projektu znajdują się następujące odniesienia DLL:
- NHibernate: dla ORM NHibernate
- MySql.Data: sterownik ADO.NET dla SGBD MySQL
- Spring.Core: dla frameworka Spring, który zapewnia integrację warstw
- log4net: biblioteka logów
- nunit.framework: biblioteka testów jednostkowych
- Spring.Data i Spring.Data.NHibernate12: zapewniają obsługę Spring / NHibernate.
Odwołania te zostały pobrane z folderu [lib] [4]. Należy upewnić się, że dla wszystkich tych odwołań właściwość „Kopia lokalna” ma wartość „True” [5]:
13.1.2. Konfiguracja projektu C#
Projekt jest skonfigurowany w następujący sposób:
![]() |
- w [1] nazwa zestawu projektu to [pam-dao-spring-nhibernate]. Nazwa ta pojawia się w różnych plikach konfiguracyjnych projektu.
13.1.3. Elementy warstwy [dao]
![]() |
Elementy (obiekty) niezbędne dla warstwy [dao] zostały zebrane w folderze [entites] [1] projektu. Są to te same elementy, co w projekcie [pam-dao-nhibernate], z jedną różnicą w plikach konfiguracyjnych NHibernate. Weźmy na przykład plik [Employe.hbm.xml]:
- w pliku [2] jest on skonfigurowany tak, aby został włączony do zestawu projektu
Jego zawartość jest następująca:
<?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>
- wiersz 2: atrybut assembly wskazuje, że plik [Employe.hbm.xml] będzie znajdował się w zestawie [pam-dao-spring-nhibernate]
13.1.4. Konfiguracja Spring / NHibernate
Wróćmy do projektu Visual C#:
![]() |
- w pliku [1] pliki [App.config] i [Dao.xml] konfigurują integrację Spring / NHibernate
13.1.4.1. Plik [App.config]
Plik [App.config] ma następującą treść:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<!-- sekcje konfiguracyjne -->
<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>
<!-- konfiguracja Spring -->
<spring>
<parsers>
<parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
</parsers>
<context>
<resource uri="Dao.xml" />
</context>
</spring>
<!-- Ta sekcja zawiera ustawienia konfiguracyjne log4net -->
<!-- NOTE IMPORTANTE: logi nie są domyślnie aktywne. Należy je aktywować programowo
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>
<!-- Ustaw domyślny poziom logowania na DEBUG -->
<root>
<level value="DEBUG" />
<appender-ref ref="ConsoleAppender" />
</root>
<!-- Ustaw rejestrowanie dla Spring. Nazwy rejestratorów w Spring odpowiadają przestrzeni nazw -->
<logger name="Spring">
<level value="INFO" />
</logger>
<logger name="Spring.Data">
<level value="DEBUG" />
</logger>
<logger name="NHibernate">
<level value="DEBUG" />
</logger>
</log4net>
</configuration>
Powyższy plik [App.config] konfiguruje Spring (wiersze 5–9, 15–22), log4net (wiersz 10, wiersze 28–53), ale nie NHibernate. Obiekty Springa nie są skonfigurowane w pliku [App.config], lecz w pliku [Dao.xml] (wiersz 20). Konfiguracja Springa / NHibernate, polegająca na zadeklarowaniu konkretnych obiektów Springa, znajduje się zatem w tym pliku.
13.1.4.2. Plik [Dao.xml]
Plik [Dao.xml], zawierający obiekty zarządzane przez Spring, wygląda następująco:
<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net"
xmlns:db="http://www.springframework.net/database">
<!-- Odwołuje się do głównego pliku konfiguracyjnego kontekstu aplikacji -->
<description>
Application Spring / NHibernate
</description>
<!-- Konfiguracja bazy danych i NHibernate -->
<db:provider id="DbProvider"
provider="MySql.Data.MySqlClient"
connectionString="Server=localhost;Database=dbpam_nhibernate;Uid=root;Pwd=;"/>
<object id="NHibernateSessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate12">
<property name="DbProvider" ref="DbProvider"/>
<property name="MappingAssemblies">
<list>
<value>pam-dao-spring-nhibernate</value>
</list>
</property>
<property name="HibernateProperties">
<dictionary>
<entry key="hibernate.dialect" value="NHibernate.Dialect.MySQLDialect"/>
<entry key="hibernate.show_sql" value="false"/>
</dictionary>
</property>
<property name="ExposeTransactionAwareSessionFactory" value="true" />
</object>
<!-- menedżer transakcji -->
<object id="transactionManager"
type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate12">
<property name="DbProvider" ref="DbProvider"/>
<property name="SessionFactory" ref="NHibernateSessionFactory"/>
</object>
<!-- Szablon 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>
<!-- Obiekty dostępu do danych -->
<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>
- Wiersze 11–13 konfigurują połączenie z bazą danych [dbpam_nhibernate]. Znajduje się tam:
- dostawca ADO.NET niezbędny do połączenia, w tym przypadku dostawca z pliku SGBD MySQL. Oznacza to, że w odwołaniach projektu muszą znajdować się DLL i [Mysql.Data].
- łańcuch połączenia z bazą danych (serwer, nazwa bazy, właściciel połączenia, jego hasło)
- wiersze 15–29 konfigurują obiekt SessionFactory z NHibernate, który służy do uzyskiwania sesji NHibernate. Przypominamy, że każda operacja na bazie danych odbywa się w ramach sesji NHibernate. W wierszu 15 widać, że SessionFactory jest zaimplementowany przez klasę Spring Spring.Data.NHibernate.LocalSessionFactoryObject znajdującą się w DLL Spring.Data.NHibernate12.
- Wiersz 16: właściwość DbProvider określa parametry połączenia z bazą danych (dostawca ADO.NET i ciąg połączenia). W tym przypadku właściwość ta odwołuje się do obiektu DbProvider zdefiniowanego wcześniej w wierszach 11–13.
- wiersze 17–20: określają listę zestawów zawierających pliki [*.hbm.xml] konfigurujące elementy zarządzane przez NHibernate. Wiersz 19 wskazuje, że pliki te będą znajdować się w zestawie projektu. Przypominamy, że nazwa ta znajduje się we właściwościach projektu C#. Przypominamy również, że wszystkie pliki [*.hbm.xml] zostały skonfigurowane tak, aby były włączone do zestawu projektowego.
- wiersze 22–27: właściwości specyficzne dla NHibernate.
- wiersz 24: używany dialekt SQL będzie pochodził z MySQL
- wiersz 25: SQL wygenerowany przez NHibernate nie pojawi się w logach konsoli. Ustawienie tej właściwości na true pozwala poznać polecenia SQL wysyłane przez NHibernate. Może to pomóc na przykład w zrozumieniu, dlaczego aplikacja działa wolno podczas dostępu do bazy danych.
- wiersz 28: ustawienie właściwości ExposeTransactionAwareSessionFactory na true spowoduje, że Spring będzie zarządzał adnotacjami dotyczącymi transakcji, które zostaną znalezione w kodzie C#. Powrócimy do tego tematu podczas pisania klasy implementującej warstwę [dao].
- Wiersze 32–36 definiują menedżera transakcji. Również w tym przypadku menedżer ten jest klasą Springa z pakietu DLL Spring.Data.NHibernate12. Menedżer ten musi znać parametry połączenia z bazą danych (wiersz 34), a także SessionFactory z NHibernate (wiersz 35).
- Wiersze 39–43 definiują właściwości klasy HibernateTemplate, która również jest klasą Springa. Klasa ta będzie wykorzystywana jako klasa pomocnicza w klasie implementującej warstwę [dao]. Ułatwia ona interakcje z obiektami NHibernate. Klasa ta posiada pewne właściwości, które należy zainicjować:
- wiersz 40: SessionFactory z NHibernate
- wiersz 41: właściwość TemplateFlushMode określa tryb synchronizacji kontekstu trwałości NHibernate z bazą danych. Tryb Auto powoduje, że synchronizacja nastąpi:
- na końcu transakcji
- przed operacją select
- wiersz 42: zapytania HQL (Hibernate Query Language) będą buforowane. Może to przyczynić się do poprawy wydajności.
- wiersze 46–48 definiują klasę implementacyjną warstwy [dao]
- wiersz 46: warstwa [dao] zostanie zaimplementowana przez klasę [PamdaoSpringNHibernate] z klasy DLL [pam-dao-spring-nhibernate]. Po utworzeniu instancji klasy natychmiast zostanie wykonana metoda init tej klasy. Po zamknięciu kontenera Spring zostanie wykonana metoda destroy tej klasy.
- wiersz 47: klasa [PamDaoSpringNHibernate] będzie posiadać właściwość HibernateTemplate, która zostanie zainicjowana wartością właściwości HibernateTemplate z wiersza 39.
13.1.5. Implementacja warstwy [dao]
13.1.5.1. Szkielet klasy implementacyjnej
Interfejs [IPamDao] jest taki sam jak w projekcie [pam-dao-nhibernate]:
using Pam.Dao.Entites;
namespace Pam.Dao.Service {
public interface IPamDao {
// lista wszystkich tożsamości pracowników
Employe[] GetAllIdentitesEmployes();
// konkretny pracownik wraz z jego dodatkami
Employe GetEmploye(string ss);
// lista wszystkich składek
Cotisations GetCotisations();
}
}
- wiersz 1: importujemy przestrzeń nazw elementów warstwy [dao].
- wiersz 3: warstwa [dao] znajduje się w przestrzeni nazw [Pam.Dao.Service]. Elementy przestrzeni nazw [Pam.Dao.Entites] mogą być tworzone w wielu egzemplarzach. Elementy przestrzeni nazw [Pam.Dao.Service] są tworzone w jednym egzemplarzu (singleton). To właśnie uzasadniało wybór nazw przestrzeni nazw.
- wiersz 4: interfejs nosi nazwę [IPamDao]. Definiuje on trzy metody:
- wiersz 6: metoda [GetAllIdentitesEmployes] zwraca tablicę obiektów typu [Employe], która przedstawia listę opiekunek do dzieci w uproszczonej formie (nazwisko, imię, SS).
- w wierszu 8 metoda [GetEmploye] zwraca obiekt typu [Employe]: pracownika o numerze ubezpieczenia społecznego przekazanym jako parametr metody wraz z dodatkami związanymi z jego wskaźnikiem.
- w wierszu 10, [GetCotisations] zwraca obiekt [Cotisations], który zawiera stawki różnych składek na ubezpieczenie społeczne pobieranych od wynagrodzenia brutto.
Szkielet klasy implementującej ten interfejs przy użyciu biblioteki Spring / NHibernate mógłby wyglądać następująco:
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 {
// pola prywatne
private Cotisations cotisations;
private Employe[] employes;
// init
[Transaction(ReadOnly = true)]
public void init() {
...
}
// usunięcie obiektu
public void destroy() {
if (HibernateTemplate.SessionFactory != null) {
HibernateTemplate.SessionFactory.Close();
}
}
// lista wszystkich tożsamości pracowników
public Employe[] GetAllIdentitesEmployes() {
return employes;
}
// konkretny pracownik wraz z jego dodatkami
[Transaction(ReadOnly = true)]
public Employe GetEmploye(string ss) {
....
}
// lista składek
public Cotisations GetCotisations() {
return cotisations;
}
}
}
- wiersz 9: klasa [PamDaoSpringNHibernate] prawidłowo implementuje interfejs warstwy [dao] [IPamDao]. Wywodzi się ona również z klasy Spring [HibernateDaoSupport]. Klasa ta posiada właściwość [HibernateTemplate], która jest inicjowana przez skonfigurowane ustawienia Springa (wiersz 2 poniżej):
<object id="pamdao" type="Pam.Dao.Service.PamDaoSpringNHibernate, pam-dao-spring-nhibernate" init-method="init" destroy-method="destroy">
<property name="HibernateTemplate" ref="HibernateTemplate"/>
</object>
- W wierszu 1 powyżej widać, że definicja obiektu [pamdao] wskazuje, iż metody init i destroy klasy [PamDaoSpringNHibernate] muszą zostać wykonane w określonych momentach. Obie te metody rzeczywiście znajdują się w tej klasie w wierszach 16 i 21.
- wiersze 15, 34: adnotacje, które powodują, że metoda opatrzona adnotacją zostanie wykonana w ramach transakcji. Atrybut ReadOnly=true oznacza, że transakcja ma charakter tylko do odczytu. Metoda wykonywana w ramach transakcji może wygenerować wyjątek. W takim przypadku Spring automatycznie Rollback transakcji. Adnotacja ta eliminuje konieczność zarządzania transakcją wewnątrz metody.
- wiersz 16: metoda init jest wykonywana przez Spring natychmiast po instancjonowaniu klasy. Zobaczymy, że jej celem jest zainicjowanie pól prywatnych z wierszy 11 i 12. Metoda ta będzie przebiegać w ramach transakcji (wiersz 15).
- Metody interfejsu [IPamDao] są zaimplementowane w wierszach 28, 35 i 40.
- wiersze 28–30: metoda [GetAllIdentitesEmployes] ogranicza się do zwracania atrybutu z wiersza 12 zainicjowanego przez metodę init.
- wiersze 40–42: metoda [GetCotisations] ogranicza się do zwracania atrybutu z wiersza 11 zainicjowanego przez metodę init.
13.1.5.2. Przydatne metody klasy HibernateTemplate
Będziemy korzystać z następujących metod klasy HibernateTemplate:
IList<T> Find<T>(string requete_hql) | wykonuje zapytanie HQL i zwraca listę obiektów typu T |
IList<T> Find<T>(string requete_hql, object[]) | wykonuje zapytanie HQL z parametrami oznaczonymi jako ?. Wartości tych parametrów są dostarczane przez tablicę obiektów. |
IList<T> LoadAll<T>() | zwraca wszystkie encje typu T |
Istnieją inne przydatne metody, których nie będziemy mieli okazji wykorzystać, a które pozwalają na wyszukiwanie, zapisywanie, aktualizowanie i usuwanie elementów:
T Load<T>(object id) | dodaje do sesji NHibernate encję typu T o kluczu głównym id. |
void SaveOrUpdate(object entité) | wstawia (INSERT) lub aktualizuje (UPDATE) obiekt entité w zależności od tego, czy posiada on klucz podstawowy (UPDATE), czy nie (INSERT). Brak klucza głównego można skonfigurować za pomocą atrybutu unsaved-values w pliku konfiguracyjnym encji. Po wykonaniu operacji SaveOrUpdate obiekt entité znajduje się w sesji NHibernate. |
void Delete(object obiekt) | usuwa obiekt entité z sesji NHibernate. |
13.1.5.3. Implementacja metody init
Metoda init klasy [PamDaoSpringNHibernate] jest domyślnie metodą wykonywaną po utworzeniu instancji klasy przez Spring. Jej celem jest zapisanie w lokalnej pamięci podręcznej uproszczonych danych identyfikacyjnych pracowników (nazwisko, imię, SS) oraz stawek składek. Jej kod mógłby wyglądać następująco.
[Transaction(ReadOnly = true)]
public void init() {
try {
// pobieramy uproszczoną listę pracowników
IList<object[]> lignes = HibernateTemplate.Find<object[]>("select e.SS,e.Nom,e.Prenom from Employe e");
// umieszczamy ją w tabeli
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++;
}
// umieszczamy stawki składek w obiekcie
cotisations = (HibernateTemplate.LoadAll<Cotisations>())[0];
} catch (Exception ex) {
// przetwarzamy wyjątek
throw new PamException(string.Format("Erreur d'accès à la BD : [{0}]", ex.ToString()), 43);
}
}
- wiersz 5: wykonywane jest zapytanie HQL. Pobiera ono pola SS, Nazwisko, Imię ze wszystkich encji Employé. Zwraca listę obiektów. Gdybyśmy zapytali o pełne dane pracownika w postaci „select e from Employe e”, otrzymalibyśmy listę obiektów typu Employe.
- wiersze 7–12: ta lista obiektów jest kopiowana do tablicy obiektów typu Employe.
- wiersz 14: żądamy listy wszystkich encji typu Cotisations. Wiemy, że lista ta zawiera tylko jeden element. Pobieramy zatem pierwszy element listy, aby uzyskać stawki składek.
- Wiersze 7 i 14 inicjują dwa pola prywatne klasy.
13.1.5.4. Implementacja metody GetEmploye
Metoda GetEmploye musi zwrócić jednostkę typu „Pracownik” o podanym numerze SS. Jej kod mógłby wyglądać następująco:
[Transaction(ReadOnly = true)]
public Employe GetEmploye(string ss) {
IList<Employe> employés = null;
try {
// zapytanie
employés = HibernateTemplate.Find<Employe>("select e from Employe e where e.SS=?", new object[]{ss});
} catch (Exception ex) {
// przekształcamy wyjątek
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);
}
// czy udało się pozyskać pracownika?
if (employés.Count == 0) {
// zgłaszamy ten fakt
throw new PamException(string.Format("L'employé de n° ss [{0}] n'existe pas", ss), 42);
} else {
return employés[0];
}
}
- wiersz 6: pobiera listę pracowników o podanym numerze SS
- wiersz 12: zazwyczaj, jeśli pracownik istnieje, powinna pojawić się lista zawierająca jeden element
- wiersz 14: jeśli tak nie jest, zgłaszamy wyjątek
- wiersz 16: jeśli tak jest, zwracamy pierwszego pracownika z listy
13.1.5.5. Conclusion
Jeśli porównamy kod warstwy [dao] w przypadku wykorzystania
- samego frameworka NHibernate
- frameworka Spring / NHibernate
można zauważyć, że drugie rozwiązanie pozwoliło na napisanie prostszego kodu.
13.2. Testy warstwy [dao]
13.2.1. Projekt w Visual Studio
Projekt Visual Studio został już przedstawiony. Przypomnijmy:
![]() |
- w [1], cały projekt
- w [2] – poszczególne klasy projektu. Folder [tests] zawiera test konsolowy [Main.cs] oraz test jednostkowy [NUnit.cs].
- w pliku [3] skompilowano program [Main.cs].
![]() |
- w pliku [4] plik [NUnit.cs] nie jest generowany.
- Projekt jest aplikacją konsolową. Klasą wykonywaną jest ta określona w pliku [5], czyli klasa z pliku [Main.cs].
13.2.2. Program testowy konsolowy [Main.cs]
Program testowy [Main.cs] jest uruchamiany w następującej architekturze:
![]() |
Jego zadaniem jest testowanie metod interfejsu [IPamDao]. Podstawowy przykład mógłby wyglądać następująco:
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 {
// instancja warstwy [dao]
IPamDao pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
// lista tożsamości pracowników
foreach (Employe Employe in pamDao.GetAllIdentitesEmployes()) {
Console.WriteLine(Employe.ToString());
}
// pracownik wraz z jego dodatkami
Console.WriteLine("------------------------------------");
Console.WriteLine(pamDao.GetEmploye("254104940426058"));
Console.WriteLine("------------------------------------");
// lista składek
Cotisations cotisations = pamDao.GetCotisations();
Console.WriteLine(cotisations.ToString());
} catch (Exception ex) {
// wyświetlenie wyjątku
Console.WriteLine(ex.ToString());
}
//przerwa
Console.ReadLine();
}
}
}
- wiersz 11: zwracamy się do Springa o odwołanie do warstwy [dao].
- wiersze 13–15: test metody [GetAllIdentitesEmployes] interfejsu [IPamDao]
- wiersz 18: test metody [GetEmploye] z interfejsu [IPamDao]
- wiersz 21: test metody [GetCotisations] z interfejsu [IPamDao]
Spring, NHibernate i log4net są konfigurowane przez plik [App.config] omówiony w paragrafie 13.1.4.1.
Uruchomienie programu z wykorzystaniem bazy danych opisanej w punkcie 6.2 daje następujący wynik na konsoli:
- wiersze 1–2: dwaj pracownicy typu [Employe], dla których podano jedynie informacje [SS, Nom, Prenom]
- wiersz 4: pracownik typu [Employe] o numerze ubezpieczenia społecznego [254104940426058]
- wiersz 5: stawki składek
13.2.3. Testy jednostkowe z NUnit
Przechodzimy teraz do testu jednostkowego NUnit. Projekt Visual Studio warstwy [dao] ulegnie następującym zmianom:
![]() |
- do [1], program testowy [NUnit.cs]
- na [2,3], projekt wygeneruje plik DLL o nazwie [pam-dao-spring-nhibernate.dll]
- w [4], odwołanie do DLL w frameworku NUnit: [nunit.framework.dll]
- w [5], klasa [Main.cs] nie zostanie uwzględniona w DLL [pam-dao-spring-nhibernate]
- w [6] klasa [NUnit.cs] zostanie uwzględniona w DLL [pam-dao-spring-nhibernate]
Klasa testowa NUnit ma następujący wygląd:
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 {
// warstwa [dao] do przetestowania
private IPamDao pamDao = null;
// konstruktor
public NunitPamDao() {
// instancjonowanie warstwy [dao]
pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
}
// inicjalizacja
[SetUp]
public void Init() {
}
[Test]
public void GetAllIdentitesEmployes() {
// weryfikacja liczby pracowników
Expect(2, EqualTo(pamDao.GetAllIdentitesEmployes().Length));
}
[Test]
public void GetCotisations() {
// weryfikacja stawek składek
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() {
// weryfikacja osób fizycznych
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() {
// weryfikacja nieistniejącej osoby
bool erreur = false;
try {
Employe employe1 = pamDao.GetEmploye("xx");
} catch {
erreur = true;
}
Expect(erreur, True);
}
}
}
Klasa ta została już omówiona w punkcie 7.3.4.
Podczas generowania projektu tworzone są pliki DLL i [pam-dao-spring-nhibernate.dll] w folderze [bin/Release].
![]() |
Pliki DLL i [pam-dao-spring-nhibernate.dll] są ładowane za pomocą narzędzia [NUnit-Gui] w wersji 2.4.6, a następnie przeprowadzane są testy:

Powyżej przedstawiono pomyślnie zakończone testy.
Zadanie praktyczne:
zrealizować na komputerze testy klasy [PamDaoSpringNHibernate].- należy użyć różnych plików konfiguracyjnych [Dao.xml], aby korzystać z innych serwerów SGBD (Firebird, MySQL, Postgres, SQL)
13.2.4. Generacj a z warstwy [dao]
Po napisaniu i przetestowaniu klasy [PamDaoNHibernate] wygenerujemy klasę DLL z warstwy [dao] w następujący sposób:
![]() |
- [1], programy testowe są wykluczone z kompilacji projektu
- [2,3], konfiguracja projektu
- [4], generowanie projektu
- plik DLL jest generowany w folderze [bin/Release] [5]. Dodajemy go do plików DLL już znajdujących się w folderze [lib] [6]:
![]() |
13.3. Warstwa biznesowa
Wróćmy do ogólnej architektury aplikacji [SimuPaie]:
![]() |
Zakładamy teraz, że warstwa [dao] została już wdrożona i została enkapsulowana w warstwie DLL [pam-dao-spring-nhibernate.dll]. Teraz skupiamy się na warstwie [metier]. To właśnie ona implementuje reguły biznesowe, w tym przypadku reguły obliczania wynagrodzenia.
Projekt Visual Studio warstwy biznesowej mógłby wyglądać następująco:
![]() |
- w [1] cały projekt skonfigurowany przez pliki [App.config] i [Dao.xml]. Plik [App.config] jest identyczny jak w projekcie warstwy [dao] i [pam-dao-spring-nhibernate]. To samo dotyczy pliku [Dao.xml], z tą różnicą, że deklaruje on dodatkowy obiekt Spring o identyfikatorze pammetier. Deklaracja tego ostatniego jest identyczna z tą, która znajdowała się w pliku [App.config] projektu [pam-metier-dao-nhibernate].
- W pliku [2] folder [pam] jest identyczny z tym, jaki znajdował się w warstwie [metier] projektu [pam-metier-dao-nhibernate]
- w [3] odwołania wykorzystywane przez projekt. Należy zwrócić uwagę na DLL i [pam-dao-spring-nhibernate] z warstwy [dao], którą analizowaliśmy wcześniej.
Zadanie: skompilować powyższy projekt [pam-metier-dao-spring-nhibernate]. Zostanie on przetestowany oddzielnie:
-
w trybie konsoli przez program konsolowy [Main.cs]
-
za pomocą testu jednostkowego [NUnit.cs] uruchamianego przez framework NUnit
Nowy projekt [pam-metier-dao-spring-nhibernate] można skompilować poprzez proste skopiowanie projektu [pam-metier-dao-nhibernate], a następnie zmodyfikowanie elementów, które wymagają zmian.
Po zakończeniu testów wygenerujemy plik DLL z warstwy [metier], który nazwiemy [pam-metier-dao-spring-nhibernate]:
![]() |
- w plik [1], test NUnit zakończył się powodzeniem
- w [2], DLL wygenerowany przez projekt
Dodamy plik DLL z warstwy [metier] do plików DLL już znajdujących się w folderze [lib] [3]:
![]() |
13.4. Warstwa [web]
Wróćmy do ogólnej architektury aplikacji [SimuPaie]:
![]() |
Zakładamy, że warstwy [dao] i [métier] zostały już przygotowane i zawarte w warstwach DLL oraz [pam-dao-spring-nhibernate, pam-metier-dao-spring-nhibernate]. Teraz opiszemy warstwę internetową.
Projekt Visual Web Developer warstwy [web] uzyskuje się najpierw poprzez proste skopiowanie folderu projektu internetowego [pam-v7-3tier-nhibernate-multivues-multipages]. Następnie projekt zmienia się nazwę na [pam-v9-3tier-spring-nhibernate-multivues-multipages]:
![]() |
Nowy projekt internetowy [pam-v9-3tier-spring-nhibernate-multivues-multipages] różni się od projektu [pam-v7-3tier-nhibernate-multivues-multipages] w następujących aspektach:
- w projekcie [1] konfigurację zapewniają pliki [Dao.xml] i [Web.config]. Plik [Dao.xml] nie istniał w projekcie [pam-v7], a plik [Web.config] musi uwzględniać konfigurację Spring / NHibernate, podczas gdy w projekcie [pam-v7] konfigurowano jedynie plik NHibernate.
- W pliku [2] pliki DLL warstw [dao] i [metier] to te, które właśnie utworzyliśmy.
Plik [Dao.xml] jest tym, który wykorzystano do utworzenia warstwy [metier]. Plik [Web.config] to plik warstwy [pam-v7], do którego dodano konfigurację Spring / NHibernate, którą znaleziono w plikach [App.config] warstw [dao] i [metier]. Plik [Web.config] z pliku [pam-v9] ma następującą treść:
<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>
<!-- konfiguracja 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]
W wierszach 7–11 i 16–23 znajduje się konfiguracja Spring, którą mieliśmy w plikach [App.config] warstw [dao] i [metier] utworzonych wcześniej, z jedną różnicą: w plikach [App.config] wiersz 17 miał następującą treść:
<resource uri="Dao.xml" />
Z następującą konfiguracją:
![]() |
plik [Dao.xml] jest kopiowany do folderu [bin] w folderze projektu internetowego. Z następującą składnią
<resource uri="Dao.xml" />
plik [Dao.xml] będzie wyszukiwany w bieżącym katalogu procesu uruchamiającego aplikację internetową. Okazuje się, że katalog ten nie jest katalogiem [bin] w katalogu projektu internetowego, który jest uruchamiany. Należy wpisać:
<resource uri="~/Dao.xml" />
aby plik [Dao.xml] był wyszukiwany w folderze [bin] w katalogu uruchomionego projektu internetowego.
Zadanie: wdrożyć tę aplikację internetową na komputerze.
13.5. Conclusion
Przeszliśmy z architektury:
![]() |
w architekturze:
![]() |
Chodziło o zaimplementowanie warstwy [dao], korzystając z możliwości oferowanych przez integrację NHibernate z frameworkiem Spring.
Okazało się, że miało to wpływ na warstwę [dao].
- wpłynęło na warstwę [dao]. Warstwa ta była prostsza do napisania, ale wymagała bardziej złożonej konfiguracji Springa.
- miało niewielki wpływ na warstwy [metier] i [web]
Był to kolejny przykład korzyści płynących z architektur warstwowych.























