9. استغلال مصدر البيانات
نهدف هنا إلى إرساء الأسس لاستخدام قواعد البيانات داخل تطبيق Struts.
9.1. تطبيق Struts /listarticles
نرغب في عرض محتوى جدول يسجل خصائص المنتجات التي تبيعها إحدى المتاجر.
![]() |
|
محتوى الجدول هو كما يلي:

سيقدم لنا تطبيق listarticles نفس النتيجة (بشكل أقل دقة) في صفحة ويب:

9.2. إعدادات تطبيق Struts /listarticles
الملف web.xml الخاص بالتطبيق هو ملف قياسي:
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>
يقدم الملف struts-config.xml قسمًا جديدًا <data-sources> يتيح تعريف مصادر البيانات وتهيئتها:
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. مصادر البيانات
يسمح القسم <data-sources> بتحديد جميع مصادر البيانات الخاصة بالتطبيق، حيث يتم وصف كل منها بواسطة قسم <data-source>. تدعم هذه العلامة عدة سمات:
يحدد مصدر البيانات في حالة وجود أكثر من مصدر. | |
اسم الفئة التي يجب إنشاء مثيل لها للوصول إلى قاعدة البيانات. توجد هذه الفئة عادةً في مكتبة فئات (.jar) مقدمة من ناشر SGBD. تحتوي هذه المكتبة على برنامج التشغيل JDBC للوصول إلى قاعدة البيانات. تشير الخاصية driverClass إلى هذا برنامج التشغيل. سيتم وضع ملف .jar الخاص بفئات الوصول إلى SGBD في المجلد WEB-INF/lib الخاص بالتطبيق. | |
سلسلة اتصال بقاعدة بيانات محددة. في الواقع، يتيح برنامج التشغيل JDBC الوصول إلى جميع قواعد البيانات التي يديرها SGBD. تتيح لنا الخاصية url تحديد القاعدة التي سنستخدمها. | |
الوصول إلى قواعد البيانات محمي. يدير SGBD المستخدمين الذين يتم تعريفهم بواسطة اسم المستخدم وكلمة المرور. ويتم تحديد هذين العنصرين في سمة user وسمة password الخاصة بالعلامة. | |
سيقوم Struts بإدارة مجموعة من الاتصالات بـ SGBD. إن فتح اتصال بـ SGBD عملية تستهلك الكثير من الموارد من حيث الوقت والذاكرة. بدلاً من فتح وإغلاق اتصال بـ SGBD لكل طلب يتم إرساله إلى التطبيق، يدير التطبيق مجموعة من n اتصالاً حيث n تقع ضمن النطاق [minCount, maxCount]. إذا احتاج التطبيق إلى اتصال أثناء إجراء طلب ما: - فسيحاول الحصول على اتصال من مجموعة الاتصالات. وإذا وجد اتصالاً متاحاً، فسيستخدمه. - إذا لم تكن هناك اتصالات متاحة في المجموعة وكان عدد الاتصالات في المجموعة أقل من maxCount، يتم فتح اتصال جديد وإضافته إلى المجموعة. ويمكن للطلب الحالي استخدامه. - إذا لم تكن هناك اتصالات متاحة ولم تكن هناك طريقة لإنشاء اتصال جديد، يتم وضع الطلب في حالة انتظار. وعندما يغلق الطلب الحالي الاتصال، لا يتم إغلاقه فعليًا بل يتم إرجاعه إلى المجمع. |
وهنا، لدينا الخصائص التالية:
dbarticles - هو الاسم الذي سيُعرف به مصدر البيانات في سياق التطبيق | |
com.mysql.jdbc.Driver. نستخدم قاعدة بيانات MySQL. | |
jdbc:mysql://localhost/dbarticles. تسمى قاعدة البيانات dbarticles وهي موجودة على الجهاز المحلي. | |
admarticles، mdparticles. تم تعيين هذا المستخدم بحيث يتمتع بجميع الامتيازات على قاعدة البيانات dbarticles. | |
2، 5. الحد الأدنى لعدد الاتصالات في المجموعة هو 2، والحد الأقصى هو 5. |
9.2.2. الإجراءات
الإجراءات المعلنة في ملف تكوين Struts هي التالية:
<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>
الإجراء الوحيد يسمى /liste وهو مرتبط بالفئة istia.st.struts.articles.ListeArticlesAction. ينتهي هذا الإجراء إما بعرض:
- صفحة المقالات (/vues/listarticles.jsp)
- صفحة الأخطاء (/vues/erreurs.jsp)
9.2.3. ملف الرسائل
سيتم وضع ملف الرسائل ApplicationResources.properties في المجلد WEB-INF/classes/istia/st/struts/articles. وسيكون محتواه كما يلي:
# الأخطاء
errors.header=<ul>
errors.footer=</ul>
erreur.dbarticles=<li>Erreur d'accès à la base des articles ({0})</li>
قد يحدث خطأ واحد فقط، وهو خطأ في الوصول إلى قاعدة البيانات. في الواقع، هناك عدة أنواع محتملة من الأخطاء، يتم تجميعها جميعًا في نفس رسالة الخطأ. ومع ذلك، سيتم تحديد السبب الدقيق للخطأ في المعلمة {0}.
9.3. طرق العرض
9.3.1. طريقة العرض erreurs.jsp
يجب أن تعرض هذه العرض قائمة بالأخطاء. وقد صادفناها عدة مرات من قبل.
<%@ 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>
تقتصر هذه الطريقة على عرض قائمة الأخطاء باستخدام العلامة <html:errors/>.
9.3.2. طريقة العرض listarticles.jsp
يجب أن تعرض هذه الطريقة محتوى الجدول ARTICLES من قاعدة البيانات. وفيما يلي كودها:
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<%
// listarticles: ArrayList في الاستعلام
// listArticles(i): مصفوفة (String[5]) مكونة من 5 عناصر
%>
<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>
ستقوم العملية /liste بنقل محتوى الجدول ARTICLES إلى كائن ArrayList الموجود في الاستعلام تحت الاسم listArticles. كل عنصر في الكائن listArticles هو مصفوفة مكونة من 5 سلاسل أحرف تمثل المعلومات الخمس (الرمز، الاسم، السعر، stockActuel، stockMinimum) المرتبطة بسلعة في الجدول. يجب أن تعرض طريقة العرض محتوى الكائن listArticles في جدول HTML. وتستخدم لعرض ذلك العلامة <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>
يوجد هنا تكراران. الأول يدور حول عناصر الكائن ArrayList listArticles. يُطلق هنا على العنصر الحالي في listArticles اسم «السطر». ويمثل عنصر «السطر» كائن String[5] الذي يتم تصفحه باستخدام التكرار الثاني. يُطلق على العنصر في هذه الدورة الثانية اسم «عمود». وهو يمثل العنصر الحالي في مصفوفة من سلاسل الأحرف، وبالتالي فهو سلسلة أحرف (code، name، price، stockActuel، stockMinimum). ويتم عرض قيمته باستخدام العلامة <bean:write>.
تستخدم طريقة العرض علامات من مكتبتَي struts-logic وstruts-bean. لذا، يجب الإعلان عن استخدامهما:
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
9.4. الإجراء /liste
تهدف الإجراء /liste إلى إدراج كائن ArrayList في الطلب، وهو يمثل محتوى الجدول ARTICLES، وذلك حتى تتمكن إحدى طرق العرض من استخدامه. وفيما يلي كودها:
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 {
// يقرأ محتوى جدول «articles» الخاص باتصال
// تم إجراؤها عند تهيئة السياق
// يتم استرداد مصدر بيانات "dbarticles"
DataSource dataSource = this.getDataSource(request, "dbarticles");
if (dataSource == null) {
// تعذر إنشاء مصدر البيانات
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");
}
// مصدر البيانات موجود هنا - يتم استخدامه Conne ction connection = null;
Statement st = null;
ResultSet rs = null;
String requête = null;
ArrayList alArticles = new ArrayList();
// نقوم بمعالجة الأخطاء
try {
// الحصول على اتصال
connexion = dataSource.getConnection();
// إعداد الاستعلام SQL
requête =
"select code, nom, prix, stockActuel, stockMinimum from articles order by nom";
// تنفيذه
st = connexion.createStatement();
rs = st.executeQuery(requête);
// تحليل النتائج
while (rs.next()) {
// تسجيل السطر الحالي
alArticles.add(
new String[] {
rs.getString("code"),
rs.getString("nom"),
rs.getString("prix"),
rs.getString("stockactuel"),
rs.getString("stockMinimum")});
// السطر التالي
} //while
// تحرير الموارد
rs.close();
st.close();
connexion.close();
} catch (Exception ex) {
// حدثت أخطاء
ActionErrors erreurs = new ActionErrors();
erreurs.add("dbarticles",new ActionError("erreur.dbarticles", ex.getMessage()));
this.saveErrors(request, erreurs);
return mapping.findForward("afficherErreurs");
}
// تم بنجاح
request.setAttribute("listArticles", alArticles);
return mapping.findForward("afficherListeArticles");
} //تنفيذ
} //فئة
لا شك أن القارئ قادر على فهم جوهر الكود أعلاه. ولن نتطرق إلا إلى الميزة الجديدة الوحيدة، وهي استخدام الكائن DataSource الذي يوفره إطار العمل Struts. ويمثل هذا الكائن DataSource مجموعة الاتصالات التي تم تكوينها بواسطة القسم <data-source key="dbarticles"> في ملف التكوين. تم وضع الكائن DataSource، بعد إنشائه، في سياق التطبيق حتى يتسنى لجميع الكائنات في هذا السياق الوصول إليه. وبالتالي يتم استرداده بالطريقة التالية:
// يتم استرداد مصدر بيانات dbarticles
DataSource dataSource = this.getDataSource(request, "dbarticles");
نطلب هنا مصدر البيانات المحدد بالمفتاح "dbarticles". إذا استرجعنا مؤشرًا null، فهذا يعني أن مصدر البيانات لم يتم إنشاؤه أثناء تهيئة التطبيق. في هذه الحالة، نسجل الخطأ في كائن ActionErrors وننتقل إلى صفحة الأخطاء المحددة بالمفتاح "afficherErreurs" في ملف التكوين:
<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>
بمجرد الحصول على مصدر البيانات، يصبح بإمكاننا الوصول إلى مجموعة الاتصالات. يتم إنشاء اتصال بالقاعدة البيانات باستخدام الكود التالي:
وهنا، سيزودنا مجمع الاتصالات باتصال معاد تدويره أو اتصالًا جديدًا إذا كانت هناك إمكانية لإنشاء اتصال جديد.
9.5. النشر
يتم تعريف سياق التطبيق في ملف التكوين server.xml الخاص بـ Tomcat:
<Context path="/listarticles" reloadable="true" docBase="E:\data\serge\web\struts\articles\liste" />
هيكل التطبيق كالتالي:
![]() ![]() | ||
![]() ![]() | ||
![]() | ||
يُلاحظ وجود المكتبة mysql-connector-java-3.0.10-stable-bin.jar في WEB-INF/lib. وهي التي تحتوي على برنامج التشغيل JDBC الخاص بقاعدة البيانات MySQL المستخدمة هنا.
9.6. الاختبارات
نقوم بتشغيل Tomcat ونطلب URL http://localhost:8080/listarticles/liste.do:

9.7. مصدر بيانات ثانٍ
نستخدم الآن مصدر بيانات ثانٍ موجود في قاعدة بيانات Postgres. وهنا أيضًا، توجد البيانات في جدول ARTICLES الذي يحتوي على نفس الأعمدة المذكورة سابقًا. ومحتواه كما يلي:

يتم تعريف مصدر البيانات الجديد هذا في ملف تكوين 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>
سيكون معرّف (key) المصدر الجديد هو pgdbarticles. ويأتي هذا التحديث من الفئة التي تنفذ الواجهة javax.sql.DataSource. الفئة الافتراضية هي org.apache.struts.util.GenericDataSource المُعرَّفة في المكتبة struts.jar. سيتم التخلي عن هذه الفئة في الإصدارات المستقبلية من Struts، ويُنصح في الإصدار 1.1 باستخدام الفئة org.apache.commons.dbcp.BasicDataSource الموجودة في المكتبة commons-dbcp-1.1.jar. ولا يتم توفير هذه المكتبة بالضرورة مع حزمة Struts. يمكن العثور عليها على الرابط http://jakarta.apache.org/commons/index.html وبشكل أكثر تحديدًا على الرابط http://jakarta.apache.org/site/binindex.cgi. يجب تنزيل المنتج المسمى Commons DBCP. بالنسبة لنظام ويندوز، يمكن تنزيل الملف .zip. يحتوي هذا الملف على الكود المصدري للمكتبة بالإضافة إلى ملف .jar المقابل. يجب استخراج هذا الملف من الأرشيف .zip ووضعه في المجلد WEB-INF/lib الخاص بالتطبيق. كما يجب وضع برنامج تشغيل SGBD المستخدم في نفس المجلد:

لاستخدام هذا المصدر، نقوم بتعديل تعليمة واحدة في كود الإجراء /liste:
نطلب هذه المرة مصدر البيانات المسمى pgdbarticles، c.a.d. مصدر البيانات Postgres.
لم يتبق لنا سوى تجميع كل ذلك، وتشغيل Tomcat، وطلب صفحة http://localhost:8080/listarticles/liste.do:

9.8. الخلاصة
لقد أوضحنا كيفية استخدام مجموعة اتصالات للوصول إلى قاعدة البيانات. تجدر الإشارة هنا إلى أننا لم نلتزم بالبنية MVC. ففي الواقع، تقوم الإجراء /liste بنفسها بإجراء الاستعلامات SQL للوصول إلى البيانات. وكان من الأفضل أن تتعامل مع فئة أعمال وسيطة تخفي حقيقة أننا نبحث عن البيانات في SGBD. ومن المرجح أن تقوم الفئة الوظيفية عندئذٍ بإنشاء مجموعة الاتصالات، ولن تكون هناك حاجة لإعلانها في ملف تكوين Struts. وهذا دليل: فقد يعني وجود قسم <data-sources> في ملف التكوين أن تطبيقنا لا يتوافق مع بنية MVC.





