多行數據提交到Struts的ActionForm的List屬性中

今天遇到提交多行數據問題, 在網上找了一點資料:  

WEB 應用中通常都會處理主從表的信息, 或者稱之爲頭層與行層的一對多的關係數據,如訂單頭/訂單明細. 對於這種關係數據提交到後臺的 Struts 的 ActionForm 的話, 這個 ActionForm 就要好好的設計一下, 否則會給自已帶來許多額外的代碼. 好比有的人的處理方法就是把頁面提交到後臺的毫無關係的散裝數據很是吃力的拼湊一對多的關係對象出來.

下面舉一個現在很是現實的關於股票的例子, 簡單的應用場景是: 記錄某個賬戶所持有的股票信息,提交到後臺,而後顯示出來. 輸入頁面以下圖  

賬戶信息包括賬戶名和資金賬號;持有股票的每一行信息包括股票代碼, 股票名稱, 成本價, 股票數量. 股票行能夠動態增刪.  






爲了簡化沒必要要的代碼, 咱們要實現的終及目標是: 在輸入頁面上點擊 "保存數據" 按鈕, 由 Struts 的 RequestProcessor.processPopulate() 方法把頁面提交的基本信息組裝到 AccountStockForm 的 account 的對應屬性中,股票行信息對應生成一個 Stock 實例加到 AccountStockForm的 List 屬性 stocks 中, 後續在 AccountStockAction 中直接處理account和stocks屬性就很是簡單了. AccountStockForm在這裏只做爲一個殼.

下面從前臺到後臺說明關鍵性的代碼, 完整的 MyEclipse 工程包能夠點擊 TestStruts135.zip下載到.  

一: struts-config.xml 配置  

Java代碼  
  1. <?xml version="1.0" encoding="UTF-8"?>   
  2. <!DOCTYPE struts-config PUBLIC   
  3.           "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN"  
  4.           "http://struts.apache.org/dtds/struts-config_1_3.dtd">   
  5. <struts-config>   
  6.     <form-beans>   
  7.         <form-bean name="accountStockForm"  
  8.           type="com.unmi.form.AccountStockForm"/>   
  9.     </form-beans>   
  10.     <action-mappings>   
  11.         <action path="/showStock" name="accountStockForm"  
  12.          type="com.unmi.action.AccountStockAction" scope="request">   
  13.             <forward name="show" path="/show.jsp"/>   
  14.         </action>   
  15.     </action-mappings>   
  16. </struts-config>  
[java]   view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <!DOCTYPE struts-config PUBLIC  
  3.           "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN"  
  4.           "http://struts.apache.org/dtds/struts-config_1_3.dtd">  
  5. <struts-config>  
  6.     <form-beans>  
  7.         <form-bean name="accountStockForm"  
  8.           type="com.unmi.form.AccountStockForm"/>  
  9.     </form-beans>  
  10.     <action-mappings>  
  11.         <action path="/showStock" name="accountStockForm"  
  12.          type="com.unmi.action.AccountStockAction" scope="request">  
  13.             <forward name="show" path="/show.jsp"/>  
  14.         </action>  
  15.     </action-mappings>  
  16. </struts-config>  


二: 輸入頁面 input.jsp, 注意表單域命名  
Java代碼  
  1. <html:form action="/showStock">   
  2.       <h3>記錄持有的股票<br></h3>   
  3.       <fieldset>s<legend>基本信息</legend>   
  4.       <table width="100%" border=0><tr>   
  5.           <td>賬戶名:<html:text property="account.name"/></td>   
  6.           <td>資金賬號:<html:text property="account.number"/></td>   
  7.       </tr></table>   
  8.       </fieldset>   
  9.       <br>   
  10.       <fieldset><legend>持有股票</legend>   
  11.       <table width=100% border=0 id="stockTable">   
  12.       <tr>   
  13.           <td><input type="checkbox" onclick="checkAll(this)"></td>   
  14.           <td>股票代碼</td>   
  15.           <td>股票名稱</td>   
  16.           <td>成本價</td>   
  17.           <td>股票數量</td>   
  18.       </tr>   
  19.       <tr>   
  20.           <td><input type="checkbox" name="check"></td>   
  21.           <td><input name="stocks[0].code" size="15"></td>   
  22.           <td><input name="stocks[0].name" size="15"></td>   
  23.           <td><input name="stocks[0].price" size="15"></td>   
  24.           <td><input name="stocks[0].quantity" size="15"></td>   
  25.       </tr>   
  26.       </table>   
  27.   </html:form>  
[java]   view plain copy
  1. <html:form action="/showStock">  
  2.       <h3>記錄持有的股票<br></h3>  
  3.       <fieldset>s<legend>基本信息</legend>  
  4.       <table width="100%" border=0><tr>  
  5.           <td>賬戶名:<html:text property="account.name"/></td>  
  6.           <td>資金賬號:<html:text property="account.number"/></td>  
  7.       </tr></table>  
  8.       </fieldset>  
  9.       <br>  
  10.       <fieldset><legend>持有股票</legend>  
  11.       <table width=100% border=0 id="stockTable">  
  12.       <tr>  
  13.           <td><input type="checkbox" onclick="checkAll(this)"></td>  
  14.           <td>股票代碼</td>  
  15.           <td>股票名稱</td>  
  16.           <td>成本價</td>  
  17.           <td>股票數量</td>  
  18.       </tr>  
  19.       <tr>  
  20.           <td><input type="checkbox" name="check"></td>  
  21.           <td><input name="stocks[0].code" size="15"></td>  
  22.           <td><input name="stocks[0].name" size="15"></td>  
  23.           <td><input name="stocks[0].price" size="15"></td>  
  24.           <td><input name="stocks[0].quantity" size="15"></td>  
  25.       </tr>  
  26.       </table>  
  27.   </html:form>  


例如輸入框名 account.name 提交後能設置到 accountStockForm 的account的name屬性  
輸入框名爲 stocks[0].code 提交後會設置到 accountStockForm 的 List stocks的第一個元素的code屬性.以此類推  
在提交表單前要重排行層的索引,從 0 起, 不然到後右的 Form 會一些空數據.  


三: AccountStockForm 的關鍵代碼  

Java代碼  
  1. private Account account = new Account();   
  2.   private List stocks = new AutoArrayList(Stock.class);   
  3.      
  4.   public void setStocks(List stocks)   
  5.   {   
  6.       this.stocks.clear();   
  7.       this.stocks.addAll(stocks);   
  8.   }  
[java]   view plain copy
  1. private Account account = new Account();  
  2.   private List stocks = new AutoArrayList(Stock.class);  
  3.     
  4.   public void setStocks(List stocks)  
  5.   {  
  6.       this.stocks.clear();  
  7.       this.stocks.addAll(stocks);  
  8.   }  



定義了兩個屬性,分別是一個bean(Account,接受基本信息)和一個List(stocks,接受股票行信息),注意這兩個屬性必須初始化,否則在表單提交後會出現空指針錯誤. setStocks方法是讓stocks屬性永遠保有持是一個 AutoArrayList 實例. 這樣在表單提交後設置值是總能調用 AutoArrayList 的 get(int index) 方法.



四: 自定義的 AutoArrayList  

Java代碼  
  1. public class AutoArrayList extends ArrayList {   
  2.        
  3.     private Class itemClass;   
  4.        
  5.     public AutoArrayList(Class itemClass) {   
  6.         this.itemClass = itemClass;   
  7.     }   
  8.        
  9.     public Object get(int index) {   
  10.         try {   
  11.             while (index >= size()) {   
  12.                 add(itemClass.newInstance());   
  13.             }   
  14.         } catch (Exception e) {   
  15.             e.printStackTrace();   
  16.         }   
  17.         return super.get(index);   
  18.     }   
  19. }  
[java]   view plain copy
  1. public class AutoArrayList extends ArrayList {  
  2.       
  3.     private Class itemClass;  
  4.       
  5.     public AutoArrayList(Class itemClass) {  
  6.         this.itemClass = itemClass;  
  7.     }  
  8.       
  9.     public Object get(int index) {  
  10.         try {  
  11.             while (index >= size()) {  
  12.                 add(itemClass.newInstance());  
  13.             }  
  14.         } catch (Exception e) {  
  15.             e.printStackTrace();  
  16.         }  
  17.         return super.get(index);  
  18.     }  
  19. }  



理解爲何要繼承一個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 中能夠打印出提交的數據  


  
Java代碼  
  1. AccountStockForm asForm = (AccountStockForm)form;   
  2.        
  3.     Account account = asForm.getAccount();   
  4.     System.out.println("Account Name:"+account.getName()+   
  5.             " Number:"+account.getNumber());   
  6.        
  7.     List stocks = asForm.getStocks();   
  8.     for (int i=0; i<stocks.size() ;i++)   
  9.     {   
  10.         Stock stock = (Stock)stocks.get(i);   
  11.         System.out.println("Stock["+i+"]Code:"+stock.getCode()+   
  12.                 " Name:"+stock.getName()+   
  13.                 " Price:"+stock.getPrice()+   
  14.                 " Quantity:"+stock.getQuantity());   
  15.     }   
  16.        
  17.     return mapping.findForward("show");  
[java]   view plain copy
  1. AccountStockForm asForm = (AccountStockForm)form;  
  2.       
  3.     Account account = asForm.getAccount();  
  4.     System.out.println("Account Name:"+account.getName()+  
  5.             " Number:"+account.getNumber());  
  6.       
  7.     List stocks = asForm.getStocks();  
  8.     for (int i=0; i<stocks.size() ;i++)  
  9.     {  
  10.         Stock stock = (Stock)stocks.get(i);  
  11.         System.out.println("Stock["+i+"]Code:"+stock.getCode()+  
  12.                 " Name:"+stock.getName()+  
  13.                 " Price:"+stock.getPrice()+  
  14.                 " Quantity:"+stock.getQuantity());  
  15.     }  
  16.       
  17.     return mapping.findForward("show");  



在Action中就能直接取用提交來的數據了,不須要 getParameterValues()了.  

六: 最後一步, 對於這樣的 ActionForm 咱們應該如何顯示出來呢,咱們用了 nested 標籤 (show.jsp)  


Java代碼  
  1. <html:form action="/showStock">   
  2.     <h3>修改持有的股票<br></h3>   
  3.     <fieldset><legend>基本信息</legend>   
  4.     <table width="100%" border=0><tr>   
  5.     <nested:nest property="account">   
  6.         <td>賬戶名:<nested:text property="name" readonly="true"/></td>   
  7.         <td>資金賬號:<nested:text property="number" readonly="true"/></td>   
  8.     </nested:nest>   
  9.     </tr></table>   
  10.     </fieldset>   
  11.     <br>   
  12.     <fieldset><legend>持有股票</legend>   
  13.     <table width=100% border=0  id="stockTable">   
  14.     <tr>   
  15.         <td><input type="checkbox" onclick="checkAll(this)"></td>   
  16.         <td>股票代碼</td>   
  17.         <td>股票名稱</td>   
  18.         <td>成本價</td>   
  19.         <td>股票數量</td>   
  20.     </tr>   
  21.     <nested:iterate id="stock" property="stocks">   
  22.     <tr>   
  23.         <td><input type="checkbox" name="check"></td>   
  24.         <td><nested:text property="code" size="15"/></td>   
  25.         <td><nested:text property="name" size="15"/></td>   
  26.         <td><nested:text property="price" size="15"/></td>   
  27.         <td><nested:text property="quantity" size="15"/></td>   
  28.     </tr>   
  29.     </nested:iterate>   
  30.     </table>   
  31. </html:form>  
[java]   view plain copy
  1. <html:form action="/showStock">  
  2.     <h3>修改持有的股票<br></h3>  
  3.     <fieldset><legend>基本信息</legend>  
  4.     <table width="100%" border=0><tr>  
  5.     <nested:nest property="account">  
  6.         <td>賬戶名:<nested:text property="name" readonly="true"/></td>  
  7.         <td>資金賬號:<nested:text property="number" readonly="true"/></td>  
  8.     </nested:nest>  
  9.     </tr></table>  
  10.     </fieldset>  
  11.     <br>  
  12.     <fieldset><legend>持有股票</legend>  
  13.     <table width=100% border=0  id="stockTable">  
  14.     <tr>  
  15.         <td><input type="checkbox" onclick="checkAll(this)"></td>  
  16.         <td>股票代碼</td>  
  17.         <td>股票名稱</td>  
  18.         <td>成本價</td>  
  19.         <td>股票數量</td>  
  20.     </tr>  
  21.     <nested:iterate id="stock" property="stocks">  
  22.     <tr>  
  23.         <td><input type="checkbox" name="check"></td>  
  24.         <td><nested:text property="code" size="15"/></td>  
  25.         <td><nested:text property="name" size="15"/></td>  
  26.         <td><nested:text property="price" size="15"/></td>  
  27.         <td><nested:text property="quantity" size="15"/></td>  
  28.     </tr>  
  29.     </nested:iterate>  
  30.     </table>  
  31. </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 的運行時具體類型不變
相關文章
相關標籤/搜索