テーブルのスコープ

今回はのスコープについてです。

【前提条件】

[環境]
[コード]
  • 前回のDataBeanは再利用しています

【概要】

タグを使用した際、
コーディングの仕方によってはうまく動かないことがあります。

今回は一覧ページからデータを選択して、
詳細ページに遷移するというサンプルを作ります。

サンプルを実行する際入り口となるページからアクセスします。
入り口となるページのソースは↓になります。
ファイル名は「dataTableIndex.xhtml」です。

<?xml version="1.0" encoding="Windows-31J" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html"> 
    <f:view>
        <h:head>
            <meta http-equiv="Content-Type" content="text/html; charset=Windows-31J" />
            <title>データテーブル</title>
        </h:head>
        <h:body>
            <h:form id="sampleForm">
                <h:commandLink value="GO" action="#{dataTableSample.init()}" />
            </h:form>
        </h:body>
    </f:view>
</html> 

【上手くいかないコード】

[一覧ページ]

まずは一覧ページからです。

ManagedBeanのソースです。

@ManagedBean(name = "dataTableSample")
@RequestScoped
public class DataTableSample {

    private List<DataBean> dataList = null;

    public String init() {

        dataList = new ArrayList<>();

        dataList.add(new DataBean("CODE-001", "えんぴつ", 100));
        dataList.add(new DataBean("CODE-002", "けしごむ", 50));
        dataList.add(new DataBean("CODE-003", "ノート", 200));

        return "dataTableSample.xhtml";
    }

    // アクセサメソッドは省略
}

このManagedBeanはリクエストスコープで作成しています。

一覧を表示するためのinitメソッドを作成します。
initメソッドは概要で作成したXHTMLから実行されます。

一覧表示用のBeanを作成した後は一覧ページに遷移します。

つづいて、XHTMLのソースです。
ファイル名は「dataTableSample.xhtml」です。

<?xml version="1.0" encoding="Windows-31J" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html"> 
    <f:view>
        <h:head>
            <meta http-equiv="Content-Type" content="text/html; charset=Windows-31J" />
            <title>データテーブル</title>
        </h:head>
        <h:body>
            <h:form id="sampleForm">
                <h:dataTable var="recordData" value="#{dataTableSample.dataList}" border="1" cellspacing="0">
                    <h:column>
                        <f:facet name="header">
                            コード
                        </f:facet>
                        <h:commandLink value="#{recordData.code}"
                            action="#{dataTableScope.init(recordData.code, recordData.name, recordData.price)}" />
                    </h:column>
                    <h:column>
                        <f:facet name="header">
                            商品名
                        </f:facet>
                        <h:outputText value="#{recordData.name}" />
                    </h:column>
                    <h:column>
                        <f:facet name="header">
                            価格
                        </f:facet>
                        <h:outputText value="#{recordData.price}" />
                        <f:facet name="footer">
                            価格フッター
                        </f:facet>
                    </h:column>
                </h:dataTable>
            </h:form>
        </h:body>
    </f:view>
</html> 

前回のサンプルをベースに作っています。

タグで詳細ページ用のManagedBean(dataTableScope)の
initメソッドを呼び出しています。

initメソッドには各レコードのデータをパラメータとして受け取ります。

[詳細ページ]

次に詳細ページです。

ManagedBeanのソースです。

@ManagedBean(name = "dataTableScope")
@RequestScoped
public class DataTableScopeSample {

    private String code = null;

    private String name = null;

    private Integer price = null;

    public String init(final String code, final String name, final Integer price) {

        this.code = code;
        this.name = name;
        this.price = price;

        return "dataTableScope.xhtml";
    }

    // アクセサメソッドは省略
}

initメソッドは一覧からリンクをクリックした時に実行されるメソッドです。
受け取ったパラメータを自分自身に設定して、
詳細ページに遷移させています。

つづいてXHTMLのソースです。
ファイル名は「dataTableScope.xhtml」です。

<?xml version="1.0" encoding="Windows-31J" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html"> 
    <f:view>
        <h:head>
            <meta http-equiv="Content-Type" content="text/html; charset=Windows-31J" />
            <title>データテーブル</title>
        </h:head>
        <h:body>
            <h:form id="sampleForm">
                <div>
                    <h:outputText value="コード:" />
                    <h:outputText value="#{dataTableScope.code}" />
                </div>
                <div>
                    <h:outputText value="商品名:" />
                    <h:outputText value="#{dataTableScope.name}" />
                </div>
                <div>
                    <h:outputText value="価格:" />
                    <h:outputText value="#{dataTableScope.price}" />
                </div>
            </h:form>
        </h:body>
    </f:view>
</html> 

DataTableScopeSampleのデータを表示しているだけのページです。

【実行】

入り口ページ(dataTableIndex.xhtml)にアクセスします。

リンクをクリックすると一覧画面が表示されます。

一覧のリンクをクリックすると・・・

一覧ページがまた表示されてしまいます。
しかも、レコードのデータが表示されていません。

【改善方法 その1 スコープを変えてみる】

[直してみる]

ManagedBeanのスコープを
リクエストスコープからセッションスコープに変えてみます。

@ManagedBean(name = "dataTableSample")
@SessionScoped
public class DataTableSample implements Serializable {

    private static final long serialVersionUID = -5247838625597831733L;

    private List<DataBean> dataList = null;

    public String init() {

        dataList = new ArrayList<>();

        dataList.add(new DataBean("CODE-001", "えんぴつ", 100));
        dataList.add(new DataBean("CODE-002", "けしごむ", 50));
        dataList.add(new DataBean("CODE-003", "ノート", 200));

        return "dataTableSample.xhtml";
    }

    // アクセサメソッドは省略
}
[実行してみる]

また、入り口ページにアクセスします。

先ほどと変わりません。

一覧のリンクをクリックすると・・・

今度は詳細ページに遷移しました。

[デメリット]

セッションスコープにしてしまうとBeanのListを全て
セッションで持つことになるということです。

件数が多かったり、一覧画面が多かったりすると
セッションによりメモリが食いつぶされてしまいます。

セッションスコープを使う場合、件数を減らすなどの工夫が必要になります。

【改善方法 その2 データ取得のタイミングを変えてみる】

[直してみる]

一覧データの取得のタイミングをinitメソッドから、
コンストラクタに移動してみます。

@ManagedBean(name = "dataTableSample")
@RequestScoped
public class DataTableSample {

    private List<DataBean> dataList = null;

    public DataTableSample() {

        dataList = new ArrayList<>();

        dataList.add(new DataBean("CODE-001", "えんぴつ", 100));
        dataList.add(new DataBean("CODE-002", "けしごむ", 50));
        dataList.add(new DataBean("CODE-003", "ノート", 200));

    }

    public String init() {

        return "dataTableSample.xhtml";
    }

    // アクセサメソッドは省略
}
[実行してみる]

また、入り口ページにアクセスします。

先ほどと変わりません。

一覧のリンクをクリックすると・・・

セッションスコープの時と同じく、
詳細ページが表示されました。

[デメリット]

リクエストスコープなのでメモリの食いつぶしについては
セッションスコープより心配しなくても良くなりました。

しかし、一覧から詳細に遷移するときも
コンストラクタが呼ばれてしまいます。

そのため、不要な処理が実行されてしまいます。

【最後に】

今回はタグのスコープについて書きました。

個人的に今後のJSFで改善してほしい点だったりします。
リクエストスコープで遷移できると良いのですが・・・