在WEB开发过程中,提交多行数据到struts1.3的FormBean的List中,发现报两个错误:
1、[Servlet 错误]-[BeanUtils.populate]:java.lang.NullPointerException
at java.lang.Throwable.<init>(Throwable.java:59)
at java.lang.Throwable.<init>(Throwable.java:73)
at java.lang.NullPointerException.<init>(NullPointerException.java:61)
at org.apache.commons.beanutils.PropertyUtils.getIndexedProperty(PropertyUtils.java:515)
at org.apache.commons.beanutils.PropertyUtils.getIndexedProperty(PropertyUtils.java:428)
at org.apache.commons.beanutils.PropertyUtils.getNestedProperty(PropertyUtils.java)
at org.apache.commons.beanutils.PropertyUtils.getProperty(PropertyUtils.java:801)
at org.apache.commons.beanutils.BeanUtils.setProperty(BeanUtils.java:881)
at org.apache.commons.beanutils.BeanUtils.populate(BeanUtils.java:808)
at org.apache.struts.util.RequestUtils.populate(RequestUtils.java:1252)
at org.apache.struts.action.RequestProcessor.processPopulate(RequestProcessor.java:821)
at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:254)
at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1482)
at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:525)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:760)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
at com.ibm.ws.webcontainer.servlet.StrictServletInstance.doService(StrictServletInstance.java:110)
at com.ibm.ws.webcontainer.servlet.StrictLifecycleServlet._service(StrictLifecycleServlet.java:174)
at com.ibm.ws.webcontainer.servlet.ServicingServletState.service(StrictLifecycleServlet.java:333)
at com.ibm.ws.webcontainer.servlet.StrictLifecycleServlet.service(StrictLifecycleServlet.java:116)
at com.ibm.ws.webcontainer.servlet.ServletInstance.service(ServletInstance.java:283)
at com.ibm.ws.webcontainer.servlet.ValidServletReferenceState.dispatch(ValidServletReferenceState.java:42)
at com.ibm.ws.webcontainer.servlet.ServletInstanceReference.dispatch(ServletInstanceReference.java:40)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:76)
at com.cosco.csis.plug.CheckSessionFilter.doFilter(CheckSessionFilter.java:99)
at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:132)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:71)
at com.cosco.csis.plug.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:49)
at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:132)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:71)
at com.ibm.ws.webcontainer.webapp.WebAppRequestDispatcher.handleWebAppDispatch(WebAppRequestDispatcher.java)
at com.ibm.ws.webcontainer.webapp.WebAppRequestDispatcher.dispatch(WebAppRequestDispatcher.java)
at com.ibm.ws.webcontainer.webapp.WebAppRequestDispatcher.forward(WebAppRequestDispatcher.java)
at com.ibm.ws.webcontainer.srt.WebAppInvoker.doForward(WebAppInvoker.java:125)
at com.ibm.ws.webcontainer.srt.WebAppInvoker.handleInvocationHook(WebAppInvoker.java:286)
at com.ibm.ws.webcontainer.cache.invocation.CachedInvocation.handleInvocation(CachedInvocation.java:71)
at com.ibm.ws.webcontainer.cache.invocation.CacheableInvocationContext.invoke(CacheableInvocationContext.java:116)
at com.ibm.ws.webcontainer.srp.ServletRequestProcessor.dispatchByURI(ServletRequestProcessor.java:186)
at com.ibm.ws.webcontainer.oselistener.OSEListenerDispatcher.service(OSEListener.java:334)
at com.ibm.ws.webcontainer.http.HttpConnection.handleRequest(HttpConnection.java:56)
at com.ibm.ws.http.HttpConnection.readAndHandleRequest(HttpConnection.java:615)
at com.ibm.ws.http.HttpConnection.run(HttpConnection.java:449)
at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:912)
经过多方查找,发现报这个错误的原因在与list对象未初始化。
package com.test;
import java.util.List;
import org.apache.struts.action.ActionForm;
public class MyForm extends ActionForm {
private List items;//=new ArrayList();
public List getItems() {
return items;
}
public void setItems(List items) {
this.items = items;
}
}
用new ArrayList();语句初始化后,就没问题了。
2、[Servlet 错误]-[BeanUtils.populate]:java.lang.IndexOutOfBoundsException: Index: 4, Size: 0
at java.lang.Throwable.<init>(Throwable.java:59)
at java.lang.Throwable.<init>(Throwable.java:73)
at java.util.ArrayList.RangeCheck(ArrayList.java)
at java.util.ArrayList.get(ArrayList.java)
at org.apache.commons.beanutils.PropertyUtils.getIndexedProperty(PropertyUtils.java:521)
at org.apache.commons.beanutils.PropertyUtils.getIndexedProperty(PropertyUtils.java:428)
at org.apache.commons.beanutils.PropertyUtils.getNestedProperty(PropertyUtils.java:770)
at org.apache.commons.beanutils.PropertyUtils.getProperty(PropertyUtils.java:801)
at org.apache.commons.beanutils.BeanUtils.setProperty(BeanUtils.java:881)
at org.apache.commons.beanutils.BeanUtils.populate(BeanUtils.java:808)
at org.apache.struts.util.RequestUtils.populate(RequestUtils.java:1252)
at org.apache.struts.action.RequestProcessor.processPopulate(RequestProcessor.java:821)
at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:254)
at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1482)
at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:525)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:760)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
at com.ibm.ws.webcontainer.servlet.StrictServletInstance.doService(StrictServletInstance.java:110)
at com.ibm.ws.webcontainer.servlet.StrictLifecycleServlet._service(StrictLifecycleServlet.java:174)
at com.ibm.ws.webcontainer.servlet.ServicingServletState.service(StrictLifecycleServlet.java:333)
at com.ibm.ws.webcontainer.servlet.StrictLifecycleServlet.service(StrictLifecycleServlet.java:116)
at com.ibm.ws.webcontainer.servlet.ServletInstance.service(ServletInstance.java:283)
at com.ibm.ws.webcontainer.servlet.ValidServletReferenceState.dispatch(ValidServletReferenceState.java:42)
at com.ibm.ws.webcontainer.servlet.ServletInstanceReference.dispatch(ServletInstanceReference.java:40)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:76)
at com.cosco.csis.plug.CheckSessionFilter.doFilter(CheckSessionFilter.java:99)
at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:132)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:71)
at com.cosco.csis.plug.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:49)
at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:132)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:71)
at com.ibm.ws.webcontainer.webapp.WebAppRequestDispatcher.handleWebAppDispatch(WebAppRequestDispatcher.java:1010)
at com.ibm.ws.webcontainer.webapp.WebAppRequestDispatcher.dispatch(WebAppRequestDispatcher.java:592)
at com.ibm.ws.webcontainer.webapp.WebAppRequestDispatcher.forward(WebAppRequestDispatcher.java:204)
at com.ibm.ws.webcontainer.srt.WebAppInvoker.doForward(WebAppInvoker.java:125)
at com.ibm.ws.webcontainer.srt.WebAppInvoker.handleInvocationHook(WebAppInvoker.java:286)
at com.ibm.ws.webcontainer.cache.invocation.CachedInvocation.handleInvocation(CachedInvocation.java:71)
at com.ibm.ws.webcontainer.cache.invocation.CacheableInvocationContext.invoke(CacheableInvocationContext.java:116)
at com.ibm.ws.webcontainer.srp.ServletRequestProcessor.dispatchByURI(ServletRequestProcessor.java:186)
at com.ibm.ws.webcontainer.oselistener.OSEListenerDispatcher.service(OSEListener.java:334)
at com.ibm.ws.webcontainer.http.HttpConnection.handleRequest(HttpConnection.java:56)
at com.ibm.ws.http.HttpConnection.readAndHandleRequest(HttpConnection.java:615)
at com.ibm.ws.http.HttpConnection.run(HttpConnection.java:439)
at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:912)
第二个错误,报inex越界错误,开始不知道为什么,看了arang的一篇文章(文章见底部)以后,终于明白。
简单了解 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'))
最后一个情况说明了List的这种情况,原来是List中没有对象,size=0,而我要存的对象有5个(index=4);
我的解决方案如下:
package com.test; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionMapping; import com.test.Item; public class MyForm extends ActionForm { private List items = new ArrayList(); private int itemSize; public int getItemSize() { return itemSize; } public void setItemSize(int itemSize) { this.itemSize = itemSize; } public List getItems() { return items; } public void setItems(List items) { this.items = items; } @Override public void reset(ActionMapping mapping, HttpServletRequest request) { itemSize = getItemSizeFromRequest(request); // 初始化List if (itemSize > 0) { for (int i = 0; i < itemSize; i++) { items.add(new Item());// 此处的Item为你自己的业务对象 } } } private int getItemSizeFromRequest(HttpServletRequest request) { String size = request.getParameter("itemSize"); if (size != null) { return Integer.parseInt(size); } return 0; } }
问题得以解决。
在此要感谢arang对这个主题的知识的细致讲解,谢谢您!
======================================================================
原文标题:提交多行数据到Struts的ActionForm的List属性中
原文地址:http://arang.iteye.com/blog/420546
WEB 应用中一般都会处理主从表的信息, 或者称之为头层与行层的一对多的关系数据,如订单头/订单明细. 对于这种关系数据提交到后台的 Struts 的 ActionForm 的话, 这个 ActionForm 就要好好的设计一下, 不然会给自已带来许多额外的代码. 比如有的人的处理方法就是把页面提交到后台的毫无关系的散装数据非常吃力的拼凑一对多的关系对象出来.
下面举一个如今非常现实的关于股票的例子, 简单的应用场景是: 记录某个帐户所持有的股票信息,提交到后台,然后显示出来. 输入页面如下图
帐户信息包括帐户名和资金帐号;持有股票的每一行信息包括股票代码, 股票名称, 成本价, 股票数量. 股票行可以动态增删.
为了简化不必要的代码, 我们要实现的终及目标是: 在输入页面上点击 "保存数据" 按钮, 由 Struts 的 RequestProcessor.processPopulate() 方法把页面提交的基本信息组装到 AccountStockForm 的 account 的对应属性中,股票行信息对应生成一个 Stock 实例加到 AccountStockForm的 List 属性 stocks 中, 后续在 AccountStockAction 中直接处理account和stocks属性就非常简单了. AccountStockForm在这里只作为一个壳.
一: 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% border=0 id="stockTable">
- <tr>
- <td><input type="checkbox" onclick="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()+