天天看點

多行資料送出到Struts的ActionForm的List屬性中

[轉自] http://extjs2.iteye.com/blog/460926

  今天遇到送出多行資料問題, 在網上找了一點資料:

WEB 應用中一般都會處理主從表的資訊, 或者稱之為頭層與行層的一對多的關系資料,如訂單頭/訂單明細. 對于這種關系資料送出到背景的 Struts 的 ActionForm 的話, 這個 ActionForm 就要好好的設計一下, 不然會給自已帶來許多額外的代碼. 比如有的人的處理方法就是把頁面送出到背景的毫無關系的散裝資料非常吃力的拼湊一對多的關系對象出來.

下面舉一個如今非常現實的關于股票的例子, 簡單的應用場景是: 記錄某個帳戶所持有的股票資訊,送出到背景,然後顯示出來. 輸入頁面如下圖

帳戶資訊包括帳戶名和資金帳号;持有股票的每一行資訊包括股票代碼, 股票名稱, 成本價, 股票數量. 股票行可以動态增删.

多行資料送出到Struts的ActionForm的List屬性中
多行資料送出到Struts的ActionForm的List屬性中

為了簡化不必要的代碼, 我們要實作的終及目标是: 在輸入頁面上點選 "儲存資料" 按鈕, 由 Struts 的 RequestProcessor.processPopulate() 方法把頁面送出的基本資訊組裝到 AccountStockForm 的 account 的對應屬性中,股票行資訊對應生成一個 Stock 執行個體加到 AccountStockForm的 List 屬性 stocks 中, 後續在 AccountStockAction 中直接處理account和stocks屬性就非常簡單了. AccountStockForm在這裡隻作為一個殼.

下面從前台到背景說明關鍵性的代碼, 完整的 MyEclipse 工程包可以點選 TestStruts135.zip下載下傳到.

一: struts-config.xml 配置

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN"
          "http://struts.apache.org/dtds/struts-config_1_3.dtd">
<struts-config>
    <form-beans>
        <form-bean name="accountStockForm"
          type="com.unmi.form.AccountStockForm"/>
    </form-beans>
    <action-mappings>
        <action path="/showStock" name="accountStockForm"
         type="com.unmi.action.AccountStockAction" scope="request">
            <forward name="show" path="/show.jsp"/>
        </action>
    </action-mappings>
</struts-config>
           

二: 輸入頁面 input.jsp, 注意表單域命名

<html:form action="/showStock">
        <h3>記錄持有的股票<br></h3>
        <fieldset>s<legend>基本資訊</legend>
        <table width="100%" border=0><tr>
            <td>帳戶名:<html:text property="account.name"/></td>
            <td>資金帳号:<html:text property="account.number"/></td>
        </tr></table>
        </fieldset>
        <br>
        <fieldset><legend>持有股票</legend>
        <table width=100% stockTable">
        <tr>
            <td><input type="checkbox" οnclick="checkAll(this)"></td>
            <td>股票代碼</td>
            <td>股票名稱</td>
            <td>成本價</td>
            <td>股票數量</td>
        </tr>
        <tr>
            <td><input type="checkbox" name="check"></td>
            <td><input name="stocks[0].code" size="15"></td>
            <td><input name="stocks[0].name" size="15"></td>
            <td><input name="stocks[0].price" size="15"></td>
            <td><input name="stocks[0].quantity" size="15"></td>
        </tr>
        </table>
    </html:form>

           

例如輸入框名 account.name 送出後能設定到 accountStockForm 的account的name屬性

輸入框名為 stocks[0].code 送出後會設定到 accountStockForm 的 List stocks的第一個元素的code屬性.以此類推

在送出表單前要重排行層的索引,從 0 起, 否則到後右的 Form 會一些空資料.

三: AccountStockForm 的關鍵代碼

private Account account = new Account();
    private List stocks = new AutoArrayList(Stock.class);
    
    public void setStocks(List stocks)
    {
        this.stocks.clear();
        this.stocks.addAll(stocks);
    }


           

定義了兩個屬性,分别是一個bean(Account,接受基本資訊)和一個List(stocks,接受股票行資訊),注意這兩個屬性必須初始化,不然在表單送出後會出現空指針錯誤. setStocks方法是讓stocks屬性永遠保有持是一個 AutoArrayList 執行個體. 這樣在表單送出後設定值是總能調用 AutoArrayList 的 get(int index) 方法.

四: 自定義的 AutoArrayList

public class AutoArrayList extends ArrayList {
    
    private Class itemClass;
    
    public AutoArrayList(Class itemClass) {
        this.itemClass = itemClass;
    }
    
    public Object get(int index) {
        try {
            while (index >= size()) {
                add(itemClass.newInstance());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return super.get(index);
    }
}
           

了解為什麼要繼承一個ArrayList, 覆寫get(int index)方法要簡單了解 Struts 處理送出資料的工作原理: 大緻如下: 頁面送出後, 由 ActionServlet交給RequestProcessor的processPopulate()方法,由processPopulate()方法收集請求資料,放在map中,key為表單域的name屬性,如 name, account.name, stocks[0].code. 然後借助于 Common-beanutils 工具包設定到 ActionForm 的相應屬性中

如果key是簡單的'name',直接form.setName(map.get('name'));

如果key是'account.name', 執行的操作是 form.getAccount().setName(map.get('account.name');

如果key是'stocks[0].code', 它可以對應到資料或集合中,如對于數組 form.stocks[0].code=map.get('stocks[0].code'); 對于集合((List)form.getStocks()).get(0).setCode(map.get('stocks[0].code'))

從上也能了解為什麼 form 中的那兩個屬性必須實始化,不然會出現空指針錯. 而且為什麼 stocks 要用 AutoArrayList 執行個體化, 避免出現索引越界的錯誤.

五: 在 AccountStockAction 中可以列印出送出的資料

AccountStockForm asForm = (AccountStockForm)form;
        
        Account account = asForm.getAccount();
        System.out.println("Account Name:"+account.getName()+
                " Number:"+account.getNumber());
        
        List stocks = asForm.getStocks();
        for (int i=0; i<stocks.size() ;i++)
        {
            Stock stock = (Stock)stocks.get(i);
            System.out.println("Stock["+i+"]Code:"+stock.getCode()+
                    " Name:"+stock.getName()+
                    " Price:"+stock.getPrice()+
                    " Quantity:"+stock.getQuantity());
        }
        
        return mapping.findForward("show");
           

在Action中就能直接取用送出來的資料了,不需要 getParameterValues()了.

六: 最後一步, 對于這樣的 ActionForm 我們應該如何顯示出來呢,我們用了 nested 标簽 (show.jsp)

<html:form action="/showStock">
        <h3>修改持有的股票<br></h3>
        <fieldset><legend>基本資訊</legend>
        <table width="100%" border=0><tr>
        <nested:nest property="account">
            <td>帳戶名:<nested:text property="name" readonly="true"/></td>
            <td>資金帳号:<nested:text property="number" readonly="true"/></td>
        </nested:nest>
        </tr></table>
        </fieldset>
        <br>
        <fieldset><legend>持有股票</legend>
        <table width=100% stockTable">
        <tr>
            <td><input type="checkbox" οnclick="checkAll(this)"></td>
            <td>股票代碼</td>
            <td>股票名稱</td>
            <td>成本價</td>
            <td>股票數量</td>
        </tr>
        <nested:iterate id="stock" property="stocks">
        <tr>
            <td><input type="checkbox" name="check"></td>
            <td><nested:text property="code" size="15"/></td>
            <td><nested:text property="name" size="15"/></td>
            <td><nested:text property="price" size="15"/></td>
            <td><nested:text property="quantity" size="15"/></td>
        </tr>
        </nested:iterate>
        </table>
    </html:form>
           

可以檢視生成的HTML源檔案, 你就能更好了解 input.jsp 中的表單域為什麼要那麼命名了.

小結的内容是請注意以下幾個重點:

1. 輸入資訊的頁面 input.jsp 沒有使用 Struts 标簽,目的是讓大家了解,表單域應如何命名才能對應上 ActionForm 中的哪一個屬性

2. 顯示資料的頁面是用的 Struts 标簽,并留意 nested 标簽的應用. 可以從生成的 HTML 源檔案中體會出什麼

3. 送出資料前要重新編排行層中輸入框 Name 屬性的下标植.

4. 回味為什麼要引入 ArrayList 的子類 AutoArrayList, 關鍵在 get(int index) 方法的覆寫

5. 最後是 ActionForm 中 List 屬性 stocks 的 setter 方法的實作, 保持那個 List 的運作時具體類型不變