Skip to content

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] у разі використання

  1. лише фреймворку NHibernate
  2. фреймворку 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
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]
  • рядки 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, і виконуємо тести:

Image

Як бачимо вище, тести пройшли успішно.

Практичне завдання:


  • запустити на комп’ютері тести класу [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]

Це став ще одним прикладом переваг багатошарової архітектури.