メンチカツには醤油でしょ!!

ITエンジニア徒然 (AWS/Java/JavaScript/Google Spreadsheets/Jenkins/Mac/外部コミュニティ・勉強会レポ)

Struts2でセレクトボックスの選択肢をDBから読む場合はprepareではなくBeforeResult

最終的にはライフサイクルの話かも。

Struts2でセレクトボックスを表現するとき

JSPでは

<s:select id="aaa_code" name="aaaCode" list="aaaCodeMap" headerKey="" headerValue="" />

Actionでは

@Getter @Setter
private Map<String, String> aaaCodeMap;

みたいにやります。

aaaCodeMapがselectの選択肢(option)になります。
固定値やproperties読みの場合はそれなりなコーディングで良いのですが、
DBから読む場合はいつそれを読み込むかによって処理結果が変わったりします。

最初はPreparableでやってた

当初、Preparableインターフェースをimplementsして
prepare()をオーバーライドして、その中で

@Override
public void prepare() throws Exception {
    this.aaaCodeMap = aaaCodeSearchService.searchMap(searchParam);
}

とやっていたのですが、
Mapを取得する処理の引数(上記で言うとsearchParam)が
主処理となるアクションメソッド内で変更されるようなケースだと
検索結果は主処理の前の状態のものとなり古くなってしまう。

ということで、
主処理が終わった描画前にセレクトボックスの内容を検索するのが正しそう。
ただアクションメソッドの終了前にいちいち検索を呼び出していると非効率。

@Action("execute")
public String execute() throws Exception {
    // 主処理

    // 後処理としてselectの選択肢を検索.
    this.aaaCodeMap = aaaCodeSearchService.searchMap(searchParam);

    return SUCCESS;
}

これは1つのActionクラスの中にいくつかのactionメソッドがあったり
validationやExceptionの場合にも呼び出さなければならず
処理の呼び出しが漏れそうで良くないですね。 

取りうる策は (@After?)

Struts2には@Before @Afterアノテーションというのがあり
Actionクラスのacitonメソッドの処理の前後に処理が挟めることとなっています。

@After 付けただけじゃ呼ばれずで、AnnotationWorkflowInterceptorがstruts.xmlのinterceptorStackに必要です。

@After
public void after() {
    this.aaaCodeMap = aaaCodeSearchService.searchMap(searchParam);
}

afterメソッド自体は呼ばれているのですが、描画後の選択肢(options)が出ない。。

d.hatena.ne.jp

d.hatena.ne.jp

結局 @BeforeResult

@BeforeResult アノテーションを試したら、できました。

上記サイトでは
@BeforeResult アクションが結果を返す直前
@After アクションの実行直後
となっており、デフォルトでの処理順は
 アクションメソッド⇒After⇒BeforeResult
という印象を受けます。
BeforeResultで効かなくてAfterで効くならなんとなくわかるんだけど。。。

@BeforeResult
public void beforeResult() {
    this.aaaCodeMap = aaaCodeSearchService.searchMap(searchParam);
}

struts.xml

<struts>
<!-- 略 -->
<package name="default" extends="struts-default">
<interceptors>
<interceptor name="annotationWorkflow" class="com.opensymphony.xwork2.interceptor.annotations.AnnotationWorkflowInterceptor" />
<!-- その他interceptors -->
<interceptor-stack name="myInterceptorStack">
<interceptor-ref name="defaultStack" />
<interceptor-ref name="annotationWorkflow" />
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="myInterceptorStack" />
</package>
<!-- 略 -->
</struts>