Skip to content

18. Webanwendung MVC in einer 3-Tier-Architektur – Beispiel 4, Postgres

18.1. Die Postgres-Datenbank

In dieser Version werden wir die Personenliste in einer Postgres-Datenbanktabelle 8.x [http://www.postgres.org] speichern. Die folgenden Screenshots stammen aus dem Client EMS PostgreSQL Manager Lite [http://www.sqlmanager.net/fr/products/postgresql/manager], einem kostenlosen Verwaltungsclient für Postgres.

Die Datenbank heißt [dbpersonnes]. Sie enthält eine Tabelle [PERSONNES]:

Image

Die Tabelle [PERSONNES] enthält die Liste der von der Webanwendung verwalteten Personen. Sie wurde mit den folgenden Befehlen SQL erstellt:

CREATE TABLE "public"."PERSONNES" (
  "ID" INTEGER NOT NULL, 
  "VERSION" INTEGER NOT NULL, 
  "NOM" VARCHAR(30) NOT NULL, 
  "PRENOM" VARCHAR(30) NOT NULL, 
  "DATENAISSANCE" DATE NOT NULL, 
  "MARIE" BOOLEAN NOT NULL, 
  "NBENFANTS" INTEGER NOT NULL, 
  CONSTRAINT "PERSONNES_pkey" PRIMARY KEY("ID"), 
  CONSTRAINT "PERSONNES_chk_NBENFANTS" CHECK ("NBENFANTS" >= 0), 
  CONSTRAINT "PERSONNES_chk_NOM" CHECK (("NOM")::text <> ''::text), 
  CONSTRAINT "PERSONNES_chk_PRENOM" CHECK (("PRENOM")::text <> ''::text)
) WITH OIDS;

Wir gehen nicht näher auf diese Tabelle ein, da sie der zuvor behandelten Firebird-Tabelle [PERSONNES] entspricht. Es ist jedoch zu beachten, dass die Namen der Spalten und Tabellen in Anführungszeichen stehen. Außerdem ist bei diesen Namen die Groß-/Kleinschreibung zu beachten. Möglicherweise lässt sich diese Funktionsweise von Postgres 8.x konfigurieren. Ich habe mich mit dieser Frage nicht näher befasst.

Die Tabelle [PERSONNES] könnte folgenden Inhalt haben:

Image

Neben der Tabelle [PERSONNES] enthält die Datenbank [dbpersonnes] ein Objekt namens „Sequenz“ mit dem Namen [SEQ_ID]. Dieser Generator liefert aufeinanderfolgende Ganzzahlen, die wir verwenden werden, um den Primärschlüssel [ID] der Klasse [PERSONNES] mit einem Wert zu versehen. Betrachten wir ein Beispiel, um die Funktionsweise zu veranschaulichen:

Man kann feststellen, dass sich der Wert [Next value] der Sequenz [SEQ_ID] geändert hat (Doppelklick darauf + F5 zum Aktualisieren):

 

Der Auftrag SQL

SELECT nextval('"SEQ_ID"')

ermöglicht somit den folgenden Wert der Sequenz [SEQ_ID]. Wir werden ihn in der Datei [personnes-postgres.xml] verwenden, die die auf dem SGBD ausgegebenen Befehle SQL zusammenfasst.

18.2. Das Eclipse-Projekt der Schichten [dao] und [service]

Um die Schichten [dao] und [service] unserer Anwendung mit der Postgres-Datenbank 8.x zu entwickeln, verwenden wir das folgende Eclipse-Projekt [spring-mvc-39]:

Image

Das Projekt ist ein einfaches Java-Projekt, kein Tomcat-Webprojekt.


Ordner [src]


Dieser Ordner enthält den Quellcode der Schichten [dao] und [service]:

Image

Alle Dateien, deren Name „[postgres]“ enthält, wurden gegenüber der Firebird-Version möglicherweise geändert oder auch nicht. Im Folgenden beschreiben wir die geänderten Dateien.


Ordner [database]


Dieser Ordner enthält das Skript zur Erstellung der Postgres-Datenbank für Personen:

Image

-- EMS PostgreSQL Manager Lite 3.1.0.1
-- ---------------------------------------
-- Host     : localhost
-- Datenbank: dbpersonnes



--
-- Definition für die Funktion plpgsql_call_handler (OID = 17230): 
--
...
--
-- Struktur für Tabelle PERSONNES (OID = 17254): 
--
CREATE TABLE "PERSONNES" (
    "ID" integer NOT NULL,
    "VERSION" integer NOT NULL,
    "NOM" varchar(30) NOT NULL,
    "PRENOM" varchar(30) NOT NULL,
    "DATENAISSANCE" date NOT NULL,
    "MARIE" boolean NOT NULL,
    "NBENFANTS" integer NOT NULL,
    CONSTRAINT "PERSONNES_chk_NBENFANTS" CHECK (("NBENFANTS" >= 0)),
    CONSTRAINT "PERSONNES_chk_NOM" CHECK ((("NOM")::text <> ''::text)),
    CONSTRAINT "PERSONNES_chk_PRENOM" CHECK ((("PRENOM")::text <> ''::text))
);
--
-- Definition für die Sequenz SEQ_ID (OID = 17261): 
--
CREATE SEQUENCE "SEQ_ID"
    INCREMENT BY 1
    NO MAXVALUE
    NO MINVALUE
    CACHE 1;
--
-- Daten für Blobs (OID = 17254) (LIMIT 0,3)
--
INSERT INTO "PERSONNES" ("ID", "VERSION", "NOM", "PRENOM", "DATENAISSANCE", "MARIE", "NBENFANTS") VALUES (1, 1, 'Major', 'Joachim', '1984-11-13', true, 2);
INSERT INTO "PERSONNES" ("ID", "VERSION", "NOM", "PRENOM", "DATENAISSANCE", "MARIE", "NBENFANTS") VALUES (2, 1, 'Humbort', 'Mélanie', '1985-01-12', false, 1);
INSERT INTO "PERSONNES" ("ID", "VERSION", "NOM", "PRENOM", "DATENAISSANCE", "MARIE", "NBENFANTS") VALUES (3, 1, 'Lemarchand', 'Charles', '1986-01-01', false, 0);
--
-- Definition für den Index PERSONNES_pkey (OID = 17256): 
--
ALTER TABLE ONLY "PERSONNES"
    ADD CONSTRAINT "PERSONNES_pkey" PRIMARY KEY ("ID");
--
-- Daten für die öffentliche Sequenz „SEQ_ID“ (OID = 17261)
--
SELECT pg_catalog.setval('"SEQ_ID"', 37, true);
--
-- Kommentare
--
COMMENT ON SCHEMA public IS 'Standard public schema';

Ordner [lib]


Dieser Ordner enthält die für die Anwendung erforderlichen Archive:

Zu beachten ist der Vorhandensein des JDBC-Treibers für SGBD Postgres 8.x. Alle diese Archive sind Teil des Eclipse-Projekts Classpath.

18.3. Die Schicht [dao]

Die Ebene [dao] sieht wie folgt aus:

Image

Wir stellen hier nur die Änderungen gegenüber der Version [Firebird] vor.

Die Mapping-Datei [personne-postgres.xml] lautet wie folgt:


<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE sqlMap
    PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN"
    "http://www.ibatis.com/dtd/sql-map-2.dtd">

<!-- Achtung – PostgreSQL 8 verlangt die exakte Schreibweise der Spaltennamen
     et des tables ainsi que des guillemets autour de ces noms -->

    <sqlMap>
        <!-- Alias der Klasse [Personne] -->
        <typeAlias alias="Personne.classe" 
            type="istia.st.mvc.personnes.entites.Personne"/>
        <!-- Zuordnungstabelle [PERSONNES] – Objekt [Personne] -->
        <resultMap id="Personne.map" 
            class="istia.st.mvc.personnes.entites.Personne">
            <result property="id" column="ID" />
            <result property="version" column="VERSION" />
            <result property="nom" column="NOM"/>
            <result property="prenom" column="PRENOM"/>
            <result property="dateNaissance" column="DATENAISSANCE"/>
            <result property="marie" column="MARIE"/>
            <result property="nbEnfants" column="NBENFANTS"/>
        </resultMap>
        <!-- Liste aller Personen -->
        <select id="Personne.getAll" resultMap="Personne.map" > select "ID", 
            "VERSION", "NOM", "PRENOM", "DATENAISSANCE", "MARIE", "NBENFANTS" FROM 
            "PERSONNES"</select>
        <!-- eine bestimmte Person abrufen -->
        <select id="Personne.getOne" resultMap="Personne.map" >select "ID", 
            "VERSION", "NOM", "PRENOM", "DATENAISSANCE", "MARIE", "NBENFANTS" FROM 
            "PERSONNES" WHERE "ID"=#Wert#</select>
        <!-- eine Person hinzufügen -->
        <insert id="Personne.insertOne" parameterClass="Personne.classe"> 
            <selectKey keyProperty="id">
                SELECT nextval('"SEQ_ID"') as value
            </selectKey> 
            insert into "PERSONNES"("ID", "VERSION", 
            "NOM", "PRENOM", "DATENAISSANCE", "MARIE", "NBENFANTS") VALUES(#id#, 
            #Version#, #Nachname#, #Vorname#, #dateNaissance#, #verheiratet#, #nbEnfants#) 
            </insert>
        <!-- Person aktualisieren -->
        <update id="Personne.updateOne" parameterClass="Personne.classe"> update 
            "PERSONNES" set "VERSION"=#Version#+1, „NOM“=#Nachname#, „PRENOM“=#Vorname#, 
            "DATENAISSANCE"=#dateNaissance#, "MARIE"=#Ehefrau#, "NBENFANTS"=#nbEnfants# 
            WHERE "ID"=#id# und „VERSION“=#version#</update>
        <!-- eine Person löschen -->
        <delete id="Personne.deleteOne" parameterClass="int"> delete FROM 
            "PERSONNES" WHERE "ID"=#Wert# </delete>
    </sqlMap>

Der Inhalt entspricht dem von [personnes-firebird.xml], mit folgenden kleinen Unterschieden:

  • Die Namen der Spalten und Tabellen stehen in Anführungszeichen, und bei diesen Namen wird zwischen Groß- und Kleinschreibung unterschieden.
  • Die Reihenfolge SQL „ Personne.insertOne “ hat sich in den Zeilen 34–41 geändert. Die Art und Weise, wie der Primärschlüssel mit Postgres generiert wird, unterscheidet sich von der bei Firebird verwendeten:
    • Zeile 36: Die Reihenfolge SQL [SELECT nextval('"SEQ_ID"')] liefert den Primärschlüssel. Die Syntax [as value] ist obligatorisch. [value] stellt den erhaltenen Schlüssel dar. Dieser Wert wird dem Feld des Objekts [Personne] zugewiesen, das durch das Attribut [keyProperty] (Zeile 35) bezeichnet wird, in diesem Fall das Feld [id].
    • Die Befehle SQL des Tags <insert> werden in der Reihenfolge ausgeführt, in der sie auftreten. Daher wird SELECT vor INSERT ausgeführt. Zum Zeitpunkt des Einfügevorgangs wurde das Feld [id] des Objekts [Personne] also durch die Befehle SQL und SELECT aktualisiert.
    • Zeilen 38–40: Einfügen des Objekts [Personne]

Die Implementierungsklasse [DaoImplCommon] der Schicht [dao] ist diejenige, die in der Version [Firebird] untersucht wurde.

Die Konfiguration der Ebene [dao] wurde an SGBD und [Postgres] angepasst. Somit lautet die Konfigurationsdatei [spring-config-test-dao-postgres.xml] wie folgt:


<?xml version="1.0" encoding="ISO_8859-1"?>
<!DOCTYPE beans SYSTEM "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <!-- die Datenquelle DBCP -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName">
            <value>org.postgresql.Driver</value>
        </property>
        <property name="url">
            <value>jdbc:postgresql:dbpersonnes</value>
        </property>
        <property name="username">
            <value>postgres</value>
        </property>
        <property name="password">
            <value>postgres</value>
        </property>
    </bean>
    <!-- SqlMapCllient -->
    <bean id="sqlMapClient" 
        class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
        <property name="dataSource">
            <ref local="dataSource"/>
        </property>
        <property name="configLocation">
            <value>classpath:sql-map-config-postgres.xml</value>
        </property>
    </bean>
    <!-- die Zugriffsklasse auf die Schicht [dao] -->
    <bean id="dao" class="istia.st.mvc.personnes.dao.DaoImplCommon">
        <property name="sqlMapClient">
            <ref local="sqlMapClient"/>
        </property>
    </bean>
</beans>
  • Zeilen 5–19: Die Bean [dataSource] verweist nun auf die Datenbank [Postgres] [dbpersonnes], deren Administrator [postgres] ist und deren Passwort [postgres] lautet. Der Leser sollte diese Konfiguration entsprechend seiner eigenen Umgebung anpassen.
  • Zeile 31: Die Klasse [DaoImplCommon] ist die Implementierungsklasse der Schicht [dao]

Nachdem diese Änderungen vorgenommen wurden, kann mit den Tests begonnen werden.

18.4. Tests der Schichten [dao] und [service]

Die Tests der Schichten [dao] und [service] sind dieselben wie für die Version [Firebird]. Starten wir zunächst SGBD Postgres und anschließend die Eclipse-Tests. Die Ergebnisse lauten wie folgt:

Es ist festzustellen, dass die Tests mit der Implementierung [DaoImplCommon] erfolgreich bestanden wurden. Wir müssen diese Klasse nicht ableiten, wie es bei den Versionen SGBD und [Firebird] erforderlich war.

18.5. Tests der Anwendung [web]

Um die Webanwendung mit den Implementierungen SGBD und [Postgres] zu testen, erstellen wir ein Eclipse-Projekt [mvc-personnes-04B] analog zu dem Verfahren, das zum Erstellen des Projekts [mvc-personnes-03B] mit der Firebird-Datenbank verwendet wurde (siehe Abschnitt 17.7). Allerdings müssen wir die Archive [personnes-dao.jar] und [personnes-service.jar] nicht neu erstellen. Tatsächlich haben wir gegenüber dem Projekt [mvc-personnes-03B] keine Klassen geändert. Lediglich das Archiv [personnes-dao.jar] enthält die Klasse [DaoImplFirebird], die nun überflüssig geworden ist.

Image

Wir stellen das Webprojekt [mvc-personnes-04B] in Tomcat bereit:

Wir sind bereit für die Test- . Der Inhalt der Tabelle [PERSONNES] sieht dann wie folgt aus:

Image

Tomcat ist gestartet. Mit einem Browser rufen wir die URL [http://localhost:8080/mvc-personnes-04B] auf:

Image

Wir fügen eine neue Person über den Link [Ajout] hinzu:

Wir überprüfen den Eintrag in der Datenbank:

Image

Der Leser wird gebeten, weitere Tests mit [modification, suppression] durchzuführen.