Skip to content

9. Arbeiten mit einer Datenquelle

Hier wollen wir die Grundlagen für die Arbeit mit Datenbanken innerhalb einer Struts-Anwendung schaffen.

9.1. Die Struts-Anwendung /listarticles

Wir möchten den Inhalt einer Tabelle anzeigen, in der die Eigenschaften der von einem Geschäft verkauften Artikel gespeichert sind.

Code
Artikelcode – Primärschlüssel
Name
Artikelname
Preis
Artikelpreis
Aktueller Lagerbestand
Aktueller Lagerbestand des Artikels
Mindestbestand
Lagerbestand, unterhalb dessen
eine Nachbestellung veranlasst werden muss

Die Tabelle enthält Folgendes:

Image

Die Anwendung „listarticles“ zeigt dieselben Informationen (wenn auch weniger elegant) auf einer Webseite an:

Image

9.2. Konfiguration der Struts-Anwendung „/listarticles“

Die Datei „web.xml“ der Anwendung entspricht dem 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>

Die Datei struts-config.xml enthält einen neuen Abschnitt <data-sources>, in dem Sie Datenquellen deklarieren und konfigurieren können:

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

Der Abschnitt <data-sources> dient zur Deklaration aller Datenquellen der Anwendung, die jeweils durch einen <data-source>-Abschnitt beschrieben werden. Dieses Tag unterstützt mehrere Attribute:

key
identifiziert die Datenquelle, wenn mehrere Quellen vorhanden sind.
driverClass
Der Name der Klasse, die instanziiert werden soll, um auf die Datenbank zuzugreifen. Diese Klasse befindet sich in der Regel in einer vom DBMS-Anbieter bereitgestellten Klassenbibliothek (.jar). Diese Bibliothek enthält den JDBC-Treiber für den Datenbankzugriff. Die Eigenschaft driverClass gibt diesen Treiber an. Die .jar-Datei mit den DBMS-Zugriffsklassen wird im Ordner WEB-INF/lib der Anwendung abgelegt.
url
Verbindungszeichenfolge zu einer bestimmten Datenbank. Der JDBC-Treiber ermöglicht den Zugriff auf alle vom DBMS verwalteten Datenbanken. Über die Eigenschaft „url“ können wir festlegen, welche davon verwendet werden soll.
Benutzername, Passwort
Der Datenbankzugriff ist geschützt. Das DBMS verwaltet Benutzer, die durch einen Benutzernamen und ein Passwort identifiziert werden. Diese werden in den Attributen „user“ und „password“ des Tags angegeben.
minCount, maxCount
Struts verwaltet einen Pool von Verbindungen zum DBMS. Das Öffnen einer Verbindung zu einem DBMS ist ein ressourcenintensiver Vorgang, was Zeit und Speicher betrifft. Anstatt für jede an die Anwendung gerichtete Anfrage eine Verbindung zum DBMS zu öffnen und zu schließen, verwaltet die Anwendung einen Pool von n Verbindungen, wobei n im Bereich [minCount, maxCount] liegt. Wenn die Anwendung während einer Anfrage eine Verbindung benötigt:
- versucht sie, eine aus dem Verbindungspool zu beziehen. Wenn sie eine freie Verbindung findet, nutzt sie diese.
- Wenn keine freien Verbindungen im Pool vorhanden sind und weniger als maxCount Verbindungen im Pool vorhanden sind, wird eine neue Verbindung geöffnet und dem Pool hinzugefügt. Die aktuelle Abfrage kann diese nutzen.
- Wenn keine freien Verbindungen vorhanden sind und keine neue Verbindung erstellt werden kann, wird die Anfrage in die Warteschlange gestellt.
Wenn die aktuelle Anfrage die Verbindung schließt, wird diese nicht tatsächlich geschlossen, sondern an den Pool zurückgegeben.

Hier haben wir die folgenden Eigenschaften:

Schlüssel
dbarticles – dies ist der Name, unter dem die Datenquelle im Anwendungskontext bekannt sein wird
driverClass
com.mysql.jdbc.Driver. Wir verwenden eine MySQL-Datenbank.
url
jdbc:mysql://localhost/dbarticles. Die Datenbank heißt dbarticles und befindet sich auf dem lokalen Rechner.
Benutzername, Passwort
admarticles, mdparticles. Diesem Benutzer wurden alle Berechtigungen für die Datenbank dbarticles erteilt.
minCount, maxCount
2, 5. Mindestens 2 Verbindungen im Pool, höchstens 5.

9.2.2. Aktionen

Die in der Struts-Konfigurationsdatei deklarierten Aktionen lauten wie folgt:

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

Die einzige Aktion heißt /list und ist der Klasse istia.st.struts.articles.ListeArticlesAction zugeordnet. Diese Aktion endet mit der Anzeige:

  • die Artikelseite (/vues/listarticles.jsp)
  • die Fehlerseite (/views/errors.jsp)

9.2.3. Die Meldungsdatei

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

Die Meldungsdatei ApplicationResources.properties wird im Ordner WEB-INF/classes/istia/st/struts/articles abgelegt. Ihr Inhalt lautet wie folgt:

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

Es kann nur ein Fehler auftreten: ein Datenbankzugriffsfehler. Tatsächlich gibt es mehrere mögliche Fehlerarten, die alle unter derselben Fehlermeldung zusammengefasst sind. Die genaue Ursache des Fehlers wird jedoch im Parameter {0} angegeben.

9.3. Ansichten

9.3.1. Die Ansicht errors.jsp

Diese Ansicht muss eine Liste von Fehlern anzeigen. Wir sind ihr bereits mehrmals begegnet.

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

Diese Ansicht zeigt einfach die Liste der Fehler mithilfe des Tags <html:errors/> an.

9.3.2. Die Ansicht listarticles.jsp

Diese Ansicht muss den Inhalt der Tabelle ARTICLES in der Datenbank anzeigen. Ihr Code lautet wie folgt:

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

Die Aktion /list fügt den Inhalt der Tabelle ARTICLES in ein ArrayList-Objekt namens listArticles in der Anfrage ein. Jedes Element des listArticles-Objekts ist ein Array aus 5 Strings, die die 5 Informationen (code, name, price, currentStock, minimumStock) darstellen, die mit einem Eintrag in der Tabelle verknüpft sind. Die Ansicht muss den Inhalt des Objekts „listArticles“ in einer HTML-Tabelle anzeigen. Dazu verwendet sie das 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>

Hier gibt es zwei Iterationen. Die erste durchläuft die Elemente des ArrayList-Objekts listArticles. Das aktuelle Element von listArticles wird hier als row bezeichnet. Das Element „row“ stellt ein String[5]-Objekt dar, das in der zweiten Iteration durchlaufen wird. Das Element dieser zweiten Iteration wird als „column“ bezeichnet. Es stellt das aktuelle Element eines Arrays von Strings dar und ist daher selbst ein String (code, name, price, currentStock, minimumStock). Sein Wert wird mithilfe des Tags <bean:write> angezeigt.

Die Ansicht verwendet Tags aus den Bibliotheken struts-logic und struts-bean. Daher müssen Sie deren Verwendung deklarieren:

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

9.4. Die Aktion /list

Der Zweck der Aktion /list besteht darin, ein ArrayList-Objekt, das den Inhalt der Tabelle ARTICLES darstellt, in die Anfrage einzubinden, damit eine Ansicht darauf zugreifen kann. Der Code lautet wie folgt:

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

Der Leser kann wahrscheinlich den Kern des obigen Codes erfassen. Wir konzentrieren uns nur auf das einzige neue Element: die Verwendung eines vom Struts-Framework bereitgestellten DataSource-Objekts. Dieses DataSource-Objekt repräsentiert den Verbindungspool, der durch den Abschnitt <data-source key="dbarticles"> der Konfigurationsdatei konfiguriert wurde. Nach seiner Erstellung wurde das DataSource-Objekt in den Anwendungskontext eingefügt, sodass alle Objekte in diesem Kontext darauf zugreifen können. Es wird daher wie folgt abgerufen:

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

Hier fordern wir die Datenquelle an, die durch den Schlüssel „dbarticles“ identifiziert wird. Wenn wir einen Null-Zeiger erhalten, bedeutet dies, dass die Datenquelle während der Anwendungsinitialisierung nicht erstellt werden konnte. In diesem Fall protokollieren wir den Fehler in einem ActionErrors-Objekt und leiten zur Fehlerseite weiter, die durch den Schlüssel „afficherErreurs“ in der Konfigurationsdatei identifiziert wird:

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

Sobald die Datenquelle abgerufen wurde, haben wir Zugriff auf den Verbindungspool. Eine Verbindung zur Datenbank wird mit dem folgenden Code hergestellt:

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

Hier stellt uns der Pool eine wiederverwendete Verbindung oder eine neu erstellte Verbindung zur Verfügung, sofern noch Verbindungen verfügbar sind.

9.5. Bereitstellung

Der Anwendungskontext wird in der Konfigurationsdatei server.xml von Tomcat definiert:

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

Die Verzeichnisstruktur der Anwendung sieht wie folgt aus:

   
  
  

Beachten Sie die Bibliothek „mysql-connector-java-3.0.10-stable-bin.jar“ im Verzeichnis „WEB-INF/lib“. Diese Datei enthält den JDBC-Treiber für die hier verwendete MySQL-Datenbank.

9.6. Testen

Wir starten Tomcat und rufen die URL http://localhost:8080/listarticles/liste.do auf:

Image

9.7. Eine zweite Datenquelle

Wir verwenden nun eine zweite Datenquelle, die sich in einer Postgres-Datenbank befindet. Auch hier befinden sich die Daten in einer Tabelle namens ARTICLES mit denselben Spalten wie zuvor. Ihr Inhalt lautet wie folgt:

Image

Diese neue Datenquelle wird in der Struts-Konfigurationsdatei deklariert:

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

Die neue Datenquelle erhält die Kennung (Schlüssel) pgdbarticles. Die neue Funktion stammt aus der Klasse, die die Schnittstelle javax.sql.DataSource implementiert. Die Standardklasse ist org.apache.struts.util.GenericDataSource, definiert in der Bibliothek struts.jar. Diese Klasse wird in zukünftigen Versionen von Struts veraltet sein, und für Version 1.1 wird empfohlen, die Klasse org.apache.commons.dbcp.BasicDataSource zu verwenden, die in der Bibliothek commons-dbcp-1.1.jar zu finden ist. Diese Bibliothek ist nicht unbedingt im Struts-Paket enthalten. Sie ist unter der URL http://jakarta.apache.org/commons/index.html zu finden, genauer gesagt unter http://jakarta.apache.org/site/binindex.cgi. Sie müssen das Produkt namens Commons DBCP herunterladen. Für Windows können Sie die ZIP-Datei herunterladen. Diese enthält den Quellcode der Bibliothek sowie die entsprechende JAR-Datei. Sie müssen diese aus dem .zip-Archiv extrahieren und im Ordner WEB-INF/lib der Anwendung ablegen. Außerdem müssen Sie den Treiber für das verwendete DBMS in denselben Ordner legen:

Image

Um diese Datenquelle zu verwenden, ändern wir eine Anweisung im Code der Aktion /list:

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

Diesmal fordern wir die Datenquelle mit dem Namen pgdbarticles an, d. h. die Postgres-Datenquelle.

Jetzt müssen wir nur noch alles kompilieren, Tomcat starten und die URL http://localhost:8080/listarticles/liste.do aufrufen:

Image

9.8. Fazit

Wir haben gezeigt, wie man einen Verbindungspool für den Zugriff auf eine Datenbank verwendet. Beachten Sie, dass wir uns hier nicht an die MVC-Architektur gehalten haben. Tatsächlich führt die Aktion /list selbst die SQL-Abfragen aus, um auf die Daten zuzugreifen. Es wäre vorzuziehen gewesen, wenn sie eine zwischengeschaltete Business-Klasse aufgerufen hätte, die die Tatsache verbergen würde, dass die Daten aus einem Datenbankmanagementsystem abgerufen werden. Wahrscheinlich würde die Business-Klasse dann den Verbindungspool erstellen, und es wäre nicht notwendig, ihn in der Struts-Konfigurationsdatei zu deklarieren. Hier ein Hinweis: Das Vorhandensein eines <data-sources>-Abschnitts in der Konfigurationsdatei könnte darauf hindeuten, dass unsere Anwendung nicht der MVC-Architektur folgt.