Skip to content

13. Die [SimuPaie]-Anwendung – Version 9 – Spring/NHibernate-Integration

Hier schlagen wir vor, die dreischichtige ASP.NET-Anwendung aus Version 7 [pam-v7-3tier-nhibernate-multivues-multipages] erneut zu betrachten. Die Schichtenarchitektur der Anwendung sah wie folgt aus:

In der oben genannten Version wurde die [DAO]-Schicht unter Verwendung des NHibernate-Frameworks implementiert. Das Spring-Framework wurde lediglich zur Integration der Schichten untereinander verwendet. Das Spring-Framework stellt Hilfsklassen für die Arbeit mit dem NHibernate-Framework bereit. Die Verwendung dieser Klassen erleichtert das Schreiben des Codes in der [DAO]-Schicht. Die bisherige Architektur entwickelt sich wie folgt weiter:

Aufgrund der verwendeten Schichtstruktur erfordert die Implementierung der Spring/NHibernate-Integration lediglich eine Änderung der [DAO]-Schicht. Die [Presentation]- (Web/ASP.NET) und [Business]-Schichten müssen nicht geändert werden. Dies ist der Hauptvorteil von mit Spring integrierten Schichtenarchitekturen.

In den folgenden Abschnitten werden wir die [DAO]-Schicht mit [Spring/NHibernate] aufbauen, indem wir den Code einer funktionierenden Lösung erläutern. Wir werden nicht versuchen, alle Konfigurationsoptionen oder Anwendungsmöglichkeiten des [Spring/NHibernate]-Frameworks abzudecken. Leser können die vorgeschlagene Lösung mithilfe der Spring.NET-Dokumentation [http://www.springframework.net/documentation.html] (Juni 2010) an ihre eigenen Bedürfnisse anpassen.

Der Ansatz zum Aufbau der [DAO]- und [Business]-Schichten entspricht der in Absatz 7 beschriebenen Version 3. Der Ansatz für die [Präsentationsschicht] entspricht der in Absatz 11 beschriebenen Version 7.

13.1. Die [DAO]-Datenzugriffsschicht

13.1.1. Das Visual Studio C#-Projekt für die [dao]-Schicht

Das Visual Studio-Projekt für die [dao]-Schicht sieht wie folgt aus:

  • in [1], das Projekt als Ganzes
    • Der Ordner [pam] enthält die Projektklassen sowie die Konfiguration der NHibernate-Entitäten
    • Die Dateien [App.config] und [Dao.xml] konfigurieren das Spring/NHibernate-Framework. Wir müssen den Inhalt dieser beiden Dateien beschreiben.
  • in [2], die verschiedenen Klassen des Projekts
    • Im Ordner [entities] finden wir die NHibernate-Entitäten, die im Projekt [pam-dao-nhibernate] behandelt wurden
    • Im Ordner [service] finden wir die Schnittstelle [IPamDao] und ihre Implementierung unter Verwendung des Spring/NHibernate-Frameworks [PamDaoSpringNHibernate]. Wir müssen diese neue Implementierung der Schnittstelle [IPamDao] schreiben
    • Der Ordner [tests] enthält dieselben Tests wie das Projekt [pam-dao-nhibernate]. Sie testen dieselbe [IPamDao]-Schnittstelle.
  • In [3] sind die Projektverweise aufgeführt. Die Spring/NHibernate-Integration erfordert zwei neue DLLs: [Spring.Data] und [Spring.Data.NHibernate12]. Diese DLLs sind im Spring.Net-Framework verfügbar. Sie wurden dem Ordner [lib] der DLLs [4] hinzugefügt:

In den Projektreferenzen [3] finden Sie die folgenden DLLs:

  • NHibernate: für das NHibernate-ORM
  • MySql.Data: der ADO.NET-Treiber für das MySQL-DBMS
  • Spring.Core: für das Spring-Framework, das die Integration der Schichten übernimmt
  • log4net: eine Logging-Bibliothek
  • nunit.framework: eine Bibliothek für Unit-Tests
  • Spring.Data und Spring.Data.NHibernate12: bieten Spring/NHibernate-Unterstützung.

Diese Referenzen wurden aus dem Ordner [lib] übernommen [4]. Stellen Sie sicher, dass die Eigenschaft „Local Copy“ für alle diese Referenzen auf „True“ gesetzt ist [5]:

13.1.2. Konfigurieren des C#-Projekts

Das Projekt ist wie folgt konfiguriert:

  • In [1] lautet der Name der Projekt-Assembly [pam-dao-spring-nhibernate]. Dieser Name taucht in verschiedenen Projektkonfigurationsdateien auf.

13.1.3. Entitäten in der [dao]-Schicht

Die für die [dao]-Schicht erforderlichen Entitäten (Objekte) wurden im Ordner [entities] [1] des Projekts zusammengestellt. Diese Entitäten entsprechen denen im Projekt [pam-dao-nhibernate], mit einer Ausnahme in den NHibernate-Konfigurationsdateien. Nehmen wir zum Beispiel die Datei [Employee.hbm.xml]:

  • In [2] ist die Datei so konfiguriert, dass sie in die Projekt-Assembly aufgenommen wird

Ihr Inhalt lautet wie folgt:


<?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>
  • Zeile 2: Das Attribut „assembly“ gibt an, dass die Datei [Employee.hbm.xml] in der Assembly [pam-dao-spring-nhibernate] zu finden ist

13.1.4. Spring-/NHibernate-Konfiguration

Kehren wir zum Visual C#-Projekt zurück:

  • In [1] konfigurieren die Dateien [App.config] und [Dao.xml] die Spring-/NHibernate-Integration

13.1.4.1. Die Datei [ App.config]

Die Datei [App.config] sieht wie folgt aus:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!-- configuration sections -->
    <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 configuration -->
    <spring>
        <parsers>
            <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
        </parsers>
        <context>
            <resource uri="Dao.xml" />
        </context>
    </spring>
 
    <!-- This section contains the log4net configuration settings -->
    <!-- NOTE IMPORTANTE: logs are not active by default. They must be activated by program
    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>
 
        <!-- Set default logging level to DEBUG -->
        <root>
            <level value="DEBUG" />
            <appender-ref ref="ConsoleAppender" />
        </root>
 
        <!-- Set logging for Spring.  Logger names in Spring correspond to the namespace -->
        <logger name="Spring">
            <level value="INFO" />
        </logger>
 
        <logger name="Spring.Data">
            <level value="DEBUG" />
        </logger>
 
        <logger name="NHibernate">
            <level value="DEBUG" />
        </logger>
    </log4net>
 
</configuration>

Die obige [App.config]-Datei konfiguriert Spring (Zeilen 5–9, 15–22) und log4net (Zeile 10, Zeilen 28–53), jedoch nicht NHibernate. Spring-Objekte werden nicht in [App.config], sondern in der [Dao.xml]-Datei (Zeile 20) konfiguriert. Die Spring/NHibernate-Konfiguration, die aus der Deklaration spezifischer Spring-Objekte besteht, befindet sich daher in dieser Datei.

13.1.4.2. Die Datei [Dao.xml]

Die Datei [Dao.xml], die die von Spring verwalteten Objekte enthält, sieht wie folgt aus:


<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net"
         xmlns:db="http://www.springframework.net/database">
 
    <!-- Referenced by main application context configuration file -->
    <description>
        Application Spring / NHibernate
    </description>
 
    <!-- Database and NHibernate Configuration -->
    <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>
 
    <!-- transaction manager -->
    <object id="transactionManager"
        type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate12">
        <property name="DbProvider" ref="DbProvider"/>
        <property name="SessionFactory" ref="NHibernateSessionFactory"/>
    </object>
 
    <!-- Hibernate Template -->
    <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>
 
    <!-- Data Access Objects -->
    <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>
  • Die Zeilen 11–13 konfigurieren die Verbindung zur Datenbank [dbpam_nhibernate]. Sie enthalten:
    • den für die Verbindung erforderlichen ADO.NET-Anbieter, in diesem Fall den MySQL-DBMS-Anbieter. Dazu muss die DLL [Mysql.Data] in die Projektreferenzen aufgenommen werden.
    • die Datenbankverbindungszeichenfolge (Server, Datenbankname, Verbindungseigentümer und Passwort)
  • In den Zeilen 15–29 wird die NHibernate-SessionFactory konfiguriert, das Objekt, das zum Abrufen von NHibernate-Sitzungen verwendet wird. Beachten Sie, dass alle Datenbankoperationen innerhalb einer NHibernate-Sitzung ausgeführt werden. In Zeile 15 ist zu sehen, dass die SessionFactory durch die Spring-Klasse `Spring.Data.NHibernate.LocalSessionFactoryObject` implementiert wird, die sich in der DLL `Spring.Data.NHibernate12` befindet.
  • Zeile 16: Die Eigenschaft „DbProvider“ legt die Datenbankverbindungsparameter fest (ADO.NET-Anbieter und Verbindungszeichenfolge). Hier verweist diese Eigenschaft auf das zuvor in den Zeilen 11–13 definierte DbProvider-Objekt.
  • Zeilen 17–20: Geben Sie die Liste der Assemblies an, die [*.hbm.xml]-Dateien enthalten, welche die von NHibernate verwalteten Entitäten konfigurieren. Zeile 19 gibt an, dass diese Dateien in der Projekt-Assembly zu finden sind. Beachten Sie, dass dieser Name in den C#-Projekteigenschaften zu finden ist. Beachten Sie außerdem, dass alle [*.hbm.xml]-Dateien so konfiguriert wurden, dass sie in die Projekt-Assembly aufgenommen werden.
  • Zeilen 22–27: NHibernate-spezifische Eigenschaften.
    • Zeile 24: Der verwendete SQL-Dialekt ist MySQL
    • Zeile 25: Das von NHibernate generierte SQL erscheint nicht in den Konsolenprotokollen. Wenn Sie diese Eigenschaft auf „true“ setzen, können Sie die von NHibernate generierten SQL-Anweisungen sehen. Dies kann Ihnen beispielsweise helfen zu verstehen, warum eine Anwendung beim Zugriff auf die Datenbank langsam ist.
  • Zeile 28: Wenn Sie die Eigenschaft „ExposeTransactionAwareSessionFactory“ auf „true“ setzen, verwaltet Spring die im C#-Code enthaltenen Annotationen zur Transaktionsverwaltung. Wir werden darauf zurückkommen, wenn wir die Klasse schreiben, die die [DAO]-Schicht implementiert.
  • Die Zeilen 32–36 definieren den Transaktionsmanager. Auch dieser Manager ist eine Spring-Klasse aus der DLL „Spring.Data.NHibernate12“. Dieser Manager benötigt die Datenbankverbindungsparameter (Zeile 34) sowie die NHibernate SessionFactory (Zeile 35).
  • Die Zeilen 39–43 definieren die Eigenschaften der Klasse „HibernateTemplate“, die ebenfalls eine Spring-Klasse ist. Diese Klasse wird als Hilfsklasse in der Klasse verwendet, die die [DAO]-Schicht implementiert. Sie erleichtert die Interaktion mit NHibernate-Objekten. Diese Klasse verfügt über bestimmte Eigenschaften, die initialisiert werden müssen:
    • Zeile 40: die NHibernate SessionFactory
    • Zeile 41: Die Eigenschaft TemplateFlushMode legt den Synchronisationsmodus des NHibernate-Persistenzkontexts mit der Datenbank fest. Der Modus „Auto“ stellt sicher, dass die Synchronisation
      • am Ende einer Transaktion
      • vor einer SELECT-Operation
    • Zeile 42: HQL-Abfragen (Hibernate Query Language) werden zwischengespeichert. Dies kann zu einer Leistungssteigerung führen.
  • Die Zeilen 46–48 definieren die Implementierungsklasse für die [dao]-Schicht
    • Zeile 46: Die [dao]-Schicht wird durch die Klasse [PamdaoSpringNHibernate] in der DLL [pam-dao-spring-nhibernate] implementiert. Nach der Instanziierung der Klasse wird die init-Methode der Klasse sofort ausgeführt. Beim Schließen des Spring-Containers wird die destroy-Methode der Klasse ausgeführt.
    • Zeile 47: Die Klasse [PamDaoSpringNHibernate] verfügt über eine HibernateTemplate-Eigenschaft, die mit der HibernateTemplate-Eigenschaft aus Zeile 39 initialisiert wird.

13.1.5. Implementierung der [dao]-Schicht

13.1.5.1. Das Gerüst der Implementierungsklasse

Die Schnittstelle [IPamDao] ist dieselbe wie im Projekt [pam-dao-nhibernate]:


using Pam.Dao.Entites;
 
namespace Pam.Dao.Service {
    public interface IPamDao {
        // list of all employee identities 
        Employe[] GetAllIdentitesEmployes();
        // an individual employee with benefits 
        Employe GetEmploye(string ss);
        // list of all contributions 
        Cotisations GetCotisations();
    }
}
  • Zeile 1: Wir importieren den Namespace für Entitäten in der [dao]-Schicht.
  • Zeile 3: Die [dao]-Schicht befindet sich im Namespace [Pam.Dao.Service]. Elemente im Namespace [Pam.Dao.Entities] können in mehreren Instanzen erstellt werden. Elemente im Namespace [Pam.Dao.Service] werden als einzelne Instanz (Singleton) erstellt. Dies begründete die Wahl der Namespace-Namen.
  • Zeile 4: Die Schnittstelle heißt [IPamDao]. Sie definiert drei Methoden:
    • Zeile 6: [GetAllIdentitesEmployes] gibt ein Array von Objekten des Typs [Employe] zurück, das die Liste der Kinderbetreuungskräfte in vereinfachter Form darstellt (Nachname, Vorname, Sozialversicherungsnummer).
    • Zeile 8: [GetEmploye] gibt ein [Employe]-Objekt zurück: den Mitarbeiter mit der Sozialversicherungsnummer, die als Parameter an die Methode übergeben wurde, zusammen mit den zu seiner Gehaltsstufe gehörenden Sozialleistungen.
    • Zeile 10: [GetCotisations] gibt das Objekt [Cotisations] zurück, das die Sätze der verschiedenen Sozialversicherungsbeiträge enthält, die vom Bruttogehalt abgezogen werden.

Das Grundgerüst der Implementierungsklasse für diese Schnittstelle unter Verwendung von Spring/NHibernate könnte wie folgt aussehen:


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 fields 
        private Cotisations cotisations;
        private Employe[] employes;
 
        // init 
        [Transaction(ReadOnly = true)]
        public void init() {
...
        }
 
        // delete object
        public void destroy() {
            if (HibernateTemplate.SessionFactory != null) {
                HibernateTemplate.SessionFactory.Close();
            }
        }
 
        // list of all employee identities
        public Employe[] GetAllIdentitesEmployes() {
            return employes;
        }
 
 
        // an individual employee with benefits 
        [Transaction(ReadOnly = true)]
        public Employe GetEmploye(string ss) {
....
        }
 
        // list of contributions 
        public Cotisations GetCotisations() {
            return cotisations;
        }
    }
}
  • Zeile 9: Die Klasse [PamDaoSpringNHibernate] implementiert korrekt die Schnittstelle [IPamDao] der [dao]-Schicht. Sie leitet sich außerdem von der Spring-Klasse [HibernateDaoSupport] ab. Diese Klasse verfügt über eine Eigenschaft [HibernateTemplate], die durch die eingerichtete Spring-Konfiguration initialisiert wird (Zeile 2 unten):

    <object id="pamdao" type="Pam.Dao.Service.PamDaoSpringNHibernate, pam-dao-spring-nhibernate" init-method="init" destroy-method="destroy">
        <property name="HibernateTemplate" ref="HibernateTemplate"/>
</object>
  • In Zeile 1 oben sehen wir, dass die Definition des [pamdao]-Objekts festlegt, dass die init- und destroy-Methoden der Klasse [PamDaoSpringNHibernate] zu bestimmten Zeitpunkten ausgeführt werden müssen. Diese beiden Methoden sind tatsächlich in der Klasse in den Zeilen 16 und 21 vorhanden.
  • Zeilen 15, 34: Annotationen, die sicherstellen, dass die annotierte Methode innerhalb einer Transaktion ausgeführt wird. Das Attribut `ReadOnly=true` gibt an, dass die Transaktion schreibgeschützt ist. Die innerhalb der Transaktion ausgeführte Methode kann eine Ausnahme auslösen. In diesem Fall rollt Spring die Transaktion automatisch zurück. Diese Annotation macht die Verwaltung einer Transaktion innerhalb der Methode überflüssig.
  • Zeile 16: Die init-Methode wird von Spring unmittelbar nach der Instanziierung der Klasse ausgeführt. Wir werden sehen, dass ihr Zweck darin besteht, die privaten Felder in den Zeilen 11 und 12 zu initialisieren. Sie wird innerhalb einer Transaktion ausgeführt (Zeile 15).
  • Die Methoden der Schnittstelle [IPamDao] werden in den Zeilen 28, 35 und 40 implementiert.
  • Zeilen 28–30: Die Methode [GetAllIdentitesEmployes] gibt einfach das Attribut aus Zeile 12 zurück, das durch die init-Methode initialisiert wurde.
  • Zeilen 40–42: Die Methode [GetCotisations] gibt lediglich das Attribut aus Zeile 11 zurück, das von der init-Methode initialisiert wurde.

13.1.5.2. Nützliche Methoden der Klasse HibernateTemplate

Wir werden die folgenden Methoden der Klasse „HibernateTemplate“ verwenden:

IList<T> Find<T>(string hql_query)
führt die HQL-Abfrage aus und gibt eine Liste von Objekten vom Typ T zurück
IList<T> Find<T>(string hql_query, object[])
führt eine HQL-Abfrage aus, die durch ? parametrisiert ist. Die Werte dieser Parameter werden durch das Objektarray bereitgestellt.
IList<T> LoadAll<T>()
gibt alle Entitäten vom Typ T zurück
  

Es gibt weitere nützliche Methoden, die wir hier nicht verwenden werden, mit denen Sie Entitäten abrufen, speichern, aktualisieren und löschen können:

T Load<T>(object id)
Fügt die Entität vom Typ T mit der Primärschlüssel-ID zur NHibernate-Sitzung hinzu.
void SaveOrUpdate(object entity)
Fügt das Entity-Objekt ein (INSERT) oder aktualisiert es (UPDATE), je nachdem, ob es einen Primärschlüssel hat (UPDATE) oder nicht (INSERT). Das Fehlen eines Primärschlüssels kann über das Attribut `unsaved-values` in der Konfigurationsdatei der Entity konfiguriert werden. Nach der Operation `SaveOrUpdate` befindet sich das Entity-Objekt in der NHibernate-Sitzung.
void Delete(object entity)
Entfernt das Entitätsobjekt aus der NHibernate-Sitzung.

13.1.5.3. Implementierung der init-Methode

Die init-Methode der Klasse [PamDaoSpringNHibernate] ist laut Konfiguration die Methode, die ausgeführt wird, nachdem Spring die Klasse instanziiert hat. Ihr Zweck besteht darin, die vereinfachten Mitarbeiteridentitäten (Nachname, Vorname, Sozialversicherungsnummer) und Beitragssätze lokal zwischenzuspeichern. Der Code könnte wie folgt aussehen.


[Transaction(ReadOnly = true)]
        public void init() {
            try {
                // on récupère la liste simplifiée des employés
                IList<object[]> lignes = HibernateTemplate.Find<object[]>("select e.SS,e.Nom,e.Prenom from Employe e");
                // on la met dans un tableau
                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++;
                }
                // on met les taux de cotisations dans un objet 
                cotisations = (HibernateTemplate.LoadAll<Cotisations>())[0];
            } catch (Exception ex) {
                // on transforme l'exception 
                throw new PamException(string.Format("Erreur d'accès à la BD : [{0}]", ex.ToString()), 43);
            }
        }
  • Zeile 5: Eine HQL-Abfrage wird ausgeführt. Sie ruft die Felder SS, LastName und FirstName aus allen Employee-Entitäten ab. Sie gibt eine Liste von Objekten zurück. Hätten wir den gesamten Employee-Datensatz mit „select e from Employee e“ angefordert, hätten wir eine Liste von Objekten vom Typ Employee erhalten.
  • Zeilen 7–12: Diese Liste von Objekten wird in ein Array von Objekten vom Typ „Employee“ kopiert.
  • Zeile 14: Wir rufen die Liste aller Entitäten vom Typ „Contributions“ ab. Wir wissen, dass diese Liste nur ein Element enthält. Daher rufen wir das erste Element der Liste ab, um die Beitragssätze zu erhalten.
  • In den Zeilen 7 und 14 werden die beiden privaten Felder der Klasse initialisiert.

13.1.5.4. Implementierung der Methode „GetEmployee“

Die Methode „GetEmployee“ muss die Entität „Employee“ mit einer bestimmten Sozialversicherungsnummer zurückgeben. Ihr Code könnte wie folgt aussehen:


[Transaction(ReadOnly = true)]
        public Employe GetEmploye(string ss) {
            IList<Employe> employés = null;
            try {
                // requête
                employés = HibernateTemplate.Find<Employe>("select e from Employe e where e.SS=?", new object[]{ss});
            } catch (Exception ex) {
                // on transforme l'exception 
                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);
            }
            // a-t-on récupéré un employé ? 
            if (employés.Count == 0) {
                // on signale le fait 
                throw new PamException(string.Format("L'employé de n° ss [{0}] n'existe pas", ss), 42);
            } else {
                return employés[0];
            }
        }
  • Zeile 6: Ruft die Liste der Mitarbeiter mit einer bestimmten Sozialversicherungsnummer ab
  • Zeile 12: Normalerweise sollten wir, wenn der Mitarbeiter existiert, eine Liste mit einem Element erhalten
  • Zeile 14: Ist dies nicht der Fall, wird eine Ausnahme ausgelöst
  • Zeile 16: Ist dies der Fall, wird der erste Mitarbeiter in der Liste zurückgegeben

13.1.5.5. Fazit

Wenn wir den Code der [DAO]-Schicht bei Verwendung

  1. des Spring/NHibernate-Frameworks
  2. dem Spring/NHibernate-Framework

stellen wir fest, dass die zweite Lösung einen einfacheren Code ermöglichte.

13.2. Testen der [DAO]-Schicht

13.2.1. Das Visual Studio-Projekt

Das Visual Studio-Projekt wurde bereits vorgestellt. Zur Erinnerung:

  • in [1] das Projekt als Ganzes
  • in [2] die verschiedenen Klassen des Projekts. Der Ordner [tests] enthält einen Konsolentest [Main.cs] und einen Unit-Test [NUnit.cs].
  • in [3] wird das Programm [Main.cs] kompiliert.
  • in [4] wird die Datei [NUnit.cs] nicht generiert.
  • Das Projekt ist eine Konsolenanwendung. Die ausgeführte Klasse ist die in [5] angegebene Klasse in der Datei [Main.cs].

13.2.2. Das Konsolentestprogramm [Main.cs]

Das Testprogramm [Main.cs] läuft in der folgenden Architektur:

Es ist für das Testen der Methoden der Schnittstelle [IPamDao] zuständig. Ein einfaches Beispiel könnte wie folgt aussehen:


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 {
                // layer instantiation [dao]
                IPamDao pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
                // list of employee identities 
                foreach (Employe Employe in pamDao.GetAllIdentitesEmployes()) {
                    Console.WriteLine(Employe.ToString());
                }
                // an employee with benefits 
                Console.WriteLine("------------------------------------");
                Console.WriteLine(pamDao.GetEmploye("254104940426058"));
                Console.WriteLine("------------------------------------");
                // list of contributions 
                Cotisations cotisations = pamDao.GetCotisations();
                Console.WriteLine(cotisations.ToString());
            } catch (Exception ex) {
                // exception display 
                Console.WriteLine(ex.ToString());
            }
            //break 
            Console.ReadLine();
        }
    }
}
  • Zeile 11: Wir fordern von Spring eine Referenz auf die [dao]-Schicht an.
  • Zeilen 13–15: Testen der Methode [GetAllEmployeeIDs] der Schnittstelle [IPamDao]
  • Zeile 18: Testen der Methode [GetEmploye] der Schnittstelle [IPamDao]
  • Zeile 21: Testen der Methode [GetCotisations] der Schnittstelle [IPamDao]

Spring, NHibernate und log4net werden über die Datei [ App.config] konfiguriert, die in Abschnitt 13.1.4.1 behandelt wird.

Die Ausführung des Codes mit der in Abschnitt 6.2 beschriebenen Datenbank erzeugt die folgende Konsolenausgabe:

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]
  • Zeilen 1–2: die 2 Mitarbeiter vom Typ [Employee] mit den Informationen [SS, Nachname, Vorname]
  • Zeile 4: der Mitarbeiter vom Typ [Employee] mit der Sozialversicherungsnummer [254104940426058]
  • Zeile 5: Beitragssätze

13.2.3. Unit-Tests mit NUnit

Wir kommen nun zu einem NUnit-Unit-Test. Das Visual Studio-Projekt für die [dao]-Schicht wird sich wie folgt weiterentwickeln:

  • in [1] das Testprogramm [NUnit.cs]
  • in [2,3] generiert das Projekt eine DLL mit dem Namen [pam-dao-spring-nhibernate.dll]
  • in [4] den Verweis auf die NUnit-Framework-DLL: [nunit.framework.dll]
  • in [5] wird die Klasse [Main.cs] nicht in die DLL [pam-dao-spring-nhibernate] aufgenommen
  • in [6] wird die Klasse [NUnit.cs] in die DLL [pam-dao-spring-nhibernate] aufgenommen

Die NUnit-Testklasse sieht wie folgt aus:


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 {
        // the [dao] layer to be tested 
        private IPamDao pamDao = null;
 
        // manufacturer 
        public NunitPamDao() {
            // layer instantiation [dao]
            pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
        }
 
        // init 
        [SetUp]
        public void Init() {
 
        }
 
        [Test]
        public void GetAllIdentitesEmployes() {
            // audit no. of employees 
            Expect(2, EqualTo(pamDao.GetAllIdentitesEmployes().Length));
        }
 
        [Test]
        public void GetCotisations() {
            // checking contribution rates 
            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() {
            // individual verification 
            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() {
            // non-existent individual verification 
            bool erreur = false;
            try {
                Employe employe1 = pamDao.GetEmploye("xx");
            } catch {
                erreur = true;
            }
            Expect(erreur, True);
        }
    }
}

Diese Klasse wurde bereits in Abschnitt 7.3.4 behandelt.

Bei der Projektgenerierung wird die DLL [pam-dao-spring-nhibernate.dll] im Ordner [bin/Release] erstellt.

Wir laden die DLL [pam-dao-spring-nhibernate.dll] mit dem Tool [NUnit-Gui], Version 2.4.6, und führen die Tests aus:

Image

Oben waren die Tests erfolgreich.

Praktische Übung:


  • Führen Sie die Tests für die Klasse [PamDaoSpringNHibernate] auf einem Rechner durch.
  • Verwenden Sie verschiedene Konfigurationsdateien [Dao.xml], um andere DBMS (Firebird, MySQL, Postgres, SQL Server) zu nutzen.

13.2.4. Generierung der DLL der [dao]-Schicht

Sobald die Klasse [PamDaoNHibernate] geschrieben und getestet wurde, wird die DLL der [dao]-Schicht wie folgt generiert:

  • [1], Testprogramme werden aus der Projekt-Assembly
  • [2,3], Projektkonfiguration
  • [4], Projekt-Build
  • Die DLL wird im Ordner [bin/Release] generiert [5]. Wir fügen sie zu den bereits im Ordner [lib] vorhandenen DLLs hinzu [6]:

13.3. Die Geschäftsschicht

Werfen wir noch einmal einen Blick auf die allgemeine Architektur der Anwendung [SimuPaie]:

Wir gehen nun davon aus, dass die [dao]-Schicht fertiggestellt und in der DLL [pam-dao-spring-nhibernate.dll] gekapselt wurde. Wir konzentrieren uns nun auf die [business]-Schicht. Dies ist die Schicht, die die Geschäftsregeln implementiert, in diesem Fall die Regeln zur Berechnung eines Gehalts.

Das Visual Studio-Projekt für die Geschäftsschicht könnte wie folgt aussehen:

  • In [1] wird das gesamte Projekt durch die Dateien [App.config] und [Dao.xml] konfiguriert. Die Datei [App.config] ist identisch mit der im Projekt [pam-dao-spring-nhibernate] der [dao]-Schicht. Dasselbe gilt für die Datei [Dao.xml], mit der Ausnahme, dass sie ein zusätzliches Spring-Objekt mit der ID pammetier deklariert. Die Deklaration dieses Objekts ist identisch mit der in der Datei [App.config] des Projekts [pam-metier-dao-nhibernate].
  • In [2] ist der Ordner [pam] identisch mit dem in der [metier]-Schicht des Projekts [pam-metier-dao-nhibernate]
  • In [3] die vom Projekt verwendeten Referenzen. Beachten Sie die DLL [pam-dao-spring-nhibernate] aus der zuvor besprochenen [dao]-Schicht.

Frage: Erstellen Sie das oben genannte Projekt [pam-metier-dao-spring-nhibernate]. Es wird separat getestet:

  • im Konsolenmodus durch das Konsolenprogramm [Main.cs]

  • durch den Unit-Test [NUnit.cs], der vom NUnit-Framework ausgeführt wird

Das neue Projekt [pam-metier-dao-spring-nhibernate] kann erstellt werden, indem Sie einfach das Projekt [pam-metier-dao-nhibernate] kopieren und anschließend die Elemente anpassen, die geändert werden müssen.


Nach dem Testen generieren wir die DLL der [business]-Schicht, die wir [pam-metier-dao-spring-nhibernate] nennen werden:

  • in [1], der erfolgreiche NUnit-Test
  • in [2] die vom Projekt generierte DLL

Wir fügen die DLL der [business]-Schicht zu den bereits im Ordner [lib] vorhandenen DLLs hinzu [3]:

13.4. Die [web]-Schicht

Werfen wir noch einmal einen Blick auf die allgemeine Architektur der [SimuPaie]-Anwendung:

Wir gehen davon aus, dass die [DAO]- und [Business]-Schichten fertiggestellt und in den DLLs [pam-dao-spring-nhibernate, pam-business-dao-spring-nhibernate] gekapselt sind. Wir werden nun die Webschicht beschreiben.

Das Visual Web Developer-Projekt für die [web]-Schicht wird zunächst durch einfaches Kopieren des Webprojektordners [pam-v7-3tier-nhibernate-multivues-multipages] erstellt. Das Projekt wird dann in [pam-v9-3tier-spring-nhibernate-multivues-multipages] umbenannt:

Das neue Webprojekt [pam-v9-3tier-spring-nhibernate-multivues-multipages] unterscheidet sich vom Projekt [pam-v7-3tier-nhibernate-multivues-multipages] in folgenden Punkten:

  • In [1] erfolgt die Konfiguration über die Dateien [Dao.xml] und [Web.config]. Die Datei [Dao.xml] gab es in [pam-v7] nicht, und die Datei [Web.config] muss die Spring/NHibernate-Konfiguration enthalten, während in [pam-v7] nur NHibernate konfiguriert wurde.
  • In [2] sind die DLLs für die [dao]- und [business]-Schichten diejenigen, die wir gerade erstellt haben.

Die Datei [Dao.xml] ist diejenige, die beim Erstellen der [business]-Schicht verwendet wurde. Die Datei [Web.config] stammt aus [pam-v7], zu der wir die Spring/NHibernate-Konfiguration hinzufügen, die in den [App.config]-Dateien der [dao]- und [business]-Schichten zu finden ist. Die Datei [Web.config] für [pam-v9] sieht wie folgt aus:


<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 configuration -->
  <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]

Die Zeilen 7–11 und 16–23 enthalten die Spring-Konfiguration, die in den zuvor erstellten [App.config]-Dateien der [dao]- und [business]-Schichten enthalten war, mit einem Unterschied: In den [App.config]-Dateien lautete Zeile 17 wie folgt:


      <resource uri="Dao.xml" />

Mit der folgenden Konfiguration:

Die Datei [Dao.xml] wird in den Ordner [bin] innerhalb des Webprojektordners kopiert. Mit der Syntax


      <resource uri="Dao.xml" />

wird im aktuellen Verzeichnis des Prozesses, der die Webanwendung ausführt, nach der Datei [Dao.xml] gesucht. Es stellt sich jedoch heraus, dass dieses Verzeichnis nicht der Ordner [bin] des ausgeführten Webprojekts ist. Sie müssen schreiben:


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

damit die Datei [Dao.xml] im Ordner [bin] des ausgeführten Webprojekts gesucht wird.


Frage: Stellen Sie diese Webanwendung auf einem Rechner bereit.


13.5. Fazit

Wir sind von der Architektur:

zur Architektur:

Das Ziel war es, die [DAO]-Schicht zu implementieren, indem die Möglichkeiten genutzt wurden, die die Integration von Spring mit NHibernate bietet.

Wir stellten fest, dass dies:

  • Auswirkungen auf die [DAO]-Schicht hatte. Diese Schicht war einfacher zu programmieren, erforderte jedoch eine komplexere Spring-Konfiguration.
  • die [Business]- und [Web]-Schichten geringfügig beeinflusste

Dies lieferte ein weiteres Beispiel für die Vorteile von Schichtenarchitekturen.