準備とDAO

しばらくはSeasar2の勉強をしよう。一ヶ月間はがんばる。


雑誌とかみてるとS2Daoはよさそう。S2JSFってのもあるらしい。
でも新しいことたくさんしすぎるとすぐにあきらめそう。
S2Strutsってのもあるのかー
Strutsなら利用したことあるからこっちを採用しよう。とっつきやすそうだし。


よし決めた。
Seasar2S2DaoS2Strutsでいこう。あと別物だけどSiteMeshも利用しよう。便利なので


勉強するものが決まったから、必要なjarの調査とディレクトリ構成をきめないと・・・
こういうことは自分で考えるよりサンプルを利用して手っ取りばやくしてしまおう。
ディレクトリ構成は、s2jsf-exampleをベースでいくことで決定。htmlファイル(JSPファイル)の格納先はWEB-INF\pagesに変えよう。


必要なjarは、ゆっくり調べるかな〜。利用していないのを置くのってやだし。


結構時間かかった。
調べた結果以下のjarを利用することに決定。

[[Seasar2]]
S2.2.9.zip
	S2.2.9\seasar2\lib\
		aopalliance.jar
		commons-logging.jar
		hsqldb.jar
		javassist.jar
		jta.jar
		junit-3.8.1.jar
		log4j-1.2.8.jar
		ognl-2.6.5.jar
		poi-2.5.1-final-20040804.jar
		s2-extension-2.2.9.jar
		s2-framework-2.2.9.jar

[[S2Dao]]
S2DaoV1.0.26.zip
	s2dao\lib\
		s2-dao-1.0.26.jar

[[S2Struts]]
S2Struts-V1.1.5.zip
	s2struts\lib\
		s2-struts-1.1.5.jar

[[sitemesh]]
sitemesh-blank.war
	WEB-INF\lib\
		sitemesh-2.2.1.jar

[[jakarta-struts]]
jakarta-struts-1.2.4.zip
	jakarta-struts-1.2.4\contrib\struts-el\lib\
		jstl.jar
		standard.jar
		struts.jar
		struts-el.jar

[[jakarta-oro]]
jakarta-oro-2.0.8.zip
	jakarta-oro-2.0.8\
		jakarta-oro-2.0.8.jar

[[jakarta-commons]]
commons-beanutils-1.7.0.zip
	commons-beanutils-1.7.0\
		commons-beanutils.jar
		(commons-beanutils-1.7.0.jar にリネームして利用)
		
commons-collections-3.1.zip
	commons-collections-3.1\
		commons-collections-3.1.jar

commons-digester-1.6.zip
	commons-digester-1.6\
		commons-digester.jar
		(commons-digester-1.6.jar にリネームして利用)
		
commons-fileupload-1.0.zip
	commons-fileupload-1.0\
		commons-fileupload-1.0.jar

commons-validator-1.1.4.zip
	commons-validator-1.1.4\
		commons-validator-1.1.4.jar

commons-lang.jarって必要なのかな。わからん。
動かしてみて動かなかったら、そのときに追加しよう。
あと、勝手に最新jarばかりに変えたけど問題ないのか?
まぁこれも動かしたときにわかるから、そのときに悩もう。


Eclipseは、kijimunaをPluginするだけでOKと。
TomcatPlubinとかHTMLEditorとかは前から入れてたし。


やっと開発(勉強)開始。

プロジェクト名決めてなかった・・・
s2xxx-studyでいこう。勉強だから
あとテストは、s2xxx-studyTestとして別プロジェクトにしよう。
パッケージは、study.xxx.*でいく。s2jsf-exampleがexample.jsf.*だったから。

まだ、たくさん準備作業があった。
まずDBは、s2jsf-exampleのWEB-INF\binとWEB-INF\dataをコピーしてもってこよう。
う〜〜〜ん
ちょっと構成を変えて、DB関連をWEB-INF\db配下にまとめてしまおう。

	WEB-INF\db\bin
	WEB-INF\db\data
	WEB-INF\db\ddl
	WEB-INF\db\lib

みたいな感じで。

Strutsを使うからWEB-INF\tldもつくらないと。下の7つあれば十分かな

	c.tld
	fmt.tld
	sitemesh-decorator.tld
	sitemesh-page.tld
	struts-bean-el.tld
	struts-html-el.tld
	struts-logic-el.tld

struts-config.xmls2strutsのドキュメントどおりにcontrollerとplug-inの記述を追加して、tilesは利用しないからtiles関連の記述を削除。
MessageResources.propertiesをsrc配下にコピーして内容を日本語にと。


SiteMeshは、

	decorators.xml

をコピーするだけ、JSPファイルはまたSiteMeshを利用するときにコピーしよう。

あとは、それぞれのドキュメントにしたがってweb.xmlの編集。
s2strutsを利用するためにfilterとservletの設定を記述。
SiteMeshのfilterも追加して完了。
動作確認はもうちょっと先になるだろうなー。確認することがたくさんあるし。

Seasar2を利用するためのDICONファイルたちも必要なのかー。
探してコピー

	aop.dicon
	app.dicon
	dao.dicon
	j2ee.dicon
	s2struts.dicon
	log4j.properties


開発(勉強)するまでに手順が多いな。でも、こんなもんか


やっと開発(勉強)開始。


まずは、S2Daoから
テーブルは、j2jsf-exampleのEmployeeを真似してPartyを作ろう。
アナリシスパターンの目次だけを見て決めたテーブル名。
最初は単純に
DDLの内容は

TABLE_PARTY.sql
	DROP   TABLE PARTY;
	CREATE TABLE PARTY (
	    ID           NUMERIC(8) NOT NULL,
	    PARENT_ID    NUMERIC(8) NOT NULL,
	    ACCOUNT      VARCHAR(10) NOT NULL,
	    PASSWORD     VARCHAR(10) NOT NULL,
	    NAME         VARCHAR(30),
	    CONSTRAINT PK_PARTY PRIMARY KEY (ID)
	);
	--
	DROP   SEQUENCE SEQ_PARTY;
	CREATE SEQUENCE SEQ_PARTY START WITH 1000;
	--
	INSERT INTO PARTY VALUES (101, 0, 'user1', 'user1', 'ユーザー1');
	INSERT INTO PARTY VALUES (102, 0, 'user2', 'user2', 'ユーザー2');
	INSERT INTO PARTY VALUES (103, 0, 'user3', 'user3', 'ユーザー3');

当面はPARENT_IDは無視で'0'固定でいく。外部キーは・・・


PARTYテーブルを操作するクラスはPartyDaoとして、そのテストクラスPartyDaoTestから作成。

S2DaoTestCaseから派生すればいいのか。でもS2TestCaseからでも十分な気もするな。
検索結果をEXCELに記述して比較するのはとってもいいことだと思うけど、途中からメンドくさくなって雑になりそうだなー。
DAOは軽めのテストにしとこっと。バグがあったらそのときに必要なテストを追加しよう

DAOのインタフェースは、データを取得するfindXxx関連とinsert、update、deleteで十分。

まずは、単純にtestFindAllTx()から作成しよう。テスト方法についてのドキュメントが充実してるから助かるなー
テストデータは、PartyTestData.xlsに記述しよう。

テストクラスこんな感じかな

PartyDaoTest.java

public class PartyDaoTest extends S2DaoTestCase {

    private PartyDao dao;

    public PartyDaoTest(String arg0) {
        super(arg0);
    }

    public void setUp() {
        include("study/xxx/dicon/alldao.dicon");
    }

    public void testFindAllTx() throws Exception {
        readXlsWriteDb("PartyTestData.xls");

        List found = dao.findAll();
        assertNotNull(found);
        assertEquals(3, found.size());
    }

}

エラーでまくりだな。とりあえずEclipseの力でコンパイルを通そう。
そして実行。即エラー
alldao.diconファイルが見つからないんだって。s2jsf-exampleから

	allaop.dicon
	alldao.dicon

をコピーしてきてちょっと修正し再度実行。
こんどは、PartyTestData.xlsが見つからないんだって。そういえば作ってなかった。
作って再度実行。

おぉ、今度もフィールドBEANが見つかりませんって注意されたけど、コンソールにINSERT文が出力されてた!!
やっと、プログラムしてるって気がしてきた。
PartyDtoクラスをGetter/Setter付きで作ってそれを指定しよう。
もうエラーは十分みたし。これで実行。


java.sql.SQLException: Statement does not generate a result set
だって。
どういうこと?ドキュメントを見よう。
PartyDtoクラスにTABLEプロパティがないからかなー。
追加して実行。


エラーから失敗に成長した。やっぱりPartyDtoクラスにTABLEプロパティがなかったのがエラーの原因だったんだ。

で、3のはずが6になってるから失敗だって。

あー、テーブル作ったときに追加した3件のデータが邪魔してるんだー。
最初にテーブルをTruncateするみたいな作業を追加するか。
簡単にできればいいな。

簡単にできた。

	        deleteTable(PartyDto.TABLE);

でOKみたい。ラクだ。

はい、実行。
グリーンバー。
findAll()メソッド完成。そしてS2Daoの設定も問題なさそう。2つ作業が完了。

次は、PartyDtoクラスのtoString、equals、hashCodeをオーバーライドしよう。
必要になったときにのみ作りなさいっていうけど、この3つくらいはDTO作ったら、オーバーライドしましょうって感じで問題ないんじゃないかなー
こういう気持ちで作ってるのが一番ダメかも・・・
でも作る。なんとなく。


equalsはオーバーライドしたけどhashCodeをオーバーライドしていないってバグがあって調査が大変だった記憶があるから、この2つメソッドのためにPartyDtoEqualsTestクラスは作ってやろう。

PartyDtoEqualsTest.java

public class PartyDtoEqualsTest extends TestCase {

    private PartyDto self = new PartyDto(new Integer(1));

    private PartyDto other = new PartyDto(new Integer(1));
    
    public void testEquals() {
        assertFalse(self == other);
        assertTrue(self.equals(other));
        assertTrue(self.hashCode() == other.hashCode());
    }

}

PartyDtoクラスに新しいコンストラクタと空のコンストラクタを追加して実行。
失敗。assertTrueで失敗してるから障害トレースを見ただけでは意味不明。
equalsとhashCodeをオーバーライドして実行。グリーンバー。

idが違った場合にfalseになることを確認するテストを追加。

    public void testIdNotEquals() {
        other.setId(new Integer(2));
        assertFalse(self.equals(other));
    }

グリーンバー。
idはいっしょだけどaccountが違う場合はtrueとかequalsに対するテストはもっとあると思うけど、これ以上テストを記述するのは疲れる。
なのでPartyDtoEqualsTestクラス完成(未完成?)。


toStringに対するテストは、もちろん作らない。疲れるから。PartyDaoTestを実行し、コンソールでの目視による確認。


今回の開発(勉強)のJUnitによるテストの方針は、最低限のテストとバグがあったらそのバグに対応するテストを記述すること。としよう。
甘すぎるかも。でも、痛い目にあうのも勉強ってことで。


PartyDaoクラスのinsert、update、deleteを作っていこう。1つずつ。
ま、コツがわかったから簡単に作れるかなーと思って、testInsertTx()を作っていたら、PrimaryKeyによるfindメソッドが必要なことがわかった。
メソッド名をfindById(Integer id)にしようと思ったけど、複合PrimaryKeyのときどうしようとちょっと考えた結果、findBean(XxxDto dto)にすることにした。
この違和感は慣れればなくなるはずだ。たぶん

って訳でtestFindBean()は、下のような感じで実行。

    private PartyDto dto = new PartyDto(new Integer(2));
    public void testFindBeanTx() {
        deleteTable(PartyDto.TABLE);
        readXlsWriteDb("PartyTestData.xls");

        PartyDto found = dao.findBean(dto);
        assertNotNull(found);
        assertEquals(dto, found);
    }

assertNotNullのところで失敗。
コンソールをみると

SELECT PARTY.name, PARTY.id, PARTY.account, PARTY.password
 FROM PARTY
 WHERE  PARTY.name = '' AND PARTY.id = 2
 AND PARTY.account = '' AND PARTY.password = ''

っていうSQLが発行されている。
やっと、sqlファイルを記述するときがきた。
これができるところを雑誌で見たから、S2Daoを勉強しようと思ったので、やっとメイン部分に少し入ったことになる。

PartyDao_findBean.sql

select *
  from PARTY
 where id = /*dto.id*/101

たったこんだけかー。まだまだ利用したことにならないなー。
しかも「select *」ってやったらみんなに嫌われてしまう。
でもグリーンバーになった。

やっと、testInsertTx()に入れる。

    public void testInsertTx() {
        deleteTable(PartyDto.TABLE);
        PartyDto ins = new PartyDto();
        ins.setAccount("INSTEST");
        ins.setPassword("INSPWD");
        ins.setName("INSテスト");

        dao.insert(ins);

        PartyDto found = dao.findBean(ins);
        assertNotNull(found);
    }

作って実行。エラー。IDにNULLは設定できません。だって。
Seaquenceはどうやれば取得できるかドキュメントを参照。「IDの自動生成」でそのままが記述されていることを発見。
PartyDtoクラスに

public static final String id_ID = "sequence, sequenceName=SEQ_PARTY";

を追加して実行。グリーンバー。

testUpdateTx()もtestDeleteTx()も作ったけど問題なし。
PartyDaoクラス、PartyDaoTestクラス完成。

次からは、DAO作成にどのくらい時間がかかるかはかってみよう。

そういえば、s2jsf-exampleでは、Entityクラスを作ってDTOはEntityから派生するって形になっていたけど、Entityはなくてもいいかもって思ったから削った。わける必要性を思いついたときにわけよう。後回し。



次はLogicクラスの作成。
テストクラスを作るべきかどうか。
普通はつくるんだと思うけどDAOのテストといっしょになりそうだから作らないことに決定。
Logicクラスでバグが発生した場合は、そのとき対応ってことで。
でもテストってロジックが正しいかを判断するためのものなのにLogicクラスのテストはしないだなんて・・・


今日は張り切って書きすぎた。
飽きたら、記述内容も減るだろうね。