13. Додаток [SimuPaie] – версія 9 – інтеграція Spring / NHibernate
Тут ми пропонуємо переробити трирівневий додаток ASP.NET на основі версії 7 [pam-v7-3tier-nhibernate-multivues-multipages]. Багаторівнева архітектура додатка була такою:
![]() |
Як показано вище, рівень [dao] було реалізовано за допомогою фреймворку NHibernate. Фреймворк Spring використовувався лише для інтеграції шарів між собою. Фреймворк Spring надає допоміжні класи для роботи з фреймворком Nhibernate. Використання цих класів спрощує написання коду шару [dao]. Попередня архітектура еволюціонує наступним чином:
![]() |
Через використовувану шарову структуру застосування інтеграції Spring / NHibernate призводить до зміни лише одного шару [dao]. Шари [presentation] (web / ASP.NET) та [metier] не потребуватимуть змін. У цьому полягає головна перевага багаторівневих архітектур, інтегрованих за допомогою Spring.
Далі ми побудуємо шар [dao] разом із [Spring / NHibernate], коментуючи код робочого рішення. Ми не будемо намагатися висвітлити всі можливості налаштування чи використання фреймворку [Spring / Nhibernate]. Читач зможе адаптувати запропоноване рішення до своїх власних завдань, скориставшись документацією до Spring.NET та [http://www.springframework.net/documentation.html] (червень 2010 року).
Підхід, який було застосовано для побудови шарів [dao] та [metier], відповідає версії 3, описаній у параграфі 7. Підхід, який було застосовано для шару [présentation], відповідає версії 7, описаній у параграфі 11.
13.1. Рівень [dao] для доступу до даних
![]() |
13.1.1. Проєкт Visual Studio C# для шару [dao]
Проєкт Visual Studio для шару [dao] має такий вигляд:
![]() |
- у [1] — проект у цілому
- папка [pam] містить класи проекту, а також конфігурацію сутностей NHibernate
- файли [App.config] та [Dao.xml] налаштовують фреймворк Spring / NHibernate. Нам доведеться описати вміст цих двох файлів.
- У файлі [2] містяться різні класи проекту
- у папці [entites] ми знаходимо сутності NHibernate, розглянуті в проєкті [pam-dao-nhibernate]
- у папці [service] ми знаходимо інтерфейс [IPamDao] та його реалізацію за допомогою фреймворку Spring / NHibernate [PamDaoSpringNHibernate]. Нам доведеться написати цю нову реалізацію інтерфейсу [IPamDao]
- папка [tests] містить ті самі тести, що й проект [pam-dao-nhibernate]. Вони тестують той самий інтерфейс [IPamdao].
- У [3] — посилання на проект. Інтеграція Spring / NHibernate вимагає двох нових DLL: [Spring.Data] та [Spring.Data.NHibernate12]. Ці DLL доступні у фреймворку Spring.Net. Вони були додані до папки [lib] з DLL та [4]:
![]() |
У посиланнях [3] цього проєкту містяться такі DLL:
- NHibernate: для ORM NHibernate
- MySql.Data: драйвер ADO.NET для SGBD MySQL
- Spring.Core: для фреймворку Spring, що забезпечує інтеграцію рівнів
- log4net: бібліотека журналів
- nunit.framework: бібліотека модульних тестів
- Spring.Data та Spring.Data.NHibernate12: забезпечують підтримку Spring / NHibernate.
Ці посилання були взяті з папки [lib] [4]. Слід переконатися, що для всіх цих посилань властивість «Локальна копія» має значення «True» [5]:
13.1.2. Конфігурація проекту C#
Проєкт налаштовано таким чином:
![]() |
- у [1] ім’я збірки проєкту — [pam-dao-spring-nhibernate]. Це ім’я використовується в різних конфігураційних файлах проєкту.
13.1.3. Елементи шару [dao]
![]() |
Елементи (об’єкти), необхідні для шару [dao], зібрано у папці [entites] [1] цього проєкту. Ці об’єкти є тими самими, що й у проєкті [pam-dao-nhibernate], за винятком однієї відмінності у файлах конфігурації NHibernate. Візьмемо, наприклад, файл [Employe.hbm.xml]:
- у [2] файл налаштований для включення до збірки проекту
Його вміст такий:
<?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>
- рядок 2: атрибут assembly вказує, що файл [Employe.hbm.xml] буде знайдено в збірці [pam-dao-spring-nhibernate]
13.1.4. Налаштування Spring / NHibernate
Повернемося до проекту Visual C#:
![]() |
- у [1] файли [App.config] та [Dao.xml] налаштовують інтеграцію Spring / NHibernate
13.1.4.1. Файл [App.config]
Файл [App.config] має такий вигляд:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<!-- розділи налаштувань -->
<configSections>
<sectionGroup name="spring">
<section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core" />
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
</sectionGroup>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
</configSections>
<!-- конфігурація Spring -->
<spring>
<parsers>
<parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
</parsers>
<context>
<resource uri="Dao.xml" />
</context>
</spring>
<!-- Цей розділ містить параметри конфігурації log4net -->
<!-- NOTE IMPORTANTE: журнали за замовчуванням неактивні. Їх потрібно активувати програмно
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>
<!-- Встановити рівень журналювання за замовчуванням на DEBUG -->
<root>
<level value="DEBUG" />
<appender-ref ref="ConsoleAppender" />
</root>
<!-- Налаштувати журналювання для Spring. Імена логерів у Spring відповідають простору імен -->
<logger name="Spring">
<level value="INFO" />
</logger>
<logger name="Spring.Data">
<level value="DEBUG" />
</logger>
<logger name="NHibernate">
<level value="DEBUG" />
</logger>
</log4net>
</configuration>
Вищезазначений файл [App.config] налаштовує Spring (рядки 5–9, 15–22), log4net (рядок 10, рядки 28–53), але не NHibernate. Об’єкти Spring налаштовуються не у файлі [App.config], а у файлі [Dao.xml] (рядок 20). Отже, конфігурація Spring / NHibernate, яка полягає у оголошенні конкретних об’єктів Spring, міститься саме в цьому файлі.
13.1.4.2. Файл [Dao.xml]
Файл [Dao.xml], що містить об’єкти, які керуються Spring, має такий вигляд:
<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net"
xmlns:db="http://www.springframework.net/database">
<!-- Посилання міститься у файлі конфігурації основного контексту додатка -->
<description>
Application Spring / NHibernate
</description>
<!-- База даних та 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>
<!-- менеджер транзакцій -->
<object id="transactionManager"
type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate12">
<property name="DbProvider" ref="DbProvider"/>
<property name="SessionFactory" ref="NHibernateSessionFactory"/>
</object>
<!-- Шаблон 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>
<!-- Об'єкти доступу до даних -->
<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>
- у рядках 11–13 налаштовується з’єднання з базою даних [dbpam_nhibernate]. У ньому міститься:
- провайдер ADO.NET, необхідний для підключення; у даному випадку це провайдер SGBD MySQL. Це означає, що у посиланнях проекту мають бути присутні DLL та [Mysql.Data].
- рядок підключення до бази даних (сервер, назва бази, власник підключення, його пароль)
- рядки 15–29 налаштовують SessionFactory з NHibernate — об’єкт, який використовується для отримання сеансів NHibernate. Нагадаємо, що будь-яка операція з базою даних виконується в межах сесії NHibernate. У рядку 15 видно, що SessionFactory реалізовано класом Spring Spring.Data.NHibernate.LocalSessionFactoryObject, який знаходиться в DLL Spring.Data.NHibernate12.
- рядок 16: властивість DbProvider встановлює параметри підключення до бази даних (провайдер ADO.NET та рядок підключення). Тут ця властивість посилається на об’єкт DbProvider, визначений раніше у рядках 11–13.
- рядки 17–20: визначають список збірників, що містять файли [*.hbm.xml], які налаштовують об’єкти, що керуються NHibernate. Рядок 19 вказує, що ці файли будуть розміщені у збірнику проекту. Нагадуємо, що ця назва міститься у властивостях проекту C#. Також нагадуємо, що всі файли [*.hbm.xml] було налаштовано для включення до збірки проекту.
- рядки 22–27: специфічні властивості NHibernate.
- рядок 24: діалект SQL, що використовується, буде таким самим, як у MySQL
- рядок 25: SQL, згенерований NHibernate, не з’являтиметься у журналах консолі. Встановлення цього параметра на true дозволяє дізнатися про команди SQL, відправлені NHibernate. Це може допомогти, наприклад, зрозуміти, чому додаток працює повільно під час доступу до бази даних.
- рядок 28: властивість ExposeTransactionAwareSessionFactory, встановлена на true, призведе до того, що Spring буде керувати анотаціями управління транзакціями, які будуть знайдені в коді C#. Ми повернемося до цього, коли будемо писати клас, що реалізує шар [dao].
- Рядки 32–36 визначають менеджер транзакцій. І тут цей менеджер є класом Spring із DLL Spring.Data.NHibernate12. Цьому менеджеру потрібно знати параметри підключення до бази даних (рядок 34), а також SessionFactory з NHibernate (рядок 35).
- Рядки 39–43 визначають властивості класу HibernateTemplate, який також є класом Spring. Цей клас буде використовуватися як допоміжний клас у класі, що реалізує рівень [dao]. Він полегшує взаємодію з об’єктами NHibernate. Цей клас має певні властивості, які потрібно ініціалізувати:
- рядок 40: SessionFactory від NHibernate
- рядок 41: властивість TemplateFlushMode визначає режим синхронізації контексту збереження NHibernate з базою даних. Режим Auto передбачає, що синхронізація відбуватиметься:
- наприкінці транзакції
- перед операцією select
- рядок 42: запити HQL (Hibernate Query Language) будуть кешовані. Це може призвести до підвищення продуктивності.
- рядки 46–48 визначають клас реалізації шару [dao]
- рядок 46: шар [dao] буде реалізовано класом [PamdaoSpringNHibernate] з ланцюжка DLL [pam-dao-spring-nhibernate]. Після створення екземпляра класу негайно буде виконано метод init цього класу. Під час закриття контейнера Spring буде виконано метод destroy цього класу.
- рядок 47: клас [PamDaoSpringNHibernate] матиме властивість HibernateTemplate, яка буде ініціалізована за допомогою властивості HibernateTemplate з рядка 39.
13.1.5. Реалізація шару [dao]
13.1.5.1. Скелет класу реалізації
Інтерфейс [IPamDao] такий самий, як і в проєкті [pam-dao-nhibernate]:
using Pam.Dao.Entites;
namespace Pam.Dao.Service {
public interface IPamDao {
// Список усіх ідентифікаторів співробітників
Employe[] GetAllIdentitesEmployes();
// конкретний працівник із його виплатами
Employe GetEmploye(string ss);
// перелік усіх внесків
Cotisations GetCotisations();
}
}
- рядок 1: імпортується простір імен сутностей шару [dao].
- рядок 3: шар [dao] знаходиться в просторі імен [Pam.Dao.Service]. Елементи простору імен [Pam.Dao.Entites] можуть створюватися у декількох екземплярах. Елементи простору імен [Pam.Dao.Service] створюються в єдиному екземплярі (синглтон). Саме це зумовило вибір назв просторів імен.
- рядок 4: інтерфейс має назву [IPamDao]. Він визначає три методи:
- рядок 6: [GetAllIdentitesEmployes] повертає масив об’єктів типу [Employe], який представляє список нянь у спрощеній формі (прізвище, ім’я, SS).
- у рядку 8 метод [GetEmploye] повертає об’єкт [Employe]: працівника, номер соціального страхування якого передано як параметр методу, разом із виплатами, пов’язаними з його індексом.
- у рядку 10 [GetCotisations] повертає об’єкт [Cotisations], який інкапсулює ставки різних соціальних внесків, що стягуються з валової заробітної плати.
Скелет класу реалізації цього інтерфейсу з підтримкою Spring / NHibernate може виглядати наступним чином:
using System;
using System.Collections;
using System.Collections.Generic;
using Pam.Dao.Entites;
using Spring.Data.NHibernate.Generic.Support;
using Spring.Transaction.Interceptor;
namespace Pam.Dao.Service {
public class PamDaoSpringNHibernate : HibernateDaoSupport, IPamDao {
// приватні поля
private Cotisations cotisations;
private Employe[] employes;
// ініціалізація
[Transaction(ReadOnly = true)]
public void init() {
...
}
// видалення об’єкта
public void destroy() {
if (HibernateTemplate.SessionFactory != null) {
HibernateTemplate.SessionFactory.Close();
}
}
// перелік усіх ідентифікаційних даних працівників
public Employe[] GetAllIdentitesEmployes() {
return employes;
}
// конкретний працівник із його виплатами
[Transaction(ReadOnly = true)]
public Employe GetEmploye(string ss) {
....
}
// список внесків
public Cotisations GetCotisations() {
return cotisations;
}
}
}
- рядок 9: клас [PamDaoSpringNHibernate] правильно реалізує інтерфейс рівня [dao] [IPamDao]. Він також походить від класу Spring [HibernateDaoSupport]. Цей клас має властивість [HibernateTemplate], яка ініціалізується за допомогою виконаної конфігурації Spring (рядок 2 нижче):
<object id="pamdao" type="Pam.Dao.Service.PamDaoSpringNHibernate, pam-dao-spring-nhibernate" init-method="init" destroy-method="destroy">
<property name="HibernateTemplate" ref="HibernateTemplate"/>
</object>
- У рядку 1 вище видно, що визначення об’єкта [pamdao] вказує на те, що методи init та destroy класу [PamDaoSpringNHibernate] мають виконуватися у певні моменти часу. Ці два методи дійсно присутні в класі у рядках 16 та 21.
- рядки 15, 34: анотації, які забезпечують виконання анотованого методу в рамках транзакції. Атрибут ReadOnly=true вказує, що транзакція є «тільки для читання». Метод, що виконується в транзакції, може викликати виняток. У цьому випадку Spring автоматично Rollback транзакцію. Ця анотація усуває необхідність керувати транзакцією всередині методу.
- рядок 16: метод init виконується Spring одразу після інстанціювання класу. Ми побачимо, що його метою є ініціалізація приватних полів у рядках 11 і 12. Він виконуватиметься в рамках транзакції (рядок 15).
- Методи інтерфейсу [IPamDao] реалізовано в рядках 28, 35 та 40.
- рядки 28–30: метод [GetAllIdentitesEmployes] лише повертає атрибут з рядка 12, ініціалізований методом init.
- рядки 40–42: метод [GetCotisations] лише повертає атрибут з рядка 11, ініціалізований методом init.
13.1.5.2. Корисні методи класу HibernateTemplate
Ми будемо використовувати такі методи класу HibernateTemplate:
IList<T> Find<T>(string requete_hql) | виконує запит HQL і повертає список об’єктів типу T |
IList<T> Find<T>(string requete_hql, object[]) | виконує запит HQL, параметри якого задаються символами «?». Значення цих параметрів надаються масивом об’єктів. |
IList<T> LoadAll<T>() | повертає всі сутності типу T |
Існують й інші корисні методи, якими ми не матимемо нагоди скористатися, що дозволяють знаходити, зберігати, оновлювати та видаляти об’єкти:
T Load<T>(object id) | додає до сесії NHibernate об’єкт типу T з первинним ключем id. |
void SaveOrUpdate(object об’єкт) | вставляє (INSERT) або оновлює (UPDATE) об’єкт entité залежно від того, чи має він первинний ключ (UPDATE), чи ні (INSERT). Відсутність первинного ключа можна налаштувати за допомогою атрибута unsaved-values у файлі конфігурації сутності. Після операції SaveOrUpdate об’єкт entité перебуває в сесії NHibernate. |
void Delete(object об'єкт) | видаляє об'єкт entité із сесії NHibernate. |
13.1.5.3. Реалізація методу init
Метод init класу [PamDaoSpringNHibernate] за налаштуванням є методом, що виконується після створення екземпляра класу Spring. Його метою є збереження в локальному кеші спрощених ідентифікаторів співробітників (прізвище, ім’я, SS) та ставок внесків. Його код може виглядати наступним чином.
[Transaction(ReadOnly = true)]
public void init() {
try {
// отримання спрощеного списку співробітників
IList<object[]> lignes = HibernateTemplate.Find<object[]>("select e.SS,e.Nom,e.Prenom from Employe e");
// вносимо його в таблицю
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++;
}
// вносимо ставки внесків до об’єкта
cotisations = (HibernateTemplate.LoadAll<Cotisations>())[0];
} catch (Exception ex) {
// перетворення винятку
throw new PamException(string.Format("Erreur d'accès à la BD : [{0}]", ex.ToString()), 43);
}
}
- рядок 5: виконується запит HQL. Він запитує поля SS, Прізвище, Ім’я для всіх сутностей Employé. Він повертає список об’єктів. Якби ми запитали повну інформацію про співробітника у формі «select e from Employe e», то отримали б список об’єктів типу Employe.
- рядки 7–12: цей список об’єктів копіюється в масив об’єктів типу Employe.
- рядок 14: запитується список усіх сутностей типу Cotisations. Відомо, що цей список містить лише один елемент. Тому отримується перший елемент списку, щоб отримати ставки внесків.
- Рядки 7 і 14 ініціалізують два приватні поля класу.
13.1.5.4. Реалізація методу GetEmploye
Метод GetEmploye повинен повернути об’єкт «Співробітник» із заданим номером SS. Його код може виглядати так:
[Transaction(ReadOnly = true)]
public Employe GetEmploye(string ss) {
IList<Employe> employés = null;
try {
// запит
employés = HibernateTemplate.Find<Employe>("select e from Employe e where e.SS=?", new object[]{ss});
} catch (Exception ex) {
// перетворюємо виняток
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);
}
// чи було прийнято на роботу співробітника?
if (employés.Count == 0) {
// повідомляємо про це
throw new PamException(string.Format("L'employé de n° ss [{0}] n'existe pas", ss), 42);
} else {
return employés[0];
}
}
- рядок 6: отримує список співробітників із заданим номером SS
- рядок 12: зазвичай, якщо співробітник існує, має бути отримано список з одним елементом
- рядок 14: якщо це не так, генерується виняток
- рядок 16: якщо це так, повертається перший співробітник зі списку
13.1.5.5. Conclusion
Якщо порівняти код шару [dao] у разі використання
- лише фреймворку NHibernate
- фреймворку Spring / NHibernate
, то можна побачити, що другий варіант дозволив написати простіший код.
13.2. Тестування шару [dao]
13.2.1. Проєкт Visual Studio
Проект Visual Studio вже було представлено. Нагадаємо:
![]() |
- у [1] — проект у цілому
- у [2] — різні класи проекту. Папка [tests] містить консольний тест [Main.cs] та модульний тест [NUnit.cs].
- у файлі [3] компілюється програма [Main.cs].
![]() |
- у [4] файл [NUnit.cs] не генерується.
- Проект є консольним додатком. Виконується клас, вказаний у [5], — це клас файлу [Main.cs].
13.2.2. Тестова консольна програма [Main.cs]
Тестова програма [Main.cs] виконується в такій архітектурі:
![]() |
Вона призначена для тестування методів інтерфейсу [IPamDao]. Ось приклад базового коду:
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 {
// інстанціювання шару [dao]
IPamDao pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
// список ідентифікаційних даних співробітників
foreach (Employe Employe in pamDao.GetAllIdentitesEmployes()) {
Console.WriteLine(Employe.ToString());
}
// працівник із його виплатами
Console.WriteLine("------------------------------------");
Console.WriteLine(pamDao.GetEmploye("254104940426058"));
Console.WriteLine("------------------------------------");
// перелік внесків
Cotisations cotisations = pamDao.GetCotisations();
Console.WriteLine(cotisations.ToString());
} catch (Exception ex) {
// відображення винятку
Console.WriteLine(ex.ToString());
}
//пауза
Console.ReadLine();
}
}
}
- рядок 11: до Spring надсилається запит на отримання посилання на рівень [dao].
- рядки 13–15: тестування методу [GetAllIdentitesEmployes] інтерфейсу [IPamDao]
- рядок 18: тестування методу [GetEmploye] інтерфейсу [IPamDao]
- рядок 21: тест методу [GetCotisations] інтерфейсу [IPamDao]
Spring, NHibernate та log4net налаштовуються за допомогою файлу [App.config] , розглянутого в розділі 13.1.4.1.
Виконання з використанням бази даних, описаної в розділі 6.2, дає такий результат у консолі:
- рядки 1–2: 2 співробітники типу [Employe] з єдиною інформацією [SS, Nom, Prenom]
- рядок 4: працівник типу [Employe] із номером соціального страхування [254104940426058]
- рядок 5: ставки внесків
13.2.3. Одиничні тести з NUnit
Тепер перейдемо до модульного тесту NUnit. Проєкт Visual Studio для шару [dao] зміниться наступним чином:
![]() |
- до [1], тестова програма [NUnit.cs]
- у [2,3], проект згенерує файл DLL з назвою [pam-dao-spring-nhibernate.dll]
- у [4], посилання на DLL фреймворку NUnit: [nunit.framework.dll]
- в [5], клас [Main.cs] не буде включено до DLL [pam-dao-spring-nhibernate]
- у [6] клас [NUnit.cs] буде включено до DLL [pam-dao-spring-nhibernate]
Тестовий клас NUnit має такий вигляд:
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 {
// шар [dao] для тестування
private IPamDao pamDao = null;
// конструктор
public NunitPamDao() {
// інстанціювання шару [dao]
pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
}
// ініціалізація
[SetUp]
public void Init() {
}
[Test]
public void GetAllIdentitesEmployes() {
// перевірка кількості працівників
Expect(2, EqualTo(pamDao.GetAllIdentitesEmployes().Length));
}
[Test]
public void GetCotisations() {
// перевірка ставки внесків
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() {
// перевірка осіб
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() {
// перевірка неіснуючої особи
bool erreur = false;
try {
Employe employe1 = pamDao.GetEmploye("xx");
} catch {
erreur = true;
}
Expect(erreur, True);
}
}
}
Цей клас уже розглядався в розділі 7.3.4.
Під час генерації проєкту у папці [bin/Release] створюються файли DLL та [pam-dao-spring-nhibernate.dll].
![]() |
Завантажуємо DLL та [pam-dao-spring-nhibernate.dll] за допомогою інструменту [NUnit-Gui], версія 2.4.6, і виконуємо тести:

Як бачимо вище, тести пройшли успішно.
Практичне завдання:
запустити на комп’ютері тести класу [PamDaoSpringNHibernate].- використовувати різні файли конфігурації [Dao.xml] для роботи з іншими серверами SGBD (Firebird, MySQL, Postgres, SQL)
13.2.4. Генеруванн у з DLL на основі шару [dao]
Після написання та тестування класу [PamDaoNHibernate] буде згенеровано DLL з шару [dao] наступним чином:
![]() |
- [1], тестові програми виключаються зі збірки проєкту
- [2,3], конфігурація проєкту
- [4], генерація проекту
- DLL генерується у папці [bin/Release] [5]. Ми додаємо його до файлів DLL, які вже знаходяться у папці [lib] [6]:
![]() |
13.3. Бізнес-шар
Повернемося до загальної архітектури додатка [SimuPaie]:
![]() |
Тепер ми вважаємо, що рівень [dao] вже реалізовано і що він був інкапсульований у DLL [pam-dao-spring-nhibernate.dll]. Тепер ми переходимо до шару [metier]. Саме він реалізує бізнес-правила, в даному випадку — правила розрахунку заробітної плати.
Проєкт Visual Studio для бізнес-шару може виглядати наступним чином:
![]() |
- у [1] — весь проект, налаштований файлами [App.config] та [Dao.xml]. Файл [App.config] ідентичний тому, що був у проекті бізнес-шару [dao] [pam-dao-spring-nhibernate]. Те саме стосується файлу [Dao.xml], за винятком того, що він оголошує додатковий об’єкт Spring з ідентифікатором pammetier. Оголошення останнього є ідентичним тому, що було у файлі [App.config] проекту [pam-metier-dao-nhibernate].
- У файлі [2] папка [pam] є ідентичною до тієї, що була у шарі [metier] проекту [pam-metier-dao-nhibernate]
- у [3] — посилання, що використовуються проектом. Зверніть увагу на DLL та [pam-dao-spring-nhibernate] з шару [dao], який розглядався раніше.
Завдання: зібрати наведений вище проект [pam-metier-dao-spring-nhibernate]. Його буде протестовано окремо:
-
у консольному режимі за допомогою консольної програми [Main.cs]
-
за допомогою модульного тесту [NUnit.cs], що виконується фреймворком NUnit
Новий проєкт [pam-metier-dao-spring-nhibernate] можна створити, просто скопіювавши проєкт [pam-metier-dao-nhibernate], а потім змінивши елементи, які потрібно змінити.
Після тестування буде згенеровано DLL з шару [metier], який отримає назву [pam-metier-dao-spring-nhibernate]:
![]() |
- у [1], тест NUnit пройшов успішно
- в [2], DLL, згенерований проектом
Додамо DLL з шару [metier] до DLL, що вже є у папці [lib] [3]:
![]() |
13.4. Шар [web]
Повернемося до загальної архітектури додатка [SimuPaie]:
![]() |
Ми вважаємо, що шари [dao] та [métier] вже створені та інкапсульовані в DLL та [pam-dao-spring-nhibernate, pam-metier-dao-spring-nhibernate]. Тепер опишемо веб-шар.
Проєкт Visual Web Developer для шару [web] спочатку отримують шляхом простого копіювання папки веб-проєкту [pam-v7-3tier-nhibernate-multivues-multipages]. Потім проєкт перейменовують на [pam-v9-3tier-spring-nhibernate-multivues-multipages]:
![]() |
Новий веб-проект [pam-v9-3tier-spring-nhibernate-multivues-multipages] відрізняється від проекту [pam-v7-3tier-nhibernate-multivues-multipages] за такими ознаками:
- у [1] він налаштований файлами [Dao.xml] та [Web.config]. [Dao.xml] не існував у [pam-v7], а файл [Web.config] повинен містити конфігурацію Spring / NHibernate, тоді як у [pam-v7] конфігурувався лише NHibernate.
- У [2] файли DLL для шарів [dao] та [metier] — це ті, що ми щойно створили.
Файл [Dao.xml] — це той, що використовувався для побудови шару [metier]. Файл [Web.config] — це файл шару [pam-v7], до якого додано конфігурацію Spring / NHibernate, яка містилася у файлах [App.config] шарів [dao] та [metier]. Файл [Web.config] з [pam-v9] має такий вигляд:
<configuration>
<configSections>
<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
........
</sectionGroup>
<sectionGroup name="spring">
<section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core" />
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
</sectionGroup>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
</configSections>
<!-- конфігурація Spring -->
<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]
У рядках 7–11 та 16–23 міститься конфігурація Spring, яка була у файлах [App.config] шарів [dao] та [metier], створених раніше, з однією відмінністю: у файлах [App.config] рядок 17 був записаний так:
<resource uri="Dao.xml" />
З такою конфігурацією:
![]() |
файл [Dao.xml] копіюється в папку [bin] у папці веб-проєкту. З синтаксисом
<resource uri="Dao.xml" />
файл [Dao.xml] буде шукатися в поточній папці процесу, що виконує веб-додаток. Виявляється, що ця папка не є папкою [bin] у папці веб-проекту, що виконується. Потрібно написати:
<resource uri="~/Dao.xml" />
щоб файл [Dao.xml] шукався у папці [bin] у папці виконуваного веб-проекту.
Завдання: розгорнути цей веб-додаток на комп’ютері.
13.5. Conclusion
Ми перейшли від архітектури:
![]() |
до архітектури:
![]() |
Завдання полягало в реалізації шару [dao], використовуючи можливості, що надаються інтеграцією NHibernate у Spring.
Ми побачили, що це:
- вплинуло на рівень [dao]. Його було простіше написати, але він вимагав більш складної конфігурації Spring.
- незначно вплинуло на шари [metier] та [web]
Це став ще одним прикладом переваг багатошарової архітектури.























