2. Integracja Spring / NHibernate
Framework Spring oferuje klasy pomocnicze do pracy z frameworkiem NHibernate. Wykorzystanie tych klas ułatwia pisanie kodu służącego do uzyskiwania dostępu do danych z SGBD. Rozważmy następującą architekturę wielowarstwową:
![]() |
W dalszej części stworzymy warstwę [dao] z wykorzystaniem [Spring / NHibernate], omawiając kod działającego rozwiązania. Nie będziemy starać się przedstawić wszystkich możliwości konfiguracji ani zastosowań frameworka [Spring / Nhibernate]. Czytelnik będzie mógł dostosować proponowane rozwiązanie do własnych potrzeb, korzystając z dokumentacji Spring.NET [Spring.NET | Homepage ] (grudzień 2011 r.).
2.1. Warstwa dostępu do danych [dao]
![]() |
Bazą danych jest baza MySQL [dbpam_nhibernate], przedstawiona już w punkcie 1.2. Warstwa [dao] implementuje następujący interfejs w języku C#:
using Pam.Dao.Entites;
namespace Pam.Dao.Service {
public interface IPamDao {
// lista wszystkich numerów identyfikacyjnych pracowników
Employe[] GetAllIdentitesEmployes();
// dany pracownik wraz z przysługującymi mu świadczeniami
Employe GetEmploye(string ss);
// lista wszystkich składek
Cotisations GetCotisations();
}
}
2.1.1. Projekt Visual Studio w języku C# dla 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 omówione w poprzednim projekcie (patrz strona 14)
- w folderze [service] znajdujemy interfejs [IPamDao] oraz jego implementację przy użyciu frameworka Spring / NHibernate [PamDaoSpringNHibernate].
- Folder [tests] zawiera testy interfejsu [IPamDao].
- W pliku [3] znajdują się referencje projektu. Integracja Spring / NHibernate wymaga nowych plików DLL i [4].
W odwołaniach projektu [3] znajdują się następujące elementy DLL:
- NHibernate: dla ORM i NHibernate
- MySql.Data: sterownik ADO.NET dla SGBD MySQL 5
- Spring.Core: dla frameworka Spring, który zapewnia integrację warstw
- log4net: biblioteka logów
- nunit.framework: biblioteka testów jednostkowych
- Spring.Aop, Spring.Data i Spring.Data.NHibernate32: zapewniają obsługę Spring / NHibernate.
Należy zadbać o to, aby wszystkie pliki DLL miały właściwość „Kopia lokalna” ustawioną na True.
2.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.
2.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 poprzednim projekcie (patrz punkt 1.3.2), z tą różnicą, że znajdują się one w plikach konfiguracyjnych NHibernate. Weźmy na przykład plik [Employe.hbm.xml]:
- w pliku [2] plik ten jest 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 3: atrybut assembly wskazuje, że plik [Employe.hbm.xml] będzie znajdował się w zestawie [pam-dao-spring-nhibernate]
Ponadto w folderze [entites] znajduje się klasa wyjątków wykorzystywana przez projekt:
using System;
namespace Pam.Dao.Entites {
public class PamException : Exception {
// kod błędu
public int Code { get; set; }
// producenci
public PamException() {
}
public PamException(int Code)
: base() {
this.Code = Code;
}
public PamException(string message, int Code)
: base(message) {
this.Code = Code;
}
public PamException(string message, Exception ex, int Code)
: base(message, ex) {
this.Code = Code;
}
}
}
Klasa [PamException] została wywodzona z klasy [Exception] (wiersz 4) w celu dodania do niej kodu błędu (wiersz 7).
2.1.4. Konfiguracja Spring / NHibernate
Wróćmy do projektu Visual C#:
![]() |
- W pliku [1] pliki [App.config] i [Dao.xml] konfigurują integrację Spring / NHibernate
2.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.
2.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.NHibernate32">
<property name="DbProvider" ref="DbProvider"/>
<property name="MappingAssemblies">
<list>
<value>pam-dao-spring-nhibernate</value>
</list>
</property>
<property name="HibernateProperties">
<dictionary>
<entry key="dialect" value="NHibernate.Dialect.MySQL5Dialect"/>
<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.NHibernate32">
<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ę dostawcy 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.NHibernate32.
- 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 projektu.
- wiersze 22–27: właściwości specyficzne dla NHibernate.
- wiersz 24: używany dialekt SQL będzie pochodził z MySQL
- wiersz 25: plik SQL wygenerowany przez NHibernate nie pojawi się w logach konsoli. Ustawienie tej właściwości na true pozwala poznać polecenia SQL wysł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 o nazwie DLL Spring.Data.NHibernate32. 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 klasy 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.
2.1.5. Implementacja warstwy [dao]
2.1.5.1. Szkielet klasy implementacyjnej
Interfejs [IPamDao] ma następujący wygląd:
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 encji 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:
- w wierszu 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 z wykorzystaniem 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.
2.1.5.2. Przydatne metody klasy HibernateTemplate
Będziemy korzystać z następujących metod klasy HibernateTemplate:
wykonuje zapytanie HQL i zwraca listę obiektów typu T | |
wykonuje zapytanie HQL z parametrami oznaczonymi jako ?. Wartości tych parametrów są dostarczane przez tablicę obiektów. | |
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:
umieszcza w sesji NHibernate encję typu T o kluczu głównym id. | |
wstawia (INSERT) lub aktualizuje (UPDATE) obiekt entité w zależności od tego, czy posiada on klucz główny (UPDATE), czy nie (INSERT). Brak klucza głównego można skonfigurować za pomocą atrybutu unsaved-values w pliku konfiguracyjnym jednostki. Po operacji SaveOrUpdate obiekt entité znajduje się w sesji NHibernate. | |
usuwa obiekt entité z sesji NHibernate. |
2.1.5.3. Implementacja metody init
Metoda init klasy [PamDaoSpringNHibernate] jest z założenia 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. Żąda ono pól SS, Nazwisko, Imię 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.
2.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ę pobrać dane 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łaszany jest wyjątek
- wiersz 16: jeśli tak jest, zwracamy pierwszego pracownika z listy
2.2. Testy warstwy [dao]
2.2.1. Projekt 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] kompilowany jest 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].
2.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 2.1.4.1.
Uruchomienie programu z wykorzystaniem bazy danych opisanej w punkcie 1.2 daje następujący wynik na konsoli:
- wiersze 1–2: dwaj pracownicy typu [Employe], zawierające wyłącznie informacje z pliku [SS, Nom, Prenom]
- wiersz 4: pracownik typu [Employe] o numerze ubezpieczenia społecznego [254104940426058]
- wiersz 5: stawki składek
2.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 z frameworka NUnit: [nunit.framework.dll]
- w [5], klasa [Main.cs] nie zostanie uwzględniona w DLL [pam-dao-spring-nhibernate]
- do [6], klasa [NUnit.cs] zostanie uwzględniona w klasie 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() {
// instancja 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);
}
}
}
- wiersz 11: klasa posiada atrybut [TestFixture], co czyni ją klasą testową [NUnit].
- wiersz 12: klasa ta wywodzi się z klasy pomocniczej AssertionHelper z frameworka NUnit (od wersji 2.4.6).
- wiersz 14: pole prywatne [pamDao] jest instancją interfejsu dostępu do warstwy [dao]. Należy zauważyć, że typem tego pola jest interfejs, a nie klasa. Oznacza to, że instancja [pamDao] udostępnia wyłącznie metody należące do interfejsu [IPamDao].
- Metody testowane w tej klasie to te, które posiadają atrybut [Test]. W przypadku wszystkich tych metod proces testowania przebiega następująco:
- najpierw wykonywana jest metoda posiadająca atrybut [SetUp]. Służy ona do przygotowania zasobów (połączeń sieciowych, połączeń z bazami danych itp.) niezbędnych do przeprowadzenia testu.
- Następnie uruchamiana jest metoda, która ma zostać przetestowana
- i na koniec wykonywana jest metoda o atrybucie [TearDown]. Służy ona zazwyczaj do zwolnienia zasobów wykorzystanych przez metodę o atrybucie [SetUp].
- W naszym teście nie ma zasobów, które należałoby przydzielać przed każdym testem, a następnie zwalniać. Dlatego nie potrzebujemy metod z atrybutami [SetUp] i [TearDown]. W ramach przykładu w wierszach 23–26 przedstawiliśmy metodę z atrybutem [SetUp].
- wiersze 17–20: konstruktor klasy inicjuje pole prywatne [pamDao] przy użyciu Springa i [App.config].
- wiersze 29–32: testują metodę [GetAllIdentitesEmployes]
- wiersze 35–42: testują metodę [GetCotisations]
- wiersze 45–53: testują metodę [GetEmploye]
- wiersze 56–65: testują metodę [GetEmploye] w przypadku wystąpienia wyjątku.
Podczas generowania projektu tworzone są pliki DLL i [pam-dao-spring-nhibernate.dll] w folderze [bin/Release]:
![]() |
Wczytujemy pliki DLL i [pam-dao-spring-nhibernate.dll] za pomocą narzędzia [NUnit-Gui] w wersji 2.5 i przeprowadzamy testy:

Powyższe testy zakończyły się powodzeniem.
2.2.4. Generacj z DLL 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].











