第Ⅰ編 例題

1.例題1

1.1 概要

例題1は単純なwebアプリです。

処理のシーケンスは図1.1-1の通りです。

 

図1.1-1 例題1のシーケンス図(p1)

図1.1-1 例題1のシーケンス図

 

(1) クライアントから要求1を送ります。

(2) その応答が戻ります。(応答1)

(3) 応答1の画面の入力項目を入力して要求2を送ります。

(4) 応答2が戻ります。

 

1.2 プログラムの構造

図1.2-1に処理の流れと関係するオブジェクトを示します。

 

図1.2-1 処理の流れと関係するオブジェクト(p2)

図1.2-1 処理の流れと関係するオブジェクト

 

図1.2-1の説明をします。なお、[]で囲んだ数字/記号は図中の四角で囲んだ数字記号に対応します。

[1]クライアントPCから要求1がAction1に入ります(四角に囲まれた1)。

要求1に付随する入力データはActionForm1が保持します。

Action1は必要な処理をし、その後、表示に必要なデータをDto1に設定します。

さらに、Dto1をHttpRequestに保存します。そのキーはtheDataです。

[2]Jsp1に移ります。

Jsp1はHttpRequestからDto1を取り出します。

Dto1に設定されているデータを画面に反映します。

[3]クライアントPCに戻ります。

[4]入力項目にデータを入れて、それを要求2としてサーバに送ります。それはAction2に入ります。

この後の処理の流れは要求1と同じです。ActionForm1、Dto1の代わりにそれぞれActionForm2、Dto2が対応します。

[5]Jsp2に移ります。

Jsp1はHttpRequestからDto2を取り出します。

Dto2に設定されているデータを画面に反映します。

[6]クライアントPCに戻ります。

 

実際のアプリでは、目的にする処理が完了するまで、(要求,応答)が繰り返されます。例題ではここから、(1)に戻るようにしています。

 

2.例題2

2.1 概要

例題2は例題1を拡張します。Actionの処理をその中で閉じないでServiceに委託します。

「いいプログラム」であるためには、適切にクラスを分ける必要があります。この解説はクラスの分け方については触れません。分けた結果できるクラスへの制御の渡し方について説明します。

ActionがServiceに処理を委託するのがその第1段階です。

 

2.2 プログラムの構造

図2.2-1に処理の流れと関係するオブジェクトを示します。

 

図2.2-1 処理の流れと関係するオブジェクト(p3)

図2.2-1 処理の流れと関係するオブジェクト

 

例題1と違う点は、Serviceが追加された点です。これに伴って例題1と違う点だけ説明します。

[1a] Action1からServiceを呼び出します。

例題2ではServiceで実質的な処理はしていません。渡された引数を戻り値として戻すだけです。

[4a] Action2からServiceを呼び出します。

Action1の場合と同様、渡された引数を戻り値として戻すだけです。

 

ここで留意することは、Serviceの参照の取り方です。

例題では、ServiceのstaticなメソッドgetInstanceを使って取り出しています。この方法を使うと、Service を「どこでいつ具現化するか」という課題に悩まされません。その代わりにServiceとActionの結合が固定されます。別の方法については他の例題で説明します。

 

3.例題3

3.1 概要

例題2を拡張します。

例題2のServiceの処理をDBアクセスを含む処理にします。例題では、そのうちの特殊な場合としてDBアクセスだけからなる処理です。これはあくまで例題で、実際のServiceはDBアクセスを含む処理になります。

DBアクセスを受け持つクラスとしてDaoクラスを追加します。Daoが処理を委託するクラスがありますが、これについては次の節で説明します。

 

3.2 クラスの構造

図3.2-1に処理の流れと関係するオブジェクトを示します。

 

図3.2-1クラス図(p4)

図3.2-1クラス図

 

Actionの周辺は例題2と同じです。

DaoからDbAccessorに処理が委託され、さらにDataBaseに委託されます。

特徴は、各クラスは直列上に関連するだけで、飛び越して関連しないということです。このような構造は層構造と言われます。Webアプリでは、Service以降を層構造にすることが肝心です。

通常、DaoとDbAccessorの(インスタンスの関係ではなく)クラスの関係は1:1です。DbAccessorはDataSourceのクラスの関係は1:1です。したがって、DaoとDbAccessorの関係は委託でなく継承でもこなせます。DbAccessorとDataSourceの関係も同じです。

ただ、できる限り継承より委託が推奨されていますので、例題では委託を使っています。

 

例題3とこの後説明する例題4及び例題5は機能は同じです。異なる点は委託するために必要なクラスの参照の取り方です。

例題3では次の方法を使っています。

(1)Action:ServiceのstaticなメソッドgetInstanceを使います。

(2)Service:Daoを(Serviceの)コンストラクタの中で具現化します。

(3)Dao:DbAccessorを(Daoの)コンストラクタの中で具現化します。

(4)DbAccessor:DataSourceを(Dbaccessorの)コンストラクタの中で具現化します。

 

DbAccessorの中でDBのドライバ名、url、ユーザID、パスワードが必要です。この情報をDaoの中で固定的に設定し(ハードコーディングし)、Daoのコンストラクタの中でDbAccessorに設定しています。DbAccessorはこのためのsetterを持っています。

 

3.3 機能

(1)要求1の応答として、データを追加する入力フォームを表示します。参考にDBのテーブルのデータすべてを表示します。

(2)入力フォームにデータを入れて送信します。(要求2)

(3)要求2の応答としてDBのテーブルのデータすべてを表示します。

 

要求1の応答として表示される入力フォームを図3.3-1に示します。

図3.3-1 例題の入力画面(p5)

図3.3-1 例題の入力画面

 

この部分は、指定したプロパティによって変わります。この例は、プロパティがprefecture、pop1995、pop2000、pop2005、ratio、densityであることを意味しています。この例題では、各プロパティとDBのテーブルの列が対応する前提になっています。

例題のテーブルの列名は表3.3-1からなっています。

 

表3.3-1 テーブルの列名

都道府県
1995年
2000年
2005年
人口性比
人口密度

 

これと先に述べたプロパティを対応付けます。たとえば、ratioで入力したデータが人口性比の列の値になります。

 

入力データチェックはしていませんので、入力データの内容次第でデータは追加されないで終了する場合があります。エラーの内容はサーバ(tomcat)のコンソールをご覧下さい。

 

4.例題4

4.1 概説

例題3と違う点は、図3.2-1に示したクラスからクラスへの委託の方法だけです。

 

4.2 Factoryパターンの利用

例題4は、委託に必要なオブジェクトの参照を得るためにFactoryを使います。

FactoryはJNDIのデータを参照します。JNDIは各オブジェクトに対応するクラスと必要なパラメタを設定してあります。

 

リスト4.2-1 JNDIを使ったオブジェクトの定義

  <Resource name="n06Example/service"
       auth="Container"
       factory="org.apache.naming.factory.BeanFactory"
       type="jp.co.nsp_ltd.test06.N06ExampleService"
   />
  <Resource name="n06Example/dao"
       auth="Container"
       factory="org.apache.naming.factory.BeanFactory"
       type="jp.co.nsp_ltd.test06.N06ExampleDao"
       dbTableName="table7.txt"
   />
  <Resource name="n06Example/dbAccessor"
       auth="Container"
       factory="org.apache.naming.factory.BeanFactory"
       type="jp.co.nsp_ltd.data.impl.DbAccessorJndi"
   />
<!-- to use my DataSource just to test -->
<!-- -->
  <Resource name="allExample/dataSource"
       auth="Container"
       factory="org.apache.naming.factory.BeanFactory"
       type="jp.co.nsp_ltd.data.impl.ConnectionBuilder"
       driverClassName="sun.jdbc.odbc.JdbcOdbcDriver"
       url="jdbc:odbc:Driver={Microsoft Text Driver (*.txt; *.csv)};Dbq=C:/@/projEclipse/pub/templateGenLite/db/textdb;Extensions=txt"
       username=""
       password=""
   />

 

DbAccessorを除くクラスは、Factoryのstaticなメソッドを使って必要なオブジェクトの参照を得ます。Factoryはそれらの参照をstaticブロックで設定します。

DbAccessorは直接JNDIを使います。技術的には他のクラスと同じようにFactoryを使えます。ただ、Factoeyは例題4に固有のクラスです。そのため、Factoryを使うとDbAccessorが例題4でしか使えないものになってしまいます。このため、DbAccessorは直接JNDIを使ってDataSourceの参照を取得する方式にしました。

この問題は一般のwebアプリでも起きます。DbAccessorは汎用的な機能を持っていますので、JNDIが使える環境に限定するのは得策ではないからです。もしくはすでに開発されているDbAccessor(相当のクラス)が利用できるかもしれません。このような場合は、Adapterパターンで解決する策があります。この例題でいえば、例題3で使ったDbAccessorをそのまま使うことにします。ただ、例題で使っているDbAccessorは小さいクラスですのでAdapterを作らないで、DbAccessorをJNDI用に書き換えました。一般のwebアプリではAdapterパターンを使う選択肢もあります。

JNDIでは、クラス名だけでなく、そのインスタンスに設定するプロパティ値も指定できます。DbAccessorのプロパティのDBのドライバ名、url、ユーザID、パスワードがそれに相当します。こうすることの具体的な得は、ソースを変更しないでDBMSを変更できるということです(*1)。

注 *1: 一般のアプリではSQLのDBMSベンダの方言に対応する必要があります。

 

5.例題5

5.1 概要

例題3、例題4と違う点は、図3.2-1に示したクラスからクラスへの委託の方法だけです。

 

5.2 DI

例題5は、委託に必要なオブジェクトの参照を得るためにDIを使います。

DIをサポートしたソフトウェア(例:Spring)を使うと簡単ですが、DIの学習を兼ねていますので、DIの処理も作ります。

Initializerで、図3.2-1に示したクラスのうちActionを除くクラスを具現化します。さらに、そのクラスを使うクラスに参照を設定します。

具現化するクラスとそれを使うクラスの具体的な名前は外部のpropertiesファイルで与えます。具体的なクラスの名前は外部から可変です。しかし、どの役割のクラスかはInitializerで固定されています。具体的には、ServiceとDao、DbAccessor、DataSourceです。前述のSpringを使うとこれらも含めて可変です。

Initializerはクラスを具現化して、その参照を必要なクラスに設定するだけでなく、プロパティも設定できます。この効用はJNDIの場合と同じです。

5.3 Initializer処理

INitializerの処理で特徴的な処理を二つ紹介します。

 

5.3.1 クラスの動的具現化

クラス名をソースに固定しないで、変数(文字列)で与えて具現化する方法です。

リスト5.3-1にそのコードを示します。

 

リスト5.3-1 クラスの動的具現化

    Class clazz = Class.forName(className);
    Object obj = clazz.newInstance();

注 必要な例外の処理は略しています。

 

このインスタンスを具体的に使うには、これはクラスではキャストできませんから(*1)、interfaceを使います。

注 *1:具体的なクラス名が分かっていれば、newで具現化します。

 

5.3.1 プロパティの設定

これもソース上でクラス名が明示されていないときの問題です。該当するクラスにプロパティを設定する必要がある場合に生じることがあります。

該当のプロパティのsetterがinterfaceに含まれていれば、クラスのインスタンスをinterfaceでキャストするとsetterが使えます。

該当のプロパティのsetterがinterfaceに含まれていない場合はキャストする方法は使えません。この場合は、JavaのReflection機能を使います。

 

リスト5.3-2 プロパティの設定

    method = obj.getClass().getDeclaredMethod(setterName, new Class[]{type});
    method.invoke(obj,v);

注 必要な例外の処理は略しています。

 

JavaのReflection機能は最小限にすべきですが、これは許される場合です。

 

6.例題6

6.1 概要

例題6は例題5をベースにして、範囲を指定して検索する機能を付けます。これはtemplateGenLiteを使って、実際にwebアプリを作る簡単な例です。

これまでの例題1~例題5は第Ⅱ編 操作書に記述に従ってツールを使うと自動的に生成されます。自動生成されたものは実際のアプリにそのままでは使えません。しかし、自動生成されたものを母体にして開発すると、最初から作るより短時間で開発が出来ます。

この例題の目的は、これまでの例題を目的の仕様になるように変更する手順と、その手順が簡潔であることを示すことです。

例題6の仕様を説明します。

要求1に対して、図6.1-1に示す入力フォームが表示されます。テーブルの全データも表示します。これは参考のためのデータです。体裁についてはここでは考慮していません。

 

図6.1-1 例題6の入力データフォーム(p9)

図6.1-1 例題6の入力データフォーム

 

これに対応するプロパティとテーブルの列名の対応を表6.1-1に示します。

表6.1-1 入力とテーブル列名及びプロパティの対応

#
テーブル列名
入力キャプション
プロパティ
1
-(*1)
検索条件

2
都道府県
都道府県(*2)
prefectures
3
1995年
1995年人口
pop1995L
4
(同上)

pop1995H
5
2000年
2000年人口
pop2000L
6
(同上)

pop2000H
7
2005年
2005年人口
pop2005L
8
(同上)

pop2005H
9
人口性比
人口性比
ratioL
10
(同上)

ratioH
11
人口密度
人口密度
densityL
12
(同上)

densityH
13

送信

14

リセット

注 *1 -記号は該当する列又はプロパティがない意味です。プロパティがないということはアプリの中で処理の対象でないことを意味します。

   *2 複数選択できる

 

たとえば入力として1995年人口とその下の~のフィールドにデータが入力されると、それらはそれぞれプロパティpop1995Lとpop1995Hに設定されます。

 

6.2 処理の流れ

具体的な処理の内容は例題5と異なりますが、構造と処理の流れは例題5と変わりません。次の5項目を変更すると目的の処理ができるようになります。この変更が定型的な手順であることが実際の開発で役立つ理由です。

(1) 画面 JSP1のform要素

(2) ActionForm2 入力項目を保持できるプロパティを持つbean

(3) Dto2 入力の内容をServiceに渡すbean

(4) Action2 入力を処理して、Serviceにさらなる処理を委託する。

(5) Dao 入力に応じてDBアクセスする

 

具体的なコードについて詳しく見ていきます。

 

6.2.1 入力フォーム

リスト6.2-1は図6.1-1に対応する入力フォームのコードです。例題5の該当部分と入れ替わります。入力の型は、都道府県以外はtextです。都道府県はselectです。複数選択できるようにmultiple="multiple"を指定しています。

 

リスト6.3-1

<form id="f1" name="f1" ...>
<fieldset>
検索条件<br/>
都道府県<select id="Z00" name="prefectures" multiple="multiple">
    <option value="0">北 海 道</option>
    <option value="1">青  森</option>
    ...
    <option value="46">沖  縄</option>
</select><br/>
1995年人口<input type="text" id="Z01" name="pop1995L" value=""/><br/>
~<input type="text" id="Z02" name="pop1995H" value=""/><br/>
2000年人口<input type="text" id="Z03" name="pop2000L" value=""/><br/>
~<input type="text" id="Z04" name="pop2000H" value=""/><br/>
2005年人口<input type="text" id="Z05" name="pop2005L" value=""/><br/>
~<input type="text" id="Z06" name="pop2005H" value=""/><br/>
人口性比<input type="text" id="Z07" name="ratioL" value=""/><br/>
~<input type="text" id="Z08" name="ratioH" value=""/><br/>
人口密度<input type="text" id="Z09" name="densityL" value=""/><br/>
~<input type="text" id="Z10" name="densityH" value=""/><br/>
<input type="submit" id="Z11" name="" value="送信"/><br/>
<input type="reset" id="Z12" name="" value="リセット"/><br/>
</fieldset>
</form>

 

 

6.3.2 ActionForm

表6.1-1のプロパティに対応するActionForm bean(抜粋)です。prefecturesは複数組のデータが入るのでStringの配列です。その他は文字型です。ActionForm2と入れ替わります。

 

リスト6.3-2 ActionForm2

package ...

import org.apache.struts.action.ActionForm;

public class ExampleActionForm extends ActionForm{

    private String[] prefectures;
    private String pop1995L;
    private String pop1995H;
    private String pop2000L;
    private String pop2000H;
    private String pop2005L;
    private String pop2005H;
    private String ratioL;
    private String ratioH;
    private String densityL;
    private String densityH;

    public String[] getPrefectures(){
        return prefectures;
    }
    public void setPrefectures(String[] prefectures){
        this.prefectures = prefectures;
    }

    public String getPop1995L(){
        return pop1995L;
    }
    public void setPop1995L(String pop1995L){
        this.pop1995L = pop1995L;
    }
    ...
}

 

ActionFormは入力フィールドに対応するプロパティを含むbeanです。

 

6.3.3 Dto2

Dto2はAction2の処理結果をServiceに渡すためのクラスです。次の項で説明するように、この例題ではAction2での実質的な処理はないので、Dto2の内容はActionForm2と変わりません。だからといって、Dto2の代わりにActionForm2を使う発想はよくありません。Dto2はwebアプリのコンテナやMVCフレームワークの影響を除く重要な役割があります。

具体的にいうと、Dto2はstrutsのActionFormを継承していないということです。一般のwebアプリではDto2の存在意義は他にもあるでしょうが、本例題では、この点だけです。

リスト6.3-3にDto2のコードを示します。

 

リスト6.3-3 Dto2のコード

package ...

public class ExampleDto2 {// ActionFormを継承しない
    // ActionForm1と同じsetter/getterがここに入る。
}

 

 

6.3.4 Action2

ActionForm2でAction2に運ばれた入力データに必要な処理をします。さらなる処理をServiceに委託するため、その結果を別のbean(クラス)に設定します。Action2でする処理の主なものを次にあげます。

 

(1) MVCのフレームワーク(*1)に依存しないようにする。

ActionFormはstrutsのActionFormを継承していますのでstrutsの影響下にあります。一方ServiceとDaoはwebアプリ用の特定のコンテナやフレームワークに影響を受けてはいけません。仮にバッチ環境であってもServiceとDaoは使えなければなりません。

 

(2) データのチェックをする。

strutsが支援する入力データのチェックは、それを使うように設定すればアクションに入る前に行われます。アクションの中での入力データのチェックは、strutsが支援する入力データのチェックを使わないか、それで対応できない場合にするチェックです。

 

(3) データの型の変換をする。

ActionFormのデータは文字列型もしくはその配列です。これは、本来の型に一致しているとは限りませんので、bean(クラス)に移すときに変換します。

注 全体の構造を把握しやすくするため、この例題では入力に対応するデータは文字型で扱って処理を簡潔にしています。

 

(4) MVCのフレームワーク(*1)の中で済ませるべき処理をする。

ServiceとDaoではwebアプリ用の特定のコンテナやフレームワークを意識する処理は出来ません。それが必要ならServiceを呼び出す前にしておく必要があります。

 

注 *1: ここではservletコンテナ(TOMCAT)とstrutsを指します。

 

この例題ではMVCのフレームワークに依存しないようにする以外に処理はありません。したがって、形式的にはActionForm2のデータを別のbean(Dto2)に複写するだけです。

 

前の項にも書きました通り、Dto2はstrutsのActionFormを継承しないbeanであることが大切なことです。

リスト6.3-3にAction2(抜粋)のコードを示します。

 

リスト6.3-4 Action2のコード(抜粋)

        String[] prefectures = frm.getPrefectures();
        String pop1995L = frm.getPop1995L();
        ...

        dto2.setPrefectures(prefectures);
        dto2.setPop1995L(pop1995L);
        ...
        List theData = service.findRecordsByCond(dto2);
        ...

 

 

6.3.5 Dao

DaoのfindPrefecturesByCondメソッドを変更します。

都道府県については複数の都道府県を指定できるようにします。その他の列については、値の範囲を指定できるようにします。ただし、簡略のため本来、数値として扱うべき列を文字型で扱っていますので、大小関係が数値の大小関係と一致しない恐れがあります。

例えば次のようなwhere句が作られます。

... WHERE prefectures IN(''北海道,'青森') AND pop1995 between '1000' and '2000'

 

このためのコードをリスト6.3-5に示します。

 

リスト6.3-5 Daoのコード(変更部分)

  String cond1 = du.buildWhereIn("都道府県",condDto.getPrefectures());
  String cond2 = du.buildWhereBtw("1995年",condDto.getPop1995L(),condDto.getPop1995H());
  String cond3 = du.buildWhereBtw("2000年",condDto.getPop2000L(),condDto.getPop2000H());
  String cond4 = du.buildWhereBtw("2005年",condDto.getPop2005L(),condDto.getPop2005H());
  String cond5 = du.buildWhereBtw("人口性比",condDto.getRatioL(),condDto.getRatioH());
  String cond6 = du.buildWhereBtw("人口密度",condDto.getDensityL(),condDto.getDensityH());

 

buildWhereInとbuildWhereBtwはwhere句のinとbetween演算の句を作るユーティリティです。これは、ダウンロード物の中に含まれているDbUtilクラスのメソッドです。

 

 

第Ⅱ編 操作書

 

7. 付録A template生成ツール

7.1 概要

本文で説明した例題1から例題5を簡単に生成できるツールです。

学習の目的には生成したソースを本文の参考に使って下さい。

学習だけではなく、効率よくwebアプリ作る道具としても利用できます。構造を整えて中身は空にしていますので、必要なことは業務アプリをそこに書き込むことです。

 

7.2 前提条件

strutsが使える環境があるとします。

 

 

7.3 ダウンロード

次のアドレスからダウンロードして下さい。

TODO ダウンロードアドレス

 

これを解凍します。解凍した後のディレクトリ構造をリスト7.3-1に示します。

 

リスト7.3-1 ディレクトリ構造

templateGenLite0
 +---db
 | +---textdb
 | +---schema.ini
 | +---table7.txt
 +---dev
 | +---gen
 | | +--- <<work>>
 | +---template
 | | +---struts-config-template.xml
 | +--- TemplateGeneratorLite.xls
 +---src
 | +---[jp.co.nsp_ltd.data](*1)
 | | +---IDbAccessor.java
 | | +---[jp.co.nsp_ltd.data.impl] (*1)
 | | | +---ConnectionBuilder.java
 | | | +---DbAccessor.java
 | | | +---DbAccessorDi.java
 | | | +---DbAccessorJndi.java
 | +---[jp.co.nsp_ltd.util] (*1)
 | | +---DbUtil.java
 | | +---SetCharsetFilter.java
 | +---<<source>>
 |
 +---web
 | +---index.jsp
 | +---images
 | +---strut-power.gif
 | +---jss
 | +---tinyScript.js
 | +---pages
 | +---styles
 | +---tinyStyle.css
 | +---WEB-INF
 | +---strut-config.xml
 | +---web.xml
 | +---validation.xml
 | +---classes
 | | +---<<最初は空>>
 | +---lib
               +---<<strutsのlibをコピーして下さい(*2)>>

注 *1:[]はJavaのパッケージに対応するディレクトリの構造を略記したものです。

   *2:libは空です。例題を動かすには次節で説明するライブラリが必要です。

 

7.4 必要なライブラリ

ダウンロードしたファイル中のlibディレクトリは空です。例題を動かすには表7.4-1のライブラリが必要です。

 

表7.4-1 例題を動かすのに必要なライブラリ


ファイル名

antlr-2.7.2.jar

commons-beanutils-1.7.0.jar

commons-chain-1.1.jar

commons-digester-1.8.jar

commons-logging-1.0.4.jar

commons-validator-1.3.1.jar

oro-2.0.8.jar

struts-core-1.3.8.jar

jstl.jar
10
standard.jar

 

 

例題はStruts1.3.8で確認しています。表7.4-1の#1~#8はStruts1.3.8に付いてくるjarファイルです。しかし。#9と#10は、Struts1.3.8に付いてくるjarファイルは旧いので、次のサイトからダウンロードして使って下さい。

http://jakarta.apache.org/site/downloads/downloads_taglibs-standard.cgi

1.1.2.zip

 

7.4 eclipaseの登録

eclipseに登録します。eclipseのプロジェクトのディレクトリはtemplateGenLite0です。

templateGenLite0は別の名前に変更しても構いません。templateGenLite0以下のディレクトリ名とファイル名は変更してはいけません。

ソースのディレクトリはsrcにして下さい。Javaのコンパイル結果の出力先はWEB-INFの下のclassesにして下さい。

StrutsではソースのディレクトリをWEB-INFの下のsrcディレクトリにするいのがデフォルトです。Strutsのdefaultに従っても問題ありません。templateGenLIteを使うときにソースディレクトリを指定します。それについては次の節で説明します。

eclipseに登録が完了すると、コンパイルエラーが表示されます。これは、servletのライブラリがないためです。servlet用のjarファイルはTOMCATのインストールディレクトリにありますので、それを外部のjarファイルとして追加します(*1)。

TOMCAT5.5.xの場合、servlet用jarファイルは次のファイルです。

(TOMCATインストールディレクトリ)\common\lib\servlet-api.jar

 

注 *1: プロジェクトを選択して、ファイル/プロパティをクリックします。ダイアログのタブライブラリをクリックして、外部jarファイル追加をクリックして追加します。

 

7.5 ツールの起動と実行時パラメータの設定

templateGenLite0\dev\templateGenLite.xlsを起動します。

rtシートをアクティブにします。

ここで次のパラメターを入力します。

あらかじめ値が入っていますが、これは実行環境に依存しますので実際の環境に合わせて値を変更して下さい。

 

表7.5-1 実行時パラメータ

#
property
value
1
projectBaseDir
C:\@\projEclipse\pub\templateGenLite0
2
webBaseDir
web
3
srcBaseDir
src
4
dbBaseDir
db\textdb

 

説明

#1:projectBaseDir プロジェクトのディレクトリ。ダウンロードしたままの場合はtemplateGenLite0です。パスも含めて指定します。

#2: webBaseDir webのドキュメントベースに相当するディレクトリです。TODO 固定した方がいい

#3:srcBaseDir ソースディレクトリ。projectBaseDirを基準にしてパスを含めて指定できます。ダウンロードしたままの場合はsrcです。WEB-INF下のsrcの場合、web/WEB-INF/srcです

#4: dbBaseDir テストに使うDBです。

 

TODO 確認のツール

 

7.6 webアプリ生成用パラメータ

生成のパラメータを設定します。例を表7.6-1に示します。webアプリ1件について1シート必要です。例題のシートをコピーして使って下さい。

 

表7.6-1 webアプリ生成用パラメータ

#
item
value
1
name
n01Example
2
package
jp.co.nsp_ltd.test01
3
page.ext
jsp
4
request1
r1_n01Example
5
action1
N01Example1Action
6
dto1
N01Example1Dto
7
view1
v1_n01Example
8
form1
N01Example1ActionForm
9
request2
r2_n01Example
10
action2
N01Example2Action
11
dto2
N01Example2Dto
12
view2
v2_n01Example
13
form2
N01Example2ActionForm

 

説明

第Ⅰ編 第1章の図1.2-1のクラスを参考にして下さい。

(1)name プロジェクトの名前、 詳細は本節の後半で説明します。

(2)package パッケージ名

(3)page.ext 画面用ファイルの拡張子。jspしか使えません。

(4)request1 要求1のパス名(urlの一部)

(5)action1 Action1のクラス名

(6)dto1 データを保存するためのbeanのクラス名(二つのうちの第1のもの)

(7)view1 応答1の画面のファイル名(拡張子は含まず)

(8)form1 ActionForm1、入力データを保持します

(9)request2 要求2のパス名(urlの一部)

(10)action2 Action2のクラス名

(11)dto2 データを保存するためのbeanのクラス名(二つのうちの第2のもの)

(12)view2 応答2の画面のファイル名(拡張子は含まず)

(13)form2 ActionForm2、入力データを保持します

 

プロジェクト名nameの用途

プロジェクト名は次に列挙する部分で使われます。クラス名の一部としても使いますので、先頭が英字で、後続の文字は英数字でなければなりません。使われる箇所によっては、Javaの通則に従って先頭文字を大文字又は小文字に変えて使います。

 

(a)クラスのdefault名の一部

表7.6-1の#4~#13はnameがn01Exampleの場合の各クラス又はリクエストのパス名のデフォルト値です。デフォルトボタンを押すとこれらが生成されます。

 

(b)次のクラス(interface)の名前

 

表7.6-2 nameから作るクラス(interface)名

#
クラス/interface
名前

Dao interface
uJsName & "IDao"

Dao
uJsName & "Dao"

Service interface
uJsName & "IService"

Service
uJsName & "Service"

BaseAction
uJsName & "BaseAction"

Initializer
uJsName & "Initializer"

.properties
jsName

Factory
uJsName & "Factory"

注 jsName、uJsNameはそれぞれ指定されたnameの先頭文字を小文字又は大文字にしたもの

 

(c)struts-config.xmlに挿入する場合のマーカ

struts-config.xmlに自動的に必要なフォームbeanとアクションマッピングの設定を追加します。その際、追加(挿入)する場所を示すマーカの一部として使います。同じプロジェクト名の場合はすでにその名前に由来するマーカがあればそれを置き換えます。form bean用とaction用のマーカの使われ方をそれぞれリスト7.6-1とリスト7.6-2に示します。

 

リスト7.6-1 struts-config.xml のform beanの定義

<!-- never change !.#.#formbean -->
...
<!-- never change sub!.#.#projectName -->
... プロジェクト名がprojectNameのプロジェクト用form beanの定義

<!-- never change sub!.#.# projectName.end -->
...
<!-- never change !.#.#formbean.end -->

 

リスト7.6-2 struts-config.xml のactionの定義

<!-- never change !.#.#actionmapping -->
...
<!-- never change sub!.#.#projectName -->
... プロジェクト名がprojectNameのプロジェクト用actionの定義

<!-- never change sub!.#.# projectName.end -->
...
<!-- never change !.#.#actionmapping.end -->

 

(d)JNDIのキー

JNDIに登録するクラスのキーの一部に使います。表7.6-3に示します。

 

表7.6-3 JNDIキー

#
一般クラス名
JNDIキー

Service
jsName & "/service"

Dao
jsName & "/dao"

DbAccessor
jsName "/dbAccessor"

 

 

7.7 生成できるプログラム

生成できるプログラムのタイプは5種類です。これは例題1~例題5に対応しています。一つの例題が1シートに対応しています。

例題とシート名の対応を表7.7-1に示します。

 

表7.7-1 例題とシート名の対応

#
マクロ名
例題名
シート名
1
gen1
例題1
jobstep(1)

gen2
例題1
jobstep(1)

gen3
例題2
jobstep(3)

gen5
例題3
jobstep(5)

gen6
例題4
jobstep(6)

gen7
例題5
jobstep(7)

 

7.8 プロパティ名の定義

例題3と例題4、例題5の場合は、マクロの実行に先立ってプロパティ名をシート上の領域に記入しておく必要があります。場所は任意です。例を表7.8-1に示します。[prpoerty]の部分は見やすくするための書き込みで、生成には使われません。

 

表7.8-1 プロパティ名の定義(例)

[property]





prefecture
pop1995
pop2000
pop2005
ratio
density

 

7.9 DBのテーブルの情報

例題3と例題4、例題5の場合は、マクロの実行に先立ってDBのテーブルの情報も必要です。

templateGenLiteでは、この情報をtextファイルを使ったDBから読み取る方式にしています。

これは、実行時パラメータで指定したdbディレクトリになければなりません。次の二つのファイルからなります。

(1)schema.ini --- テーブルに関する情報。ファイル名は固定

(2)テーブルファイル --- データ。ファイル名はschema.iniの中に記述

 

ダウンロードしたファイルに含まれるschema.iniをリスト7.9-1に示します。

 

リスト7.9-1 schema.ini

[table7.txt]
Format=TabDelimited
ColNameHeader=TRUE
Col1=都道府県 char width 16
Col2=1995年 integer width 16
Col3=2000年 integer width 16
Col4=2005年 integer width 16
Col5=人口性比 double width 16
Col6=人口密度 integer width 16

 

先頭の[ ]の中がテーブル名です。それは、そのデータを保持するファイルの名前に一致しています。

Formatはcsv形式の区切り文字です。

ColNameHeaderはテーブルの先頭が列名であることを示しています。

Col1~Col6は各列の列名と型を示す情報です。Col1~Col6があるので、ColNameHeader=FALSEにして、テーブルのデータの第1行を列名にしない選択肢もできます。管理がしやすいのでColNameHeader=TRUEにしています。

処理を簡単にするため、ActionFormとDtoのプロパティはすべてを文字型にしています。そのため、それらとDB上のデータの型とを整合する処理がDaoに入っています。このため、Daoはやや読みづらいコードになっています。プロパティはすべてを文字型にしているのは、型を正確に処理すると例題の狙いでないところが複雑になるからです。結果として例題の狙いであるプログラムの構造化が見えにくくなるためです。実際のアプリでは正しく扱う必要があります。

 


schema.iniのテーブル名と列の情報をプログラムの生成時に使います。
生成したプログラムを実行するのにtext DBを使う場合はテーブルファイルも必要です。実行時に使うDBMSは生成したプログラムと設定(ファイル)を変更してtext DB以外のDBも使えます。text DB以外にEXCEL、MS ACCESS、HSQLDBで確認しています。

テーブルのデータを保持したファイルをリスト7.9-2に示します。

 

リスト7.9-2 テーブル

都道府県 1995年 2000年 2005年 人口性比 人口密度
北 海 道 5,692 5,683 5,627 90.6 72
青  森 1,482 1,476 1,437 89.6 150
...
沖  縄 1,273 1,318 1,361 96.4 598

 

templateGenLiteはDBデータのうちテーブル名とそのテーブルに書かれている列名を読み取って利用しています。

テーブルの列と、前の節で説明したプロパティは、その順序で対応を付けます。そのため、個数は一致していなければなりません。

 

表7.8-1のプロパティとリスト7.9-2のテーブルは表7.9-1に示すの対応付けがされます。

 

表7.9-1 プロパティとテーブルの列の対応

番号
1
2
3
4
5
6
プロパティ名
prefecture
pop1995
pop2000
pop2005
ratio
density
列名
都道府県
1995年
2000年
2005年
人口性比
人口密度

 

生成されたプログラムは、そのままであればこのDBを使ってテストできます。また、生成されたプログラムはプログラム又は設定パラメータを変更すると、別のDBMSを使って実行できます。

 

 

7.10 生成物

生成されるものの出力先は表7.10-1に示す通りです。ただし、gen1の場合は結果はファイルに出力されません。シート名がgenCodeのシートに出力されます。

 

表7.10-1 生成物とその出力先

#
生成物
出力先

クラス
ソースディレクトリの下の、パッケージに応じたディレクトリ

Jsp
WEB-INF/pagesの中

strruts-config
strruts-config.xmlを更新します。(*1)

注*1:変更されたものは2世代までバックアップファイルに保存されます。

 

7.10 マクロの実行

マクロの実行に先立って次の設定されていることを確認して下さい。

(1)実行時パラメータ

(2)webアプリ生成用パラメータ

(3)プロパティ

(4)text DBの設定

 

マクロの実行は、VBAマクロの一般的な形で出来ます。しかし、例題のシートをコピーして使うことをお勧めします。シート上に入りつけている実行ボタンを押すとマクロが実行されます。

 

例題3と例題4、例題5用のマクロを実行すると、プロパティ名を入力するプロンプトが出ます。シートに記入したパラメタ名の領域(例:表7.8-1の2行目)を選択します。

 

 

7.11 Resourceの定義

例題6の場合は、context.xmlにResourceを定義しなければなりません。これはJNDIで参照される情報です。設定の例をリスト7.11-1に示します。

 

リスト7.11 Resourceの定義

①<?xml version='1.0' encoding='utf-8'?>
②<Context docBase="C:\@\projEclipse\pub\templateGenLite_test/web" workDir="C:\@\projEclipse\pub\templateGenLite_test/web/work" reloadable="false">

③ <Resource name="n06Example/service"
       auth="Container"
       factory="org.apache.naming.factory.BeanFactory"
       type="jp.co.nsp_ltd.test06.N06ExampleService"
   />
④ <Resource name="n06Example/dao"
       auth="Container"
       factory="org.apache.naming.factory.BeanFactory"
       type="jp.co.nsp_ltd.test06.N06ExampleDao"
       dbTableName="table7.txt"
   />
⑤ <Resource name="n06Example/dbAccessor"
       auth="Container"
       factory="org.apache.naming.factory.BeanFactory"
       type="jp.co.nsp_ltd.data.impl.DbAccessorJndi"
   />
⑥ <Resource name="allExample/dataSource"
       auth="Container"
       factory="org.apache.naming.factory.BeanFactory"
       type="jp.co.nsp_ltd.data.impl.ConnectionBuilder"
       driverClassName="sun.jdbc.odbc.JdbcOdbcDriver"
       url="jdbc:odbc:Driver={Microsoft Text Driver (*.txt; *.csv)};Dbq=C:/@/projEclipse/pub/templateGenLite_test/db/textdb;Extensions=txt"
       username=""
       password=""
   />
</Context>

 

説明

①context.xmlのxml指令文 TODO

②Context要素

③Resource要素 Serviceの定義

    name属性は、プロジェクト名/service。生成したプログラムからこの名前で参照しています。プロジェクト名/serviceに従わない場合は、プログラム(Factoryクラス)も変更します。

    type属性は、Serviceの完全クラス名を指定します。

    auth属性とfactory属性は例の通り指定します。

④Resource要素 Daoの定義

    name属性は、プロジェクト名/dao。生成したプログラムからこの名前で参照しています。プロジェクト名/daoに従わない場合は、プログラム(Factoryクラス)も変更します。

    type属性は、Daoの完全クラス名を指定します。

    auth属性とfactory属性は例の通り指定します。

⑤Resource要素 DbAccessorの定義

    name属性は、プロジェクト名/dbAccessor。生成したプログラムからこの名前で参照しています。プロジェクト名/dbAccessorに従わない場合は、プログラム(Factoryクラス)も変更します。

    type属性は、DbAccessorの完全クラス名を指定します。

    auth属性とfactory属性は例の通り指定します。

⑥Resource要素 DataSourceの定義

    name属性は、allExample/dataSource。生成したプログラムからこの名前で参照しています。プロジェクト名/serviceに従わない場合は、プログラム(DbAccessorJndiクラス)も変更します。

    type属性は、Serviceの完全クラス名を指定します。

    driverClassName属性,url属性,username属性,password属性はDBMSに従って指定します。

    auth属性とfactory属性は例の通り指定します。

 

 

7.11 生成したプログラムの実行

gen66で生成した場合は、context.xmlにResourceを正しく設定していることを確認して下さい。

TOMCATを立ち上げます。

urlを入れます。urlは例えば次の形をしています。

http://localhost:8080/templateGenLite/r1_n01Example.do

 

r1_n01Exampleはwebアプリ生成用パラメータのrequest1の値です。最後に「.do」が必要です。

 

前もって必要なクラス

import jp.co.nsp_ltd.data.xxxxxxxxxx

import jp.co.nsp_ltd.data.impl.DbAccessor;

import jp.co.nsp_ltd.util.DbUtil;

 

 

gen1TemplateLite MVコード作成(シート出力)

gen2TemplateLite MVコード作成(ファイル出力)

Example1Action.java

Example1ActionForm.java プロパティnameだけ

Example1Dto.java

Example2Action.java

Example2ActionForm.java

Example2Dto.java

 

gen3TemplateLite MVBコード作成(ファイル出力)

N03Example1Action.java service.findData1

N03Example1ActionForm.java

N03Example1Dto.java

N03Example2Action.java service.findData2

N03Example2ActionForm.java

N03Example2Dto.java

N03ExampleService.java

N03ExampleServiceImpl.java 2メソッド 引数をそのまま戻す

 

gen5TemplateLite MVBDコード作成

DBを定義したシートも同時に必要になる。(列名だけでいい)

N04Example1Action.java service.findRecordsByCond(Dto2=)を呼び出す。引数に値が設定されていないとすべてのレコードを読む仕様

N04Example1ActionForm.java 指定したプロパティを持つ

N04Example1Dto.java Form1と同じプロパティ

N04Example2Action.java service.addRecord(dto1); dto1form1のプロパティから移される。

N04Example2ActionForm.java Form1と同じ

N04Example2Dto.java Form1と同じ

N04ExampleBaseAction.java 実質的な処理は含まない

N04ExampleDao.java

N04ExampleDaoImpl.java

次のメソッドを実装している。

    public List findAllRecords();

    public N04Example1Dto findRecordByPrefecture(String prefecture);

    public List findRecordsByCond(N04Example2Dto dto);

    public void addRecord(N04Example1Dto dto);

    public void updateRecordByPrefecture(N04Example1Dto dto);

    public void deleteRecordByPrefecture(String prefecture);

N04ExampleService.java

N04ExampleServiceImpl.java 同名のDaoのメソッドを呼び出す。

 

 

ERROR genSqlTagJsp SqlTag JSPコード作成---マクロがない。

 

createHtmlForm FORM作成

html formの作成 gen5TemplateLiteで作成されるhtml formDBの列にのみ依存する。これは実際のformと差が大きいのでそれを補正する内容を作る。

genCodeシートに出力するので、jspの該当部分(formタグで挟まれている部分)を置き換える。

 

createActionForm ActionForm作成

createHtmlFormに対応するActionFormを生成します。

genCodeシートに出力するので、actionFormの該当部分(class{...}で挟まれている部分)を置き換える。

genCodeシートには、該当するActionFormを処理するのに必要なActionの処理も同時に出力する。ActionFormからデータを取り出して、それをDto2に設定する処理です。Actionの該当部分をこれで置き換えます。

 

createDao_COND Dao変更分作成

createHtmlFormに対応するDao(一部)を生成します。where句を作ります。form captionに~(全角)があると、その直前のフィールドと対になって範囲を表すと解釈します。

 

createHtmlForm, createActionForm, createDao_CONDを使うために次のテーブルを作成しておく必要があります。

 

col name
form caption
prop name
input control
attribute

検索条件



都道府県
都道府県
prefectures
selectm
北 海 道
1995年
1995年人口
pop1995L
text



pop1995H
text

...
...
...
...
...
...
...
...
...
...

 

form captionに付随するフィールドの値がprop pnameのプロパティに保存されます。form-captionに対応するprop nameは必ずしも必要ありません。対応するprop nameを持たないフィールドのデータはActionFormに含まれません。

col nameはform captionに対応する、DBの列名です。form captionが~の場合は、直前のフィールドと対になって範囲を表すと解釈します。

 

 

wiring

gen5TemplateLiteまでは

Serviceは自ら具現化する。コンストラクタの中でDaoを具現化する。Daoの中でDbAccessorを具現化する。

gen1TemplateLitegen2TemplateLitegen3TemplateLiteBaseActionがない。

gen1TemplateLiteServiceクラスがない。

gen2TemplateLitegen3TemplateLitegen5TemplateLitestaticなメソッドgetInstanceServiceクラスのインスタンスを取得する。gen5TemplateLiteBaseActionがあるが実質何もしていない。

gen2TemplateLite

 

 

 

gen6TemplateLite

FactoryでService、Dao、DbAccessor、DataSourceを具現化する。この部分はstaticブロックで実行されます。

JNDIを使う。

Factoryクラスに、Service、Dao、DbAccessor、DataSource それぞれのインスタンスを取り出すためのstaticなメソッドがある。

Service、Dao、DbAccessorは参照するインスタンスを取り出す方法が違うのでgen5TemplateLiteまでのものとは違う。

BaseActionでserviceを取り出し、子のActionはgetServiceでserviceを取り出す。

 

gen7TemplateLite

InitializerクラスでService、Dao、DbAccessor、DataSourceを具現化する。この部分はBaseActionでserviceがnullのときに呼び出す仕掛けである(実際のアプリでどうするかは別の問題)

Initializerの入力として.propertiesファイルを作る。

 

 

直説法

  DaoImplの

    private String dbq="C:/@/projEclipse/pub/templateGenLite/db/xlTestDb.xls";

    private String dbTableName = "table7"; //"[table7$]";

JNDI

  context.xml のJNDI DBのdbqを変更する

 

DI

  .properties

 

TODO gen6Factory

DataSourceもfactoryで面倒見ているが、これを削除する。

DataSourceを参照するDbAccessorJndiの中でDataSourceの参照を取り出す。

理由:Factoryが特定の例題に依存しているので、DbAccessorJndiも特定の例題に依存してしまう。DbAccessorJndiはDbAccessorと同じように特定の例題に依存しないようにするため(Jndiを使うかDiを使うかの手法に依存するのはやむをえない。名前もそれに依存する名前にしている)

 

 

 

#
gen type
gen1
gen2
gen3
1
Action1
genAction1
同左
gen3Action1
2
Action2
genAction2
同左
gen3Action2
3
ActionForm1
genActionForm1
同左
同左
4
ActionForm2
genActionForm2
同左
同左
5
Dto1
genDto1
同左
同左
6
Dto2
genDto2
同左
同左
7
Jsp1
genJsp1
同左
同左
8
Jsp2
genJsp2
同左
同左
9
Struts-conf FormBean
genStrutsConfFormBean
同左
同左
10
Struts-conf Action
genStrutsConfAction
同左
同左
11
IService
なし
同左
gen3IService
12
Service
なし
同左
gen3Service
13
IDao
なし
なし
なし
14
Dao
なし
なし
なし
15
BaseAction
なし
なし
なし
16
Factory
なし
なし
なし
17
Initialize
なし
なし
なし
18
.properties
なし
なし
なし

 

 

 

 

#
gen type
gen5
gen6
gen7
1
Action1
gen6Action1
同左
同左
2
Action2
gen6Action2
同左
同左
3
ActionForm1
gen6ActionForm
同左
同左
4
ActionForm2
gen6ActionForm
同左
同左
5
Dto1
gen6Dto
同左
同左
6
Dto2
gen6Dto
同左
同左
7
Jsp1
gen6Jsp1
同左
同左
8
Jsp2
gen6Jsp2
同左
同左
9
Struts-conf FormBean
genStrutsConfFormBean
同左
同左
10
Struts-conf Action
genStrutsConfAction
同左
同左
11
IService
gen6IService
同左
同左
12
Service
gen5Service
gen6Service
gen7Service
13
IDao
gen6IDao
同左
同左
14
Dao
gen5Dao
gen6Dao
gen7Dao
15
BaseAction
gen5BaseAction
gen6BaseAction
gen7BaseAction
16
Factory
なし
gen6Factory
なし
17
Initialize
なし
なし
gen7Initializer
18
.properties
なし
なし
gen7Props

gen1とgen2の違い

内容は同じ。gen1は生成したソースをファイルに出さない。gen2は生成したソースをファイルに出す。

gen2とgen3の違い

gen2はServiceはない。gen3はServiceがある。Serviceでは実質的な処理をしていないので、処理という観点では同じ。しかし、Serviceの参照の取り方を示す意味でgen2とgen3は異なる。

 

gen3とgen5の違い

gen3はServiceまでしかない。gen5はService以降Daoをつけて実際にDBアクセスする処理を含む。Daoはその機能を実現するため、DbAccessorクラスDataSourceクラスが後に続く

 

gen5とgen6、gen7の違い

インスタンスの参照の取り方が違う。

gen5はnewを使って直接具現化してインスタンスを取得する。gen6はFactoryを使う。FactoryからJNDIでインスタンスの参照を取得する。gen7はINitializerを使いDI方式で参照を設定する。Initializerのパラメータを供給する.propertiesファイルを作る。まとめると次の通り。

 

 

インスタンスの参照の取り方

#

gen5
gen6
gen7
1
Service
ServiceのstaticなメソッドgetInstance
Factoryのstaticなメソッド
servletContextにInitializerで設定し、使う側はそこから取り出す。

Dao
Serviceの中で具現化
Factoryのstaticなメソッド
DI

DbAccessor
Daoの中で具現化
Factoryのstaticなメソッド
DI

DataSource
DbAccessorの中で具現化
JNDIで取り出す。
DI