機能リファレンス
SAStrutsで使われている機能の説明をします。
プロジェクト構成
SAStrutsでは、ルートパッケージの配下にactionなどのパッケージを作って、 そこに必要なファイルを格納します。 ルートパッケージ名は、任意の名前を指定することができます。 例えば、sa-struts-tutorialプロジェクトでは、ルートパッケージ名は、tutorialになっています。
ルートパッケージ名は、convention.diconで指定します。 sa-struts-tutorialプロジェクトでは、src/main/resourcesで次のように指定されています。
convention.dicon
<components> <component class="org.seasar.framework.convention.impl.NamingConventionImpl"> <initMethod name="addRootPackageName"> <arg>"tutorial"</arg> </initMethod> </component> <component class="org.seasar.framework.convention.impl.PersistenceConventionImpl"/> </components>
アクションは、ルートパッケージ.actionに格納します。 例えば、http://ホスト名/プロジェクト名/xxx/のURLに対応するアクションクラスは、 XxxActionという名前にします。
アクションフォームは、ルートパッケージ.formに格納します。 例えば、XxxActionで利用するアクションフォームは、XxxFormという名前にします。 アクションフォームの役割は、リクエストのパラメータを管理することです。
エンティティは、 ルートパッケージ.entityに格納します。 エンティティとは、データベースに永続化されるデータです。 エンティティの名前は任意の名前にすることができますが、 通常は、テーブルの名前にあわせます。
エンティティに対する操作を格納するクラスをサービスといいます。 サービスは、ルートパッケージ.serviceに格納します。 名前は、XxxServiceのようにServiceで終わるようにします。 Xxxは、任意の名前を指定することができますが、通常は、エンティティ名にします。
ユーティリティは、ルートパッケージ.utilに格納します。 クラス名は自由につけてかまいません。 ユーティリティクラスは、通常staticメソッドで構成されています。
JSPは、アクションに対応するディレクトリに格納します。 例えば、XxxActionで使うJSPは、/xxx/に格納すると良いでしょう。
アプリケーションアーキテクチャ
SAStrutsは、MVC(Model View Controller)のアーキテクチャに基づいていて、 Modelはエンティティ、 ViewはJSP、Controllerはアクションになります。
アクションは、複数の実行メソッド を持つことができ、通常は、1ユースケースを1アクションにマッピングします。 複数の画面で構成される意味のある単位をユースケースだと、とらえればよいでしょう。 プレゼンテーション(表示)に関するロジックは、アクションに定義します。
ビジネスロジックは、エンティティかサービスに定義します。 データアクセスのロジックもビジネスロジックの中に含めて良いでしょう。 ビジネスロジックがデータアクセスフレームワークに依存しないように、 データアクセスのロジックをDao(Data Access Object)に切り出す方法もありますが、 開発の途中でデータアクセスフレームワークを変えることは、現実的にはほとんどないので、 データアクセスのロジックもビジネスロジックの中に含めて良いと思っています。
ビジネスロジックをエンティティとサービスのどちらに定義するのかは、とても重要な問題です。 データアクセスロジックは、サービスに定義したほうが良いでしょう。 なぜかというと、検索系のメソッドは、エンティティを取得するためのメソッドなので、エンティティ自身には定義できません。 エンティティのstaticメソッドとして定義すれば、できないこともありませんが、 staticメソッドだと、テスト用にオーバーライドすることができないので、やめておいたほうが良いでしょう。
また、更新系のメソッドは、エンティティに持たせることもできますが、 そのためには、エンティティが更新系のメソッドが定義されている特定のクラスを継承する必要があり、 最近のPOJO指向とかみ合いません。データアクセスロジックは、エンティティではなく、サービスに定義するほうが良いのです。
エンティティの導出プロパティは、エンティティに定義したほうが良いでしょう。 導出プロパティとは、計算によって求められるようなプロパティで、getterメソッドとして実装します。 導出プロパティは、エンティティのプロパティの1つなので、エンティティに定義するのが自然です。
データアクセスロジックと導出プロパティ以外は、ケースバイケースで、 エンティティとサービスのどちらに定義するのか判断する必要があります。 場合によっては、エンティティとサービスで処理を分担する必要もあるでしょう。
ケースバイケースっていわれたら、どうしたら良いか困りますよね。 私がいつも使ってい方法は、最も短いソースコードですむ方法を選ぶということです。 短いソースコードは、バグが入り込む余地も少なくなるからです。 この方法だけで、すべての問題が解決するわけではありませんが、1つの判断基準にはなるでしょう。
最後に、特定のエンティティに関連する定数(区分値・属性値)は、それぞれに対応するエンティティに定義しておきます。
アクション
リクエストに応じて起動されるクラスをアクションといいます。 Strutsでは、URLとアクションの関係をstruts-config.xmlに記述しますが、 SAStrutsでは、次のルールに従って自動的に決まるので、 設定ファイルを書く必要がありません。
http://localhost:8080/sa-struts-tutorial/login/にアクセスしたとします。 sa-struts-tutorialはWebアプリケーション名です。 SAStrutsは、次のような手順でURLをActionクラスに変換します。
- Webアプリケーション名の後ろのパス(/login/)の最後のスラッシュをActionに変換(/loginAction)します。 スラッシュがない場合は後ろにActionを付け加えます。
- 最後のスラッシュの直後を大文字(/LoginAction)にします。
- スラッシュをドットに変換(.LoginAction)します。
- ルートパッケージ名.actionを先頭につけます(ルートパッケージ名.action.LoginAction)。 ルートパッケージ名の詳細は、こちらを参照してください。
- 最終的に、/login/に対応するアクションクラスはtutorial.action.LoginActionになります。
アクション用のパスがない場合、ルートパッケージ.action.IndexActionがあれば、 そのアクションが呼び出されます。 例えば、http://localhost:8080/sa-struts-tutorial/にアクセスすると、 tutorial.action.IndexActionが呼び出されます。
IndexActionを呼び出したいときは、Webのルートにindex.jspをおかないようにしてください。 index.jspの方が優先されて呼び出されるためです。
大規模なアプリケーションでは、パスを分割することもできます。 例えば、/aaa/bbb/のパスに対応するアクションクラスは、 ルートパッケージ.action.aaa.BbbActionになります。
アクションはPOJO(普通のJavaのクラス)にします。 StrutsのようにActionを継承する必要はありません。
スコープはリクエストです。Strutsのようにシングルトンじゃないのは、 アクションフォームなどのリクエストやセッションで管理されているオブジェクトを プロパティとして受け取るためです。 リクエストで管理されているので、Strutsのようにスレッドセーフかどうか気にする必要はありません。
アクションフォームを利用するには、次のようにフィールドを定義し、 @ActionFormと@Resourceをつけます。 @Resourceは、フィールドを設定するようにSeasar2に指示するためのアノテーションです。 フィールド名は、クラス名の先頭を小文字にしたものにします。
@ActionForm @Resource protected AddForm addForm;
JSPに出力するだけの値は、アクションにプロパティを定義し、 そのプロパティ経由で、JSPに出力します。 リクエストのパラメータは、アクションフォームで受け取るようにしてください。
ログインしたユーザに関する情報など、 アクションフォーム以外をセッションで管理する場合、 ルートパッケージ.dtoにXxxDto作成し、@Componentでセッションで管理されるように定義します。
Seasar2は、Dtoのフィールドがpublicで宣言されている場合、 自動的にプロパティとみなすことができます。
@Component(instance = InstanceType.SESSION) public class UserDto implements Serializable { private static final long serialVersionUID = 1L; public String userName; ... }
作成したUserDtoを利用するには、次のようにフィールドを定義し、 @Resourceをつけます。 (以下のコードは、スコープがpublicでないためにJSPから参照できないので注意して下さい。) フィールド名は、クラス名の先頭を小文字にしたものにします。
@Resource protected UserDto userDto;
Dtoをセッションに保存している場合に、DtoのHOT deployが効かない場合があります。 Tomcatを使っているなら、context.xmlを リンクの指示に従って修正してください。
ビジネスロジックをサービスに格納する場合、ルートパッケージ.serviceにXxxServiceを作成します。
public class XxxService { ... }
作成したXxxServiceを利用するには、次のようにフィールドを定義し、 @Resourceをつけます。フィールド名は、クラス名の先頭を小文字にしたものにします。
@Resource protected XxxService xxxService;
HttpServletRequestやHttpServletResponseなどのServlet API関連のオブジェクトは、 次のようにフィールドを定義し、@Resourceをつけます。 フィールド名は、下記のとおりにしてください。
public class MyAction { @Resource protected HttpServletRequest request; @Resource protected HttpServletResponse response; @Resource protected HttpSession session; @Resource protected ServletContext application; ... }
リクエストに対する処理は、 実行メソッドに記述します。
実行メソッド
リクエストを処理するのがアクションの実行メソッドです。 実行メソッドは、名前は任意、 @Executeがついていて、 戻り値はString、引数は無しにする必要があります。
@Execute public String xxx() { ... return ...; }
実行メソッドの戻り値は、遷移先のパスです。 パスが/ではじまっていない場合、アクションのパスからみた相対パスとみなされます。
例えば、/add/のアクションで戻り値をindex.jspとした場合、 /add/index.jspとみなされます。 さらにweb.xml でVIEW_PREFIXが指定されていると、プレフィックスとしてパスに追加されます。 sa-struts-tutorialプロジェクトでは、VIEW_PREFIXが/WEB-INF/viewと指定されているので /WEB-INF/view/add/index.jspに遷移することになります。
パスが/ではじまっている場合、Webアプリケーションのルートからの相対パスと みなされます。例えば、戻り値を/select/とした場合、 http://localhost:8080/sa-struts-tutorial/select/に遷移します。
デフォルトではフォワードで遷移します。 リダイレクトで遷移したい場合は、 パスの最後にredirect=trueを追加します。 =の前後に余分な空白は含めないでください。 redirect=trueの部分は、実際に実行されるときには、消去されます。
... return "xxx.jsp?redirect=true";
... return "xxx.jsp?key=value&redirect=true";
別のサイトに遷移したい場合は、パスをhttpやhttpsではじめ、 リダイレクトするように指定します。
... return "https://ホスト名/アプリケーション名/パス/?redirect=true";
ファイルのダウンロードのように既にレスポンスに出力済みの場合は、 遷移先(戻り値)はnullにしてください。
1つのアクションに複数の実行メソッドを定義することができます。 どの実行メソッドが選択されるのかは、URLで指定するか、 リクエストのパラメータのキーにメソッド名が含まれている(値が1文字以上あること)か、 リクエストのSAStruts.methodパラメータにメソッド名が指定されているかどうかで決まります。
次の例では、AddAction#index()が呼び出されます。
http://localhost:8080/sa-struts-tutorial/add/index
メソッドが定義されていない場合、index()が呼び出されるので、 上記の呼び出しは、下記の呼び出しと一緒です。
http://localhost:8080/sa-struts-tutorial/add/
@ExecuteでURLのパターン(urlPattern)を指定することで、 URLがパターンに一致したときにそのメソッドが呼び出されるように指定できます。 また、URLの一部をパラメータの値として受け取ることができます。
内部的な仕組みとしては、RoutingFilterでURLがパターンに一致するか調べます。 一致した場合は、SAStruts.methodをキーにしてそのメソッド名をパラメータに追加します。 パターンの{パラメータ名}の部分は、 パラメータ名がキーで実際のURLで一致した部分を値としてパラメータに追加します。 パラメータをクエリストリングとして組み立てた後に、 Strutsにフォワードします。
例えば、EmployeeAction#edit()が次のように定義されているとします。@Execute(urlPattern = "edit/{id}") public String edit() { ... }
/employee/list.jspに次のようなアンカータグが定義されているとします。
<a href="edit/1">編集画面へ</a>
アンカータグをクリックするとRoutingFilterがURL(edit/1)がパターン(edit/{id})に一致していることを検知し、 次のようなURLを組み立ててStrutsにフォワードします。
/employee.do?id=1&SAStruts.method=edit
SAStrutsは、idパラメータの値をアクションのプロパティに設定し、 SAStruts.methodパラメータで指定されているeditメソッドを呼び出します。
フォームをサブミットする場合は、ボタン系タグのname属性に実行メソッド名を指定します。 次の例では、confirmメソッドを呼び出します。
<input type="submit" name="confirm" value="確認"/>
実行メソッドを呼び出す前に、アノテーションを使った検証を行なうには、 @Executeのvalidator要素をtrueにします。デフォルトはtrueです。
validator=trueの場合は、検証結果がNGのときの遷移先のパスをinput要素で指定します。 パスの書き方は、実行メソッドの戻り値の書き方に加えて、 urlPattern要素のようにパラメータをパス({パラメータ名})で指定することもできます。 アノテーションを使った検証の詳細は、こちらを参照してください。
@Execute(validator = true, input = "edit.jsp")
実行メソッドを呼び出す前に、独自の検証を行なうには、 @Executeのvalidate要素でアクションフォームの検証メソッド名を指定します。
validate要素を指定した場合は、検証結果がNGのときの遷移先のパスをinput要素で指定します。 validator要素とvalidate要素を両方指定した場合には、 アノテーションを使った検証が先に実行され、 その後にvalidateで指定した検証メソッドが実行されます。 独自に定義する検証メソッドの詳細は、こちらを参照してください。
@Execute(validate = "validate", input = "login.jsp")
roles要素を指定して、メソッドを呼び出すのに必要なロールを設定できます。 複数のロールを設定するには、カンマ(,)で区切ります。 必要なロールがない場合、org.seasar.struts.exception.NoRoleRuntimeExceptionがスローされます。 NoRoleRuntimeExceptionがスローされた場合に、適切なページ(/error/norole.jsp)に遷移して、 適切なメッセージ(errors.norole)を表示するには次のように設定します。
application_jp.properties
errors.norole=適切なロールがありません。
struts-config.xml
<global-exceptions> <exception path="/error/norole.jsp" key="errors.norole" type="org.seasar.struts.exception.NoRoleRuntimeException"/> </global-exceptions>
/error/norole.jsp
<html> <head> <title>Role error</title> </head> <body> <html:errors property="errors.norole"/> </body> </html>
アクションフォーム
アクションフォームとは、リクエストのパラメータを管理するクラスです。 SAStrutsでは、アクションフォームもPOJOで記述することができます。
リクエストのパラメータと同じ名前のプロパティをアクションフォームに定義します。 入力値を受け取るためのプロパティは、検証エラーになっても値を格納できるように 型をStringあるいはbooleanで定義してください。 Seasar2は、アクションフォームのフィールドがpublicで宣言されている場合、 自動的にプロパティとみなすことができます。
アクションフォームを使用する場合は、アクションのプロパティでリクエストのパラメータを 受け取ることができなくなるので注意して下さい。
アクションフォームクラスは、ルートパッケージ.formにおき、クラス名の最後は、Formで終わるようにします。 (バージョン 1.0.3-rc1 よりアクションフォームは ~Dtoに代わって ~Formが推奨になりました。)
SAStrutsのアクションフォームは、デフォルトだとリクエストスコープで管理されますが、 次のようにアノテーションを指定することで、 セッションスコープで管理することもできます。 セッションスコープで管理するコンポーネントは、 java.io.Serializableをimplementsする必要があります。
@Component(instance = InstanceType.SESSION) public class XxxForm implements Serializable { private static final long serialVersionUID = 1L; ... }
アクションフォームをセッションに保存している場合、 @ExecuteのremoveActionForm要素をtrueに設定しておくと、 実行メソッドの正常終了時にセッションからアクションフォームが削除されます。 デフォルトはfalseで、削除しません。
アクションフォームをセッションに保存する設定のときに、アクションフォームのHOT deployが効かない場合があります。 Tomcatを使っているなら、context.xmlを リンクの指示に従って修正してください。
アノテーションを使った検証は次のように、 フィールドに対して指定します。 アノテーションを使った検証の詳細は、こちら を参照してください。
@Required public String arg1;
独自の検証は、次のように、検証メソッドでおこないます。 検証メソッドの詳細は、こちら を参照してください。
public ActionMessages validate() { ActionMessages errors = new ActionMessages(); ... return errors; }
チェックボックスなどの初期化は、次のように、リセットメソッドでおこないます。 リセットメソッドの詳細は、こちらを参照してください。
public void reset() { checked = false; }
バリデータ
アノテーションを使った検証を行なうには、 アクションフォームのフィールドに検証用のアノテーションを指定します。 検証用アノテーションの詳細は、こちらを参照してください。
@Required public String userName;
検証結果がNGの場合に出力されるメッセージは、メッセージリソースに 記述します。検証用アノテーションとメッセージのキーは次のようになっています。
アノテーション | メッセージのキー |
---|---|
Required | errors.required |
Validwhen | 開発者が指定 |
Minlength | errors.minlength |
Maxlength | errors.maxlength |
Minbytelength | errors.minbytelength |
Maxbytelength | errors.maxbytelength |
Mask | errors.invalid |
IntRange | errors.range |
LongRange | errors.range |
FloatRange | errors.range |
DoubleRange | errors.range |
ByteType | errors.byte |
ShortType | errors.short |
IntegerType | errors.integer |
LongType | errors.long |
FloatType | errors.float |
DoubleType | errors.double |
DateType | errors.date |
CreditCardType | errors.creditcard |
EmailType | errors.email |
UrlType | errors.url |
メッセージをカスタマイズしたい場合は、キーに対応する値を書き換えてください。
特定のプロパティのみ、メッセージをカスタマイズしたい場合は、 メッセージリソースに、 メッセージを追加し、検証用のアノテーションのmsg要素で指定します。
errors.required2={0}は必須だぜ。
@Required(msg = @Msg(key = "errors.required2"))
メッセージには、{位置}で引数を渡すことができます。 位置は0からはじまります。 検証用のアノテーションで、引数を指定するには、arg0, arg1, ..., args要素で指定します。
@Required(arg0 = @Arg(key = "ほげ", resource = false))
@Validwhen(test = "((validwhen1Text == null) or (*this* != null))", msg = @Msg(key = "errors.required.other"), args = @Arg(key = "validwhen1Text", resource = false, position = 1))
最初の引数は、プロパティ名が自動的に設定されます。
プロパティの表示をカスタマイズしたい場合は、
メッセージリソースに
labels.プロパティ名=...のエントリを追加してください。
labels.userName=ユーザ名
target要素を指定することで、特定のメソッドの場合だけ、検証を行なうようにすることもできます。 複数のメソッドを指定する場合は、カンマで区切ります。 target要素が指定されていない場合、@Executeでvalidator=trueのすべての実行メソッドが対象になります。 次の例では、secondプロパティは、goThirdメソッドが呼び出されるときだけ、 入力必須になります。
@Required(target = "goThird") public String second;
JSPでエラーメッセージを出力するには、次のようにhtml:errorsタグを使います。
<%@taglib prefix="html" uri="http://struts.apache.org/tags-html"%> ... <html:errors />
独自の検証用アノテーションを追加したい場合は、 org.seasar.struts.validator.S2FieldChecksを参考にして、 検証ロジックを実装し、そのメソッドをvalidator-rules.xmlで指定します。
そして、validator-rules.xmlで指定したvalidatorの名前を Validatorアノテーションで 指定してください。
JavaScriptを使ったクライアントサイドの検証は、 このチュートリアルを参照してください。
検証メソッド
検証用アノテーションではできないような個別の検証は、検証メソッドを使います。 検証メソッドは、アクションフォーム、アクションのどちらにも記述できますが、 検証対象のデータを持っているアクションフォームに記述したほうがいいでしょう。
検証メソッドは、名前は任意、引数は無し、戻り値はActionMessagesにする必要があります。 戻り値のActionMessagesが空でない場合、検証結果はNGだとみなされます。
public ActionMessages validate() { ActionMessages errors = new ActionMessages(); ... return errors; }
実行メソッドで、どの検証メソッドを使うのかは、validate要素で指定します。 validate要素を指定した場合は、 input要素に検証結果がNGのときの遷移先も指定する必要があります。
@Execute(validate = "validate", input = "login.jsp")
任意のメソッドで検証を行なうには、検証結果がNGの場合に、 ActionMessagesExceptionをスローします。
public void validateLogin(String userName, String password) { if (!userName.equals(password)) { throw new ActionMessagesException("errors.invalid.login"); } }
ActionMessagesExceptionの処理は、app.diconに
org.seasar.struts.interceptor.ActionMessagesThrowsInterceptorを登録し、
customizer.diconで登録したインターセプタを使うように記述します。
app.diconとcustomizer.diconは、sa-struts-tutorialプロジェクトの場合、
src/main/resourcesにおかれています。
app.dicon
<component name="actionMessagesThrowsInterceptor" class="org.seasar.struts.interceptor.ActionMessagesThrowsInterceptor"/>
customizer.dicon
<component name="actionCustomizer" class="org.seasar.framework.container.customizer.CustomizerChain"> ... <initMethod name="addAspectCustomizer"> <arg>"actionMessagesThrowsInterceptor"</arg> </initMethod> ... </component>
リセットメソッド
チェックボックス(<input type="checkbox" .../>)や 複数選択リスト(<select multiple="multiple" ...></select>) は、選択された値だけがリクエストで送信されます。 何も選択しなかった場合は、何も送信されません。 そのため、アクションフォームをセッションで管理している場合、 チェックボックスや複数選択リストで選択状態をすべて解除しても 何もリクエストで送信されないため、元の状態のまま残ってしまいます。
この問題に対応するために用意されているのが、 アクションフォームのリセットメソッドです。 リセットメソッドは、リクエストパラメータをアクションフォームにセットする直前に呼び出されるため、 リセットメソッドで、チェックボックスや複数選択リストのプロパティを選択されていない状態に更新することで、 リクエストで何も送られてこなかった場合でも、選択状態を解除することができます。
@Component(instance = InstanceType.SESSION) public class XxxForm implements Serializable { private static final long serialVersionUID = 1L; public boolean foo; public String[] bar; ... public void reset() { foo = false; bar = new String[0]; } }
リセットメソッドは、実行メソッドごとに指定することができます。 デフォルトは、resetになります。
@Execute(reset = "リセットメソッド名", ...)
ファイルアップロード
ファイルアップロードは、FormFileを型にしたプロパティをActionまたはActionFormに定義し、 FormFile経由でアップロードされたファイルを取得します。
プロパティの型をFormFile[]のように配列指定することで、 複数のFormFileを受け取ることができます。 詳しくはチュートリアルのファイルアップロードを参照してください。
Ajax
SAStrutsは、任意のAjaxのライブラリと組み合わせて使えます。 ここではjQueryを使った例で説明します。
アクションの実行メソッドでは、ResponseUtil.write("文字列")を使って、 レスポンスに文字列を書き込みます。 画面遷移をするわけではないので、戻り値はnullになります。
@Execute(validator = false) public String hello() { ResponseUtil.write("こんにちは"); return null; }
JSPでは、jQueryを使うためにヘッダでjquery.jsを取り込みます。 あるタグをアクションの実行メソッドがレスポンスに書き込んだ値で置き換える場合は、 $('タグのid').load('実行メソッド名');を呼び出します。
<script src="${f:url('/js/jquery.js')}"></script> ... <span id="message"></span><br /> <input type="button" value="hello" onclick="$('#message').load('hello');"/>
アクションにパラメータを渡す場合は、アクションにパラメータ用のプロパティを追加し、 jQuery側は、load()の二番目の引数で指定します。
public String greeting; ... @Execute(validator = false) public String hello() { ResponseUtil.write(greeting); return null; }
<input type="button" value="hello" onclick="$('#message').load('hello',{'greeting':'Hello'});"/>
URLを使って、パラメータを渡すこともできます。
public String greeting; ... @Execute(validator = false, urlPattern = "hello/{greeting}) public String hello() { ResponseUtil.write(greeting); return null; }
<input type="button" value="hello" onclick="$('#message').load('hello/Hello');"/>
サービス
機能中心のビジネスロジックは、サービスで実装します。 S2JDBCに サービスの雛形が 用意されているので、それを利用すると良いでしょう。
S2JDBCを 使う場合、データベースへ接続するための設定は、jdbc.diconに記述します。 jdbc.diconの設定は、こちらを参照してください。 sa-struts-tutorialプロジェクトの場合、jdbc.diconは、src/main/resourcesにおかれています。
S2JDBC自体の設定は、 s2jdbc.diconに記述します。 s2jdbc.diconの設定は、こちらを参照してください。 sa-struts-tutorialプロジェクトの場合、jdbc.diconは、src/main/resourcesにおかれています。
S2JDBCは、 JdbcManager経由で利用します。 JdbcManagerをサービスで利用するには、 次のようにフィールドを定義し、@Resourceをつけます。
@Resource protected JdbcManager jdbcManager;
トランザクション
SAStrutsでは、トランザクション処理は、JTAの実装に任せていて、 SAStruts自体がトランザクション処理に関与することはありません。 JTAの設定は、s2container.diconで行ないます。 詳細は、こちらを参照してください。 sa-struts-tutorialプロジェクトの場合、s2container.diconは、src/main/resourcesにおかれています。
アクションやサービスのメソッドが呼び出されたときに、 自動的にトランザクションを開始するには、customizer.diconにトランザクション用の設定を記述します。 sa-struts-tutorialプロジェクトの場合、customizer.diconは、src/main/resourcesにおかれています。
cutomizer.dicon
<component name="actionCustomizer" class="org.seasar.framework.container.customizer.CustomizerChain"> <initMethod name="addCustomizer"> <arg> <component class="org.seasar.framework.container.customizer.TxAttributeCustomizer"/> </arg> </initMethod> ... </component> <component name="serviceCustomizer" class="org.seasar.framework.container.customizer.CustomizerChain"> <initMethod name="addCustomizer"> <arg> <component class="org.seasar.framework.container.customizer.TxAttributeCustomizer"/> </arg> </initMethod> ... </component>
TxAttributeCustomizerを使うと、Objectクラス以外のpublicなメソッドが呼び出されると、 自動的にトランザクション処理が行なわれます。 デフォルトのトランザクション属性は、Requiredです。
Requiredは、トランザクションが開始されていなければ、 自動的にトランザクションを開始し、 既にトランザクションが開始されていれば、 そのトランザクションを引き継ぎます。 例外が起こった場合は、自動的にロールバックします。
特定のクラスのトランザクション属性を変えたい場合は、 クラスに@TransactionAttributeを指定します。
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public class XxxAction { ... }特定のメソッドのトランザクション属性を変えたい場合は、 メソッドに@TransactionAttributeを指定します。 次の例では、someMethod()はTransactionAttributeType.NEVERで、 someMethod2()はTransactionAttributeType.REQUIRES_NEWになります。
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public class XxxAction { @TransactionAttribute(TransactionAttributeType.NEVER) public void someMethod() { ... } public void someMethod2() { ... } ... }
データ変換
アクションとエンティティ間でデータを変換するには、 Beansの機能を使うのが良いでしょう。 単純に値をコピーするだけでなく、文字列と数値などの変換も行なってくれます。 Commons BeanUtilsはpublicフィールドに対応していないので、 publicフィールドを使いたい場合は、Beans を使ってください。
プライマリーキーで取得したデータをアクションフォームにコピーするには次のようにします。
Employee emp = employeeService.findById(Integer.valueOf(employeeForm.id)); Beans.copy(emp, employeeForm).execute();
アクションフォームのデータでテーブルを更新するには次のようにします。
Employee emp = Beans.createAndCopy(Employee.class, employeeForm).execute(); employeeService.update(emp);
JSP
JSPは、アクションに対応するディレクトリに格納します。 例えば、XxxActionで使うJSPは、/xxx/に格納すると良いでしょう。
アクションやアクションフォームのプロパティの値は、 リクエストの属性にプロパティと同じ名前でセットされています。 そのため、次のように参照することができます。
${f:h(プロパティ名)} <a href="${f:u(プロパティ名)}">...</a> <c:forEach items="${プロパティ名}" ... ${f:br(f:h(プロパティ名))} <fmt:formatDate value="${f:date(hireDate, 'yyyyMMdd')}" pattern="yyyy/MM/dd"/> <fmt:formatNumber value="${f:number(salary, '####')}" pattern="#,###"/>
HTMLエスケープ(クロスサイトスクリプティング対策で"<"を"<"などに変換すること)が必要なケースは、f:h()を使ってください。 f:h()は、配列もリストと同じように表示できます。
URLエンコーディング(クロスサイトスクリプティング対策で「javascript:alert('hoge')」のような入力を 「javascript%3Aalert%28%27hoge%27%29」のようにJavaScriptが実行されないように変換すること)が 必要なケースは、f:u()を使ってください。
ELは、items="${プロパティ名}"のように要素の設定のみに使い、 タグのボディでは使わないようにしてください。 クロスサイトスクリプティングの問題が起こる危険があります。 タグのボディでは、f:h()を使ってください。
aタグのhref要素などを設定するときに、コンテキストルートを自動的に補完させる場合は、 f:url()を使います。 パスを/ではじめた場合は、コンテキストルートからみたパスになります。 パスが/ではじまっていない場合は、JSPからみたパスになります。
テキストエリアで入力した値を表示するときに、改行を<br />に変換したい場合は、 f:br()を使います。空白をそのまま表示したい場合は、f:nbsp()を使います。 f:br()とf:nbsp()を組み合わせるには次のようにします。
${f:br(f:nbsp(f:h(textarea)))}
日付をフォーマットして表示するには、fmt:formatDateを使いますが、 value要素に設定する値はDate型である必要があります。 Strutsの場合、入力値は、文字列で定義するのが一般的なので、 文字列で定義されている入力値は、fmt:formatDateでフォーマットできません。 このような場合は、f:date()を使って文字列をDate型に変換します。 2番めの引数は、SimpleDateFormatの形式で指定します。
数値をフォーマットして表示するには、fmt:formatNumberを使いますが、 value要素に設定する値はNumber型である必要があります。 Strutsの場合、入力値は、文字列で定義するのが一般的なので、 文字列で定義されている入力値は、fmt:formatNumberでフォーマットできません。 このような場合は、f:number()を使って文字列をNumber型に変換します。 2番めの引数は、DecimalFormatの形式で指定します。
ネストしたプロパティにはドット(.)でアクセスできます。
${プロパティ名.ネストしたプロパティ名}
ActionやActionFormのプロパティは、 publicフィールドをELやStrutsが参照できるようにするために、 JavaBeansはMapに、配列はListにラップされています。 プロパティアクセス(.)や配列アクセス([数値])以外のやり方で、 変数にアクセスするときには気をつけてください。
任意のオブジェクトの値を読みやすい文字列で表示するには、 f:label(value, dataList, valueName, labelName)を使います。 valueは、任意のオブジェクトの値です。 dataListはJavaBeansかMap<String, Object>のリストで、 どのオブジェクトの値をどの文字列に変換するのかを指定します。 valueNameはvalue用のプロパティ名(Mapの場合はキー名)、 labelNameはlabel用のプロパティ名(Mapの場合はキー名)です。
例えば、部署のidプロパティの値をnameプロパティの値で表示したい場合、 次のように指定します。deptItemsには、部署のデータが全件入っているとします。
${f:label(e.departmentId, deptItems, "id", "name")}
どのJSPでも共通で使うような宣言は、1つのJSPにまとめ、web.xmlでそのJSPを指定します。 sa-struts-tutorialプロジェクトでは、webapp/WEB-INF/view/common/common.jspに共通で使う宣言が 定義されています。
commons.jsp
<%@page pageEncoding="UTF-8"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> <%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> <%@taglib prefix="html" uri="http://struts.apache.org/tags-html"%> <%@taglib prefix="bean" uri="http://struts.apache.org/tags-bean"%> <%@taglib prefix="tiles" uri="http://jakarta.apache.org/struts/tags-tiles"%> <%@taglib prefix="s" uri="http://sastruts.seasar.org"%> <%@taglib prefix="f" uri="http://sastruts.seasar.org/functions"%>
web.xml
<web-app> ... <jsp-config> <jsp-property-group> <url-pattern>*.jsp</url-pattern> <el-ignored>false</el-ignored> <page-encoding>UTF-8</page-encoding> <scripting-invalid>false</scripting-invalid> <include-prelude>/common/common.jsp</include-prelude> </jsp-property-group> </jsp-config> </web-app>
SAStrutsには、Strutsのhtml:formを継承したs:formが用意されています。 action属性は自動的に計算されますが、Tilesのレイアウト側のJSPでは、 明示的にaction属性を指定してください。
SAStrutsには、Strutsのhtml:linkを継承したs:linkが用意されています。 href属性を設定するときに、コンテキストルートを自動的に補完させる場合に使います。 パスを/ではじめた場合は、コンテキストルートからみたパスになります。 パスが/ではじまっていない場合は、JSPからみたパスになります。
JSPへのダイレクト(methodがGETによる)アクセスは、デフォルトで禁止されています。 違反した場合は、400のステータスコードが返されます。 JSPはアクションを経由して表示したほうがいいでしょう。 最初は、アクションで何もすることがなくても、 後から必要になった場合に、URLが変わらないようにするためです。 また、セキュリティ的なチェックを入れたいときも、 アクションを経由するほうが何かと便利です。
リダイレクトで、他のJSPへ遷移するときも、戻り値を下記のようにして、 アクションを経由するようにします。
return "/アクション名/メソッド名?redirect=true";
どうしても、JSPへ直接アクセスする必要がでてきた場合は、 web.xmlのroutingfilterのinit-paramでjspDirectAccessの値をtrueにします。 詳しくは、web.xmlを参照してください。
アプリケーションの自動生成
Doltengを使って、データベースにアクセスするアプリケーションを自動生成することができます。 詳しくは、Database Viewを参照してください。
SQLファイルからDTOの自動生成
Doltengを使って、SQLファイルに対応するDTOを自動生成することができます。 詳しくは、こちらを参照してください。 SQLファイルの詳細は、こちらを参照してください。