2008年6月21日土曜日

ZK: Pagingコンポーネントでライブデータ表示

Smalltaksの記事Use Load-On-Demand to Handle Huge Dataを試してみました。
リストボックスに一度に大量なデータを表示するとWEBアプリにもサーバにも大きな負担がかかります。
Pagingコンポーネントを明示的に使いオンデマンドでページ単位にクエリーを実行し、取得したライブデータでリストボックスの表示を効果的に処理する方法が紹介されていました。

<window
title="Listbox with Paging Performance using ZK version: ${desktop.webApp.version}"
border="normal" use="sample.ui.EmployeeUI"
xmlns:zk="http://www.zkoss.org/2005/zul"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.zkoss.org/2005/zul http://www.zkoss.org/2005/zul/zul.xsd">
<zk:toolbarbutton href="/" label="Back" />
<zk:listbox id="listEmployee" width="350px" checkmark="true">
<zk:listhead sizable="true">
<zk:listheader label="ID" sort="auto" />
<zk:listheader label="Full Name" sort="auto" />
<zk:listheader label="User Name" sort="auto" />
</zk:listhead>
</zk:listbox>
<!-- 明示的にpagingコンポーネントを使用する。 -->
<zk:paging id="pageEmployee" pageSize="30" />
</window>

Windowを継承したクラスでインターフェースAfterComposeを実装します。
afterComposeメソッドでは
  • データのトータル行数をセット
  • 最初のページにデータを表示
  • pagingコンポーネントに onPaging イベントリスナを登録
    を行います。

    public class EmployeeUI extends SimpleWindow implements AfterCompose {
    private final String LISTBOX_ID= "listEmployee"; //zulファイルの listbox コンポーネントの id
    private final String PAGING_ID = "pageEmployee"; //zulファイルの paging コンポーネントのid
    private JdbcManager jdbcManager; //Seasar変数

    public EmployeeUI(){
    DelegatingVariableResolver variableResolver = new DelegatingVariableResolver();
    jdbcManager = (JdbcManager)variableResolver.resolveVariable("jdbcManager");
    }

    /**
    * AfterComposeインターフェースのafterComposeメソッドはコンポーネント作成段階で処理される。
    * コンポーネントを作成した直後に初期化できパフォーマンスの改善に役立つ。
    *
    */
    public void afterCompose() {
    Paging page = getPaging(PAGING_ID);

    //データのトータル行数を取得
    page.setTotalSize((int)jdbcManager.from(Employee.class).getCount());

    final int PAGE_SIZE = page.getPageSize();

    // Listboxに最初のページを表示 オフセット--0 行数---PAGE_SIZE
    redraw(0, PAGE_SIZE);

    // onPagingイベントリスナの登録 決まり文句
    page.addEventListener("onPaging", new EventListener() {
    public void onEvent(Event event) {
    PagingEvent pageEvent = (PagingEvent) event;
    int pageNo = pageEvent.getActivePage();
    int offset = pageNo * PAGE_SIZE;

    // クリックされたページ番号でライブデータを取得し再表示
    redraw(offset, PAGE_SIZE);
    }
    });

    }


    /**
    * afterCompose及びonPagingイベントリスナから呼び出されListboxを描画する。
    * コールされる度にクエリを実行しデータベースからライブデータを取得する。
    *
    * @param firstResult オフセット
    * @param maxResults 行数
    */
    private void redraw(int firstResult, int maxResults) {
    Listbox listBox = getListbox(LISTBOX_ID);
    listBox.getItems().clear();

    // データ取得
    List<Employee> emps = jdbcManager.from(Employee.class)
    .orderBy("id") // 問い合わせ結果をページングするにはorder by が必要
    .offset(firstResult)
    .limit(maxResults)
    .getResultList();
    // 表示
    for (Employee employee : emps) {
    Listitem li = new Listitem();
    li.setValue(employee);
    li.appendChild(new Listcell("" + employee.id));
    li.appendChild(new Listcell(employee.fullname));
    li.appendChild(new Listcell(employee.username));
    listBox.appendChild(li);
    }
    }
    }


    大量なデータでも paging コンポーネントを用いライブデータで処理を行うとアプリの動作は非常に軽快ですね。
  • 2008年6月1日日曜日

    ZK: Spreadsheet

    以前から気になっていたZK Spreadsheetをいじってみました。ZK SpreadsheetではあらかじめExcelで作成しておいた表のテンプレートを読み込むことでZKのコンポーネントとして操作可能になるようです。つまり使い慣れたエクセルファイルがそのままZKのコンポーネントになるんですね。
    Integrate ZK Spreadsheet with Springを元ネタにしてZK SpreadsheetとSeasarの統合を試して見ました。
    まずはZssSpring.warをダウンロードし動作確認。

    次にデモと同じ表示をさせるようにテーブルを作成。

    create table FinancialData (
    fyear integer,
    fquarter integer,
    item char(10),

    liquidAssets decimal(10, 0),
    fundInvestment decimal(10, 0),
    fixedAssets decimal(10, 0),
    intangibleAsset decimal(10, 0),
    otherAssets decimal(10, 0),

    currentLiabilities decimal(10, 0),
    longTermLiabilities decimal(10, 0),
    otherLiabilities decimal(10, 0),

    capitalStock decimal(10, 0),
    capitalSurplus decimal(10, 0),
    retainedEarnings decimal(10, 0),
    otherEquity decimal(10, 0),
    treasuryStock decimal(10, 0)

    );


    insert into FinancialData(
    fyear,
    fquarter,
    item,

    liquidAssets,
    fundInvestment,
    fixedAssets,
    intangibleAsset,
    otherAssets,

    currentLiabilities,
    longTermLiabilities,
    otherLiabilities,

    capitalStock,
    capitalSurplus,
    retainedEarnings,
    otherEquity,
    treasuryStock
    )values(
    2007,
    1,
    'Quarter',
    146504221,
    23181709,
    7168392,
    221426,
    2270018,

    102515784,
    3000,
    456175,

    33630080,
    7127901,
    34420905,
    1826731,
    -634810
    );
    ...



    Bean作成
    キャメルケースでテーブルを作るとBeanの作成時にテーブル名と列名を一々指定しなければ
    ならなかった(^^;;

    package churazk.entity;
    import java.math.BigDecimal;
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.Table;

    @Entity
    @Table(name="FINANCIALDATA")
    public class FinancialData {
    @Column(name="fYear")
    public Integer fYear;

    @Column(name="fQuarter")
    public Integer fQuarter;

    @Column(name="item")
    public String item;

    @Column(name="liquidAssets")
    public BigDecimal liquidAssets;

    @Column(name="fundInvestment")
    public BigDecimal fundInvestment;

    @Column(name="fixedAssets")
    public BigDecimal fixedAssets;

    @Column(name="intangibleAsset")
    public BigDecimal intangibleAsset;

    @Column(name="otherAssets")
    public BigDecimal otherAssets;

    @Column(name="currentLiabilities")
    public BigDecimal currentLiabilities;

    @Column(name="longTermLiabilities")
    public BigDecimal longTermLiabilities;

    @Column(name="otherLiabilities")
    public BigDecimal otherLiabilities;

    @Column(name="capitalStock")
    public BigDecimal capitalStock;

    @Column(name="capitalSurplus")
    public BigDecimal capitalSurplus;

    @Column(name="retainedEarnings")
    public BigDecimal retainedEarnings;

    @Column(name="otherEquity")
    public BigDecimal otherEquity;

    @Column(name="treasuryStock")
    public BigDecimal treasuryStock;
    }


    つづいてzulファイルを編集。

    <?xml version="1.0" encoding="UTF-8"?>
    <?variable-resolver class="org.zkoss.zkplus.seasar.DelegatingVariableResolver"?>
    <window>
    Quarter:
    <listbox id="quarter" mold="select" rows="1" onSelect="refreshQuarter()">
    <listitem value="0" label="Select"/>
    <listitem value="1" label="Quarter 1"/>
    <listitem value="2" label="Quarter 2"/>
    <listitem value="3" label="Quarter 3"/>
    <listitem value="4" label="Quarter 4"/>
    </listbox>
    Style:
    <listbox id="style" mold="select" rows="1" onSelect="changeStyle()">
    <listitem label="Style 1" value="/BalanceSheet.xls"/>
    <listitem label="Style 2" value="/NewSheet.xls"/>
    </listbox>
    <zscript>
    import churazk.entity.FinancialData;
    import org.seasar.extension.jdbc.where.SimpleWhere;

    FinancialData reloadQuarter(int quarter){
    FinancialData bean = jdbcManager.from(FinancialData.class)
    .where(new SimpleWhere()
    .eq("fYear", 2007)
    .eq("fQuarter", quarter)
    )
    .getSingleResult();

    return bean;
    }

    void refreshQuarter() {
    Listitem listitem = quarter.getSelectedItem();
    int quarter = Integer.parseInt(listitem.value);
    //Call the method to reload data
    dataBean = reloadQuarter(quarter);
    //Call the method to refresh values of cells
    balance.book.notifyChange(new String[]{"dataBean"});
    }

    void changeStyle() { //Change the URL of spreadsheet
    balance.url = style.getSelectedItem().value;
    }

    dataBean = reloadQuarter(0);
    </zscript>
    <spreadsheet id="balance" url="/BalanceSheet.xls" maxrow="40" maxcolumn="20" height="600px" width="1300px"/>
    </window>


    スプレッドシートデモのWEB-INF/libより次のファイルをプロジェクトWEB-INF/libへインポート

    commons-math-1.2.jar
    jxl.jar
    zss.jar
    zssex.jar
    zssjxl.jar


    プロジェクトのWEBルートにBalanceSheet.xlsとNewSheet.xlsを配置して実行

    ZKコンポーネントとは言えExcelシートにビーンの値が表示され不思議な感じです、でもとても使い勝手がよさそうなコンポーネントです。

    動作環境

  • OS: Ubuntu 8.04
  • JDK: 1.6.0_04
  • Tomcat: apache-tomcat-6.0.16
  • Eclipse: Europa wtp-all-in-one-sdk
  • ZK: zk-3.0.5
  • Seasar: DoltengプラグインよりChuraプロジェクト(Teeda+S2Dao)として
  • S2JDBC関連: sa-struts-tutorial-1.0.2-rc4.zipより

    geronimo-jpa_3.0_spec-1.0.jar
    geronimo-ejb_3.0_spec-1.0.jar
    s2jdbc.dicon

  • web.xml追加

    <!-- ZK -->
    <listener>
    <listener-class>org.zkoss.zk.ui.http.HttpSessionListener</listener-class>
    </listener>
    <servlet>
    <servlet-name>zkLoader</servlet-name>
    <servlet-class>org.zkoss.zk.ui.http.DHtmlLayoutServlet</servlet-class>
    <init-param>
    <param-name>update-uri</param-name>
    <param-value>/zkau</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup><!-- Must -->
    </servlet>
    <servlet-mapping>
    <servlet-name>zkLoader</servlet-name>
    <url-pattern>*.zul</url-pattern>
    </servlet-mapping>

    <servlet>
    <servlet-name>auEngine</servlet-name>
    <servlet-class>org.zkoss.zk.au.http.DHtmlUpdateServlet</servlet-class>
    </servlet>
    <servlet-mapping>
    <servlet-name>auEngine</servlet-name>
    <url-pattern>/zkau/*</url-pattern>
    </servlet-mapping>

    <!-- Miscellaneous -->
    <session-config>
    <session-timeout>120</session-timeout>
    </session-config>
    <!-- ZK -->