- 追加された行はこの色です。
- 削除された行はこの色です。
[[FrontPage]]
#contents
2008/01/24からのアクセス回数 &counter;
* Cart問題 [#c949db9e]
Agile Web Development with Railsの例題と同じ問題をSpringを使って実装を試みたときの
メモです。
もう一つの目的は、Spring-MVCプラグインがどの程度実際の問題解決に役立つかを検証することです。
** プロジェクトの作成 [#y8873d2a]
mavenを使ってプロジェクトを生成します。
- groupIdは、example.cart
- artifactIdは、cart
とします。ecliseでプロジェクトを管理できるようにeclipseプラグインも起動します。
#pre{{
mvn archetype:create \
-DgroupId=example.cart \
-DartifactId=cart \
-DarchetypeArtifactId=spring-mvc-archetype \
-DarchetypeGroupId=jp.co.pwv.spring-mvc-archetype \
-DarchetypeVersion=1.1.1
cd cart
mvn eclipse:eclipse -DdownloadSources=true
}}
データベースは、HsqlDBのサーバを使用するため、db.propertiesの内容を修正します。
#pre{{
db.url=jdbc:hsqldb:hsql://localhost
}}
最後にeclipseでcartプロジェクトをimportし、CVSに登録します。
* [[session scopeについて]] [#fddab324]
長くなったので別タイトルにしました。
* Product(製品の)の管理 [#r6b52203]
最初にProductを管理するページを作成します。
さしあたり、管理機能として
- 製品の一覧表示
- 製品の追加、編集、削除
ができるようにします。
** Productドメインモデルの作成 [#g8855bc8]
最初にProductのドメインモデルを作成します。
ドメインモデルは、example.cart.domainパッケージ内に定義します。
eclipseで以下のように入力した後、getter/setterを自動生成してください。
#pre{{
package example.cart.domain;
public class Product {
private Integer id;
private String title;
private String description;
private String image_url;
}
}}
** GenMVCプラグインの起動 [#x97dd396]
GenMVCプラグインのscaffoldゴールを指定して、ProductのDao、 Controller、 View、データベーステーブル
を自動生成します。
その前に、GenMVCプラグインは、Productのクラスファイルを見に行くので、mavenのpackageを実行します。
#pre{{
mvn package
mvn GenMVC:scaffold
}}
このコマンドで、
#pre{{
[INFO] ------------------------------------------------------------------------
[INFO] Building Unnamed - example.cart:cart:war:1.0-SNAPSHOT
[INFO] task-segment: [GenMVC:scaffold]
[INFO] ------------------------------------------------------------------------
[INFO] [GenMVC:scaffold]
[INFO] pkgName:example.cart
[INFO] runtime.classpath:/Users/take/Documents/workspace/cart/target/classes
[INFO] cls: example.cart.domain.Member
[INFO] template fullpath:velocity/IDao.vm
[INFO] template fullpath:velocity/Dao.vm
[INFO] template fullpath:velocity/edit_stub.vm
[INFO] template fullpath:velocity/list_stub.vm
[INFO] template fullpath:velocity/hbm.vm
[INFO] cls: example.cart.domain.Product
[INFO] template fullpath:velocity/IDao.vm
[INFO] template fullpath:velocity/Dao.vm
[INFO] template fullpath:velocity/Manager.vm
[INFO] template fullpath:velocity/EditController.vm
[INFO] template fullpath:velocity/OpsController.vm
[INFO] template fullpath:velocity/edit.vm
[INFO] template fullpath:velocity/edit_stub.vm
[INFO] template fullpath:velocity/list.vm
[INFO] template fullpath:velocity/list_stub.vm
[INFO] template fullpath:velocity/hbm.vm
[INFO] template fullpath:velocity/servlet-stub.vm
[INFO] template fullpath:velocity/sql.vm
[INFO] template fullpath:velocity/applicationContext.vm
[INFO] template fullpath:velocity/form-messages.vm
[INFO] template fullpath:velocity/validation.vm
}}
と出力され、必要なファイルがすべて生成されます。
再度、mvn packageを実行してtarget/cart.warをtomcatのwebappsにコピーします。
これだけで、Productのリスト表示、編集の画面が生成されます。
#ref(product_list.jpg);
** 属性の追加 [#b2ed20d1]
scaffoldの後にProductに属性を追加したくなることはよくあります。
Product の属性を変更したときの手順は以下の通りです。
- テーブルの削除
- velocity/*_stub.vmファイルのバックアップ
- validation.xmlファイルのバックアップ
通常は、これで十分ですが、以下のファイルを修正した場合にはバックアップを取ってください。
- webapp/WEB-INF/hbm-dir/Product.hbm.xml
- main/resources/form-messages.properties
今回は、自動生成されたファイルを全く変更していないので、テーブルの削除だけを行います。
*** テーブルの削除 [#x9330700]
開発の途中ではデータベースのテーブルを変更したり、値を参照します。このような用途に便利なのが
Ecl,ipseのプラグインDbEditです。
DbEditのインストール方法はhttp://www.pwv.co.jp/take_public_html/DevTool/DevTool_c9.html#doc1_589
を参照してください。
DbEditのTableタグを開くと以下のようにT_MEMBERとT_PRODUCTの2つのテーブルが作られています。
#ref(DbEdit.jpg);
GenMVCプラグインでは、クラス名の前にT_を付けたテーブルが作成されます。
T_PRODUCTを削除するには、 T_PRODUCTで右マウスクリックから削除を選択してください。
- すでに沢山のデータが入っていたらどうすれば良いのか?
この答えは、テストケースのデバッグで紹介する予定です。
*** Productクラスの変更 [#j28b6d69]
Productに価格(price)を追加します。
以下のように属性priceを追加し、getter/setterを自動生成するだけです。
#pre{{
private Double price;
}}
日本では価格に小数点はないのですが、ここでは例としてDouble型を使いました。
それでは、先ほどと同様にGenMVCプラグインを起動します。
#pre{{
mvn package
mvn GenMVC:scaffold
}}
** 画面(Velocityテンプレート)の変更 [#b82e17e4]
GenMVCプラグインが生成する画面は、属性の出力順がProductクラスの定義順に並んでいないので、実際には手で修正する必要があります。
ProductのVelocityテンプレートは、main/webapp/WEB-INF/velocity/productops/以下にあります。
- list_stub.vm
- list.vm
が一覧を表示するテンプレートです。
list.vmを見ると
#pre{{
parse ( "productops/list_stub.vm" )
}}
だけです。
これは、GenMVCプラグインがlist.vmを変更しないようするためにlist_stub.vmをインクルードする
2段階で処理しています。
従ってユーザvelocityテンプレートを変更する場合には、list_stub.vmをlist.vmにコピーして編集します。
以下に順序を入れ替えたlist.vmを示します。
#pre{{
<html>
<head>
<title>Product</title>
</head>
<body>
<h1>Listing product</h1>
<table>
<tr>
<td>id</td>
<td>title</td>
<td>description</td>
<td>image_url</td>
<td>price</td>
</tr>
#foreach (${product} in ${productList})
<tr>
<td>${product.id}</td>
<td>${product.title}</td>
<td>${product.description}</td>
<td>${product.image_url}</td>
<td>${product.price}</td>
#set( $editLink = "/editproduct.htm?id=${product.id}" )
<td><a href="#springUrl(${editLink})">[edit]</a></td>
#set( $deleteLink = "/productops/delete.htm?id=${product.id}" )
<td><a href="#springUrl(${deleteLink})">[delete]</a></td>
</tr>
#end
</table>
<a href='#springUrl("/editproduct.htm")'>add</a>
</body>
</html>
}}
同様に編集画面も順序を変え、Descriptionをtextareaに変えてます。
#pre{{
<html>
<head>
<title>Products</title>
</head>
<body>
Edit Product
<form method="post" action="#springUrl("/editproduct.htm")">
#springFormHiddenInput( "product.id" "" )
<table>
<tr>
<td>title:</td>
<td>#springFormInput( "product.title" "size='35'" )</td>
<td>
#springBind("product.title")
<font color="red">${status.errorMessage}</font>
</td>
</tr>
<tr>
<td>description:</td>
<td>#springFormTextarea( "product.description" "rows='4' cols='40'" )</td>
<td>
#springBind("product.description")
<font color="red">${status.errorMessage}</font>
</td>
</tr>
<tr>
<td>image_url:</td>
<td>#springFormInput( "product.image_url" "size='35'" )</td>
<td>
#springBind("product.image_url")
<font color="red">${status.errorMessage}</font>
</td>
</tr>
<tr>
<td>price:</td>
<td>#springFormInput( "product.price" "size='10'" )</td>
<td>
#springBind("product.price")
<font color="red">${status.errorMessage}</font>
</td>
</tr>
<tr>
<td colspan="3">
<input type="submit" value="Save Changes"/>
</td>
</tr>
</table>
</form>
</html>
</body>
}}
** Validationの変更 [#pda78f32]
現在の入力フォームは、各フィールドが必須だけのチェックしかしていません。
priceに文字を入力して、Save Chageボタンを押すと
#ref(validation_error1.jpg);
が出力されます。
これでは、エラーの原因が分かりづらいので、validation.xmlを修正してpriceをdoubleとしてふさわしい値になるようにしようと、
#pre{{
<field property="price" depends="required">
<arg0 key="product.price" />
</field>
}}
を
#pre{{
<field property="price" depends="required,double">
<arg0 key="product.price" />
</field>
}}
としたが、ダメでした。
原因は、Validationが行われる前に、Productの値がHTTPのパラメータからセットされるためです。
*** Validationエラーへの対応 [#l2ea6018]
ソースをトレースした結果、bindAndValidationを使用する場合、最初にHTTPリクエストから値をセットするcommandオブジェクトへのbind操作が先行します、ここでStringからDoubleへの変換に失敗するため、その後のValidationのエラーチェックでは値がセットされていないので、requiredのエラーが追加されますが、表示されません。
対応策としては、ProductionのPriceをDoubleからStringに変えるという方法しかありません。
これでは、GenMVCプラグインを起動すると間違ったCreate table文が生成されてしまいます。
*** commandクラスの追加 [#x8f77839]
そこで、domainクラスに対応するcommandクラスをGenMVCプラグインで生成し、その属性をすべてString型としました。
更に、EditProductControllerでcomanndオブジェクトとdomainオブジェクトの値を変換するメソッドcommandToDomain, domainToCommandを追加しました。
#pre{{
protected void bind(Object target, Object source) throws Exception {
CustomDataBinder binder = new CustomDataBinder(target, source);
this.prepareBinder(binder);
binder.bind();
}
protected Object commandToDomain(Object source) throws Exception {
Object object = new Product();
bind(object, source);
return (object);
}
protected Object domainToCommand(Object source) throws Exception {
Object object = new ProductCommand();
bind(object, source);
return (object);
}
}}
これでようやく、priceのValidationが正常に行えるようになりました。
#ref(validation_error2.jpg);