Skip to content

9. استفاده از یک منبع داده

در اینجا هدف ما ایجاد پایه‌ای برای بهره‌گیری از پایگاه‌های داده در یک برنامه Struts است.

9.1. برنامه Struts /listarticles

ما می‌خواهیم محتویات جدولی را که جزئیات اقلام فروخته‌شده توسط یک فروشگاه را ذخیره می‌کند، نمایش دهیم.

code
کد کالا – کلید اصلی
nom
نام کالا
prix
قیمت آیتم
stockActuel
موجودی فعلی کالا
stockMinimum
سطح موجودی زیر که
باید سفارش مجدد آغاز شود

محتویات جدول به شرح زیر است:

Image

برنامه listarticles همان اطلاعات را (هرچند با نظم کمتری) در یک صفحه وب نمایش می‌دهد:

Image

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> توصیف می‌شود. این تگ از چندین ویژگی پشتیبانی می‌کند:

key
منبع داده را زمانی که چندین منبع وجود دارد، مشخص می‌کند.
driverClass
نام کلاسی که برای دسترسی به پایگاه داده نمونه‌سازی می‌شود. این کلاس معمولاً در یک کتابخانه کلاس (.jar) که توسط ناشر SGBD ارائه می‌شود، یافت می‌شود. این کتابخانه شامل درایور دسترسی به پایگاه داده JDBC است. ویژگی driverClass به این درایور اشاره می‌کند. فایل .jar حاوی کلاس‌های دسترسی به SGBD در پوشه WEB-INF/lib برنامه قرار داده خواهد شد.
url
رشته اتصال برای یک پایگاه داده خاص. درایور JDBC دسترسی به تمام پایگاه‌های داده‌ای را که توسط SGBD مدیریت می‌شوند، فراهم می‌کند. خاصیت url به ما امکان می‌دهد مشخص کنیم کدام یک را قصد داریم استفاده کنیم.
user, password
دسترسی به پایگاه‌های داده محافظت شده است. SGBD کاربران را که با نام کاربری و رمز عبور شناسایی شده‌اند، مدیریت می‌کند. این موارد در ویژگی‌های 'user' و 'password' تگ مشخص می‌شوند.
minCount, maxCount
Struts یک استخر اتصال به SGBD را مدیریت می‌کند. برقراری اتصال به SGBD عملیاتی است که از نظر زمان و حافظه منابع زیادی مصرف می‌کند. به جای باز و بسته کردن یک اتصال به SGBD برای هر درخواست به برنامه، برنامه یک استخر از n اتصال را مدیریت می‌کند، که در محدوده [minCount, maxCount] قرار دارد. اگر در طول یک درخواست، برنامه به یک اتصال نیاز داشته باشد:
- تلاش می‌کند یکی را از استخر اتصالات دریافت کند. اگر یکی آزاد پیدا کند، از آن استفاده می‌کند.
- اگر در استخر هیچ اتصال آزاد وجود نداشته باشد و تعداد اتصالات در استخر کمتر از maxCount باشد، یک اتصال جدید باز شده و به استخر اضافه می‌شود. درخواست فعلی قادر به استفاده از آن خواهد بود.
- اگر هیچ اتصال آزاد وجود نداشته باشد و ایجاد اتصال جدید امکان‌پذیر نباشد، درخواست به حالت تعلیق درمی‌آید.
وقتی درخواست جاری اتصال را می‌بندد، اتصال در واقع بسته نمی‌شود، بلکه به استخر بازگردانده می‌شود.

در اینجا، ویژگی‌های زیر را داریم:

key
dbarticles – این نامی است که منبع داده در زمینهٔ برنامه با آن شناخته می‌شود
driverClass
com.mysql.jdbc.Driver. ما از پایگاه‌داده با نام MySQL استفاده می‌کنیم.
url
jdbc:mysql://localhost/dbarticles. پایگاه داده dbarticles نام دارد و روی ماشین محلی قرار دارد.
user, password
admarticles, mdparticles. به این کاربر تمامی امتیازات پایگاه داده dbarticles اعطا شده است.
minCount, maxCount
۲، ۵. حداقل ۲ اتصال در استخر، حداکثر ۵.

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>

تنها اکشن با نام /list است و با کلاس istia.st.struts.articles.ListeArticlesAction مرتبط است. این اکشن منجر به نمایش موارد زیر می‌شود:

  • صفحه مقالات (/vues/listarticles.jsp)
  • صفحهٔ خطاها (/views/erreurs.jsp)

9.2.3. فایل پیام

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

فایل پیام 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): آرایه‌ای از 5 عنصر از نوع String[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>    

عمل /list محتویات جدول ARTICLES را در یک شیء ArrayList قرار می‌دهد که در پرس‌وجو تحت نام listArticles گنجانده شده است. هر عنصر از شیء listArticles آرایه‌ای از ۵ رشتهٔ متنی است که نمایانگر ۵ مورد اطلاعات (کد، نام، قیمت، 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 در اینجا به‌عنوان یک ردیف نامیده می‌شود. عنصر ردیف نمایانگر یک شیء رشته‌ای ([5]) است که با استفاده از تکرار دوم پیمایش می‌شود. عنصر در این تکرار دوم «ستون» نامیده می‌شود. این عنصر نمایانگر عنصر فعلی یک آرایه از رشته‌ها است و بنابراین یک رشته (کد، نام، قیمت، 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. اقدام /list

هدف اکشن /list این است که یک شی 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");
        }
         // در اینجا منبع داده وجود دارد  از آن استفاده         میشود Connection 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")});
                // خط بعدی
            } //در حالی که
             // منابع آزاد
            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>

پس از به‌دست‌آوردن منبع داده، به استخر اتصالات دسترسی داریم. یک اتصال به پایگاه داده با استفاده از کد زیر برقرار می‌شود:

             // ایجاد اتصال
            Connection connexion = dataSource.getConnection();

در اینجا، استخر اتصال به ما یک اتصال بازیافتی ارائه می‌دهد یا در صورت وجود اتصالات آزاد، یک اتصال جدید ایجاد می‌کند.

9.5. استقرار

زمینهٔ برنامه در فایل پیکربندی Tomcat به صورت زیر تعریف شده است: server.xml:

<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. آزمایش

تام‌کت را راه‌اندازی کرده و http://localhost:8080/listarticles/liste.do را درخواست می‌کنیم:

Image

9.7. یک منبع داده دوم

اکنون از یک منبع داده دوم که در پایگاه داده Postgres ذخیره شده است استفاده می‌کنیم. در اینجا نیز داده‌ها در جدولی به نام ARTICLES با همان ستون‌های قبلی قرار دارند. محتویات آن به شرح زیر است:

Image

این منبع داده جدید در فایل پیکربندی 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>

منبع جدید دارای شناسه‌ی (کلید) 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 مورد استفاده را در همان پوشه قرار دهید:

Image

برای استفاده از این کد منبع، یک دستور در کد مربوط به عمل /list را تغییر می‌دهیم:

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

این بار، منبع داده‌ای به نام pgdbarticles، c.a.d، را به عنوان منبع داده Postgres مشخص می‌کنیم.

تنها کاری که باقی مانده این است که همه چیز را کامپایل کرده، تام‌کت را راه‌اندازی کرده و http://localhost:8080/listarticles/liste.do را درخواست کنیم:

Image

9.8. نتیجه‌گیری

ما نشان دادیم چگونه از یک استخر اتصال برای دسترسی به یک پایگاه داده استفاده کنیم. باید توجه داشت که در اینجا از معماری MVC پیروی نکرده‌ایم. در واقع، خود اکشن /list استعلام‌های SQL را برای دسترسی به داده‌ها انجام می‌دهد. ترجیح داده می‌شد که این اکشن یک کلاس کسب‌وکار واسط را فراخوانی کند تا واقعیت استخراج داده‌ها از یک SGBD را پنهان سازد. احتمالاً کلاس کسب‌وکار سپس استخر اتصال را ایجاد می‌کرد و بنابراین نیازی به تعریف آن در فایل پیکربندی Struts نبود. این یک سرنخ است: وجود بخش <data-sources> در فایل پیکربندی ممکن است نشان دهد که برنامه ما از معماری MVC پیروی نمی‌کند.