9. Wykorzystanie źródła danych
W niniejszym rozdziale zamierzamy przedstawić podstawy korzystania z baz danych w ramach aplikacji Struts.
9.1. Aplikacja Struts /listarticles
Chcemy wyświetlić zawartość tabeli przechowującej cechy artykułów sprzedawanych przez sklep.
![]() |
|
Zawartość tabeli jest następująca:

Aplikacja listarticles wyświetli nam to samo (choć w gorszej jakości) na stronie internetowej:

9.2. Konfiguracja aplikacji Struts /listarticles
Plik web.xml aplikacji ma standardową postać:
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>
Plik struts-config.xml wprowadza nową sekcję <data-sources>, która umożliwia deklarowanie i konfigurowanie źródeł danych:
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. Źródła danych
Sekcja <data-sources> służy do deklarowania wszystkich źródeł danych aplikacji, z których każde jest opisane w sekcji <data-source>. Ten tag obsługuje kilka atrybutów:
identyfikuje źródło danych, gdy występuje ich więcej niż jedno. | |
nazwa klasy, którą należy zainicjować, aby uzyskać dostęp do bazy danych. Klasa ta znajduje się zazwyczaj w bibliotece klas (.jar) dostarczonej przez wydawcę SGBD. Biblioteka ta zawiera sterownik JDBC umożliwiający dostęp do bazy danych. Właściwość driverClass określa ten sterownik. Plik .jar zawierający klasy dostępu do SGBD zostanie umieszczony w folderze WEB-INF/lib aplikacji. | |
ciąg połączenia z konkretną bazą danych. Sterownik JDBC umożliwia bowiem dostęp do wszystkich baz danych zarządzanych przez SGBD. Właściwość url pozwala nam wskazać tę, z której będziemy korzystać. | |
Dostęp do baz danych jest chroniony. SGBD obsługuje użytkowników identyfikowanych za pomocą loginu i hasła. Są one określone w atrybutach „user” i „password” tego tagu. | |
Struts będzie zarządzał pulą połączeń z serwerem SGBD. Otwarcie połączenia z serwerem SGBD jest operacją wymagającą znacznych zasobów czasu i pamięci. Zamiast otwierać i zamykać połączenie z SGBD dla każdego żądania kierowanego do aplikacji, aplikacja zarządza pulą n połączeń, gdzie n mieści się w przedziale [minCount, maxCount]. Jeśli podczas żądania aplikacja potrzebuje połączenia: - spróbuje pobrać je z puli połączeń. Jeśli znajdzie wolne połączenie, wykorzystuje je. - jeśli w puli nie ma wolnych połączeń, a liczba połączeń w puli jest mniejsza niż maxCount, otwierane jest nowe połączenie i umieszczane w puli. Będzie je mogło wykorzystać bieżące żądanie. - jeśli nie ma wolnych połączeń i nie ma możliwości utworzenia nowego, zapytanie zostaje zawieszone. Gdy bieżące żądanie zamyka połączenie, nie jest ono faktycznie zamykane, lecz zwracane do puli. |
W tym przypadku mamy następujące właściwości:
dbarticles – jest to nazwa, pod którą źródło danych będzie rozpoznawane w kontekście aplikacji | |
com.mysql.jdbc.Driver. Korzystamy z bazy danych o nazwie MySQL. | |
jdbc:mysql://localhost/dbarticles. Baza danych nosi nazwę dbarticles i znajduje się na komputerze lokalnym. | |
admarticles, mdparticles. Użytkownik ten został skonfigurowany z pełnymi uprawnieniami do bazy dbarticles. | |
2, 5. Minimum 2 połączeń w puli, maksymalnie 5. |
9.2.2. Akcje
W pliku konfiguracyjnym Struts zadeklarowano następujące akcje:
<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>
Jedyna akcja nosi nazwę /liste i jest powiązana z klasą istia.st.struts.articles.ListeArticlesAction. Akcja ta kończy się wyświetleniem:
- strony z artykułami (/vues/listarticles.jsp)
- strony błędów (/vues/erreurs.jsp)
9.2.3. Plik komunikatów
Plik komunikatów ApplicationResources.properties zostanie umieszczony w folderze WEB-INF/classes/istia/st/struts/articles. Jego zawartość będzie następująca:
# błędy
errors.header=<ul>
errors.footer=</ul>
erreur.dbarticles=<li>Erreur d'accès à la base des articles ({0})</li>
Może wystąpić tylko jeden błąd – błąd dostępu do bazy danych. W rzeczywistości istnieje kilka możliwych rodzajów błędów, które są jednak ujęte w tym samym komunikacie o błędzie. Dokładna przyczyna błędu zostanie jednak określona w parametrze {0}.
9.3. Widoki
9.3.1. Widok erreurs.jsp
Ten widok powinien wyświetlać listę błędów. Spotkaliśmy się z nim już kilkakrotnie.
<%@ 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>
Ten widok wyświetla jedynie listę błędów za pomocą tagu <html:errors/>.
9.3.2. Widok listarticles.jsp
Ten widok ma wyświetlać zawartość tabeli ARTICLES z bazy danych. Jego kod wygląda następująco:
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<%
// listarticles: ArrayList w zapytaniu
// listArticles(i): tablica (String[5]) zawierająca 5 elementów
%>
<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>
Akcja /liste umieści zawartość tabeli ARTICLES w obiekcie ArrayList, umieszczonym w zapytaniu pod nazwą listArticles. Każdy element obiektu listArticles stanowi tablicę składającą się z 5 ciągów znaków, reprezentujących 5 informacji (kod, nazwa, cena, stockActuel, stockMinimum) związanych z pozycją w tabeli. Widok musi wyświetlać zawartość obiektu listArticles w tabeli HTML. W tym celu wykorzystuje 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>
Mamy tu do czynienia z dwiema iteracjami. Pierwsza z nich przebiega po elementach obiektu ArrayList listArticles. Bieżący element obiektu listArticles nazywany jest tutaj wierszem. Element wiersz reprezentuje obiekt typu String[5], który jest przeglądany za pomocą drugiej iteracji. Element tej drugiej iteracji nazywany jest kolumną. Reprezentuje on bieżący element tablicy ciągów znaków i jest zatem ciągiem znaków (kod, nazwa, cena, stockActuel, stockMinimum). Jego wartość jest wyświetlana za pomocą tagu <bean:write>.
Widok wykorzystuje tagi z bibliotek struts-logic i struts-bean. Należy zatem zadeklarować ich użycie:
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
9.4. Akcja /liste
Akcja /liste ma na celu umieszczenie w żądaniu obiektu ArrayList reprezentującego zawartość tabeli ARTICLES, aby widok mógł z niego skorzystać. Jej kod wygląda następująco:
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 {
// odczytuje zawartość tabeli artykułów z połączenia
// nawiązanej podczas inicjalizacji kontekstu
// pobierane jest źródło danych „dbarticles”
DataSource dataSource = this.getDataSource(request, "dbarticles");
if (dataSource == null) {
// nie udało się utworzyć źródła danych
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");
}
// tutaj źródło danych istnieje – wykorzystujemy je Connection connection = null;
Statement st = null;
ResultSet rs = null;
String requête = null;
ArrayList alArticles = new ArrayList();
// obsługujemy błędy
try {
// uzyskiwanie połączenia
connexion = dataSource.getConnection();
// przygotowanie zapytania SQL
requête =
"select code, nom, prix, stockActuel, stockMinimum from articles order by nom";
// wykonanie zapytania
st = connexion.createStatement();
rs = st.executeQuery(requête);
// przetwarzanie wyników
while (rs.next()) {
// zapisać bieżący wiersz
alArticles.add(
new String[] {
rs.getString("code"),
rs.getString("nom"),
rs.getString("prix"),
rs.getString("stockactuel"),
rs.getString("stockMinimum")});
// następny wiersz
} //while
// zwolnij zasoby
rs.close();
st.close();
connexion.close();
} catch (Exception ex) {
// wystąpiły błędy
ActionErrors erreurs = new ActionErrors();
erreurs.add("dbarticles",new ActionError("erreur.dbarticles", ex.getMessage()));
this.saveErrors(request, erreurs);
return mapping.findForward("afficherErreurs");
}
// w porządku
request.setAttribute("listArticles", alArticles);
return mapping.findForward("afficherListeArticles");
} //wykonaj
} //klasa
Czytelnik z pewnością jest w stanie zrozumieć istotę powyższego kodu. Skupimy się jedynie na jedynej nowości, jaką jest wykorzystanie obiektu DataSource dostarczanego przez framework Struts. Obiekt ten reprezentuje pulę połączeń skonfigurowaną w sekcji <data-source key="dbarticles"> pliku konfiguracyjnego. Obiekt DataSource został umieszczony, po utworzeniu, w kontekście aplikacji, aby wszystkie obiekty w tym kontekście miały do niego dostęp. Jest on zatem pobierany w następujący sposób:
// pobieramy źródło danych dbarticles
DataSource dataSource = this.getDataSource(request, "dbarticles");
W tym miejscu żądamy źródła danych oznaczonego kluczem „dbarticles”. Jeśli otrzymujemy wskaźnik null, oznacza to, że źródło danych nie mogło zostać utworzone podczas inicjalizacji aplikacji. W takim przypadku odnotowujemy błąd w obiekcie ActionErrors i przekazujemy kontrolę do strony błędów oznaczonej kluczem „afficherErreurs” w pliku konfiguracyjnym:
<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>
Po uzyskaniu źródła danych uzyskujemy dostęp do puli połączeń. Połączenie z bazą danych nawiązuje się za pomocą kodu:
W tym przypadku pula połączeń zwróci nam połączenie z puli lub utworzy nowe, jeśli nadal istnieje możliwość jego utworzenia.
9.5. Wdrożenie
Kontekst aplikacji jest zdefiniowany w pliku konfiguracyjnym server.xml serwera Tomcat:
<Context path="/listarticles" reloadable="true" docBase="E:\data\serge\web\struts\articles\liste" />
Struktura drzewa aplikacji wygląda następująco:
![]() ![]() | ||
![]() ![]() | ||
![]() | ||
Warto zwrócić uwagę na bibliotekę mysql-connector-java-3.0.10-stable-bin.jar znajdującą się w katalogu WEB-INF/lib. To właśnie ona zawiera sterownik JDBC dla bazy MySQL, z której tutaj korzystamy.
9.6. Testy
Uruchamiamy Tomcat i wywołujemy plik URL http://localhost:8080/listarticles/liste.do:

9.7. Drugie źródło danych
Korzystamy teraz z drugiego źródła danych znajdującego się w bazie Postgres. Również w tym przypadku dane znajdują się w tabeli o nazwie ARTICLES, zawierającej te same kolumny co poprzednio. Jej zawartość jest następująca:

To nowe źródło danych jest zadeklarowane w pliku konfiguracyjnym 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>
Nowe źródło będzie miało identyfikator (key) pgdbarticles. Nowością jest klasa implementująca interfejs javax.sql.DataSource. Klasą domyślną jest org.apache.struts.util.GenericDataSource zdefiniowana w bibliotece struts.jar. Klasa ta zostanie wycofana w przyszłych wersjach Struts, dlatego w wersji 1.1 zaleca się korzystanie z klasy org.apache.commons.dbcp.BasicDataSource, którą można znaleźć w bibliotece commons-dbcp-1.1.jar. Nie jest ona koniecznie dostarczana wraz z pakietem Struts. Można ją znaleźć pod adresem http://jakarta.apache.org/commons/index.html, a dokładniej pod adresem http://jakarta.apache.org/site/binindex.cgi. Należy pobrać produkt o nazwie Commons DBCP. W systemie Windows można pobrać plik .zip. Zawiera on kod źródłowy biblioteki oraz odpowiadający jej plik .jar. Należy go wyodrębnić z archiwum .zip i umieścić w folderze WEB-INF/lib aplikacji. W tym samym folderze należy również umieścić sterownik używanego modułu SGBD:

Aby skorzystać z tego kodu źródłowego, zmieniamy jedną instrukcję w kodzie akcji /liste:
Tym razem jako źródło danych o nazwie pgdbarticles podajemy plik c.a.d – źródło danych Postgres.
Pozostaje nam tylko skompilować całość, uruchomić Tomcat i wywołać stronę http://localhost:8080/listarticles/liste.do:

9.8. Wnioski
Pokazaliśmy, jak korzystać z puli połączeń w celu uzyskania dostępu do bazy danych. Należy tutaj zauważyć, że nie zastosowaliśmy architektury MVC. W rzeczywistości akcja /liste sama wysyła zapytania SQL w celu uzyskania dostępu do danych. Lepiej byłoby, gdyby zwracała się do pośredniej klasy biznesowej, która ukrywałaby fakt, że dane są pobierane z SGBD. Prawdopodobnie to właśnie ta klasa biznesowa utworzyłaby wówczas pulę połączeń i nie byłoby wtedy potrzeby deklarowania jej w pliku konfiguracyjnym Struts. Jest to pewna wskazówka: obecność sekcji <data-sources> w pliku konfiguracyjnym może oznaczać, że nasza aplikacja nie jest zgodna z architekturą MVC.





