第Ⅰ編 例題
1.例題1
1.1 概要
例題1は単純なwebアプリです。
処理のシーケンスは図1.1-1の通りです。
図1.1-1 例題1のシーケンス図
(1) クライアントから要求1を送ります。
(2) その応答が戻ります。(応答1)
(3) 応答1の画面の入力項目を入力して要求2を送ります。
(4) 応答2が戻ります。
1.2 プログラムの構造
図1.2-1に処理の流れと関係するオブジェクトを示します。
図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 処理の流れと関係するオブジェクト
例題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クラス図
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 例題の入力画面
この部分は、指定したプロパティによって変わります。この例は、プロパティが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を使ったオブジェクトの定義
|
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 クラスの動的具現化
|
注 必要な例外の処理は略しています。
このインスタンスを具体的に使うには、これはクラスではキャストできませんから(*1)、interfaceを使います。
注 *1:具体的なクラス名が分かっていれば、newで具現化します。
5.3.1 プロパティの設定
これもソース上でクラス名が明示されていないときの問題です。該当するクラスにプロパティを設定する必要がある場合に生じることがあります。
該当のプロパティのsetterがinterfaceに含まれていれば、クラスのインスタンスをinterfaceでキャストするとsetterが使えます。
該当のプロパティのsetterがinterfaceに含まれていない場合はキャストする方法は使えません。この場合は、JavaのReflection機能を使います。
リスト5.3-2 プロパティの設定
|
注 必要な例外の処理は略しています。
JavaのReflection機能は最小限にすべきですが、これは許される場合です。
6.例題6
6.1 概要
例題6は例題5をベースにして、範囲を指定して検索する機能を付けます。これはtemplateGenLiteを使って、実際にwebアプリを作る簡単な例です。
これまでの例題1~例題5は第Ⅱ編 操作書に記述に従ってツールを使うと自動的に生成されます。自動生成されたものは実際のアプリにそのままでは使えません。しかし、自動生成されたものを母体にして開発すると、最初から作るより短時間で開発が出来ます。
この例題の目的は、これまでの例題を目的の仕様になるように変更する手順と、その手順が簡潔であることを示すことです。
例題6の仕様を説明します。
要求1に対して、図6.1-1に示す入力フォームが表示されます。テーブルの全データも表示します。これは参考のためのデータです。体裁についてはここでは考慮していません。
図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
|
6.3.2 ActionForm
表6.1-1のプロパティに対応するActionForm bean(抜粋)です。prefecturesは複数組のデータが入るのでStringの配列です。その他は文字型です。ActionForm2と入れ替わります。
リスト6.3-2 ActionForm2
|
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のコード
|
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のコード(抜粋)
|
6.3.5 Dao
DaoのfindPrefecturesByCondメソッドを変更します。
都道府県については複数の都道府県を指定できるようにします。その他の列については、値の範囲を指定できるようにします。ただし、簡略のため本来、数値として扱うべき列を文字型で扱っていますので、大小関係が数値の大小関係と一致しない恐れがあります。
例えば次のようなwhere句が作られます。
|
このためのコードをリスト6.3-5に示します。
リスト6.3-5 Daoのコード(変更部分)
|
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 ディレクトリ構造
|
注 *1:[]はJavaのパッケージに対応するディレクトリの構造を略記したものです。
*2:libは空です。例題を動かすには次節で説明するライブラリが必要です。
7.4 必要なライブラリ
ダウンロードしたファイル中のlibディレクトリは空です。例題を動かすには表7.4-1のライブラリが必要です。
表7.4-1 例題を動かすのに必要なライブラリ
# |
ファイル名 |
1 |
antlr-2.7.2.jar |
2 |
commons-beanutils-1.7.0.jar |
3 |
commons-chain-1.1.jar |
4 |
commons-digester-1.8.jar |
5 |
commons-logging-1.0.4.jar |
6 |
commons-validator-1.3.1.jar |
7 |
oro-2.0.8.jar |
8 |
struts-core-1.3.8.jar |
9 |
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 |
名前 |
1 |
Dao interface |
uJsName & "IDao" |
2 |
Dao |
uJsName & "Dao" |
3 |
Service interface |
uJsName & "IService" |
4 |
Service |
uJsName & "Service" |
5 |
BaseAction |
uJsName & "BaseAction" |
6 |
Initializer |
uJsName & "Initializer" |
7 |
.properties |
jsName |
8 |
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の定義
|
リスト7.6-2 struts-config.xml のactionの定義
|
(d)JNDIのキー
JNDIに登録するクラスのキーの一部に使います。表7.6-3に示します。
表7.6-3 JNDIキー
# |
一般クラス名 |
JNDIキー |
1 |
Service |
jsName & "/service" |
2 |
Dao |
jsName & "/dao" |
3 |
DbAccessor |
jsName "/dbAccessor" |
7.7 生成できるプログラム
生成できるプログラムのタイプは5種類です。これは例題1~例題5に対応しています。一つの例題が1シートに対応しています。
例題とシート名の対応を表7.7-1に示します。
表7.7-1 例題とシート名の対応
# |
マクロ名 |
例題名 |
シート名 |
1 |
gen1 |
例題1 |
jobstep(1) |
2 |
gen2 |
例題1 |
jobstep(1) |
3 |
gen3 |
例題2 |
jobstep(3) |
4 |
gen5 |
例題3 |
jobstep(5) |
5 |
gen6 |
例題4 |
jobstep(6) |
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 テーブル
|
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 生成物とその出力先
# |
生成物 |
出力先 |
1 |
クラス |
ソースディレクトリの下の、パッケージに応じたディレクトリ |
2 |
Jsp |
WEB-INF/pagesの中 |
3 |
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の定義
|
説明
①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); dto1にform1のプロパティから移される。
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 formはDBの列にのみ依存する。これは実際の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を具現化する。
gen1TemplateLite、gen2TemplateLite、gen3TemplateLiteはBaseActionがない。
gen1TemplateLiteはServiceクラスがない。
gen2TemplateLiteとgen3TemplateLiteとgen5TemplateLiteはstaticなメソッドgetInstanceでServiceクラスのインスタンスを取得する。gen5TemplateLiteはBaseActionがあるが実質何もしていない。
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 |