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 з’єднань, де 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
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. Файл повідомлень

    <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): масив (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 символьних рядків, що представляють 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], який обходиться за допомогою другого циклу. Елемент цієї другої ітерації називається «колонка». Він представляє поточний елемент масиву рядків і, отже, є рядком (код, назва, ціна, 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");
        }
         // тут джерело даних існує  його використовують         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")});
                // наступний рядок
            } //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>

Отримавши джерело даних, ми отримуємо доступ до пулу з’єднань. З’єднання з базою даних встановлюється за допомогою коду:

             // встановлено з'єднання
            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. Тестування

Запускаємо Tomcat і запитуємо URL 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>

Нове джерело матиме ідентифікатор (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. Для Windows можна завантажити файл .zip. Він містить вихідний код бібліотеки, а також відповідний файл .jar. Його потрібно розпакувати з архіву .zip і помістити в папку WEB-INF/lib програми. У цю ж папку також потрібно помістити драйвер використовуваного SGBD:

Image

Щоб скористатися цим кодом, ми змінюємо одну інструкцію в коді дії /liste:

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

Цього разу ми вказуємо джерело даних під назвою pgdbarticles, c.a.d — джерело даних Postgres.

Залишається лише скомпілювати все, запустити Tomcat і звернутися до URL http://localhost:8080/listarticles/liste.do:

Image

9.8. Висновок

Ми показали, як використовувати пул з'єднань для доступу до бази даних. Зазначимо, що ми не дотрималися архітектури MVC. Дійсно, дія /liste сама виконує запити SQL для доступу до даних. Було б краще, якби вона зверталася до проміжного бізнес-класу, який приховував би той факт, що дані отримуються з SGBD. Ймовірно, саме цей бізнес-клас створював би пул з’єднань, і тоді не було б потреби заявляти його у конфігураційному файлі Struts. Це є підказкою: наявність розділу <data-sources> у конфігураційному файлі може означати, що наш додаток не дотримується архітектури MVC.