Skip to content

18. Webapplicatie MVC in een 3-tier-architectuur – Voorbeeld 4, Postgres

18.1. De Postgres-database

In deze versie gaan we de lijst met personen opslaan in een Postgres-databasetabel 8.x [http://www.postgres.org]. De onderstaande schermafbeeldingen zijn afkomstig van de client EMS PostgreSQL Manager Lite [http://www.sqlmanager.net/fr/products/postgresql/manager], een gratis beheerclient voor Postgres.

De database heet [dbpersonnes]. Deze bevat een tabel [PERSONNES]:

Image

De tabel [PERSONNES] bevat de lijst met personen die door de webapplicatie worden beheerd. Deze is opgebouwd met de volgende SQL-opdrachten:

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;

We gaan niet verder in op deze tabel, aangezien deze vergelijkbaar is met de eerder besproken Firebird-tabel [PERSONNES]. Er zij echter opgemerkt dat de namen van de kolommen en tabellen tussen aanhalingstekens staan. Bovendien zijn deze namen hoofdlettergevoelig. Het is mogelijk dat deze werkwijze van Postgres 8.x configureerbaar is. Ik heb dit niet verder onderzocht.

De tabel [PERSONNES] zou de volgende inhoud kunnen hebben:

Image

Naast de tabel [PERSONNES] bevat de database [dbpersonnes] een object dat een sequentie wordt genoemd, namelijk [SEQ_ID]. Deze generator levert opeenvolgende gehele getallen op die we zullen gebruiken om de primaire sleutel [ID] van de klasse [PERSONNES] een waarde te geven. Laten we een voorbeeld nemen om de werking ervan te illustreren:

We zien dat de waarde [Next value] van de reeks [SEQ_ID] is gewijzigd (dubbelklik erop + F5 om te vernieuwen):

 

De opdracht SQL

SELECT nextval('"SEQ_ID"')

levert dus de volgende waarde op voor de reeks [SEQ_ID]. We zullen deze gebruiken in het bestand [personnes-postgres.xml], dat de opdrachten SQL verzamelt die zijn verzonden naar SGBD.

18.2. Het Eclipse-project van de lagen [dao] en [service]

Om de lagen [dao] en [service] van onze applicatie met de Postgres-database 8.x te ontwikkelen, gebruiken we het volgende Eclipse-project [spring-mvc-39]:

Image

Het project is een eenvoudig Java-project, geen Tomcat-webproject.


Map [src]


Deze map bevat de broncode van de lagen [dao] en [service]:

Image

Alle bestanden met [postgres] in hun naam kunnen al dan niet gewijzigd zijn ten opzichte van de Firebird-versie. Hieronder beschrijven we de gewijzigde bestanden.


Map [database]


Deze map bevat het script voor het aanmaken van de Postgres-database met personen:

Image

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



--
-- Definitie voor functie plpgsql_call_handler (OID = 17230): 
--
...
--
-- Structuur voor tabel 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))
);
--
-- Definitie voor reeks SEQ_ID (OID = 17261): 
--
CREATE SEQUENCE "SEQ_ID"
    INCREMENT BY 1
    NO MAXVALUE
    NO MINVALUE
    CACHE 1;
--
-- Gegevens voor 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);
--
-- Definitie voor index PERSONNES_pkey (OID = 17256): 
--
ALTER TABLE ONLY "PERSONNES"
    ADD CONSTRAINT "PERSONNES_pkey" PRIMARY KEY ("ID");
--
-- Gegevens voor de openbare reeks „SEQ_ID“ (OID = 17261)
--
SELECT pg_catalog.setval('"SEQ_ID"', 37, true);
--
-- Opmerkingen
--
COMMENT ON SCHEMA public IS 'Standard public schema';

Map [lib]


Deze map bevat de archieven die nodig zijn voor de applicatie:

Let op: de JDBC-driver voor SGBD Postgres 8.x is aanwezig. Al deze bestanden maken deel uit van het Eclipse-project Classpath.

18.3. De laag [dao]

De laag [dao] ziet er als volgt uit:

Image

We vermelden alleen wat er is veranderd ten opzichte van de versie [Firebird].

Het mappingbestand [personne-postgres.xml] is als volgt:


<?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">

<!-- let op - PostgreSQL 8 vereist de exacte spelling van kolomnamen
     et des tables ainsi que des guillemets autour de ces noms -->

    <sqlMap>
        <!-- klasse-alias [Personne] -->
        <typeAlias alias="Personne.classe" 
            type="istia.st.mvc.personnes.entites.Personne"/>
        <!-- toewijzingstabel [PERSONNES] - object [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>
        <!-- lijst van alle personen -->
        <select id="Personne.getAll" resultMap="Personne.map" > select "ID", 
            "VERSION", "NOM", "PRENOM", "DATENAISSANCE", "MARIE", "NBENFANTS" FROM 
            "PERSONNES"</select>
        <!-- een specifieke persoon opvragen -->
        <select id="Personne.getOne" resultMap="Personne.map" >select "ID", 
            "VERSION", "NOM", "PRENOM", "DATENAISSANCE", "MARIE", "NBENFANTS" FROM 
            "PERSONNES" WHERE "ID"=#waarde#</select>
        <!-- een persoon toevoegen -->
        <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#, 
             #versie#, #achternaam#, #voornaam#, #dateNaissance#, #getrouwd met#, #nbEnfants#) 
            </insert>
        <!-- een persoon bijwerken -->
        <update id="Personne.updateOne" parameterClass="Personne.classe"> update 
            "PERSONNES" set "VERSION"=#versie#+1, "NOM"=#achternaam#, "PRENOM"=#voornaam#, 
            "DATENAISSANCE"=#dateNaissance#, "MARIE"=#marie#, "NBENFANTS"=#nbEnfants# 
            WHERE "ID"=#id# en "VERSION"=#versie#</update>
        <!-- een persoon verwijderen -->
        <delete id="Personne.deleteOne" parameterClass="int"> delete FROM 
            "PERSONNES" WHERE "ID"=#waarde# </delete>
    </sqlMap>

De inhoud is identiek aan die van [personnes-firebird.xml], op de volgende details na:

  • de namen van de kolommen en tabellen staan tussen aanhalingstekens en er wordt onderscheid gemaakt tussen hoofdletters en kleine letters
  • de volgorde SQL " Personne.insertOne " is gewijzigd in de regels 34-41. De manier waarop de primaire sleutel met Postgres wordt gegenereerd, verschilt van die bij Firebird:
    • regel 36: de volgorde SQL [SELECT nextval('"SEQ_ID"')] levert de primaire sleutel op. De syntaxis [as value] is verplicht. [value] vertegenwoordigt de verkregen sleutel. Deze waarde wordt toegewezen aan het veld van het object [Personne] dat wordt aangeduid door het attribuut [keyProperty] (regel 35), in dit geval het veld [id].
    • De opdrachten SQL van de tag <insert> worden uitgevoerd in de volgorde waarin ze worden aangetroffen. SELECT wordt dus uitgevoerd vóór INSERT. Op het moment van de invoegbewerking is het veld [id] van het object [Personne] dus bijgewerkt door de opdracht SQL SELECT.
    • regels 38-40: invoegen van het object [Personne]

De implementatieklasse [DaoImplCommon] van de laag [dao] is dezelfde als die welke in versie [Firebird] is onderzocht.

De configuratie van de laag [dao] is aangepast aan SGBD en [Postgres]. Het configuratiebestand [spring-config-test-dao-postgres.xml] ziet er dus als volgt uit:


<?xml version="1.0" encoding="ISO_8859-1"?>
<!DOCTYPE beans SYSTEM "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <!-- de gegevensbron 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>
    <!-- de toegangsklasse tot de laag [dao] -->
    <bean id="dao" class="istia.st.mvc.personnes.dao.DaoImplCommon">
        <property name="sqlMapClient">
            <ref local="sqlMapClient"/>
        </property>
    </bean>
</beans>
  • regels 5-19: de bean [dataSource] verwijst nu naar de database [Postgres] [dbpersonnes], waarvan de beheerder [postgres] is met het wachtwoord [postgres]. De lezer past deze configuratie aan zijn eigen omgeving aan.
  • regel 31: de klasse [DaoImplCommon] is de implementatieklasse van de laag [dao]

Nadat deze wijzigingen zijn aangebracht, kunnen we overgaan tot het testen.

18.4. De tests van de lagen [dao] en [service]

De tests van de lagen [dao] en [service] zijn dezelfde als voor de versie [Firebird]. Laten we SGBD Postgres starten en vervolgens de Eclipse-tests uitvoeren. De verkregen resultaten zijn als volgt:

We zien dat de tests met de implementatie [DaoImplCommon] met succes zijn doorlopen. We hoeven deze klasse niet af te leiden, zoals wel nodig was bij de SGBD en [Firebird].

18.5. Tests van de applicatie [web]

Om de webapplicatie met de SGBD en [Postgres] te testen, bouwen we een Eclipse-project [mvc-personnes-04B] op, op dezelfde manier als bij het bouwen van het project [mvc-personnes-03B] met de Firebird-database (zie paragraaf 17.7). We hoeven de archieven [personnes-dao.jar] en [personnes-service.jar] echter niet opnieuw aan te maken. We hebben namelijk geen enkele klasse gewijzigd ten opzichte van het project [mvc-personnes-03B]. Alleen bevat het archief [personnes-dao.jar] de klasse [DaoImplFirebird], die overbodig is geworden.

Image

We implementeren het webproject [mvc-personnes-04B] in Tomcat:

We zijn klaar voor de test . De inhoud van de tabel [PERSONNES] is dan als volgt:

Image

Tomcat is gestart. Met een browser roepen we de URL [http://localhost:8080/mvc-personnes-04B] op:

Image

We voegen een nieuwe persoon toe via de link [Ajout]:

We controleren de toevoeging in de database:

Image

De lezer wordt uitgenodigd om nog meer tests uit te voeren: [modification, suppression].