Skip to content

9. Lavorare con una fonte di dati

In questa sezione, miriamo a gettare le basi per lavorare con i database all'interno di un'applicazione Struts.

9.1. L'applicazione Struts /listarticles

Vogliamo visualizzare il contenuto di una tabella che memorizza le caratteristiche degli articoli venduti da un negozio.

codice
codice articolo - chiave primaria
nome
nome articolo
prezzo
prezzo dell'articolo
Stock attuale
Stock attuale dell'articolo
scorte minime
livello di scorte al di sotto del quale
deve essere avviato un rifornimento

La tabella contiene quanto segue:

Image

L'applicazione listarticles visualizzerà le stesse informazioni (anche se in modo meno elegante) su una pagina web:

Image

9.2. Configurazione dell'applicazione Struts /listarticles

Il file web.xml dell'applicazione è standard:

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>

Il file struts-config.xml introduce una nuova sezione <data-sources> che consente di dichiarare e configurare le origini dati:

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. Origini dati

La sezione <data-sources> viene utilizzata per dichiarare tutte le fonti dati dell'applicazione, ciascuna delle quali è descritta da una sezione <data-source>. Questo tag supporta diversi attributi:

key
identifica l'origine dati in presenza di più origini.
driverClass
il nome della classe da istanziare per accedere al database. Questa classe si trova in genere in una libreria di classi (.jar) fornita dal fornitore del DBMS. Questa libreria contiene il driver JDBC per l'accesso al database. La proprietà driverClass specifica questo driver. Il file .jar contenente le classi di accesso al DBMS verrà collocato nella cartella WEB-INF/lib dell'applicazione.
url
Stringa di connessione a un database specifico. Il driver JDBC consente l'accesso a tutti i database gestiti dal DBMS. La proprietà url permette di specificare quale utilizzare.
utente, password
L'accesso al database è protetto. Il DBMS gestisce gli utenti identificati da un nome utente e una password. Questi sono specificati negli attributi user e password del tag.
minCount, maxCount
Struts gestisce un pool di connessioni al DBMS. L'apertura di una connessione a un DBMS è un'operazione che richiede molte risorse in termini di tempo e memoria. Anziché aprire e chiudere una connessione al DBMS per ogni richiesta effettuata all'applicazione, l'applicazione gestisce un pool di n connessioni, dove n è compreso nell'intervallo [minCount, maxCount]. Se, durante una richiesta, l'applicazione necessita di una connessione:
- tenterà di ottenerne una dal pool di connessioni. Se ne trova una libera, la utilizza.
- se non ci sono connessioni libere nel pool e nel pool ci sono meno connessioni di maxCount, viene aperta una nuova connessione e aggiunta al pool. La query corrente può utilizzarla.
- Se non ci sono connessioni libere e non è possibile crearne una nuova, la richiesta viene messa in attesa.
Quando la richiesta corrente chiude la connessione, questa non viene effettivamente chiusa ma restituita al pool.

Qui abbiamo le seguenti proprietà:

chiave
dbarticles - questo è il nome con cui l'origine dati sarà identificata nel contesto dell'applicazione
driverClass
com.mysql.jdbc.Driver. Stiamo utilizzando un database MySQL.
url
jdbc:mysql://localhost/dbarticles. Il database si chiama dbarticles e si trova sul computer locale.
utente, password
admarticles, mdparticles. A questo utente sono stati concessi privilegi completi sul database dbarticles.
minCount, maxCount
2, 5. Minimo 2 connessioni nel pool, massimo 5.

9.2.2. Azioni

Le azioni dichiarate nel file di configurazione di Struts sono le seguenti:

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

L'unica azione si chiama /list ed è associata alla classe istia.st.struts.articles.ListeArticlesAction. Questa azione termina visualizzando:

  • la pagina degli articoli (/vues/listarticles.jsp)
  • la pagina degli errori (/views/errors.jsp)

9.2.3. Il file dei messaggi

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

Il file dei messaggi ApplicationResources.properties verrà inserito nella cartella WEB-INF/classes/istia/st/struts/articles. Il suo contenuto sarà il seguente:

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

Può verificarsi un solo errore: un errore di accesso al database. In realtà, esistono diversi tipi di errore possibili, tutti raggruppati sotto lo stesso messaggio di errore. La causa esatta dell'errore sarà tuttavia specificata nel parametro {0}.

9.3. Viste

9.3.1. La vista errors.jsp

Questa vista deve visualizzare un elenco di errori. L'abbiamo già incontrata diverse volte.

<%@ 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>

Questa vista visualizza semplicemente l'elenco degli errori utilizzando il tag <html:errors/>.

9.3.2. La vista listarticles.jsp

Questa vista deve visualizzare il contenuto della tabella ARTICLES nel database. Il suo codice è il seguente:

<%@ 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>    

L'azione /list inserirà il contenuto della tabella ARTICLES in un oggetto ArrayList denominato listArticles nella richiesta. Ogni elemento dell'oggetto listArticles è un array di 5 stringhe che rappresentano le 5 informazioni (codice, nome, prezzo, stock attuale, stock minimo) associate a un articolo nella tabella. La vista deve visualizzare il contenuto dell'oggetto listArticles in una tabella HTML. A tal fine, utilizza il 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>

Qui ci sono due iterazioni. La prima esegue un ciclo sugli elementi dell'oggetto ArrayList listArticles. L'elemento corrente di listArticles è chiamato qui row. L'elemento row rappresenta un oggetto String[5] che viene attraversato utilizzando la seconda iterazione. L'elemento di questa seconda iterazione è chiamato column. Rappresenta l'elemento corrente di un array di stringhe ed è quindi una stringa (code, name, price, currentStock, minimumStock). Il suo valore viene visualizzato utilizzando il tag <bean:write>.

La vista utilizza tag delle librerie struts-logic e struts-bean. Pertanto, è necessario dichiararne l'uso:

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

9.4. L'azione /list

Lo scopo dell'azione /list è quello di includere nella richiesta un oggetto ArrayList che rappresenti il contenuto della tabella ARTICLES, in modo che una vista possa utilizzarlo. Il suo codice è il seguente:

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

Il lettore è probabilmente in grado di cogliere l'essenza del codice sopra riportato. Ci concentreremo solo su un unico elemento nuovo: l'uso di un oggetto DataSource fornito dal framework Struts. Questo oggetto DataSource rappresenta il pool di connessioni configurato dalla sezione <data-source key="dbarticles"> del file di configurazione. Dopo la sua creazione, l'oggetto DataSource è stato inserito nel contesto dell'applicazione in modo che tutti gli oggetti in quel contesto possano accedervi. Viene quindi recuperato come segue:

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

Qui richiediamo l'origine dati identificata dalla chiave "dbarticles". Se recuperiamo un puntatore nullo, significa che l'origine dati non è stata creata durante l'inizializzazione dell'applicazione. In questo caso, registriamo l'errore in un oggetto ActionErrors e reindirizziamo alla pagina di errore identificata dalla chiave "afficherErreurs" nel file di configurazione:

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

Una volta ottenuta la fonte dati, abbiamo accesso al pool di connessioni. Una connessione al database viene ottenuta utilizzando il seguente codice:

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

In questo caso, il pool ci fornirà una connessione riutilizzata o una di nuova creazione, se sono ancora disponibili connessioni.

9.5. Distribuzione

Il contesto dell'applicazione è definito nel file di configurazione server.xml di Tomcat:

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

La struttura delle directory dell'applicazione è la seguente:

   
  
  

Si noti la libreria mysql-connector-java-3.0.10-stable-bin.jar nella directory WEB-INF/lib. Questo è il file che contiene il driver JDBC per il database MySQL utilizzato in questo contesto.

9.6. Test

Avviamo Tomcat e richiediamo l'URL http://localhost:8080/listarticles/liste.do:

Image

9.7. Una seconda fonte di dati

Ora stiamo utilizzando una seconda fonte di dati situata in un database Postgres. Anche in questo caso, i dati si trovano in una tabella ARTICLES con le stesse colonne di prima. Il suo contenuto è il seguente:

Image

Questa nuova fonte di dati è dichiarata nel file di configurazione di 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>

La nuova fonte dati avrà l'identificatore (chiave) pgdbarticles. La nuova funzionalità deriva dalla classe che implementa l'interfaccia javax.sql.DataSource. La classe predefinita è org.apache.struts.util.GenericDataSource, definita nella libreria struts.jar. Questa classe sarà deprecata nelle future versioni di Struts e, per la versione 1.1, si consiglia di utilizzare la classe org.apache.commons.dbcp.BasicDataSource presente nella libreria commons-dbcp-1.1.jar. Questa libreria non è necessariamente inclusa nel pacchetto Struts. È disponibile all'URL http://jakarta.apache.org/commons/index.html, in particolare all'indirizzo http://jakarta.apache.org/site/binindex.cgi. È necessario scaricare il prodotto denominato Commons DBCP. Per Windows, è possibile scaricare il file .zip. Questo contiene il codice sorgente della libreria e il corrispondente file .jar. È necessario estrarlo dall'archivio .zip e inserirlo nella cartella WEB-INF/lib dell'applicazione. È inoltre necessario inserire il driver per il DBMS utilizzato in questa stessa cartella:

Image

Per utilizzare questa origine dati, modifichiamo un'istruzione nel codice dell'azione /list:

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

In questo caso, richiediamo l'origine dati denominata pgdbarticles, ovvero l'origine dati Postgres.

Non resta che compilare il tutto, avviare Tomcat e richiedere l'URL http://localhost:8080/listarticles/liste.do:

Image

9.8. Conclusione

Abbiamo mostrato come utilizzare un pool di connessioni per accedere a un database. Si noti che in questo caso non abbiamo seguito l'architettura MVC. Infatti, l'azione /list esegue direttamente le query SQL per accedere ai dati. Sarebbe stato preferibile che chiamasse una classe di business intermedia che nascondesse il fatto che i dati vengono recuperati da un sistema di gestione di database. È probabile che la classe di business creerebbe quindi il pool di connessioni e non ci sarebbe bisogno di dichiararlo nel file di configurazione di Struts. Ecco un indizio: la presenza di una sezione <data-sources> nel file di configurazione potrebbe indicare che la nostra applicazione non segue l'architettura MVC.