首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Struts 1.x 多行數據提交到 ActionForm List 屬性完整指南

     編輯:Java 相關知識

問題背景

Web 應用常見主從表場景(訂單/明細、帳戶/持股)。Struts 1.x 需正確映射表單多行數據到 ActionForm 的 List 屬性,避免手工拼裝。

核心原理:索引式屬性命名

Struts 使用 Commons BeanUtils 根據表單域 name 屬性自動填充:

  • account.name → form.getAccount().setName()
  • stocks[0].code → form.getStocks().get(0).setCode()

完整實現步驟

1. 領域模型

// 帳戶信息
public class Account {
    private String name;
    private String number;
    // getter/setter 省略
}

// 持股明細
public class Stock {
    private String code;
    private String name;
    private BigDecimal price;
    private Integer quantity;
    // getter/setter 省略
}

2. 關鍵:AutoArrayList 避免索引越界


import java.util.ArrayList;

public class AutoArrayList extends ArrayList {
    private final Class<?> elementType;

    public AutoArrayList(Class<?> elementType) {
        this.elementType = elementType;
    }

    @Override
    public Object get(int index) {
        // 自動擴容並實例化元素
        while (index >= size()) {
            try {
                add(elementType.getDeclaredConstructor().newInstance());
            } catch (Exception e) {
                throw new RuntimeException("無法實例化 " + elementType, e);
            }
        }
        return super.get(index);
    }
}

3. ActionForm 設計


import org.apache.struts.action.ActionForm;
import java.util.List;

public class AccountStockForm extends ActionForm {
    private Account account = new Account();                    // 必須初始化
    private List<Stock> stocks = new AutoArrayList(Stock.class); // 關鍵:使用自動擴容列表

    public Account getAccount() { return account; }
    public void setAccount(Account account) { this.account = account; }

    public List<Stock> getStocks() { return stocks; }
    
    // 關鍵:setter 保持運行時類型為 AutoArrayList
    public void setStocks(List<Stock> stocks) {
        this.stocks.clear();
        this.stocks.addAll(stocks);
    }
}

4. JSP 輸入頁面 (input.jsp)

<html:form action="/showStock">
    <fieldset>
        <legend>基本信息</legend>
        帳戶名: <html:text property="account.name" />
        資金帳號: <html:text property="account.number" />
    </fieldset>

    <fieldset>
        <legend>持有股票</legend>
        <table id="stockTable">
            <tr>
                <th>選擇</th><th>股票代碼</th><th>股票名稱</th><th>成本價</th><th>數量</th>
            </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>
            <!-- JS 動態添加行時,name 必須為 stocks[1].code, stocks[2].code ... -->
        </table>
    </fieldset>
    
    <input type="hidden" name="fileCount" id="fileCount" value="1" />
    <input type="button" value="新增行" onclick="addRow()" />
    <html:submit value="保存數據" />
</html:form>

5. JavaScript 動態行處理 (關鍵:重排索引)


function addRow() {
    var count = parseInt($('#fileCount').val());
    var newRow = '<tr>' +
        '<td><input type="checkbox" name="check" /></td>' +
        '<td><input name="stocks[' + count + '].code" size="15" /></td>' +
        '<td><input name="stocks[' + count + '].name" size="15" /></td>' +
        '<td><input name="stocks[' + count + '].price" size="15" /></td>' +
        '<td><input name="stocks[' + count + '].quantity" size="15" /></td>' +
        '</tr>';
    $('#stockTable').append(newRow);
    $('#fileCount').val(count + 1);
}

// 刪除行後必須重排所有索引!
function reindexRows() {
    $('#stockTable tr:gt(0)').each(function(i) {
        $(this).find('input[name^="stocks["]').each(function() {
            this.name = this.name.replace(/stocks\[\d+\]/, 'stocks[' + i + ']');
        });
    });
    $('#fileCount').val($('#stockTable tr:gt(0)').length);
}

6. Action 處理


public class AccountStockAction extends Action {
    @Override
    public ActionForward execute(ActionMapping mapping, ActionForm form,
                                 HttpServletRequest request, HttpServletResponse response) {
        AccountStockForm asForm = (AccountStockForm) form;
        
        // 直接使用自動填充的對象圖
        Account account = asForm.getAccount();
        System.out.println("帳戶: " + account.getName() + ", " + account.getNumber());
        
        List<Stock> stocks = asForm.getStocks();
        for (int i = 0; i < stocks.size(); i++) {
            Stock s = stocks.get(i);
            // 過濾空行 (用戶可能留空)
            if (s.getCode() != null && !s.getCode().trim().isEmpty()) {
                System.out.printf("股票[%d]: %s %s %.2f %d%n", 
                    i, s.getCode(), s.getName(), s.getPrice(), s.getQuantity());
            }
        }
        
        return mapping.findForward("show");
    }
}

7. 顯示頁面 (show.jsp) 使用 Nested 標簽


<html:form action="/showStock">
    <nested:nest property="account">
        帳戶名: <nested:text property="name" readonly="true" />
        資金帳號: <nested:text property="number" readonly="true" />
    </nested:nest>

    <table>
        <nested:iterate property="stocks" id="stock">
            <tr>
                <td><nested:text property="code" size="15" readonly="true" /></td>
                <td><nested:text property="name" size="15" readonly="true" /></td>
                <td><nested:text property="price" size="15" readonly="true" /></td>
                <td><nested:text property="quantity" size="15" readonly="true" /></td>
            </tr>
        </nested:iterate>
    </table>
</html:form>

nested:iterate 會自動生成 stocks[0].code 等索引名,與輸入頁面完美對應。

8. struts-config.xml 配置


<form-bean name="accountStockForm" type="com.unmi.form.AccountStockForm" />

<action path="/showStock" 
        name="accountStockForm" 
        type="com.unmi.action.AccountStockAction" 
        scope="request">
    <forward name="show" path="/show.jsp" />
</action>

關鍵點總結

  1. 屬性初始化:account 和 stocks 必須在聲明時初始化,否則 NPE
  2. AutoArrayList:重寫 get(index) 實現自動擴容與實例化,解決索引越界
  3. 索引連續性:提交前 JS 必須重排索引從 0 開始連續,否則產生空元素
  4. Setter 保護類型:setStocks() 使用 clear()+addAll() 保持運行時為 AutoArrayList
  5. Nested 標簽:顯示頁面使用 nested:iterate 自動生成正確索引名

常見坑點

現象原因解決
List 為 null未初始化聲明時 new AutoArrayList()
IndexOutOfBoundsException普通 ArrayList使用 AutoArrayList
提交數據亂序/丟失索引不連續JS reindexRows() 重排
Setter 後類型變為 ArrayList直接賦值clear()+addAll() 保持類型
空行被提交用戶未填寫Action 中過濾 code 為空的元素
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved