Skip to content

9. Trabalhar com uma fonte de dados

Aqui, pretendemos estabelecer as bases para trabalhar com bases de dados numa aplicação Struts.

9.1. A aplicação Struts /listarticles

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

código
código do artigo - chave primária
nome
nome do item
preço
preço do artigo
Stock atual
Stock atual do artigo
stock mínimo
Nível de stock abaixo do qual
deve ser iniciado um reabastecimento

A tabela contém o seguinte:

Image

A aplicação listarticles apresentará as mesmas informações (embora 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> é utilizada para declarar todas as fontes de dados da aplicação, cada uma das quais é descrita por uma secção <data-source>. Esta tag suporta vários atributos:

key
identifica a fonte de dados quando existem várias fontes.
driverClass
o nome da classe a ser instanciada para aceder à base de dados. Esta classe encontra-se normalmente numa biblioteca de classes (.jar) fornecida pelo fornecedor do SGBD. Esta biblioteca contém o controlador JDBC para acesso à base de dados. A propriedade driverClass especifica este controlador. O ficheiro .jar que contém as 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. O controlador JDBC permite o acesso a todas as bases de dados geridas pelo SGBD. A propriedade url permite-nos especificar qual delas iremos utilizar.
utilizador, palavra-passe
O acesso à base de dados é 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 tag.
minCount, maxCount
O Struts gere 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 pedido feito à aplicação, a aplicação gere um conjunto de n ligações, em que n está no intervalo [minCount, maxCount]. Se, durante um pedido, a aplicação precisar de uma ligação:
- tentará obter uma do conjunto de conexões. Se encontrar uma livre, utiliza-a.
- se não houver ligações livres no conjunto e houver menos de maxCount ligações no conjunto, é aberta uma nova ligação e adicionada ao conjunto. A consulta atual pode utilizá-la.
- Se não houver ligações livres e não for possível criar uma nova, a solicitação é colocada em espera.
Quando a solicitação atual encerra a conexão, ela não é realmente encerrada, mas devolvida ao pool.

Aqui, temos as seguintes propriedades:

chave
dbarticles - este é o nome pelo qual a fonte de dados será identificada no contexto da aplicação
driverClass
com.mysql.jdbc.Driver. Estamos a utilizar uma base de dados MySQL.
url
jdbc:mysql://localhost/dbarticles. A base de dados chama-se dbarticles e está localizada na máquina local.
usuário, senha
admarticles, mdparticles. A este utilizador foram concedidos privilégios totais na base de dados dbarticles.
minCount, maxCount
2, 5. Mínimo de 2 ligações no conjunto, 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 /list e está associada à classe istia.st.struts.articles.ListeArticlesAction. Esta ação termina com a exibição:

  • a página de artigos (/vues/listarticles.jsp)
  • a página de erro (/views/errors.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:

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

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

9.3. Visualizações

9.3.1. A vista errors.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 apresenta simplesmente a lista de erros utilizando a tag <html:errors/>.

9.3.2. A vista listarticles.jsp

Esta vista deve apresentar o conteúdo da tabela ARTICLES na 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 dans la requête
    // listArticles(i) : tableau (String[5]) de 5 éléments 
%>

<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 /list irá colocar o conteúdo da tabela ARTICLES num objeto ArrayList denominado listArticles na solicitação. Cada elemento do objeto listArticles é uma matriz de 5 strings que representam as 5 informações (código, nome, preço, stock atual, stock mínimo) associadas a um item na tabela. A vista deve apresentar o conteúdo do objeto listArticles numa tabela HTML. Para tal, utiliza a tag <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 row. O elemento row representa um objeto String[5] que é percorrido utilizando a segunda iteração. O elemento desta segunda iteração é chamado de column. Representa o elemento atual de uma matriz de strings e é, portanto, uma string (code, name, price, currentStock, minimumStock). O seu valor é exibido utilizando a tag <bean:write>.

A vista utiliza tags das bibliotecas struts-logic e struts-bean. Por conseguinte, deve declarar a sua utilização:

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

9.4. A ação /list

O objetivo da ação /list é incluir um objeto ArrayList que represente o conteúdo da tabela ARTICLES na solicitação, para que uma vista possa utilizá-lo. 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 {

         // reads the contents of a connection's items table
         // performed at context init
         // retrieve the dbarticles data source
        DataSource dataSource = this.getDataSource(request, "dbarticles");
        if (dataSource == null) {
             // the data source could not be created
            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");
        }
         // here the data source exists - we use it Conne        ction connexion = null;
        Statement st = null;
        ResultSet rs = null;
        String requête = null;
        ArrayList alArticles = new ArrayList();
         // error handling
        try {
             // get a connection
            connexion = dataSource.getConnection();
             // prepare query SQL
            requête =
                "select code, nom, prix, stockActuel, stockMinimum from articles order by nom";
             // execute it
            st = connexion.createStatement();
            rs = st.executeQuery(requête);
             // exploit results
            while (rs.next()) {
                 // save current line
                alArticles.add(
                    new String[] {
                        rs.getString("code"),
                        rs.getString("nom"),
                        rs.getString("prix"),
                        rs.getString("stockactuel"),
                        rs.getString("stockMinimum")});
                // next line
            } //while
             // free up resources
            rs.close();
            st.close();
            connexion.close();
        } catch (Exception ex) {
             // errors have occurred
            ActionErrors erreurs = new ActionErrors();
            erreurs.add("dbarticles",new ActionError("erreur.dbarticles", ex.getMessage()));
            this.saveErrors(request, erreurs);
            return mapping.findForward("afficherErreurs");
        }
         // it's good
        request.setAttribute("listArticles", alArticles);
        return mapping.findForward("afficherListeArticles");
    } //execute
} //class

É provável que o leitor consiga compreender a essência do código acima. Vamos concentrar-nos apenas num único elemento novo: a utilização de um objeto DataSource fornecido pela estrutura Struts. Este objeto DataSource representa o conjunto de ligações configurado pela secção <data-source key="dbarticles"> do ficheiro de configuração. Após a sua criação, o objeto DataSource foi colocado no contexto da aplicação para que todos os objetos nesse contexto possam aceder-lhe. É, portanto, recuperado da seguinte forma:

         // retrieve the dbarticles data source
        DataSource dataSource = this.getDataSource(request, "dbarticles");

Aqui, solicitamos a fonte de dados identificada pela chave "dbarticles". Se recuperarmos um ponteiro nulo, isso significa que a fonte de dados não pôde ser criada durante a inicialização da aplicação. Neste caso, registamos o erro num objeto ActionErrors e redirecionamos para a página de erro 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 é obtida, temos acesso ao conjunto de ligações. Uma ligação à base de dados é obtida utilizando o seguinte código:

             // get a connection
            Connection connexion = dataSource.getConnection();

Aqui, o pool irá fornecer-nos uma ligação reutilizada ou uma ligação recém-criada, caso ainda existam ligações disponíveis.

9.5. Implantaçã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 de diretórios da aplicação é a seguinte:

   
  
  

Repare na biblioteca mysql-connector-java-3.0.10-stable-bin.jar em WEB-INF/lib. Este é o ficheiro que contém o controlador JDBC para a base de dados MySQL aqui utilizada.

9.6. Teste

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

Image

9.7. Uma segunda fonte de dados

Estamos agora a utilizar uma segunda fonte de dados localizada 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 de dados terá o identificador (chave) pgdbarticles. A nova funcionalidade provém da classe que implementa a interface javax.sql.DataSource. A classe padrão é org.apache.struts.util.GenericDataSource, definida na biblioteca struts.jar. Esta classe será descontinuada em versões futuras do Struts e, para a versão 1.1, recomenda-se a utilização da classe org.apache.commons.dbcp.BasicDataSource, encontrada na biblioteca commons-dbcp-1.1.jar. Esta biblioteca não está necessariamente incluída no pacote Struts. Pode ser encontrada no URL http://jakarta.apache.org/commons/index.html, especificamente em http://jakarta.apache.org/site/binindex.cgi. Deve descarregar o produto chamado Commons DBCP. Para Windows, pode descarregar o ficheiro .zip. Este contém o código-fonte da biblioteca, bem como o ficheiro .jar correspondente. Deve extrair estes ficheiros do arquivo .zip e colocá-los na pasta WEB-INF/lib da aplicação. Deve também colocar o controlador do SGBD que está a ser utilizado nesta mesma pasta:

Image

Para utilizar esta fonte de dados, alteramos uma instrução no código da ação /list:

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

Desta vez, solicitamos a fonte de dados denominada pgdbarticles, ou seja, a fonte de dados Postgres.

Resta apenas compilar tudo, iniciar o Tomcat e aceder ao URL 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 que, neste caso, não seguimos a arquitetura MVC. Na verdade, a própria ação /list executa as consultas SQL para aceder aos dados. Teria sido preferível que ela chamasse uma classe de negócio intermédia que ocultasse o facto de os dados estarem a ser recuperados de um sistema de gestão de bases de dados. É provável que a classe de negócio criasse então o conjunto de ligações, e não haveria necessidade de o declarar no ficheiro de configuração do Struts. Aqui fica uma pista: a presença de uma secção <data-sources> no ficheiro de configuração pode indicar que a nossa aplicação não segue a arquitetura MVC.