开发一个网上商店系统

  作者:板桥banq

上页

8.4.3  创建Product相关类实现

第三步是创建数据增、删、改、查框架的具体实现,Product、ProductForm和ProductHandler。
Product主要代码如下:
public class Product implements Model {
  private String productId;
  private String catId;
  private String name;
  private byte[] image;
  private String description;
  private Collection items = new ArrayList();

  public String getProductId() {
    return productId;
  }
  public void setProductId(String productId) {
    this.productId = productId;
  }
  …
}
ProductForm基本与Product内容相似,代码省略。
ProductHandler是继承ModelHandler,暂时不编写具体实现内容。因为商品数据管理有点特殊,需要上传图片等,等创建好JSP页面后,再在两者之间调整。

8.4.4  Web配置

第四步是配置文件编写,在modelmapping.xml加入如下代码:
<modelmapping  formName = "productForm"
                    key="productId"
                    model="com.jdon.estore.model.Product"
                    handler = "com.jdon.estore.web.catalog.ProductHandler" />
在struts_config.xml中加入如下代码:
<form-bean name="productForm" type="com.jdon.estore.web.catalog.ProductForm" />

    <action attribute="productForm"
         type="com.jdon.strutsutil.ModelViewAction"
         validate="false" scope="request"
         path="/admin/productAction">
      <forward name="create" path="/admin/product.jsp" />
      <forward name="edit" path="/admin/product.jsp" />
</action>
<action name="productForm"
           type="com.jdon.strutsutil.ModelSaveAction"
           input="/admin/product.jsp" 
           scope="request"
           path="/admin/saveProductAction">
      <forward name="success" path="/admin/productOk.jsp" />
      <forward name="failure" path="/admin/productOk.jsp" />
</action>
对比发现,Product的配置和前面Category的配置相差不多,这就降低了系统的配置风险。

8.4.5  创建product.jsp

第五步是创建product.jsp页面,主要代码如下:
<html:errors/>
<html:form action="/admin/saveProductAction.do" method="POST"
          onsubmit="return checkform(this)">
<html:hidden property="action" /> <!--  关键一句,必须   -->
<html:hidden property="productId"/><!--  关键一句,必须   -->
商品类别:
<html:select property="catId" >
   <html:options name="productForm" property="values" labelProperty="labels"/>
</html:select>
<input type="button" value="新增/编辑 商品类别" onclick="MM_openBrWindow('<%=request.getContextPath()%>/admin/categoryListAction.do','width=400,height=250,scrollbars=yes')">
商品名称:<html:text property="name" size="20" />
商品说明:<html:textarea rows="4" cols="32" property="description"/>
商品图片:
   <img src="<%=request.getContextPath()%>/imageShowAction.do?Id=<bean:write name="productForm" property="productId"/>" name="ico" />
 <input type="button" value="上传图片"
onclick="MM_openBrWindow('uploadImg.jsp','width=400,height=250,scrollbars=yes,resizable=yes')">
<logic:equal name="productForm" property="action" value="create">
  <html:submit property="submit" value="新增"/>
</logic:equal>
<logic:equal name="productForm" property="action" value="edit">
  <html:submit property="submit" value="修改"/>
</logic:equal>
<html:reset value ="复位"/>
</html:form>

<!-- 在编辑情况下,陈列本商品所有的品种 开始-->
<logic:equal name="productForm" property="action" value="edit">
<form method="POST" action=""  name="listForm">
<html:hidden name="productForm" property="productId"/>
<tr>
    <td bgcolor="#ffffff">品种名称</td>
    <td bgcolor="#ffffff">单价</td>
    <td bgcolor="#ffffff">销售价</td>
    <td bgcolor="#ffffff">库存</td>
    <td bgcolor="#ffffff"> </td>
  </tr> <tr>
<logic:iterate id="item" name="productForm" property="items">
<tr>
    <td bgcolor="#ffffff"><bean:write name="item" property="name"/></td>
    <td bgcolor="#ffffff"><bean:write name="item" property="unitcost"/></td>
    <td bgcolor="#ffffff"><bean:write name="item" property="listprice"/></td>
    <td bgcolor="#ffffff"><bean:write name="item" property="qty"/></td>
    <td bgcolor="#ffffff">
    <input type="radio" name="itemId" value="<bean:write name="item" property="itemId" />">
    </td>
  </tr>
</logic:iterate>
<tr> <td bgcolor="#ffffff" colspan ="5" >
<input type="button" name="create" value="新增品种"
onclick="addAction('<%=request.getContextPath()%>/admin/itemAction.do')" >
<input type="button" name="edit" value="编辑品种"
onclick="editAction('<%=request.getContextPath()%>/admin/itemAction.do?action=edit', 'itemId')" >
</td></tr></table>
</form>
</logic:equal>
<!-- 在编辑情况下,这是本段总的条件 结束-->
</td></tr></table>
</body>
</html:html>
该product.jsp编辑功能页面示意图如8-9所示。
在商品数据新增、修改界面中,需要选择商品所属类别。商品类别是在CategoryForm中,而当前ActionForm是ProductForm,那么如何在product.jsp中通过下列语句实现商品类别列表?
<html:select property="catId" >
   <html:options name="productForm" property="values" labelProperty="labels"/>
</html:select>
这就需要在productHandler中加入相应的内容:
1
图8-9  商品编辑功能页面
public class ProductHandler implements ModelHandler {
  private final static String module = ProductHandler.class.getName();
  private final static ServiceServerFactory sf = ServiceServerFactory.
      getInstance();
  protected final static CacheFactory cacheFactory = CacheFactory.getInstance();
  //本方法是JSP页面产生前执行
  public ModelForm initForm(HttpServletRequest request) throws Exception{

    CatalogManagerLocal catalogManager = (CatalogManagerLocal) sf.getService(
        FrameworkServices.CatalogEJB, request);
    int AllCount = catalogManager.getCategoryAllCount();
    //获得所有类别ID
    PageIterator pageIterator = catalogManager.getCategories(0, AllCount);
    ProductForm form = new ProductForm();
    List labels = new ArrayList(pageIterator.getSize());
    List values = new ArrayList(pageIterator.getSize());
    labels.add("");
    values.add("");
    while (pageIterator.hasNext()) {
      String catId = (String) pageIterator.next();
      //首先从缓存中获取
      Category model = (Category) cacheFactory.getObect(catId);
      if (model == null) {
        model = catalogManager.getCategoryById(catId);
        if (model.CACHEABLE) {
          cacheFactory.putObect(catId, model);
        }
      }
      labels.add(model.getName());
      values.add(model.getCatId());
    }
    pageIterator.reset();
     //转换成数组,保存到productForm中
    form.setLabels((String[])labels.toArray(new String[0]));
    form.setValues((String[])values.toArray(new String[0]));
    return  form;
  }
 //查询一个Product实例 
public Model findModelByKey(String keyValue, HttpServletRequest request) throws Exception{
    ProductManagerLocal productManager = (ProductManagerLocal) sf.getService(
        FrameworkServices.ProductEJB, request);
    return productManager.getProductById(keyValue);
  }
  //实行最后的数据库保存等操作
  public void serviceAction(EventModel em, HttpServletRequest request) throws
      java.lang.Exception {
    ProductManagerLocal productManager = (ProductManagerLocal) sf.getService(
        FrameworkServices.ProductEJB, request);
      //从HttpSession中获得上传的图片,保存到数据库中
      byte[] data = (byte[]) UploadAction.getUploadData(request);
      if (data != null) {
        Product product = (Product) em.getModel();
        product.setImage(data);
      }
      try {
      switch (em.getActionType()) {
        case Event.CREATE:
          productManager.createProduct(em);
          break;
        case Event.EDIT:
          productManager.updateProduct(em);
          break;
        case Event.DELETE:
          productManager.deleteProduct(em);
          break;
      }
    } catch (Exception ex) {

 

      throw new Exception("System operation Error:" + ex);
    }
  }
}

 


在initForm方法中,是将所有类别的ID和类别名称集合转换成数组,保存到productForm中。这是符合Strut的html:options标签使用要求的。

8.4.6  商品图片上传功能

在productHandler中,有从HttpSession中取出上传的图片的功能。UploadAction是负责上传功能,接受上传的图片后,转换成byte数组保存在HttpSession中,代码如下:
public class UploadAction extends Action {
  public ActionForward execute(ActionMapping mapping,
                               ActionForm form,
                               HttpServletRequest request,
                               HttpServletResponse response) throws Exception {
    UploadForm theForm = (UploadForm) form;
    //retrieve the file representation
    FormFile file = theForm.getTheFile();
    String size = (file.getFileSize() + " bytes");
    try {
      //retrieve the file data
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      InputStream stream = file.getInputStream();
      byte[] buffer = new byte[8192];
      int bytesRead = 0;
      while ( (bytesRead = stream.read(buffer, 0, 8192)) != -1) {
        baos.write(buffer, 0, bytesRead);
      }
      byte[] data = baos.toByteArray();
      baos.close();
      //close the stream
      stream.close();
      //save to the session , then load it! 暂时保存到HttpSession中
      HttpSession session = request.getSession(true);
      session.setAttribute(UploadForm.module, data);
      //destroy the temporary file created
    } catch (Exception ex) {
      throw new Exception(ex);
    } finally {
      file.destroy();
    }
    //return a forward to display.jsp
    return mapping.findForward("display");
  }
  //供外部调用,获取上传的数据。productHandler调用的是该方法
  public  static byte[] getUploadData(HttpServletRequest request) {
    //load  the image in the session
    HttpSession session = request.getSession();
    byte[] data = null;
    if (session != null) {
      data = (byte[]) session.getAttribute(UploadForm.module);
      session.removeAttribute(UploadForm.module);
    }
    return data;
  }
}
UploadAction是接受客户上传的图片,然后暂时保存在客户HttpSession中,ProductHandler将HttpSession中的图片取出后,保存到数据库中,可见ProductHandler的serviceAction一段代码。
UploadForm是ActionForm的子类,UploadAction是从UploadForm中接受上传图片的,UploadForm代码如下:
public class UploadForm extends ActionForm {
  public final static String module = UploadForm.class.getName();
  public static final String ERROR_PROPERTY_MAX_LENGTH_EXCEEDED =
      "com.jdon.strutsutil.file.MaxLengthExceeded";
  private FormFile theFile;
  public FormFile getTheFile() {    return theFile;  }
  public void setTheFile(FormFile theFile) {    this.theFile = theFile;  }

  public ActionErrors validate(ActionMapping mapping,
                               HttpServletRequest request) {

    ActionErrors errors = null;
    //检查上传图片大小是否超过在struts_config.xml中规定的大小
    Boolean maxLengthExceeded = (Boolean)
        request.getAttribute(MultipartRequestHandler.
                             ATTRIBUTE_MAX_LENGTH_EXCEEDED);
    if ( (maxLengthExceeded != null) && (maxLengthExceeded.booleanValue())) {
      errors = new ActionErrors();
      errors.add(ERROR_PROPERTY_MAX_LENGTH_EXCEEDED,
                 new ActionError("maxLengthExceeded"));
    }else{
    //检查上传文件是否为图片文件   
    String fileName = theFile.getFileName();
    if ( (!fileName.toLowerCase().endsWith(".gif")) &&
          ! (fileName.toLowerCase().endsWith(".jpg")) &&
          ! (fileName.toLowerCase().endsWith(".png"))) {
        errors = new ActionErrors();
        errors.add("notImage", new ActionError("notImage"));
      }
    }
    return errors;
  }
}
在UploadForm中可以检查图片大小。在Struts 1.1中,图片大小是在struts_config.xml中设置的,相关设置如下:
<form-beans>
    <form-bean name="uploadForm" type="com.jdon.strutsutil.file.UploadForm" />
    …
</form-beans>
<action-mappings>
    <action name="uploadForm"
           type="com.jdon.strutsutil.file.UploadAction"
           input="/admin/uploadImg.jsp" scope="request"
           path="/admin/uploadAction">
      <forward name="display" path="/admin/uploadOk.jsp" />
    </action>
    <action type="com.jdon.estore.web.catalog.ImgShowAction"
       validate="false" path="/imageShowAction" />
    …
</action-mappings>
配置中/imageShowAction是专门用来显示图片的Action实现。当图片上传后,或者商品数据列表时,都需要显示图片。ImgShowAction先从缓存中获取图片数据byte数组,如果缓存没有,从数据库images中根据Id读取;如果没有输入参数Id,则从客户HttpSession中查询,是否是客户刚刚上传。

8.5  商品批量查询和多页显示

在本系统中,一个商品类别下可能有很多商品,这些商品可能需要多个页面才能完全显示。这涉及到一个批量查询、多页显示的框架设计。
该框架需要分别在EJB和Web两个层面实现。EJB实现需要使用DAO+JDBC模式,而Web主要是解决多页显示的问题。
通常的思路是:从数据库中将当前页面需要显示的数据对象查询后,组成数据Model集合,传送到Web层用于显示。
这种思路有一个缺点:将不能使用设置在Web层的Model缓存系统。Web层的Model缓存保存着大量之前调用过的数据Model。在批量规模查询时,不使用这些缓存将浪费很多性能,特别是这些数据Model被不同客户反复调用查询时。
相比而下,前面章节“Jive论坛系统”中介绍的Jive缓存设计是比较合理而且灵活的方案。它不是查询出当前页面完整的数据对象,而只是查询出当前页面的数据对象ID集合。这样做的优点有两个:
因为只是查询ID,相对提高查询速度,节约查询内存。
EJB层返回ID集合后,Web层可以根据ID,首先查询缓存系统的Model数据,如果没有,则再根据ID查询数据库。
因此,批量查询的设计方案如下:
获得整个数据表记录总数。
获取当前页面所有数据的ID集合,该部分也可实现缓存。
在ModelListAction中在iterator中遍历时,根据ID获取完整数据对象,先查询缓冲,如果没有,再根据ID读取数据库。完整数据对象包装成List,赋予到ListForm(ActionForm的一个子类实现),
使用PagerTag等标签库显示多页标识,标签库主要从ListForm中获取相关信息。

JSP页面显示,使用loigc:iterator 从ListForm对象中遍历数据对象。

 

 

下页