Skip to content

18. MVC Web Application in a 3-Tier Architecture – Example 4, Postgres

18.1. The Postgres database

In this version, we will store the list of people in a Postgres 8.x database table [http://www.postgres.org]. In the following, the screenshots are taken from the EMS PostgreSQL Manager Lite client [http://www.sqlmanager.net/fr/products/postgresql/manager], a free administration client for the Postgres DBMS.

The database is named [dbpersonnes]. It contains a table named [PERSONNES]:

Image

The [PERSONNES] table will contain the list of people managed by the web application. It was created using the following SQL statements:

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 will not dwell on this table, which is analogous to the Firebird [PERSONNES] table discussed earlier. Note, however, that the column and table names are enclosed in quotes. Furthermore, these names are case-sensitive. It is possible that this behavior in Postgres 8.x is configurable. I have not investigated this further.

The [PERSONNES] table could have the following content:

Image

In addition to the [PERSONNES] table, the [dbpersonnes] database has an object called a sequence named [SEQ_ID]. This generator produces successive integers that we will use to populate the [ID] primary key of the [PERSONNES] class. Let’s take an example to illustrate how it works:

We can see that the [Next value] of the [SEQ_ID] sequence has changed (double-click on it + F5 to refresh):

 

The SQL statement

SELECT nextval('"SEQ_ID"')

therefore allows us to retrieve the next value in the [SEQ_ID] sequence. We will use it in the [people-postgres.xml] file, which contains the SQL statements executed on the DBMS.

18.2. The Eclipse project for the [dao] and [service] layers

To develop the [dao] and [service] layers of our application with the Postgres 8.x database, we will use the following Eclipse project [spring-mvc-39]:

Image

The project is a simple Java project, not a Tomcat web project.


[src] folder


This folder contains the source code for the [dao] and [service] layers:

Image

All files with [postgres] in their names may or may not have been modified compared to the Firebird version. Below, we describe the modified files.


[database] folder


This folder contains the script for creating the Postgres database for users:

Image

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



--
-- Definition for function plpgsql_call_handler (OID = 17230) : 
--
...
--
-- Structure for table 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 for sequence SEQ_ID (OID = 17261) : 
--
CREATE SEQUENCE "SEQ_ID"
    INCREMENT BY 1
    NO MAXVALUE
    NO MINVALUE
    CACHE 1;
--
-- Data for 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 for index PERSONNES_pkey (OID = 17256) : 
--
ALTER TABLE ONLY "PERSONNES"
    ADD CONSTRAINT "PERSONNES_pkey" PRIMARY KEY ("ID");
--
-- Data for sequence public."SEQ_ID" (OID = 17261)
--
SELECT pg_catalog.setval('"SEQ_ID"', 37, true);
--
-- Comments
--
COMMENT ON SCHEMA public IS 'Standard public schema';

[lib] folder


This folder contains the archives required by the application:

Note the presence of the Postgres 8.x DBMS JDBC driver. All these files are part of the Eclipse project's classpath.

18.3. The [dao] layer

The [dao] layer is as follows:

Image

We are only presenting the changes compared to the [Firebird] version.

The mapping file [personne-postgres.xml] is as follows:


<?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">
 
<!-- warning - Postgresql 8 requires exact spelling of column names
     et des tables ainsi que des guillemets autour de ces noms -->
 
    <sqlMap>
        <!-- alias class [Person] -->
        <typeAlias alias="Personne.classe" 
            type="istia.st.mvc.personnes.entites.Personne"/>
        <!-- mapping table [PERSONNES] - object [Person] -->
        <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>
        <!-- list of all persons -->
        <select id="Personne.getAll" resultMap="Personne.map" > select "ID", 
            "VERSION", "NOM", "PRENOM", "DATENAISSANCE", "MARIE", "NBENFANTS" FROM 
            "PERSONNES"</select>
        <!-- get a specific person -->
        <select id="Personne.getOne" resultMap="Personne.map" >select "ID", 
            "VERSION", "NOM", "PRENOM", "DATENAISSANCE", "MARIE", "NBENFANTS" FROM 
            "PERSONNES" WHERE "ID"=#value#</select>
        <!-- add a person -->
        <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#, #nom#, #prenom#, #dateNaissance#, #marie#, #nbEnfants#) 
            </insert>
        <!-- update a person -->
        <update id="Personne.updateOne" parameterClass="Personne.classe"> update 
            "PERSONNES" set "VERSION"=#version#+1, "NOM"=#nom#, "PRENOM"=#prenom#, 
            "DATENAISSANCE"=#dateNaissance#, "MARIE"=#marie#, "NBENFANTS"=#nbEnfants# 
            WHERE "ID"=#id# and "VERSION"=#version#</update>
        <!-- delete a person -->
        <delete id="Personne.deleteOne" parameterClass="int"> delete FROM 
            "PERSONNES" WHERE "ID"=#value# </delete>
    </sqlMap>

This is the same content as [people-firebird.xml] with the following minor differences:

  • column and table names are enclosed in quotes and are case-sensitive
  • the SQL statement "Person.insertOne" has changed in lines 34–41. The way the primary key is generated with Postgres differs from that used with Firebird:
    • line 36: the SQL statement [SELECT nextval('"SEQ_ID"')] provides the primary key. The [as value] syntax is mandatory. [value] represents the key obtained. This value will be assigned to the field of the [Person] object designated by the [keyProperty] attribute (line 35), here the [id] field.
    • The SQL statements within the <insert> tag are executed in the order they appear. Therefore, the SELECT is executed before the INSERT. By the time the insertion operation occurs, the [id] field of the [Person] object will have been updated by the SELECT SQL statement.
    • Lines 38–40: insertion of the [Person] object

The implementation class [DaoImplCommon] of the [dao] layer is the one studied in the [Firebird] version.

The configuration of the [dao] layer has been adapted to the [Postgres] DBMS. Thus, the configuration file [spring-config-test-dao-postgres.xml] is as follows:


<?xml version="1.0" encoding="ISO_8859-1"?>
<!DOCTYPE beans SYSTEM "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <!-- data source 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>
    <!-- the [dao] layer access classes -->
    <bean id="dao" class="istia.st.mvc.personnes.dao.DaoImplCommon">
        <property name="sqlMapClient">
            <ref local="sqlMapClient"/>
        </property>
    </bean>
</beans>
  • lines 5–19: The [dataSource] bean now refers to the [Postgres] database [dbpersonnes], whose administrator is [postgres] with the password [postgres]. The reader should modify this configuration according to their own environment.
  • line 31: the [DaoImplCommon] class is the implementation class for the [dao] layer

With these changes made, we can move on to testing.

18.4. Tests for the [dao] and [service] layers

The tests for the [dao] and [service] layers are the same as for the [Firebird] version. Let’s launch the Postgres DBMS and then run the Eclipse tests. The results obtained are as follows:

We can see that the tests passed successfully with the [DaoImplCommon] implementation. We will not need to derive this class as we had to do with the [Firebird] DBMS.

18.5. [Web] Application Tests

To test the web application with the [Postgres] DBMS, we build an Eclipse project [mvc-personnes-04B] in a manner analogous to that used to build the [mvc-personnes-03B] project with the Firebird database (see Section 17.7). However, we do not need to recreate the [personnes-dao.jar] and [personnes-service.jar] archives. In fact, we have not modified any classes compared to the [mvc-personnes-03B] project. The [personnes-dao.jar] archive simply contains the [DaoImplFirebird] class, which is no longer needed.

Image

We deploy the web project [mvc-personnes-04B] within Tomcat:

We are ready for test s. The contents of the [PERSONNES] table are now as follows:

Image

Tomcat is running. Using a browser, we request the URL [http://localhost:8080/mvc-personnes-04B]:

Image

We add a new person using the [Add] link:

We verify the addition in the database:

Image

The reader is invited to perform further tests [update, delete].