Skip to content

9. Exploração de uma fonte de dados

Propomos aqui estabelecer as bases para a exploração de bancos de dados dentro de uma aplicação Struts.

9.1. A aplicação Struts /listarticles

Desejamos exibir o conteúdo de uma tabela que registra 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
estoque atual do artigo
stockMinimum
estoque abaixo do qual
é necessário iniciar um reabastecimento

O conteúdo da tabela é o seguinte:

Image

O aplicativo listarticles nos mostrará o mesmo (mas de forma menos elegante) em uma página da web:

Image

9.2. Configuração do aplicativo Struts /listarticles

O arquivo 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 arquivo struts-config.xml introduz uma nova seçã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 seção <data-sources> permite declarar todas as fontes de dados do aplicativo, sendo que cada uma delas é descrita por uma seção <data-source>. Essa tag admite vários atributos:

key
identifica a fonte de dados quando há mais de uma.
driverClass
o nome da classe a ser instanciada para acessar o banco de dados. Essa classe geralmente se encontra em uma biblioteca de classes (.jar) fornecida pelo editor do SGBD. Essa biblioteca contém o driver JDBC para acesso ao banco de dados. A propriedade driverClass identifica esse driver. O arquivo .jar das classes de acesso ao SGBD será colocado na pasta WEB-INF/lib do aplicativo.
url
cadeia de conexão a um banco de dados específico. De fato, o driver JDBC permite o acesso a todos os bancos de dados gerenciados pelo SGBD. A propriedade url permite indicar qual deles será utilizado.
user, password
O acesso aos bancos de dados é protegido. O SGBD gerencia usuários identificados por um login e uma senha. Esses dados são especificados nos atributos user e password da tag.
minCount, maxCount
O Struts irá gerenciar um pool de conexões com o SGBD. Abrir uma conexão com um SGBD é uma operação que consome muitos recursos de tempo e memória. Em vez de abrir e fechar uma conexão com o SGBD para cada uma das solicitações feitas à aplicação, a aplicação gerencia um pool de n conexões, onde n está no intervalo [minCount, maxCount]. Se, durante uma solicitação, a aplicação precisar de uma conexão:
- ela tentará obter uma no pool de conexões. Se encontrar uma disponível, ela a utilizará.
- se não houver conexões livres no pool e o número de conexões no pool for menor que maxCount, uma nova conexão é aberta e adicionada ao pool. A solicitação atual poderá utilizá-la.
- se não houver conexões disponíveis e não for possível criar uma nova, a consulta fica em espera.
Quando a consulta atual encerra a conexão, esta não é realmente encerrada, mas devolvida ao pool.

Aqui, temos as seguintes propriedades:

key
dbarticles — é o nome pelo qual a fonte de dados será identificada no contexto do aplicativo
driverClass
com.mysql.jdbc.Driver. Estamos utilizando um banco de dados MySQL.
url
jdbc:mysql://localhost/dbarticles. O banco de dados se chama dbarticles e está na máquina local.
user, password
admarticles, mdparticles. Esse usuário foi configurado com todos os privilégios no banco de dados dbarticles.
minCount, maxCount
2, 5. Mínimo de 2 conexões no pool, máximo de 5.

9.2.2. Ações

As ações declaradas no arquivo 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 se chama /liste e está associada à classe istia.st.struts.articles.ListeArticlesAction. Essa ação é concluída com a exibição:

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

9.2.3. O arquivo de mensagens

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

O arquivo de mensagens ApplicationResources.properties será colocado na pasta WEB-INF/classes/istia/st/struts/articles. 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 ao banco 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 visualizações

9.3.1. A visualização erreurs.jsp

Essa visualização deve exibir uma lista de erros. Já a encontramos 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 visualização se limita a exibir a lista de erros com a tag <html:errors/>.

9.3.2. A visualização listarticles.jsp

Esta visualização deve exibir o conteúdo da tabela ARTICLES do banco de dados. 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): matriz (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á inserir o conteúdo da tabela ARTICLES em um objeto ArrayList, que será incluído 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) relacionadas a um item na tabela. A visualização deve exibir o conteúdo do objeto listArticles em uma tabela HTML. Para isso, ela 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>

Há aqui duas iterações. A primeira percorre os elementos do objeto ArrayList listArticles. O elemento atual de listArticles é chamado aqui de linha. O elemento linha representa um objeto String[5], que é percorrido por meio da segunda iteração. O elemento dessa segunda iteração é chamado de coluna. Ele representa o elemento atual de uma matriz de cadeias de caracteres e, portanto, é uma cadeia de caracteres (código, nome, preço, stockActuel, stockMinimum). Seu valor é exibido por meio da tag <bean:write>.

A visualização utiliza tags das bibliotecas struts-logic e struts-bean. Portanto, é necessário declarar o uso dessas bibliotecas:

<%@ 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 visualização possa utilizá-lo. 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 conexã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  ela é utiliza        da Conexão conexão = null;
        Statement st = null;
        ResultSet rs = null;
        String requête = null;
        ArrayList alArticles = new ArrayList();
         // gerenciamos os erros
        try {
             // obtendo uma conexão
            connexion = dataSource.getConnection();
             // preparando 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 certo
        request.setAttribute("listArticles", alArticles);
        return mapping.findForward("afficherListeArticles");
    } //executar
} //classe

O leitor certamente é capaz de compreender a essência do código acima. Vamos nos concentrar apenas na única novidade: o uso de um objeto DataSource fornecido pelo framework Struts. Esse objeto DataSource representa o pool de conexões configurado pela seção <data-source key="dbarticles"> do arquivo de configuração. O objeto DataSource foi inserido, após sua criação, no contexto da aplicação para que todos os objetos desse contexto tenham acesso a ele. Portanto, ele é recuperado da seguinte maneira:

         // recuperando 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, registramos o erro em um objeto ActionErrors e passamos o controle para a página de erros identificada pela chave “afficherErreurs” no arquivo 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>

Depois de obter a fonte de dados, temos acesso ao pool de conexões. Uma conexão com o banco de dados é estabelecida pelo código:

             // obtendo uma conexão
            Connection connexion = dataSource.getConnection();

Nesse caso, o pool nos fornecerá uma conexão reutilizada ou criada, caso ainda haja disponibilidade para criar uma nova.

9.5. Implantação

O contexto da aplicação é definido no arquivo 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:

  
 
 

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

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

Agora utilizamos uma segunda fonte de dados presente em um banco de dados Postgres. Também nesse caso, os dados estão na tabela ARTICLES, com as mesmas colunas da anterior. Seu conteúdo é o seguinte:

Image

Essa nova fonte de dados é declarada no arquivo 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 vem da classe que implementa a interface javax.sql.DataSource. A classe padrão é org.apache.struts.util.GenericDataSource, definida na biblioteca struts.jar. Essa classe será descontinuada em versões futuras do Struts, e recomenda-se, para a versão 1.1, utilizar a classe org.apache.commons.dbcp.BasicDataSource, que se encontra na biblioteca commons-dbcp-1.1.jar. Essa biblioteca não é necessariamente fornecida com o pacote do Struts. Ela 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. É preciso baixar o produto chamado Commons DBCP. No Windows, é possível baixar o arquivo .zip. Ele contém o código-fonte da biblioteca, bem como o arquivo .jar correspondente. É necessário extrair esse arquivo do .zip e colocá-lo na pasta WEB-INF/lib do aplicativo. Também é necessário colocar nessa mesma pasta o driver do SGBD utilizado:

Image

Para utilizar esse 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 chamada pgdbarticles, c.a.d, a fonte de dados Postgres.

Agora basta compilar tudo, iniciar o Tomcat e acessar a página http://localhost:8080/listarticles/liste.do:

Image

9.8. Conclusão

Mostramos como usar um pool de conexões para acessar um banco de dados. Vale ressaltar aqui que não seguimos a arquitetura MVC. De fato, a ação /liste realiza ela mesma as consultas SQL para acessar os dados. Teria sido preferível que ela se dirigisse a uma classe de negócios intermediária, que ocultasse o fato de que os dados estão sendo buscados em um SGBD. Provavelmente, seria essa classe de negócio que criaria o pool de conexões, e assim não haveria necessidade de declará-lo no arquivo de configuração do Struts. Isso é um indício: a presença de uma seção <data-sources> no arquivo de configuração pode significar que nossa aplicação não segue a arquitetura MVC.