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, vamos a almacenar 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 proceden 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 gestionadas por la aplicación web. Se ha creado 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 hemos analizado anteriormente. Sin embargo, cabe señalar que los nombres de las columnas y de las tablas aparecen 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 la cuestión.

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

Image

Además de la tabla [PERSONNES], la base de datos [dbpersonnes] contiene un objeto denominado «secuencia» y denominado [SEQ_ID]. Este generador proporciona 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 (haz doble clic sobre él + F5 para actualizar):

 

El pedido SQL

SELECT nextval('"SEQ_ID"')

permite, por tanto, obtener el siguiente valor de la secuencia [SEQ_ID]. Lo utilizaremos en el archivo [personnes-postgres.xml], que recopila 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 simple proyecto 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 alguna modificación con respecto a la versión de Firebird. A continuación, describimos los archivos modificados.


Carpeta [database]


Esta carpeta contiene el script de creación de 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 del índice PERSONNES_pkey (OID = 17256): 
--
ALTER TABLE ONLY "PERSONNES"
    ADD CONSTRAINT "PERSONNES_pkey" PRIMARY KEY ("ID");
--
-- Datos de 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 exige 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 correspondencias [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 concreto -->
        <select id="Personne.getOne" resultMap="Personne.map" >select "ID", 
            "VERSION", "NOM", "PRENOM", "DATENAISSANCE", "MARIE", "NBENFANTS" FROM 
            "PERSONNES" WHERE "ID"=#valor#</select>
        <!-- añadir 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#, #apellidos#, #nombre#, #dateNaissance#, #marie#, #nbEnfants#) 
            </insert>
        <!-- actualizar una persona -->
        <update id="Personne.updateOne" parameterClass="Personne.classe"> update 
            "PERSONNES" set "VERSION"=#versión#+1, «NOM»=#apellidos#, «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# </delete>
    </sqlMap>

El contenido es idéntico al 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-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. En el momento de la operación de inserción, el campo [id] del objeto [Personne] habrá sido actualizado por las órdenes SQL y SELECT.
    • líneas 38-40: inserción del objeto [Personne]

La clase de implementación [DaoImplCommon] de la capa [dao] es la que se analiza 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 hace referencia a la base de datos [Postgres] [dbpersonnes], cuyo administrador es [postgres] con la contraseña [postgres]. El lector deberá modificar esta configuración según 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]. Ejecutemos SGBD Postgres y, a continuación, 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 hacer 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 Eclipse [mvc-personnes-04B] de forma similar a la utilizada para crear el proyecto [mvc-personnes-03B] con la base de datos Firebird (véase el apartado 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:

Ya estamos listos para las pruebas de « ». El contenido de la tabla [PERSONNES] es entonces el siguiente:

Image

Tomcat se ha iniciado. Con un navegador, accedemos a la URL [http://localhost:8080/mvc-personnes-04B]:

Image

Añadimos una nueva persona mediante el enlace [Ajout]:

Comprobamos la incorporación en la base de datos:

Image

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