随笔-34  评论-1965  文章-0  trackbacks-0

在我早前的文章《转换器(Converter)——Struts 2.0中的魔术师》(以下简称为《转》)中,提及在Struts 1.x中实现批量封装对象,并不是一件容易的事,这需要一些技巧。昨天,有一位同事又和我讨论起这个问题,所以鉴于此场景(scenario)较为普遍,我决定写一篇有关的文章。

应用场景

本文使用《转》中的最后一个例子作为应用场景,即是批量发布产品信息。页面输出如下图所示:

图1 发布产品
图1 发布产品

图2 查看产品
图2 查看产品

具体实现

首先创建代表产品的类tipsAndTricks.Product,代码如下:

package tipsAndTricks;

import java.sql.Date;

public class Product {
   
private String name;
   
private double price;
   
private Date dateOfProduction;
   
   
public Date getDateOfProduction() {
       
return dateOfProduction;
   }

   
   
public void setDateOfProduction(Date dateOfProduction) {
       
this .dateOfProduction = dateOfProduction;
   }

   
   
public String getName() {
       
return name;
   }

   
   
public void setName(String name) {
       
this .name = name;
   }

   
   
public double getPrice() {
       
return price;
   }

   
   
public void setPrice( double price) {
       
this .price = price;
   }
   
}

与《转》例中的Product不同的是,本例子中的dateOfProduction属性使用了java.sql.Date,而不是java.util.Date。这是因为Struts 1.x不支持请求参数到java.util.Date的转换,归根到底是由于org.apache.commons.beanutils.ConvertUtilsBean.convert()不支持关于java.util.Date的转换。另外,值得注意的是common-beanutils是通过java.sql.Date.valueOf()方法工作的,所以在页面输入的字符串的格式必须为“yyyy-MM-dd”。

实现上述功能大概有三种方法,下面我会分别对这三种方法进行详细的讲述。

方法一、动态表单(Dynamic Actoin Form)+ 数组

首先,让我们来看一下Struts的配置文件WEB-INF/struts-config.xml,内容如下:

<? xml version="1.0" encoding="UTF-8" ?>
<! DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "http://struts.apache.org/dtds/struts-config_1_2.dtd" >

< struts-config >
   
< data-sources />
   
< form-beans >
       
< form-bean name ="dynaProductsForm"
            type
="org.apache.struts.action.DynaActionForm" >
           
< form-property name ="products"
                type
="tipsAndTricks.Product[]" size ="3" />
       
</ form-bean >

   
</ form-beans >

   
< global-exceptions />
   
< global-forwards />
   
< action-mappings >
       
< action attribute ="dynaProductsForm" input ="/addProducts.jsp"
            name
="dynaProductsForm" path ="/batchWrappingWithArray"
            scope
="request" type ="tipsAndTricks.BatchWrappingWithArrayAction"
            validate
="false" >
           
< forward name ="success" path ="/viewProducts.jsp" />
       
</ action >

   
</ action-mappings >

   
< message-resources parameter ="tipsAndTricks.ApplicationResources" />
</ struts-config >

我想这些配置应该用不着怎么解释了,有Struts 1.x验证的朋友对此都不会陌生。因此,接下来创建/addProducts.jsp文件,代码如下:

<% @ page language = " java " pageEncoding = " utf-8 " %>

<% @ taglib uri = " http://struts.apache.org/tags-html " prefix = " html " %>
<% @ taglib uri = " http://java.sun.com/jsp/jstl/core " prefix = " c " %>

<! DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" >
< html >
< head >
< title > Add Products </ title >
</ head >
< body >
< html:form action ="/batchWrappingWithArray" method ="post" >
   
< table border ="0" >
       
< tr style ="background-color:powderblue; font-weight:bold;" >
           
< td > Product Name </ td >
           
< td > Price </ td >
           
< td > Date of production </ td >
       
</ tr >
       
< c:forEach var ="products" items ="${dynaProductsForm.map.products}" >
           
< tr >
               
< td >< html:text indexed ="true" name ="products" property ="name" /></ td >
               
< td >< html:text indexed ="true" name ="products" property ="price" /></ td >
               
< td >< html:text indexed ="true" name ="products" property ="dateOfProduction" /></ td >
           
</ tr >
       
</ c:forEach >
       
< tr >
           
< td colspan ="3" >< html:submit /></ td >
       
</ tr >
   
</ table >
</ html:form >
</ body >
</ html >

例中,我使用了JSTL 1.1,如果大家还没有尝试过使用JSP 2.0的JSTL和EL,建议大家去看看相关文章。上面的<c:forEach />的作用是到dynaProductsForm的map属性中取出products数组,并对其进行遍历,再依靠<html:text />标志将products的元素的属性以输入框的形式输出。<html:text />标志的属性indexed="true"则表示在输出HTML时,将<input>的命名为类似products[0].name的名字。

然后,再创建/viewProducts.jsp页面,内容如下:

<% @ page language = " java " pageEncoding = " utf-8 " %>

<% @ taglib uri = " http://java.sun.com/jsp/jstl/core " prefix = " c " %>

<! DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" >
< html >
< head >
< title > View Products </ title >
</ head >
< body >
< table border ="0" >
   
< tr style ="background-color:powderblue; font-weight:bold;" >
       
< td > Product Name </ td >
       
< td > Price </ td >
       
< td > Date of production </ td >
   
</ tr >
   
< c:forEach var ="product" items ="${products}" >
       
< tr >
           
< td > ${product.name} </ td >
           
< td > ${product.price} </ td >
           
< td > ${product.dateOfProduction} </ td >
       
</ tr >
   
</ c:forEach >
</ table >
</ body >
</ html >

我想这份也不多作说明。不过大家可以通过上述代码看出使用JSTL + EL的确比Struts 1.x的logic + bean要方便和简洁。不仅如此,EL还支持一定的运算符和函数操作。

最后是建立Action文件tipsAndTricks.BatchWrappingWithArrayAction,代码如下:

package tipsAndTricks;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.DynaActionForm;

public class BatchWrappingWithArrayAction extends Action {
   
public ActionForward execute(ActionMapping mapping, ActionForm form,
                 HttpServletRequest request, HttpServletResponse response)
{
           DynaActionForm dynaProductsForm
= (DynaActionForm) form;
           request.setAttribute(
" products " , dynaProductsForm.get( " products " ));
       
return mapping.findForward( " success " );
   }

}

此Action将动态表单传过来的products数组放到request中,转到/viewProducts.jsp。发布运行应用程序,在浏览器的地址栏中输入:http://localhost:8080/Struts1_Batch/addProducts.jsp。效果请参考如图1、图2。

在/addProducts.jsp的“Date of production”必须以(yyyy-MM-dd)的形式正确填写,且不能为空。

方法二、表单(Actoin Form)+ 列表(List)

方法一虽然简单,但是有一个明显的缺点——数组的长度已经固定,故我们不能在运行时通过程序设置对象数量。下面将要介绍的方法可以很好地解决这个问题。

首先,我们要创建类tipsAndTricks.AutoInitArrayList,代码如下:

package tipsAndTricks;

import java.util.ArrayList;

public class AutoInitArrayList < T > extends ArrayList < T > {
   
private static final long serialVersionUID = 1L
   
   
private Class < T > t = null ;
   
   
public AutoInitArrayList(Class < T > t) {
       
this .t = t;
   }

   
   @Override
   
public T get( int index) {
       
try {
           
while (index >= size()) {
               add(t.newInstance());
           }

       }
catch (Exception e) {
           e.printStackTrace();
       }

       
return super .get(index);
   }
   
}

AutoInitArrayList继承ArrayList并重载get()方法,作用就是在Struts 1.x框架调用这个方法时,如果index超出列表大小,则会实例化新项放到列表中,避免出现(IndexOutOfBoundsException)异常。

接着,让我们看Struts的配置,内容如下:

<? xml version="1.0" encoding="UTF-8" ?>
<! DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "http://struts.apache.org/dtds/struts-config_1_2.dtd" >

< struts-config >
   
< data-sources />
   
< form-beans >
       
< form-bean name ="dynaProductsForm"
            type
="org.apache.struts.action.DynaActionForm" >
           
< form-property name ="products"
                type
="tipsAndTricks.Product[]" size ="3" />
       
</ form-bean >
       
< form-bean name ="normalProductsForm"
            type
="tipsAndTricks.NormalProductsForm" />

   
</ form-beans >

   
< global-exceptions />
   
< global-forwards />
   
< action-mappings >
       
< action attribute ="dynaProductsForm" input ="/addProducts.jsp"
            name
="dynaProductsForm" path ="/batchWrappingWithArray"
            scope
="request" type ="tipsAndTricks.BatchWrappingWithArrayAction"
            validate
="false" >
           
< forward name ="success" path ="/viewProducts.jsp" />
       
</ action >
       
< action attribute ="normalProductsForm" input ="/addProducts.jsp"
            name
="normalProductsForm" path ="/batchWrappingNormal" scope ="request"
            type
="tipsAndTricks.BatchWrappingNormalAction" validate ="false" >
           
< forward name ="success" path ="/viewProducts.jsp" />
       
</ action >

   
</ action-mappings >

   
< message-resources parameter ="tipsAndTricks.ApplicationResources" />
</ struts-config >

然后,创建表单类tipsAndTricks.NormalProductsForm,代码如下:

package tipsAndTricks;

import java.util.List;

import org.apache.struts.action.ActionForm;

public class NormalProductsForm extends ActionForm {
   
private List products = new AutoInitArrayList < Product > (Product. class );

   
public List getProducts() {
       
return products;
   }


   
public void setProducts(List products) {
       
this .products = products;
   }
   
}

接下来是Action类tipsAndTricks.BatchWrappingNormalAction,代码如下:

/*
* Generated by MyEclipse Struts
* Template path: templates/java/JavaClass.vtl
*/

package tipsAndTricks;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

public class BatchWrappingNormalAction extends Action {
   
public ActionForward execute(ActionMapping mapping, ActionForm form,
           HttpServletRequest request, HttpServletResponse response)
{
       NormalProductsForm normalProductsForm
= (NormalProductsForm) form;
       request.setAttribute(
" products " , normalProductsForm.getProducts());
       
return mapping.findForward( " success " );
   }

}

基本上与方法一的Action一样。下面,再看看新的输入文件/addProducts2.jsp,内容如下:

<% @ page language = " java " pageEncoding = " utf-8 " %>

<% @ taglib uri = " http://struts.apache.org/tags-html " prefix = " html " %>
<% @ taglib uri = " http://java.sun.com/jsp/jstl/core " prefix = " c " %>

<! DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" >
< html >
< head >
< title > Add Products </ title >
</ head >
< body >
< html:form action ="/batchWrappingNormal" method ="post" >
   
< table border ="0" >
       
< tr style ="background-color:powderblue; font-weight:bold;" >
           
< td > Product Name </ td >
           
< td > Price </ td >
           
< td > Date of production </ td >
       
</ tr >
       
< c:forEach begin ="0" end ="2" var ="i" >
           
< tr >
               
< td >< input name ="products[${i}].name" /></ td >                
               
< td >< input name ="products[${i}].price" /></ td >
               
< td >< input name ="products[${i}].dateOfProduction" /></ td >
           
</ tr >
       
</ c:forEach >
       
< tr >
           
< td colspan ="3" >< html:submit /></ td >
       
</ tr >
   
</ table >
</ html:form >
</ body >
</ html >

/addProducts2.jsp主要作用组装<input>的元素名称,Struts 1.x对名称格式类似“xxx[9].xx”的请求,会进行封装。发布运行应用程序,在浏览器的地址栏中输入:http://localhost:8080/Struts1_Batch/addProducts2.jsp。效果请参考如图1、图2。

总结

两种方法各有优缺点,选择原则是如果不需要动态设置元素个数,则使用方法一,否则请使用方法二。

posted on 2006-12-08 19:51 Max 阅读(7796) 评论(40)  编辑  收藏 所属分类: 方法与技巧(Tips & tricks)

评论:
# re: Struts 1.x中批量封装对象 2006-12-08 22:12 | 龙卷风
不错,以前我都是直接在action端用request接受html过来的数组,然后循环操作

你这样做感觉更上路子一点  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2006-12-09 10:28 | 张先生
太精彩了!我还是老问题,一直没解决!在weblogic下部署struts2,页面跳转找不着目标而出错!您有weblogic下的例子吗?谢谢!  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2006-12-09 19:03 | kuan
见解一下两种方法中的提交时form是怎么包装(形成)的好吗  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2006-12-09 19:08 | kuan
这段好像有问题巴?
public class AutoInitArrayList < T > extends ArrayList < T > {
private static final long serialVersionUID = 1L ;

private Class < T > t = null ;

public AutoInitArrayList(Class < T > t) {
this .t = t;
}

@Override
public T get( int index) {
try {
while (index >= size()) {
add(t.newInstance());
}
} catch (Exception e) {
e.printStackTrace();
}
return super .get(index);
}
}   回复  更多评论
  
# re: Struts 1.x中批量封装对象 2006-12-10 22:04 | Max
@kuan
在我的环境中是没有问题的。  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2006-12-13 09:08 | 忧郁的龙卷风
批量对象个数不确定的时候就是用方法2做的 我在项目中已经用过这种方法了.
但是就是验证的时候有些问题..  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2006-12-27 10:33 | allen[匿名]
你好,我做了一个actionForm中的list设置到表单,提交后取不到list中的值,郁闷死了,能够把第二种方法的源码全部发给我吗,xian_Lun@sina.com
谢谢  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2006-12-28 10:12 | Max
@allen[匿名]
源码已经发送到你的邮箱。  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2006-12-28 18:35 | kuan
也发份给我看看
feinali@gmail.com  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2006-12-30 14:32 | Max
@kuan
Gmail好难上啊,还好源码已经发送到你的邮箱。  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2007-01-30 14:46 | Peter
我也用过类似方法。后来发现不用自己实现,在common-collection包中的LazyList就是做这个的。  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2007-03-08 07:19 | 对象
能够把第二种方法的源码全部发给我吗
我的邮箱是348539181@qq.com  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2007-03-16 15:22 | kyuy
如果name属性值是从数据库中读到,将后两项添好后,在提交又怎么样取呢!  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2007-03-16 15:23 | kyuy
如果愿意请发邮箱到zhjyiqing@163.com  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2007-03-23 10:47 | suixincha
现在在研究批量封装,希望能参考你的代码,suixincha3388@163.com,谢谢!  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2007-04-27 16:26 | lvq
批量处理数据不是集合就是数组
很正常  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2007-05-22 15:51 | linyelong
调试失败,麻烦发送源代码一份:yelonglin@yahoo.com.cn. thanks  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2007-05-25 09:04 | lin
郁闷了一天,第二种方法需要这样写,否则解析不了:addProducts2.jsp
<c:forEach begin ="0" end ="1" var ="i" >
<tr>
<td><input name =products[<c:out value="${i}"/>].name /></td>
<td><input name ="products[<c:out value="${i}"/>].price" /></td>
<td><input name ="products[<c:out value="${i}"/>].dateOfProduction" /></td>
</tr>
</c:forEach >
  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2007-05-25 09:46 | Max
@linyelong
源码以发到你的邮箱。
@lin
这应该是JSP 1.2与JSP 2.0的区别。  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2007-06-04 12:39 | 阿泰
能够把第二种方法的源码全部发给我吗
我的邮箱是348539181@qq.com   回复  更多评论
  
# re: Struts 1.x中批量封装对象 2007-06-04 23:36 | Max
@阿泰
已发送  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2007-06-07 09:27 | 求助的人
能把这两个例子的源码发给我吗?感谢-ing
yinj_good@163.com  回复  更多评论
  
# re: Struts 1.x中批量封装对象[未登录] 2007-09-19 14:15 | 冷血
请问这种方式,如果对输入进行后台验证呢?
如果不能验证这种方式有何意义?  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2007-12-12 15:38 | cq
第二种方法用到了JDK1.5的泛型,有1.4的解决方案吗?
有的话可以发给我吗?
cheng888qi@163.com  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2007-12-19 14:23 | 你好.
可以给个联系方式吗?QQ或者是MSN  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2008-04-22 09:25 | sanit
批量封装对象的确。但lz有没有。在提交表单的时候,数据校验的时候是不是也增加了相应的难度呢?
可能lz当时只是为了说明批量封装对象的功能强大之处,而并没有考虑到数据校验等等真实存在的一些可能发生的问题呢。
还希望lz能够更好的解决方案。这样就可以让初学者避免走更多的弯路了。  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2008-05-03 06:45 | zw
能给我也发一份吗?关于struts2的一些东东,全一点的例子,plugin开发的例子?
zw7534313@163.com  回复  更多评论
  
# re: Struts 1.x中批量封装对象[未登录] 2008-05-27 16:34 | haha
wusuo_007@163.com谢谢偶要!  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2008-07-27 23:11 | silence1214
你们都这样用啊,好麻烦
我一直用LazyValidatorForm 不管个数多少都可以啊
也不用写这么麻烦 也不用写actionForm  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2008-08-09 09:51 | zrw
请问为什么我得不到用户输入的数据啊~
我的邮箱zhaorongwei520@163.com  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2008-09-16 22:15 | 冯强
大侠
为什么我用这种方法都取不到值呢
normalProductsForm.getProducts()总是为空?
能否把答案发给我fq_1219@163.com
拜托了  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2009-02-26 18:25 | e_ville
好,不错,我使用楼主的方法实现了。
关键是表单域的命名要像这样子(迭代的是另一个Decorator列表,以在列表中显示数据):

<input type="text" name="listData[<%= index %>].listOrder" value="<bean:write name='objDecorator' property='decoratorObject.listOrder'/>" >

这样子在提交表单的时候就会将数据封装到Form中listData列表中的对应元素中去了。listData类型为楼主上面修改过的 AutoInitArrayList  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2009-06-08 00:31 | 〃差不多先生
大侠,能把2种方法的源码发过来吗?谢啦。!
caofengwei55@vip.sina.com  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2009-07-16 21:13 | ..
呵呵,在您这里真的能学到需要的东东.  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2009-10-14 18:17 | john.jiao
第二个方法只能插入小于等于六条数据!!  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2010-01-22 09:22 |
我的封装后为0,不走get(index)方法呢?咋回事啊  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2010-01-22 09:55 |
谁有以上两种的源码,发给我一份好不?yan3194283@163.com谢谢!  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2010-08-31 08:54 | leson
有第二种代码的,共享一下啊。谢谢
i.leson@163.com  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2012-08-15 14:50 | li yong
我的封装意思也是为0 谁有源码发给下lys221221@163.com  回复  更多评论
  
# re: Struts 1.x中批量封装对象 2013-08-08 17:21 | frice
谁有 第二种方法的原码,共享一下,万分感谢  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航: