Skip to content

18. Aplicación web MVC en una arquitectura de tres capas – Ejemplo 4, Postgres

18.1. La base de datos Postgres

En esta versión, instalaremos la lista de personas en una tabla de la base de datos Postgres 8.x [http://www.postgres.org]. A continuación, las capturas de pantalla provienen del cliente EMS PostgreSQL Manager Lite [http://www.sqlmanager.net/fr/products/postgresql/manager], un cliente de administración gratuito de SGBD Postgres.

La base de datos se llama [dbpersonnes]. Contiene una tabla [PERSONNES]:

Image

La tabla [PERSONNES] contendrá la lista de personas administradas por la aplicación web. Se creó con las siguientes órdenes 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;

No nos detendremos en esta tabla, ya que es análoga a la tabla [PERSONNES] de tipo Firebird que analizamos anteriormente. Sin embargo, cabe señalar que los nombres de las columnas y las tablas están entre comillas. Además, estos nombres distinguen entre mayúsculas y minúsculas. Es posible que este modo de funcionamiento de Postgres 8.x sea configurable. No he profundizado en el tema.

La tabla [PERSONNES] podría tener el siguiente contenido:

Image

Además de la tabla [PERSONNES], la base de datos [dbpersonnes] cuenta con un objeto llamado secuencia y denominado [SEQ_ID]. Este generador produce números enteros sucesivos que utilizaremos para asignar un valor a la clave primaria [ID] de la clase [PERSONNES]. Veamos un ejemplo para ilustrar su funcionamiento:

Se puede observar que el valor [Next value] de la secuencia [SEQ_ID] ha cambiado (haga doble clic en él + F5 para actualizar):

 

El pedido SQL

SELECT nextval('"SEQ_ID"')

permite, por lo tanto, obtener el siguiente valor de la secuencia [SEQ_ID]. Lo utilizaremos en el archivo [personnes-postgres.xml], que reúne las órdenes SQL emitidas en el SGBD.

18.2. El proyecto Eclipse de las capas [dao] y [service]

Para desarrollar las capas [dao] y [service] de nuestra aplicación con la base de datos Postgres 8.x, utilizaremos el siguiente proyecto de Eclipse [spring-mvc-39]:

Image

El proyecto es un proyecto sencillo de Java, no un proyecto web de Tomcat.


Carpeta [src]


Esta carpeta contiene los códigos fuente de las capas [dao] y [service]:

Image

Todos los archivos que incluyen [postgres] en su nombre pueden haber sufrido o no modificaciones con respecto a la versión de Firebird. A continuación, describimos los archivos modificados.


Carpeta [database]


Esta carpeta contiene el script para crear la base de datos Postgres de personas:

Image

-- EMS PostgreSQL Manager Lite 3.1.0.1
-- ---------------------------------------
-- Host      : localhost
-- Base de datos: dbpersonnes



--
-- Definición de la función plpgsql_call_handler (OID = 17230): 
--
...
--
-- Estructura de la tabla 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))
);
--
-- Definición de la secuencia SEQ_ID (OID = 17261): 
--
CREATE SEQUENCE "SEQ_ID"
    INCREMENT BY 1
    NO MAXVALUE
    NO MINVALUE
    CACHE 1;
--
-- Datos para 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);
--
-- Definición para el índice PERSONNES_pkey (OID = 17256): 
--
ALTER TABLE ONLY "PERSONNES"
    ADD CONSTRAINT "PERSONNES_pkey" PRIMARY KEY ("ID");
--
-- Datos para la secuencia pública «SEQ_ID» (OID = 17261)
--
SELECT pg_catalog.setval('"SEQ_ID"', 37, true);
--
-- Comentarios
--
COMMENT ON SCHEMA public IS 'Standard public schema';

Carpeta [lib]


Esta carpeta contiene los archivos necesarios para la aplicación:

Cabe destacar la presencia del controlador JDBC de SGBD para Postgres 8.x. Todos estos archivos forman parte del proyecto Eclipse Classpath.

18.3. La capa [dao]

La capa [dao] es la siguiente:

Image

Solo presentamos los cambios respecto a la versión [Firebird].

El archivo de mapeo [personne-postgres.xml] es el siguiente:


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

<!-- Atención: PostgreSQL 8 requiere la ortografía exacta de los nombres de las columnas
     et des tables ainsi que des guillemets autour de ces noms -->

    <sqlMap>
        <!-- alias de clase [Personne] -->
        <typeAlias alias="Personne.classe" 
            type="istia.st.mvc.personnes.entites.Personne"/>
        <!-- tabla de mapeo [PERSONNES] - objeto [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>
        <!-- lista de todas las personas -->
        <select id="Personne.getAll" resultMap="Personne.map" > select "ID", 
            "VERSION", "NOM", "PRENOM", "DATENAISSANCE", "MARIE", "NBENFANTS" FROM 
            "PERSONNES"</select>
        <!-- obtener una persona en particular -->
        <select id="Personne.getOne" resultMap="Personne.map" >select "ID", 
            "VERSION", "NOM", "PRENOM", "DATENAISSANCE", "MARIE", "NBENFANTS" FROM 
            "PERSONNES" WHERE "ID"=#valor#</select>
        <!-- agregar 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#, 
             #versión#, #apellido#, #nombre#, #dateNaissance#, #esposa#, #nbEnfants#) 
            </insert>
        <!-- actualizar una persona -->
        <update id="Personne.updateOne" parameterClass="Personne.classe"> update 
            "PERSONNES" set "VERSION"=#versión#+1, "NOM"=#apellido#, "PRENOM"=#nombre#, 
            "DATENAISSANCE"=#dateNaissance#, "MARIE"=#marie#, "NBENFANTS"=#nbEnfants# 
            WHERE "ID"=#id# y "VERSION"=#versión#</update>
        <!-- eliminar a una persona -->
        <delete id="Personne.deleteOne" parameterClass="int"> delete FROM 
            "PERSONNES" WHERE "ID"=#valor# </eliminar>
    </sqlMap>

Es el mismo contenido que el de [personnes-firebird.xml], salvo por los siguientes detalles:

  • los nombres de las columnas y las tablas están entre comillas y distinguen entre mayúsculas y minúsculas
  • el orden SQL " Personne.insertOne " ha cambiado en las líneas 34 a 41. La forma de generar la clave primaria con Postgres es diferente a la utilizada con Firebird:
    • línea 36: el orden SQL [SELECT nextval('"SEQ_ID"')] proporciona la clave primaria. La sintaxis [as value] es obligatoria. [value] representa la clave obtenida. Este valor se asignará al campo del objeto [Personne] designado por el atributo [keyProperty] (línea 35), en este caso el campo [id].
    • Las órdenes SQL de la etiqueta <insert> se ejecutan en el orden en que aparecen. Por lo tanto, la orden SELECT se ejecuta antes que la orden INSERT. Por lo tanto, al momento de la operación de inserción, el campo [id] del objeto [Personne] ya habrá sido actualizado por la orden SQL SELECT.
    • líneas 38-40: inserción del objeto [Personne]

La clase de implementación [DaoImplCommon] de la capa [dao] es la que se analizó en la versión [Firebird].

La configuración de la capa [dao] se ha adaptado a SGBD y [Postgres]. Por lo tanto, el archivo de configuración [spring-config-test-dao-postgres.xml] es el siguiente:


<?xml version="1.0" encoding="ISO_8859-1"?>
<!DOCTYPE beans SYSTEM "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <!-- la fuente de datos 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>
    <!-- las clases de acceso a la capa [dao] -->
    <bean id="dao" class="istia.st.mvc.personnes.dao.DaoImplCommon">
        <property name="sqlMapClient">
            <ref local="sqlMapClient"/>
        </property>
    </bean>
</beans>
  • líneas 5-19: el bean [dataSource] ahora apunta a la base de datos [Postgres] [dbpersonnes], cuyo administrador es [postgres] con la contraseña [postgres]. El lector deberá modificar esta configuración de acuerdo con su propio entorno.
  • Línea 31: la clase [DaoImplCommon] es la clase de implementación de la capa [dao]

Una vez realizadas estas modificaciones, podemos pasar a las pruebas.

18.4. Pruebas de las capas [dao] y [service]

Las pruebas de las capas [dao] y [service] son las mismas que para la versión [Firebird]. Iniciemos el Postgres SGBD y luego las pruebas de Eclipse. Los resultados obtenidos son los siguientes:

Se observa que las pruebas se han superado con éxito con la implementación [DaoImplCommon]. No tendremos que derivar esta clase, como había sido necesario hacerlo con SGBD y [Firebird].

18.5. Pruebas de la aplicación [web]

Para probar la aplicación web con SGBD y [Postgres], creamos un proyecto de Eclipse [mvc-personnes-04B] de manera similar a la utilizada para crear el proyecto [mvc-personnes-03B] con la base de datos Firebird (véase el párrafo 17.7). Sin embargo, no es necesario volver a crear los archivos [personnes-dao.jar] y [personnes-service.jar]. De hecho, no hemos modificado ninguna clase con respecto al proyecto [mvc-personnes-03B]. Simplemente, el archivo [personnes-dao.jar] contiene la clase [DaoImplFirebird], que ya no es necesaria.

Image

Implementamos el proyecto web [mvc-personnes-04B] en Tomcat:

Estamos listos para las pruebas de . El contenido de la tabla [PERSONNES] es entonces el siguiente:

Image

Tomcat está en ejecución. Con un navegador, accedemos a la URL [http://localhost:8080/mvc-personnes-04B]:

Image

Agregamos una nueva persona mediante el enlace [Ajout]:

Verificamos la adición en la base de datos:

Image

Se invita al lector a realizar otras pruebas con [modification, suppression].