Skip to content

18. Aplicação web MVC numa arquitetura de três camadas – Exemplo 4, Postgres

18.1. A base de dados Postgres

Nesta versão, vamos instalar a lista de pessoas numa tabela da base de dados Postgres 8.x [http://www.postgres.org]. A seguir, as capturas de ecrã provêm do cliente EMS PostgreSQL Manager Lite [http://www.sqlmanager.net/fr/products/postgresql/manager], um cliente de administração gratuito do SGBD Postgres.

A base de dados chama-se [dbpersonnes]. Contém uma tabela [PERSONNES]:

Image

A tabela [PERSONNES] conterá a lista de pessoas geridas pela aplicação web. Foi criada com os seguintes comandos 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;

Não nos detemos nesta tabela, que é análoga à tabela [PERSONNES] do tipo Firebird analisada anteriormente. Note-se, no entanto, que os nomes das colunas e das tabelas estão entre aspas. Além disso, estes nomes distinguem maiúsculas de minúsculas. É possível que este modo de funcionamento do Postgres 8.x seja configurável. Não aprofundei esta questão.

A tabela [PERSONNES] poderia ter o seguinte conteúdo:

Image

Para além da tabela [PERSONNES], a base de dados [dbpersonnes] possui um objeto denominado «sequência» e com o nome [SEQ_ID]. Este gerador produz números inteiros sucessivos que utilizaremos para atribuir um valor à chave primária [ID] da classe [PERSONNES]. Vejamos um exemplo para ilustrar o seu funcionamento:

Pode-se verificar que o valor [Next value] da sequência [SEQ_ID] mudou (clique duas vezes nele + F5 para atualizar):

 

A ordem SQL

SELECT nextval('"SEQ_ID"')

permite, assim, obter o seguinte valor da sequência [SEQ_ID]. Iremos utilizá-lo no ficheiro [personnes-postgres.xml], que reúne as ordens SQL emitidas no SGBD.

18.2. O projeto Eclipse das camadas [dao] e [service]

Para desenvolver as camadas [dao] e [service] da nossa aplicação com a base de dados Postgres 8.x, utilizaremos o seguinte projeto Eclipse [spring-mvc-39]:

Image

O projeto é um projeto Java simples, não um projeto web Tomcat.


Pasta [src]


Esta pasta contém os códigos-fonte das camadas [dao] e [service]:

Image

Todos os ficheiros cujo nome inclua [postgres] podem ou não ter sofrido alterações em relação à versão Firebird. A seguir, descrevemos os ficheiros alterados.


Pasta [database]


Esta pasta contém o script de criação da base de dados Postgres das pessoas:

Image

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



--
-- Definição da função plpgsql_call_handler (OID = 17230): 
--
...
--
-- Estrutura da tabela 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))
);
--
-- Definição para a sequência SEQ_ID (OID = 17261): 
--
CREATE SEQUENCE "SEQ_ID"
    INCREMENT BY 1
    NO MAXVALUE
    NO MINVALUE
    CACHE 1;
--
-- Dados 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);
--
-- Definição para o índice PERSONNES_pkey (OID = 17256): 
--
ALTER TABLE ONLY "PERSONNES"
    ADD CONSTRAINT "PERSONNES_pkey" PRIMARY KEY ("ID");
--
-- Dados para a sequência pública «SEQ_ID» (OID = 17261)
--
SELECT pg_catalog.setval('"SEQ_ID"', 37, true);
--
-- Comentários
--
COMMENT ON SCHEMA public IS 'Standard public schema';

Pasta [lib]


Esta pasta contém os ficheiros necessários para a aplicação:

De salientar a presença do controlador JDBC do SGBD Postgres 8.x. Todos estes ficheiros fazem parte do Classpath do projeto Eclipse.

18.3. A camada [dao]

A camada [dao] é a seguinte:

Image

Apresentamos apenas as alterações em relação à versão [Firebird].

O ficheiro de mapeamento [personne-postgres.xml] é o seguinte:


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

<!-- Atenção — o PostgreSQL 8 exige a grafia exata dos nomes das colunas
     et des tables ainsi que des guillemets autour de ces noms -->

    <sqlMap>
        <!-- alias da classe [Personne] -->
        <typeAlias alias="Personne.classe" 
            type="istia.st.mvc.personnes.entites.Personne"/>
        <!-- tabela de mapeamento [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 as pessoas -->
        <select id="Personne.getAll" resultMap="Personne.map" > select "ID", 
            "VERSION", "NOM", "PRENOM", "DATENAISSANCE", "MARIE", "NBENFANTS" FROM 
            "PERSONNES"</select>
        <!-- obter uma pessoa específica -->
        <select id="Personne.getOne" resultMap="Personne.map" >select "ID", 
            "VERSION", "NOM", "PRENOM", "DATENAISSANCE", "MARIE", "NBENFANTS" FROM 
            "PERSONNES" WHERE "ID"=#valor#</select>
        <!-- adicionar uma pessoa -->
        <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#, 
             #versão#, #apelido#, #nome próprio#, #dateNaissance#, #casada com#, #nbEnfants#) 
            </insert>
        <!-- atualizar um utilizador -->
        <update id="Personne.updateOne" parameterClass="Personne.classe"> update 
            "PERSONNES" set "VERSION"=#versão#+1, "NOM"=#apelido#, "PRENOM"=#nome próprio#, 
            "DATENAISSANCE"=#dateNaissance#, "MARIE"=#marie#, "NBENFANTS"=#nbEnfants# 
            WHERE "ID"=#id# e "VERSION"=#versão#</update>
        <!-- eliminar uma pessoa -->
        <delete id="Personne.deleteOne" parameterClass="int"> delete FROM 
            "PERSONNES" WHERE "ID"=#valor# </delete>
    </sqlMap>

O conteúdo é idêntico ao do ficheiro [personnes-firebird.xml], com as seguintes diferenças:

  • os nomes das colunas e das tabelas estão entre aspas e distinguem maiúsculas de minúsculas
  • a ordem SQL " Personne.insertOne " foi alterada nas linhas 34-41. A forma de gerar a chave primária com o Postgres é diferente da utilizada com o Firebird:
    • linha 36: a ordem SQL [SELECT nextval('"SEQ_ID"')] fornece a chave primária. A sintaxe [as value] é obrigatória. [value] representa a chave obtida. Este valor será atribuído ao campo do objeto [Personne] designado pelo atributo [keyProperty] (linha 35), neste caso o campo [id].
    • As ordens SQL da baliza <insert> são executadas pela ordem em que são encontradas. Assim, a ordem SELECT é executada antes da ordem INSERT. No momento da operação de inserção, o campo [id] do objeto [Personne] terá, portanto, sido atualizado pela ordem SQL SELECT.
    • linhas 38-40: inserção do objeto [Personne]

A classe de implementação [DaoImplCommon] da camada [dao] é a que foi analisada na versão [Firebird].

A configuração da camada [dao] foi adaptada para SGBD e [Postgres]. Assim, o ficheiro de configuração [spring-config-test-dao-postgres.xml] é o seguinte:


<?xml version="1.0" encoding="ISO_8859-1"?>
<!DOCTYPE beans SYSTEM "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <!-- a fonte de dados 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>
    <!-- a classe de acesso à camada [dao] -->
    <bean id="dao" class="istia.st.mvc.personnes.dao.DaoImplCommon">
        <property name="sqlMapClient">
            <ref local="sqlMapClient"/>
        </property>
    </bean>
</beans>
  • linhas 5-19: o bean [dataSource] refere-se agora à base de dados [Postgres] [dbpersonnes], cujo administrador é [postgres] com a palavra-passe [postgres]. O leitor deverá adaptar esta configuração ao seu próprio ambiente.
  • linha 31: a classe [DaoImplCommon] é a classe de implementação da camada [dao]

Feitas estas alterações, pode-se passar aos testes.

18.4. Testes das camadas [dao] e [service]

Os testes das camadas [dao] e [service] são os mesmos que para a versão [Firebird]. Vamos iniciar o SGBD Postgres e, em seguida, os testes do Eclipse. Os resultados obtidos são os seguintes:

Verifica-se que os testes foram concluídos com sucesso com a implementação [DaoImplCommon]. Não será necessário derivar esta classe, como tinha sido necessário fazer com o SGBD e o [Firebird].

18.5. Testes da aplicação [web]

Para testar a aplicação web com o SGBD e o [Postgres], criamos um projeto Eclipse [mvc-personnes-04B] de forma semelhante à utilizada para criar o projeto [mvc-personnes-03B] com a base de dados Firebird (ver parágrafo 17.7). No entanto, não precisamos de recriar os arquivos [personnes-dao.jar] e [personnes-service.jar]. Com efeito, não alterámos nenhuma classe em relação ao projeto [mvc-personnes-03B]. Simplesmente, o arquivo [personnes-dao.jar] contém a classe [DaoImplFirebird], que se tornou desnecessária.

Image

Estamos a implementar o projeto web [mvc-personnes-04B] no Tomcat:

Estamos prontos para os testes de « ». O conteúdo da tabela [PERSONNES] é, então, o seguinte:

Image

O Tomcat está em execução. Num navegador, acedemos à URL [http://localhost:8080/mvc-personnes-04B]:

Image

Adicionamos uma nova pessoa através do link [Ajout]:

Verificamos a adição na base de dados:

Image

O leitor é convidado a realizar outros testes com o [modification, suppression].