Skip to content

18. Applicazione web MVC in un’architettura a tre livelli – Esempio 4, Postgres

18.1. Il database Postgres

In questa versione, installeremo l’elenco delle persone in una tabella del database Postgres 8.x [http://www.postgres.org]. Di seguito, le schermate provengono dal client EMS PostgreSQL Manager Lite [http://www.sqlmanager.net/fr/products/postgresql/manager], un client di amministrazione gratuito per Postgres.

Il database si chiama [dbpersonnes]. Contiene una tabella denominata [PERSONNES]:

Image

La tabella [PERSONNES] conterrà l'elenco delle persone gestite dall'applicazione web. È stata creata con i seguenti comandi SQL:

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;

Non ci soffermiamo su questa tabella, che è analoga alla tabella [PERSONNES] di tipo Firebird esaminata in precedenza. Si noti tuttavia che i nomi delle colonne e delle tabelle sono racchiusi tra virgolette. Inoltre, tali nomi distinguono tra maiuscole e minuscole. È possibile che questa modalità di funzionamento di Postgres 8.x sia configurabile. Non ho approfondito la questione.

La tabella [PERSONNES] potrebbe avere il seguente contenuto:

Image

Oltre alla tabella [PERSONNES], il database [dbpersonnes] contiene un oggetto denominato «sequenza» e denominato [SEQ_ID]. Questo generatore produce numeri interi consecutivi che useremo per assegnare un valore alla chiave primaria [ID] della classe [PERSONNES]. Facciamo un esempio per illustrarne il funzionamento:

Si può notare che il valore [Next value] della sequenza [SEQ_ID] è cambiato (fare doppio clic su di esso + F5 per aggiornare):

 

L'ordine SQL

SELECT nextval('"SEQ_ID"')

consente quindi di ottenere il seguente valore della sequenza [SEQ_ID]. Lo useremo nel file [personnes-postgres.xml] che raggruppa i comandi SQL emessi sul SGBD.

18.2. Il progetto Eclipse dei livelli [dao] e [service]

Per sviluppare i livelli [dao] e [service] della nostra applicazione con il database Postgres 8.x, utilizzeremo il seguente progetto Eclipse [spring-mvc-39]:

Image

Il progetto è un semplice progetto Java, non un progetto web Tomcat.


Cartella [src]


Questa cartella contiene i codici sorgente dei livelli [dao] e [service]:

Image

Tutti i file che contengono [postgres] nel nome potrebbero aver subito o meno una modifica rispetto alla versione Firebird. Di seguito descriviamo i file modificati.


Cartella [database]


Questa cartella contiene lo script per la creazione del database Postgres delle persone:

Image

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



--
-- Definizione della funzione plpgsql_call_handler (OID = 17230): 
--
...
--
-- Struttura della tabella 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))
);
--
-- Definizione per la sequenza SEQ_ID (OID = 17261): 
--
CREATE SEQUENCE "SEQ_ID"
    INCREMENT BY 1
    NO MAXVALUE
    NO MINVALUE
    CACHE 1;
--
-- Dati per i blob (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);
--
-- Definizione per l'indice PERSONNES_pkey (OID = 17256): 
--
ALTER TABLE ONLY "PERSONNES"
    ADD CONSTRAINT "PERSONNES_pkey" PRIMARY KEY ("ID");
--
-- Dati per la sequenza pubblica "SEQ_ID" (OID = 17261)
--
SELECT pg_catalog.setval('"SEQ_ID"', 37, true);
--
-- Commenti
--
COMMENT ON SCHEMA public IS 'Standard public schema';

Cartella [lib]


Questa cartella contiene gli archivi necessari all'applicazione:

Da notare la presenza del driver JDBC per Postgres 8.x (SGBD). Tutti questi file fanno parte del progetto Eclipse (Classpath).

18.3. Il livello [dao]

Il livello [dao] è il seguente:

Image

Presentiamo solo le modifiche rispetto alla versione [Firebird].

Il file di mappatura [personne-postgres.xml] è il seguente:


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

<!-- Attenzione: PostgreSQL 8 richiede l'ortografia esatta dei nomi delle colonne
     et des tables ainsi que des guillemets autour de ces noms -->

    <sqlMap>
        <!-- alias classe [Personne] -->
        <typeAlias alias="Personne.classe" 
            type="istia.st.mvc.personnes.entites.Personne"/>
        <!-- tabella di mappatura [PERSONNES] - oggetto [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>
        <!-- elenco di tutte le persone -->
        <select id="Personne.getAll" resultMap="Personne.map" > select "ID", 
            "VERSION", "NOM", "PRENOM", "DATENAISSANCE", "MARIE", "NBENFANTS" FROM 
            "PERSONNES"</select>
        <!-- recupera una persona specifica -->
        <select id="Personne.getOne" resultMap="Personne.map" >select "ID", 
            "VERSION", "NOM", "PRENOM", "DATENAISSANCE", "MARIE", "NBENFANTS" FROM 
            "PERSONNES" WHERE "ID"=#valore#</select>
        <!-- aggiungere una persona -->
        <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#, 
             #versione#, #cognome#, #nome#, #dateNaissance#, #maria#, #nbEnfants#) 
            </insert>
        <!-- aggiornare una persona -->
        <update id="Personne.updateOne" parameterClass="Personne.classe"> update 
            "PERSONNES" set "VERSION"=#versione#+1, "NOM"=#cognome#, "PRENOM"=#nome#, 
            "DATENAISSANCE"=#dateNaissance#, "MARIE"=#marie#, "NBENFANTS"=#nbEnfants# 
            WHERE "ID"=#id# e "VERSION"=#versione#</aggiornamento>
        <!-- eliminare una persona -->
        <delete id="Personne.deleteOne" parameterClass="int"> delete FROM 
            "PERSONNES" WHERE "ID"=#valore# </delete>
    </sqlMap>

Il contenuto è identico a quello di [personnes-firebird.xml], salvo le seguenti differenze:

  • i nomi delle colonne e delle tabelle sono racchiusi tra virgolette e distinguono tra maiuscole e minuscole
  • l’ordine SQL " Personne.insertOne " è cambiato nelle righe 34-41. Il modo di generare la chiave primaria con Postgres è diverso da quello utilizzato con Firebird:
    • riga 36: l’ordine SQL [SELECT nextval('"SEQ_ID"')] fornisce la chiave primaria. La sintassi [as value] è obbligatoria. [value] rappresenta la chiave ottenuta. Questo valore verrà assegnato al campo dell’oggetto [Personne] indicato dall’attributo [keyProperty] (riga 35), in questo caso il campo [id].
    • I comandi SQL contenuti nel tag <insert> vengono eseguiti nell'ordine in cui vengono incontrati. Pertanto, il comando SELECT viene eseguito prima del comando INSERT. Al momento dell’operazione di inserimento, il campo [id] dell’oggetto [Personne] sarà quindi stato aggiornato dal comando SQL SELECT.
    • righe 38-40: inserimento dell’oggetto [Personne]

La classe di implementazione [DaoImplCommon] del livello [dao] è quella analizzata nella versione [Firebird].

La configurazione del livello [dao] è stata adattata a SGBD e [Postgres]. Pertanto, il file di configurazione [spring-config-test-dao-postgres.xml] è il seguente:


<?xml version="1.0" encoding="ISO_8859-1"?>
<!DOCTYPE beans SYSTEM "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <!-- la fonte dei dati 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>
    <!-- le classi di accesso al livello [dao] -->
    <bean id="dao" class="istia.st.mvc.personnes.dao.DaoImplCommon">
        <property name="sqlMapClient">
            <ref local="sqlMapClient"/>
        </property>
    </bean>
</beans>
  • righe 5-19: il bean [dataSource] ora fa riferimento al database [Postgres] [dbpersonnes], il cui amministratore è [postgres] con la password [postgres]. Il lettore dovrà modificare questa configurazione in base al proprio ambiente.
  • riga 31: la classe [DaoImplCommon] è la classe di implementazione del livello [dao]

Una volta apportate queste modifiche, si può procedere ai test.

18.4. Test dei livelli [dao] e [service]

I test dei livelli [dao] e [service] sono gli stessi della versione [Firebird]. Avviamo SGBD Postgres e poi i test di Eclipse. I risultati ottenuti sono i seguenti:

Si nota che i test sono stati superati con successo con l’implementazione [DaoImplCommon]. Non sarà necessario derivare questa classe, come invece era stato necessario fare con SGBD e [Firebird].

18.5. Test dell’applicazione [web]

Per testare l’applicazione web con SGBD e [Postgres], creiamo un progetto Eclipse [mvc-personnes-04B] in modo analogo a quello utilizzato per creare il progetto [mvc-personnes-03B] con il database Firebird (cfr. paragrafo 17.7). Tuttavia, non è necessario ricreare gli archivi [personnes-dao.jar] e [personnes-service.jar]. Infatti, non abbiamo modificato alcuna classe rispetto al progetto [mvc-personnes-03B]. Semplicemente, l’archivio [personnes-dao.jar] contiene la classe [DaoImplFirebird], che è diventata superflua.

Image

Stiamo distribuendo il progetto web [mvc-personnes-04B] all’interno di Tomcat:

Siamo pronti per i test di . Il contenuto della tabella [PERSONNES] è quindi il seguente:

Image

Tomcat è in esecuzione. Con un browser, accediamo all’URL [http://localhost:8080/mvc-personnes-04B]:

Image

Aggiungiamo una nuova persona tramite il link [Ajout]:

Verifichiamo l’aggiunta nel database:

Image

Il lettore è invitato a effettuare ulteriori test con [modification, suppression].