Skip to content

9. Veri kaynağının kullanımı

Burada, bir Struts uygulaması içinde veritabanlarından yararlanmanın temellerini atmayı amaçlıyoruz.

9.1. Struts /listarticles uygulaması

Bir mağazanın sattığı ürünlerin özelliklerini kaydeden bir tablonun içeriğini görüntülemek istiyoruz.

code
ürün kodu - birincil anahtar
nom
ürün adı
prix
ürün fiyatı
stockActuel
ürünün mevcut stoğu
stockMinimum
stok seviyesi bu seviyenin altına düştüğünde
yeniden tedarik başlatılması gereken seviye

Tablonun içeriği şöyledir:

Image

listarticles uygulaması da bir web sayfasında aynı sonucu (ancak daha az iyi bir şekilde) verecektir:

Image

9.2. Struts /listarticles uygulamasının yapılandırılması

Uygulamanın web.xml dosyası standarttır:

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 dosyası, veri kaynaklarını tanımlamaya ve yapılandırmaya olanak tanıyan yeni bir <data-sources> bölümü ekler:

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. Veri kaynakları

<data-sources> bölümü, uygulamanın tüm veri kaynaklarını tanımlamaya yarar; her bir veri kaynağı bir <data-source> bölümü ile tanımlanır. Bu etiket çeşitli öznitelikleri destekler:

key
birden fazla veri kaynağı olduğunda kaynağı tanımlar.
driverClass
veritabanına erişmek için örneklenecek sınıfın adıdır. Bu sınıf genellikle SGBD'in yayıncısı tarafından sağlanan bir sınıf kütüphanesinde (.jar) bulunur. Bu kütüphane, veritabanına erişim için JDBC sürücüsünü içerir. driverClass özelliği bu sürücüyü belirtir. SGBD'e erişim sağlayan sınıfların .jar dosyası, uygulamanın WEB-INF/lib klasörüne yerleştirilecektir.
url
belirli bir veritabanına bağlantı dizesi. Aslında, JDBC sürücüsü, SGBD tarafından yönetilen tüm veritabanlarına erişim sağlar. url özelliği, hangi veritabanını kullanacağımızı belirtmemizi sağlar.
user, password
Veritabanlarına erişim korumalıdır. SGBD, kullanıcı adı ve şifre ile tanımlanan kullanıcıları yönetir. Bunlar, etiketin user ve password özniteliklerinde belirtilir.
minCount, maxCount
Struts, SGBD'e bir bağlantı havuzu yönetecektir. SGBD'e bir bağlantı açmak, zaman ve bellek açısından kaynak tüketen bir işlemdir. Uygulamaya yapılacak her istek için SGBD'e bir bağlantı açıp kapatmak yerine, uygulama n adet bağlantıdan oluşan bir havuzu yönetir; burada n, [minCount, maxCount] aralığındadır. Bir istek sırasında uygulamanın bir bağlantıya ihtiyacı olursa:
- bağlantı havuzundan bir bağlantı almaya çalışır. Boş bir bağlantı bulursa, onu kullanır.
- havuzda boş bağlantı yoksa ve havuzdaki bağlantı sayısı maxCount'ten azsa, yeni bir bağlantı açılır ve havuza eklenir. Mevcut istek bu bağlantıyı kullanabilir.
- Eğer boş bağlantı yoksa ve yeni bir bağlantı oluşturmanın bir yolu yoksa, istek beklemeye alınır.
Mevcut istek bağlantıyı kapattığında, bağlantı gerçek anlamda kapatılmaz, ancak havuza geri verilir.

Burada şu özellikler mevcuttur:

key
dbarticles - bu, uygulama bağlamında veri kaynağının tanınacağı addır
driverClass
com.mysql.jdbc.Driver. MySQL veritabanını kullanıyoruz.
url
jdbc:mysql://localhost/dbarticles. Veritabanının adı dbarticles'dir ve yerel makinede bulunmaktadır.
user, password
admarticles, mdparticles. Bu kullanıcı, dbarticles veritabanında tüm ayrıcalıklara sahip olacak şekilde tanımlanmıştır.
minCount, maxCount
2, 5. Bağlantı havuzunda en az 2, en fazla 5 bağlantı.

9.2.2. Eylemler

Struts yapılandırma dosyasında bildirilen eylemler şunlardır:

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

Tek eylem /liste olarak adlandırılır ve istia.st.struts.articles.ListeArticlesAction sınıfıyla ilişkilidir. Bu eylem, aşağıdakilerden biriyle sonuçlanır:

  • makaleler sayfası (/vues/listarticles.jsp)
  • hata sayfası (/vues/erreurs.jsp)

9.2.3. Mesaj dosyası

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

ApplicationResources.properties mesaj dosyası, WEB-INF/classes/istia/st/struts/articles klasörüne yerleştirilecektir. İçeriği şu şekilde olacaktır:

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

Tek bir hata meydana gelebilir: veritabanına erişim hatası. Aslında birkaç farklı hata türü söz konusu olabilir, ancak hepsi aynı hata mesajında toplanır. Bununla birlikte, hatanın kesin nedeni {0} parametresinde belirtilecektir.

9.3. Görünümler

9.3.1. erreurs.jsp görünümü

Bu görünüm bir hata listesi göstermelidir. Bu görünümü daha önce birkaç kez gördük.

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

Bu görünüm, yalnızca <html:errors/> etiketiyle hata listesini görüntüler.

9.3.2. listarticles.jsp görünümü

Bu görünüm, veritabanındaki ARTICLES tablosunun içeriğini görüntülemelidir. Kod şöyledir:

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

<%
     // listarticles: Sorguda ArrayList
     // listArticles(i): 5 elemanlı dizi (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>    

/liste komutu, ARTICLES tablosunun içeriğini, sorguda listArticles adıyla yer alan ArrayList nesnesine aktaracaktır. listArticles nesnesinin her bir öğesi, tablodaki bir ürünle ilgili 5 bilgiyi (kod, ad, fiyat, stockActuel, stockMinimum) temsil eden 5 karakter dizisinden oluşan bir dizidir. Görünüm, listArticles nesnesinin içeriğini bir HTML tablosunda görüntülemelidir. Bunun için <logic:iterate> etiketini kullanır:

            <logic:iterate id="ligne" name="listArticles">
                <tr>
                    <logic:iterate id="colonne" name="ligne">
                        <td><bean:write name="colonne"/></td>
                    </logic:iterate>
                </tr>
            </logic:iterate>

Burada iki yineleme vardır. İlki, ArrayList ve listArticles nesnelerinin öğeleri üzerinde döngü yapar. listArticles nesnesinin geçerli öğesine burada satır denir. Satır öğesi, ikinci yineleme yardımıyla taranan bir String[5] nesnesini temsil eder. Bu ikinci yinelemenin öğesine "sütun" denir. Bu, bir karakter dizisi tablosunun geçerli öğesini temsil eder ve dolayısıyla bir karakter dizisidir (kod, isim, fiyat, stockActuel, stockMinimum). Değeri, <bean:write> etiketiyle görüntülenir.

Görünüm, struts-logic ve struts-bean kütüphanelerine ait etiketleri kullanır. Bu nedenle, bunların kullanımını bildirmek gerekir:

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

9.4. /liste eylemi

/liste eyleminin amacı, bir görünümün bu verileri kullanabilmesi için ARTICLES tablosunun içeriğini temsil eden ArrayList nesnesini isteğe eklemektir. Kod şu şekildedir:

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 {

         // bir bağlantının "articles" tablosunun içeriğini okur
         // bağlamın başlatılması sırasında gerçekleştirilen
         // "dbarticles" veri kaynağı alınır
        DataSource dataSource = this.getDataSource(request, "dbarticles");
        if (dataSource == null) {
             // veri kaynağı oluşturulamadı
            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");
        }
         // burada veri kaynağı mevcut - bu kullanılıyor          Connection bağlantı = null;
        Statement st = null;
        ResultSet rs = null;
        String requête = null;
        ArrayList alArticles = new ArrayList();
         // hataları işliyoruz
        try {
             // bağlantı kuruluyor
            connexion = dataSource.getConnection();
             // sorguyu hazırla SQL
            requête =
                "select code, nom, prix, stockActuel, stockMinimum from articles order by nom";
             // yürütülür
            st = connexion.createStatement();
            rs = st.executeQuery(requête);
             // sonuçları değerlendirme
            while (rs.next()) {
                 // geçerli satırı kaydet
                alArticles.add(
                    new String[] {
                        rs.getString("code"),
                        rs.getString("nom"),
                        rs.getString("prix"),
                        rs.getString("stockactuel"),
                        rs.getString("stockMinimum")});
                // sonraki satır
            } //while
             // kaynakları serbest bırak
            rs.close();
            st.close();
            connexion.close();
        } catch (Exception ex) {
             // hatalar oluştu
            ActionErrors erreurs = new ActionErrors();
            erreurs.add("dbarticles",new ActionError("erreur.dbarticles", ex.getMessage()));
            this.saveErrors(request, erreurs);
            return mapping.findForward("afficherErreurs");
        }
         // tamam
        request.setAttribute("listArticles", alArticles);
        return mapping.findForward("afficherListeArticles");
    } //çalıştır
} //sınıf

Okuyucu, şüphesiz yukarıdaki kodun özünü anlayabilir. Burada sadece tek bir yenilik üzerinde duracağız: Struts çerçeve yapısı tarafından sağlanan DataSource nesnesinin kullanımı. Bu DataSource nesnesi, yapılandırma dosyasının <data-source key="dbarticles"> bölümüyle yapılandırılan bağlantı havuzunu temsil eder. DataSource nesnesi, oluşturulduktan sonra uygulama bağlamına yerleştirilmiştir; böylece bu bağlamdaki tüm nesneler bu nesneye erişebilir. Dolayısıyla, şu şekilde çağrılır:

         // dbarticles veri kaynağını alıyoruz
        DataSource dataSource = this.getDataSource(request, "dbarticles");

Burada, "dbarticles" anahtarıyla tanımlanan veri kaynağını talep ediyoruz. Eğer null işaretçisini alırsak, bu, uygulamanın başlatılması sırasında veri kaynağının oluşturulamadığı anlamına gelir. Bu durumda, hatayı bir ActionErrors nesnesine kaydediyoruz ve yapılandırma dosyasında "afficherErreurs" anahtarıyla tanımlanan hata sayfasına kontrolü devrediyoruz:

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

Veri kaynağına ulaştıktan sonra, bağlantı havuzuna erişebiliriz. Veritabanına bağlantı şu kodla kurulur:

             // bağlantı kuruluyor
            Connection connexion = dataSource.getConnection();

Burada, havuz bize geri dönüştürülmüş bir bağlantı ya da yeni oluşturulabilecek bir bağlantı sağlayacaktır.

9.5. Dağıtım

Uygulamanın bağlamı, Tomcat'ın server.xml yapılandırma dosyasında tanımlanır:

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

Uygulamanın dizin yapısı şu şekildedir:

  
 
 

WEB-INF/lib dizininde bulunan mysql-connector-java-3.0.10-stable-bin.jar kütüphanesine dikkat edin. Burada kullanılan MySQL tabanındaki JDBC sürücüsü bu kütüphanede yer almaktadır.

9.6. Testler

Tomcat’ı başlatıyoruz ve URL http://localhost:8080/listarticles/liste.do adresini çağırıyoruz:

Image

9.7. İkinci bir veri kaynağı

Şimdi bir Postgres veritabanında bulunan ikinci bir veri kaynağını kullanıyoruz. Burada da veriler, öncekiyle aynı sütunlara sahip ARTICLES tablosunda yer almaktadır. İçeriği şöyledir:

Image

Bu yeni veri kaynağı, Struts yapılandırma dosyasında şu şekilde tanımlanmıştır:

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

Yeni kaynağın tanımlayıcısı (anahtarı) pgdbarticles olacaktır. Bu yenilik, javax.sql.DataSource arayüzünü uygulayan sınıftan kaynaklanmaktadır. Varsayılan sınıf, struts.jar kütüphanesinde tanımlanan org.apache.struts.util.GenericDataSource sınıfıdır. Bu sınıf, Struts’un gelecek sürümlerinde kullanımdan kaldırılacaktır ve 1.1 sürümü için commons-dbcp-1.1.jar kütüphanesinde bulunan org.apache.commons.dbcp.BasicDataSource sınıfının kullanılması tavsiye edilir. Bu sınıf, Struts paketiyle birlikte gelmeyebilir. Bu sınıfa http://jakarta.apache.org/commons/index.html adresinden, daha spesifik olarak da http://jakarta.apache.org/site/binindex.cgi adresinden ulaşılabilir. Commons DBCP adlı ürünü indirmeniz gerekir. Windows için .zip dosyasını indirebilirsiniz. Bu dosya, kütüphanenin kaynak kodunu ve ilgili .jar dosyasını içerir. Bu dosyayı .zip arşivinden çıkararak uygulamanın WEB-INF/lib klasörüne yerleştirmelisiniz. Ayrıca, kullanılan SGBD sürücüsünü de aynı klasöre yerleştirmelisiniz:

Image

Bu kaynak kodunu kullanmak için, /liste eyleminin kodundaki bir komutu değiştiriyoruz:

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

Bu sefer, pgdbarticles adlı veri kaynağını, c.a.d olarak belirleyerek Postgres veri kaynağını talep ediyoruz.

Artık tek yapmamız gereken, tümünü derlemek, Tomcat'i başlatmak ve URL http://localhost:8080/listarticles/liste.do adresini çağırmak:

Image

9.8. Sonuç

Bir veritabanına erişmek için bağlantı havuzunun nasıl kullanılacağını gösterdik. Burada, MVC mimarisine uymadığımızı belirtelim. Aslında, /liste eylemi verilere erişmek için SQL sorgularını kendisi gerçekleştiriyor. Bu eylemin, verilerin bir SGBD'ten alınacağını gizleyecek bir ara iş sınıfına başvurması daha uygun olurdu. Muhtemelen bu durumda bağlantı havuzunu iş sınıfı oluştururdu ve Struts yapılandırma dosyasında bunu belirtmeye gerek kalmazdı. Bu bir ipucu: Yapılandırma dosyasında bir <data-sources> bölümünün bulunması, uygulamamızın MVC mimarisine uymadığı anlamına gelebilir.