9. Exploitation d'une source de données
Nous nous proposons ici de poser les bases de l'exploitation de bases de données au sein d'une application Struts.
9.1. L'application Struts /listarticles
Nous souhaitons afficher le contenu d'une table enregistrant les caractéristiques d'articles vendus par un magasin.
![]() |
|
Le contenu de la table est le suivant :

L'application listarticles nous donnera la même chose (en moins bien) dans une page web :

9.2. Configuration de l'application Struts /listarticles
Le fichier web.xml de l'application est 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>
Le fichier struts-config.xml introduit une nouvelle section <data-sources> qui permet de déclarer et configurer des sources de données :
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. Sources de données
La section <data-sources> permet de déclarer toutes les sources de données de l'application, chacune d'elles étant décrite par une section <data-source>. Cette balise admet plusieurs attributs :
identifie la source de données lorsqu'il y en a plusieurs. | |
le nom de la classe à instancier pour avoir accès à la base de données. Cette classe se trouve généralement dans une bibliothèque de classes (.jar) livrée par l'éditeur du SGBD. Cette bibliothèque contient le pilote JDBC d'accès à la base. La propriété driverClass désigne ce pilote. Le fichier .jar des classes d'accès au SGBD sera placé dans le dossier WEB-INF/lib de l'application. | |
chaîne de connexion à une base précise. En effet, le pilote JDBC permet l'accès à toutes les bases gérées par le SGBD. La propriété url nous permet d'indiquer celle que nous allons exploiter. | |
L'accès aux bases est protégé. Le SGBD gère des utilisateurs identifiés par un login et un mot de passe. Ceux-ci sont précisés dans les attributs user et password de la balise. | |
Struts va gérer un pool de connexions au SGBD. L'ouverture d'une connexion à un SGBD est une opération coûteuse en ressources temps et mémoire. Plutôt que d'ouvrir et fermer une connexion au SGBD pour chacune des requêtes qui seront faites sur l'application, l'application gère un pool de n connexions où n est dans l'intervalle [minCount, maxCount]. Si lors d'une requête, l'application a besoin d'une connexion : - elle va chercher à en obtenir une dans le pool de connexions. Si elle en trouve une de libre, elle l'utilise. - s'il n'y a pas de connexions libres dans le pool et qu'il y a moins de maxCount connexions dans le pool, une nouvelle connexion est ouverte et mise dans le pool. La requête courante pourra l'utiliser. - s'il n'y a pas de connexions libres et pas moyen d'en créer une nouvelle, la requête est mise en attente. Lorsque la requête courante ferme la connexion, celle-ci n'est pas fermée réellement mais rendue au pool. |
Ici, nous avons les propriétés suivantes :
dbarticles - c'est le nom sous lequel sera connu la source de données dans le contexte de l'application | |
com.mysql.jdbc.Driver. Nous utilisons une base MySQL. | |
jdbc:mysql://localhost/dbarticles. La base s'appelle dbarticles et est présente sur la machine locale. | |
admarticles, mdparticles. Cet utilisateur a été défini comme ayant tous les privilèges sur la base dbarticles. | |
2, 5. Minimum de 2 connexions dans le pool, maximum de 5. |
9.2.2. Actions
Les actions déclarées dans le fichier de configuration de Struts sont les suivantes :
<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'unique action s'appelle /liste et est associée à la classe istia.st.struts.articles.ListeArticlesAction. Cette action se termine soit par l'affichage :
- de la page des articles (/vues/listarticles.jsp)
- de la page des erreurs (/vues/erreurs.jsp)
9.2.3. Le fichier des messages
Le fichier des messages ApplicationResources.properties sera placé dans le dossier WEB-INF/classes/istia/st/struts/articles. Son contenu sera le suivant :
# erreurs
errors.header=<ul>
errors.footer=</ul>
erreur.dbarticles=<li>Erreur d'accès à la base des articles ({0})</li>
Une seule erreur peut se produire, un erreur d'accès à la base. Il y a en fait plusieurs types d'erreurs possibles, toutes rassemblées dans le même message d'erreur. La cause exacte de l'erreur sera néanmoins précisée dans le paramètre {0}.
9.3. Les vues
9.3.1. La vue erreurs.jsp
Cette vue doit afficher une liste d'erreurs. Nous l'avons rencontrée à plusieurs reprises déjà.
<%@ 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>
Cette vue se contente d'afficher la liste des erreurs avec la balise <html:errors/>.
9.3.2. La vue listarticles.jsp
Cette vue doit afficher le contenu de la table ARTICLES de la base de données. Son code est le suivant :
<%@ 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'action /liste va mettre le contenu de la table ARTICLES dans un objet ArrayList placé dans la requête sous le nom listArticles. Chaque élément de l'objet listArticles est un tableau de 5 chaînes de caractères représentant les 5 informations (code, nom, prix, stockActuel, stockMinimum) liées à un article dans la table. La vue doit afficher le contenu de l'objet listArticles dans un tableau HTML. Elle s'aide pour cela de la balise <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>
Il y a ici deux itérations. La première boucle sur les éléments de l'objet ArrayList listArticles. L'élément courant de listArticles est ici appelé ligne. L'élément ligne représente un objet String[5] qu'on parcourt à l'aide de la deuxième itération. L'élément de cette seconde itération est appelé colonne. Il représente l'élément courant d'un tableau de chaînes de caractères et est donc une chaîne de caractères (code, nom, prix, stockActuel, stockMinimum). Sa valeur est affichée à l'aide de la balise <bean:write>.
La vue utilise des balises des bibliothèques struts-logic et struts-bean. Il faut donc déclarer l'utilisation de celles-ci :
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
9.4. L'action /liste
L'action /liste a pour but de mettre dans la requête un objet ArrayList représentant le contenu de la table ARTICLES, ceci afin qu'une vue l'exploite. Son code est le suivant :
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 {
// lit le contenu de la table articles d'une connexion
// réalisée à l'init du contexte
// on récupère a source de données dbarticles
DataSource dataSource = this.getDataSource(request, "dbarticles");
if (dataSource == null) {
// la source de données n'a pas pu être créée
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");
}
// ici la source de données existe - on l'exploite Connection connexion = null;
Statement st = null;
ResultSet rs = null;
String requête = null;
ArrayList alArticles = new ArrayList();
// on gère les erreurs
try {
// obtenir une connexion
connexion = dataSource.getConnection();
// préparer la requête SQL
requête =
"select code, nom, prix, stockActuel, stockMinimum from articles order by nom";
// l'exécuter
st = connexion.createStatement();
rs = st.executeQuery(requête);
// exploiter les résultats
while (rs.next()) {
// enregistrer la ligne courante
alArticles.add(
new String[] {
rs.getString("code"),
rs.getString("nom"),
rs.getString("prix"),
rs.getString("stockactuel"),
rs.getString("stockMinimum")});
// ligne suivante
} //while
// libérer les ressources
rs.close();
st.close();
connexion.close();
} catch (Exception ex) {
// des erreurs se sont produites
ActionErrors erreurs = new ActionErrors();
erreurs.add("dbarticles",new ActionError("erreur.dbarticles", ex.getMessage()));
this.saveErrors(request, erreurs);
return mapping.findForward("afficherErreurs");
}
// c'est bon
request.setAttribute("listArticles", alArticles);
return mapping.findForward("afficherListeArticles");
} //execute
} //classe
Le lecteur est sans doute capable de comprendre l'essence du code ci-dessus. Nous ne nous attarderons que sur l'unique nouveauté, l'utilisation d'un objet DataSource fourni par le framework Struts. Cet objet DataSource représente le pool de connexions configuré par la section <data-source key="dbarticles"> du fichier de configuration. L'objet DataSource a été placé, après sa création, dans le contexte de l'application afin que tous les ojets de ce contexte y aient accès. Il est donc récupéré de la façon suivante :
// on récupère a source de données dbarticles
DataSource dataSource = this.getDataSource(request, "dbarticles");
Nous demandons ici la source de données identifiée par la clé "dbarticles". Si nous récupérons un pointeur null, c'est ce que la source de données n'a pu être créée lors de l'initilisation de l'application. Dans ce cas, nous notons l'erreur dans un objet ActionErrors et nous passons la main à la page d'erreurs identifiée par la clé "afficherErreurs" dans le fichier de configuration :
<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>
Une fois la source de données obtenue, nous avons accès au pool des connexions. Une connexion à la base est obtenue par le code :
Ici, le pool nous délivrera une connexion recyclée ou créée s'il y avait encore possibilité d'en créer.
9.5. Déploiement
Le contexte de l'application est défini dans le fichier de configuration server.xml de Tomcat :
<Context path="/listarticles" reloadable="true" docBase="E:\data\serge\web\struts\articles\liste" />
L'arborescence de l'application est la suivante :
![]() ![]() | |
![]() ![]() | |
![]() | |
On remarquera la bibliothèque mysql-connector-java-3.0.10-stable-bin.jar dans WEB-INF/lib. C'est elle qui contient le pilote JDBC de la base MySQL utilisée ici.
9.6. Les tests
Nous lançons Tomcat et demandons l'URL http://localhost:8080/listarticles/liste.do :

9.7. Une seconde source de données
Nous utilisons maintenant une seconde source de données présente dans une base Postgres. Là également, les données sont dans une table ARTICLES avec les mêmes colonnes que précédemment. Son contenu est le suivant :

Cette nouvelle source de données est déclarée dans le fichier de configuration de 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 nouvelle source aura pour identificateur (key) pgdbarticles. La nouveauté vient de la classe implémentant l'interface javax.sql.DataSource. La classe par défaut est org.apache.struts.util.GenericDataSource définie dans la bibliothèque struts.jar. Cette classe va être délaissée dans les versions futures de Struts et il est conseillé pour la version 1.1 d'utiliser la classe org.apache.commons.dbcp.BasicDataSource que l'on trouve dans la bibliothèque commons-dbcp-1.1.jar. Celle-ci n'est pas nécessairement livrée avec le paquetage Struts. On peut la trouver à l'url http://jakarta.apache.org/commons/index.html et plus particulièrement à l'url http://jakarta.apache.org/site/binindex.cgi. Il faut télécharger le produit appelé Commons DBCP. On peut pour windows, télécharger le fichier .zip. Celui-ci contient le code source de la bibliothèque ainsi que le .jar correspondant. Il faut extraire celui-ci de l'archive .zip et le placer dans le dossier WEB-INF/lib de l'application. Il faut également placer dans ce même dossier le pilote du SGBD utilisé :

Pour utiliser cette source, nous changeons une instruction dans le code de l'action /liste :
Nous demandons cette fois, la source de données s'appelant pgdbarticles, c.a.d. la source de données Postgres.
Il ne nous reste plus qu'à compiler le tout, lancer Tomcat et demander l'URL http://localhost:8080/listarticles/liste.do :

9.8. Conclusion
Nous avons montré comment utiliser un pool de connexions pour accéder à une base de données. Remarquons ici que nous n'avons pas respecté l'architecture MVC. En effet, l'action /liste fait elle même les requêtes SQL pour accéder aux données. Il aurait été préférable qu'elle s'adresse à une classe métier intermédiaire qui cacherait le fait qu'on va chercher les données dans un SGBD. C'est probablement la classe métier qui créerait alors le pool de connexions et il n'y aurait alors pas besoin de le déclarer dans le fichier de configuration de Struts. C'est un indice : la présence d'une section <data-sources> dans le fichier de configuration peut vouloir dire que notre application ne respecte pas l'architecture MVC.





