Skip to content

9. Exploração de uma fonte de dados

Propomos aqui estabelecer as bases para a exploração de bases de dados no âmbito de uma aplicação Struts.

9.1. A aplicação Struts /listarticles

Pretendemos apresentar o conteúdo de uma tabela que regista as características dos artigos vendidos por uma loja.

code
código do artigo - chave primária
nom
nome do artigo
prix
preço do artigo
stockActuel
stock atual do artigo
stockMinimum
stock abaixo do qual
é necessário iniciar um reabastecimento

O conteúdo da tabela é o seguinte:

Image

A aplicação listarticles irá apresentar-nos o mesmo resultado (de forma menos elegante) numa página web:

Image

9.2. Configuração da aplicação Struts /listarticles

O ficheiro web.xml da aplicação é padrão:

web.xml

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
    <servlet>
      <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    <init-param>
        <param-name>config</param-name>
      <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
      <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>

  <taglib>
      <taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri>
    <taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
  </taglib>
  <taglib>
      <taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
    <taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
  </taglib>

</web-app>

O ficheiro struts-config.xml introduz uma nova secção <data-sources> que permite declarar e configurar fontes de dados:

struts-config.xml

<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">

<struts-config>
    <data-sources>
        <data-source key="dbarticles">
            <set-property property="driverClass" value="com.mysql.jdbc.Driver"></set-property>
            <set-property property="url" value="jdbc:mysql://localhost/dbarticles"></set-property>
            <set-property property="user" value="admarticles"></set-property>
            <set-property property="password" value="mdparticles"></set-property>
            <set-property property="minCount" value="2"></set-property>
            <set-property property="maxCount" value="5"></set-property>
        </data-source>
    </data-sources>

    <action-mappings>
        <action path="/liste" type="istia.st.struts.articles.ListeArticlesAction">
            <forward name="afficherListeArticles" path="/vues/listarticles.jsp"/>
            <forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
        </action>
    </action-mappings>

    <message-resources parameter="istia.st.struts.articles.ApplicationResources" 
        null="false" />

</struts-config>

9.2.1. Fontes de dados

A secção <data-sources> permite declarar todas as fontes de dados da aplicação, sendo cada uma delas descrita por uma secção <data-source>. Esta baliza admite vários atributos:

key
identifica a fonte de dados quando existem várias.
driverClass
o nome da classe a instanciar para aceder à base de dados. Esta classe encontra-se geralmente numa biblioteca de classes (.jar) fornecida pelo editor do SGBD. Esta biblioteca contém o controlador JDBC de acesso à base de dados. A propriedade driverClass identifica este controlador. O ficheiro .jar das classes de acesso ao SGBD será colocado na pasta WEB-INF/lib da aplicação.
url
cadeia de ligação a uma base de dados específica. Com efeito, o controlador JDBC permite o acesso a todas as bases de dados geridas pelo SGBD. A propriedade url permite-nos indicar qual delas iremos utilizar.
user, password
O acesso às bases de dados está protegido. O SGBD gere utilizadores identificados por um nome de utilizador e uma palavra-passe. Estes são especificados nos atributos «user» e «password» da baliza.
minCount, maxCount
O Struts irá gerir um conjunto de ligações ao SGBD. Abrir uma ligação a um SGBD é uma operação que consome muitos recursos em termos de tempo e memória. Em vez de abrir e fechar uma ligação ao SGBD para cada uma das solicitações que serão feitas à aplicação, a aplicação gere um conjunto de n ligações, em que n está no intervalo [minCount, maxCount]. Se, durante uma solicitação, a aplicação precisar de uma ligação:
- procura obter uma no conjunto de ligações. Se encontrar uma disponível, utiliza-a.
- se não houver ligações livres no conjunto e se houver menos de maxCount ligações no conjunto, é aberta uma nova ligação e adicionada ao conjunto. A solicitação atual poderá utilizá-la.
- se não houver ligações livres e não for possível criar uma nova, o pedido fica em espera.
Quando a consulta atual encerra a ligação, esta não é realmente encerrada, mas sim devolvida ao conjunto.

Aqui, temos as seguintes propriedades:

key
dbarticles — é o nome pelo qual a fonte de dados será identificada no contexto da aplicação
driverClass
com.mysql.jdbc.Driver. Utilizamos uma base de dados MySQL.
url
jdbc:mysql://localhost/dbarticles. A base de dados chama-se dbarticles e está presente no computador local.
user, password
admarticles, mdparticles. Este utilizador foi definido com todos os privilégios sobre a base de dados dbarticles.
minCount, maxCount
2, 5. Mínimo de 2 ligações no pool, máximo de 5.

9.2.2. Ações

As ações declaradas no ficheiro de configuração do Struts são as seguintes:

    <action-mappings>
        <action path="/liste" type="istia.st.struts.articles.ListeArticlesAction">
            <forward name="afficherListeArticles" path="/vues/listarticles.jsp"/>
            <forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
        </action>
    </action-mappings>

A única ação chama-se /liste e está associada à classe istia.st.struts.articles.ListeArticlesAction. Esta ação termina com a exibição:

  • da página dos artigos (/vues/listarticles.jsp)
  • da página de erros (/vues/erreurs.jsp)

9.2.3. O ficheiro de mensagens

    <message-resources parameter="istia.st.struts.articles.ApplicationResources" 
        null="false" />

O ficheiro de mensagens ApplicationResources.properties será colocado na pasta WEB-INF/classes/istia/st/struts/articles. O seu conteúdo será o seguinte:

# erros
errors.header=<ul>
errors.footer=</ul>
erreur.dbarticles=<li>Erreur d'accès à la base des articles ({0})</li>

Pode ocorrer apenas um erro: um erro de acesso à base de dados. Na verdade, existem vários tipos de erros possíveis, todos agrupados na mesma mensagem de erro. A causa exata do erro será, no entanto, especificada no parâmetro {0}.

9.3. As vistas

9.3.1. A vista erreurs.jsp

Esta vista deve apresentar uma lista de erros. Já a encontrámos várias vezes.

<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>

<html>
    <head>
      <title>Liste d'articles - erreurs</title>
  </head>
  <body>
      <h2>Les erreurs suivantes se sont produites</h2>
        <html:errors/>
  </body>
</html>

Esta vista limita-se a apresentar a lista de erros com a baliza <html:errors/>.

9.3.2. A vista listarticles.jsp

Esta vista deve apresentar o conteúdo da tabela ARTICLES da base de dados. O seu código é o seguinte:

<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

<%
     // listarticles: ArrayList na consulta
     // listArticles(i): tabela (String[5]) com 5 elementos 
%>

<html>
    <head>
        <title>DataSource Struts</title>
    </head>
    <body>
        <h3>Liste des articles</h3>
        <hr>
        <table border="1">
            <logic:iterate id="ligne" name="listArticles">
                <tr>
                    <logic:iterate id="colonne" name="ligne">
                        <td><bean:write name="colonne"/></td>
                    </logic:iterate>
                </tr>
            </logic:iterate>
        </table>
    </body>
</html>    

A ação /liste irá colocar o conteúdo da tabela ARTICLES num objeto ArrayList, inserido na consulta com o nome listArticles. Cada elemento do objeto listArticles é uma matriz de 5 cadeias de caracteres que representam as 5 informações (código, nome, preço, stockActuel, stockMinimum) associadas a um artigo na tabela. A vista deve apresentar o conteúdo do objeto listArticles numa tabela HTML. Para tal, utiliza a baliza <logic:iterate>:

            <logic:iterate id="ligne" name="listArticles">
                <tr>
                    <logic:iterate id="colonne" name="ligne">
                        <td><bean:write name="colonne"/></td>
                    </logic:iterate>
                </tr>
            </logic:iterate>

Existem aqui duas iterações. A primeira percorre os elementos do objeto ArrayList listArticles. O elemento atual de listArticles é aqui designado por «linha». O elemento «linha» representa um objeto String[5] que é percorrido através da segunda iteração. O elemento desta segunda iteração é denominado «coluna». Representa o elemento atual de uma matriz de cadeias de caracteres e é, portanto, uma cadeia de caracteres (código, nome, preço, stockActuel, stockMinimum). O seu valor é apresentado através da baliza <bean:write>.

A vista utiliza tags das bibliotecas struts-logic e struts-bean. Por isso, é necessário declarar a utilização destas:

<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

9.4. A ação /liste

A ação /liste tem como objetivo inserir na solicitação um objeto ArrayList que representa o conteúdo da tabela ARTICLES, para que uma vista o possa utilizar. O seu código é o seguinte:

package istia.st.struts.articles;

import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

public class ListeArticlesAction extends Action {

    public ActionForward execute(
        ActionMapping mapping,
        ActionForm form,
        HttpServletRequest request,
        HttpServletResponse response)
        throws IOException, ServletException {

         //  o conteúdo da tabela de artigos de uma ligação
         // estabelecida na inicialização do contexto
         // recupera-se a fonte de dados «dbarticles»
        DataSource dataSource = this.getDataSource(request, "dbarticles");
        if (dataSource == null) {
             // não foi possível criar a fonte de dados
            ActionErrors erreurs = new ActionErrors();
            erreurs.add(    "dbarticles",new ActionError("erreur.dbarticles","La source de données n'a pu être créée"));
            this.saveErrors(request, erreurs);
            return mapping.findForward("afficherErreurs");
        }
         // aqui, a fonte de dados existe  é utilizada         Conexão conexão = null;
        Statement st = null;
        ResultSet rs = null;
        String requête = null;
        ArrayList alArticles = new ArrayList();
         // gestão de erros
        try {
             // obter uma ligação
            connexion = dataSource.getConnection();
             // preparar a consulta SQL
            requête =
                "select code, nom, prix, stockActuel, stockMinimum from articles order by nom";
             // executá-la
            st = connexion.createStatement();
            rs = st.executeQuery(requête);
             // analisar os resultados
            while (rs.next()) {
                 // gravar a linha atual
                alArticles.add(
                    new String[] {
                        rs.getString("code"),
                        rs.getString("nom"),
                        rs.getString("prix"),
                        rs.getString("stockactuel"),
                        rs.getString("stockMinimum")});
                // linha seguinte
            } //enquanto
             // liberar os recursos
            rs.close();
            st.close();
            connexion.close();
        } catch (Exception ex) {
             // ocorreram erros
            ActionErrors erreurs = new ActionErrors();
            erreurs.add("dbarticles",new ActionError("erreur.dbarticles", ex.getMessage()));
            this.saveErrors(request, erreurs);
            return mapping.findForward("afficherErreurs");
        }
         // tudo bem
        request.setAttribute("listArticles", alArticles);
        return mapping.findForward("afficherListeArticles");
    } //executar
} //classe

O leitor é, sem dúvida, capaz de compreender a essência do código acima. Iremos centrar-nos apenas na única novidade: a utilização de um objeto DataSource fornecido pelo framework Struts. Este objeto DataSource representa o conjunto de ligações configurado pela secção <data-source key="dbarticles"> do ficheiro de configuração. O objeto DataSource foi colocado, após a sua criação, no contexto da aplicação para que todos os objetos desse contexto tenham acesso ao mesmo. É, portanto, recuperado da seguinte forma:

         // a recuperar a fonte de dados dbarticles
        DataSource dataSource = this.getDataSource(request, "dbarticles");

Aqui, solicitamos a fonte de dados identificada pela chave «dbarticles». Se recuperarmos um ponteiro null, isso significa que a fonte de dados não pôde ser criada durante a inicialização da aplicação. Nesse caso, registamos o erro num objeto ActionErrors e passamos o controlo para a página de erros identificada pela chave «afficherErreurs» no ficheiro de configuração:

        <action path="/liste" type="istia.st.struts.articles.ListeArticlesAction">
            <forward name="afficherListeArticles" path="/vues/listarticles.jsp"/>
            <forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
        </action>
    </action-mappings>

Assim que a fonte de dados for obtida, temos acesso ao conjunto de ligações. Uma ligação à base de dados é estabelecida através do código:

             // obter uma ligação
            Connection connexion = dataSource.getConnection();

Neste caso, o pool fornecer-nos-á uma ligação reutilizada ou criada, caso ainda haja possibilidade de criar uma nova.

9.5. Implementação

O contexto da aplicação é definido no ficheiro de configuração server.xml do Tomcat:

<Context path="/listarticles" reloadable="true" docBase="E:\data\serge\web\struts\articles\liste" />

A estrutura da aplicação é a seguinte:

  
 
 

Note-se a biblioteca mysql-connector-java-3.0.10-stable-bin.jar em WEB-INF/lib. É esta que contém o controlador JDBC da base MySQL aqui utilizada.

9.6. Os testes

Iniciamos o Tomcat e solicitamos o URL http://localhost:8080/listarticles/liste.do:

Image

9.7. Uma segunda fonte de dados

Utilizamos agora uma segunda fonte de dados presente numa base de dados Postgres. Também aqui, os dados encontram-se numa tabela ARTICLES com as mesmas colunas que anteriormente. O seu conteúdo é o seguinte:

Image

Esta nova fonte de dados é declarada no ficheiro de configuração do Struts:

    <data-sources>
        <data-source key="dbarticles" >
            <set-property property="driverClass" value="com.mysql.jdbc.Driver"></set-property>
            <set-property property="url" value="jdbc:mysql://localhost/dbarticles"></set-property>
            <set-property property="user" value="admarticles"></set-property>
            <set-property property="password" value="mdparticles"></set-property>
            <set-property property="minCount" value="2"></set-property>
            <set-property property="maxCount" value="5"></set-property>
        </data-source>
        <data-source key="pgdbarticles" type="org.apache.commons.dbcp.BasicDataSource">
            <set-property property="driverClassName" value="org.postgresql.Driver" />
            <set-property property="url" value="jdbc:postgresql://localhost/dbarticles" />
            <set-property property="username" value="serge" />
            <set-property property="password" value="serge" />
            <set-property property="maxActive" value="10" />
            <set-property property="maxWait" value="5000" />
            <set-property property="defaultAutoCommit" value="false" />
            <set-property property="defaultReadOnly" value="false" />
        </data-source>
    </data-sources>

A nova fonte terá como identificador (chave) pgdbarticles. A novidade provém da classe que implementa a interface javax.sql.DataSource. A classe predefinida é a org.apache.struts.util.GenericDataSource, definida na biblioteca struts.jar. Esta classe será descontinuada em futuras versões do Struts e, para a versão 1.1, recomenda-se a utilização da classe org.apache.commons.dbcp.BasicDataSource, que se encontra na biblioteca commons-dbcp-1.1.jar. Esta não é necessariamente fornecida com o pacote Struts. Pode ser encontrada no endereço http://jakarta.apache.org/commons/index.html e, mais especificamente, no endereço http://jakarta.apache.org/site/binindex.cgi. É necessário descarregar o produto denominado Commons DBCP. No Windows, é possível descarregar o ficheiro .zip. Este contém o código-fonte da biblioteca, bem como o ficheiro .jar correspondente. É necessário extrair este último do arquivo .zip e colocá-lo na pasta WEB-INF/lib da aplicação. É igualmente necessário colocar nessa mesma pasta o controlador do SGBD utilizado:

Image

Para utilizar este código-fonte, alteramos uma instrução no código da ação /liste:

        DataSource dataSource = this.getDataSource(request, "pgdbarticles");

Desta vez, solicitamos a fonte de dados denominada pgdbarticles, c.a.d, a fonte de dados Postgres.

Resta-nos apenas compilar tudo, iniciar o Tomcat e aceder à página http://localhost:8080/listarticles/liste.do:

Image

9.8. Conclusão

Mostrámos como utilizar um conjunto de ligações para aceder a uma base de dados. Note-se aqui que não respeitámos a arquitetura MVC. Com efeito, a ação /liste efetua ela própria as consultas SQL para aceder aos dados. Teria sido preferível que ela recorresse a uma classe de negócio intermédia que ocultasse o facto de que os dados são obtidos num SGBD. Provavelmente, seria essa classe de negócio a criar o conjunto de ligações, pelo que não seria necessário declará-lo no ficheiro de configuração do Struts. Isto é um indício: a presença de uma secção <data-sources> no ficheiro de configuração pode significar que a nossa aplicação não respeita a arquitetura MVC.