Struts1.x系列教程(1):用MyEclipse开发第一个Struts程序(二)

五、通过模型类操作数据库

    在这一节我们来编写用于操作数据库的模型类。由于本例子是 Web 程序,因此,建议在连接数据库时使用数据库连接池。在 <Tomcat 安装目录 >"conf"Catalina"localhost 目录中打开 samples.xml 文件(如果没有该文件,则建立一个 samples.xml 文件),在 <Context> 节点中加入如下的内容:
配置连接池(用于连接数据库 struts
   < Resource  name ="jdbc/struts"  auth ="Container"
                type
="javax.sql.DataSource"
                driverClassName
="com.mysql.jdbc.Driver"
                url
="jdbc:mysql://localhost:3306/struts?characterEncoding=GBK"
                username
="root"
                password
="1234"               
                maxActive
="200"
                maxIdle
="50"
                maxWait
="3000" />

本例中提供了两个可以操作数据库的模型类:Product SearchProduct 。其中Product 用于验证由客户端提交的产品信息,并向t_products 表中写入这些信息。而SearchProduct 类用于对t_products 表的product_name 字段进行模糊查询,并返回查询到的产品信息(包括产品ID 、产品名称和产品价格)。
   由于 Product SearchProduct 都需要使用数据库连接池来连接数据库,因此,可以将连接数据库的工作提出来作为一个父类 (Struts ) 提供,代码如下:

package  util;
import  java.sql.Connection;
public   class  Struts
{
    protected  javax.naming.Context ctx  =   new  javax.naming.InitialContext();
    protected  javax.sql.DataSource ds;
   
protected  Connection conn;
   
public  Struts()  throws  Exception
    {
        ds 
=  (javax.sql.DataSource) ctx.lookup( " java:/comp/env/jdbc/struts " );
        conn 
=  ds.getConnection();   //  从数据库连接池获得一个Connection
    }
}

    <samples 工程目录 >"src 目录中建立一个 Product.java 文件,代码所示:

   package  mystruts.model;
  
  
import  java.sql. * ;
  
import  mystruts.actionform. * ;
  
  
public   class  Product  extends  util.Struts
  {
      
private  ProductForm form;
  
      
public  Product(ProductForm form)  throws  Exception
      {
          
super ();
          
this .form  =  form;
          validate();
      }
      
//  验证客户端提交的数据
       public   void  validate()  throws  Exception
      {
          
if  (form.getProductID().trim().equals( "" ))
              
throw   new  Exception( " 产品ID不能为空! " );
          
if (form.getProductID().length()  >   4 )
              
throw   new  Exception( " 产品ID最长为4位! " );
          
if  (form.getProductName().trim().equals( "" ))
              
throw   new  Exception( " 产品名称不能为空 " );
          
if  (Float.compare(form.getPrice(),  0 <=   0 )
              
throw   new  Exception( " 产品价格必须大于0 " );
      }
      
//  将客户端提交的产品信息保存到t_products中
       public   void  save()  throws  Exception
      {
          
try
          {
              String productID 
=  form.getProductID();
              String productName 
=  form.getProductName();
              
float  price  =  form.getPrice();
              String sql 
=   " INSERT INTO t_products VALUES(' "   +  productID  +   " ', "
                      
+   " ' "   +  productName  +   " ', "   +  String.valueOf(price)  +   " ) " ;
              PreparedStatement pstmt 
=  conn.prepareStatement(sql);
              pstmt.executeUpdate();   
//  执行INSERT语句
              pstmt.close();
              conn.close();
          }
          
catch  (Exception e)
          {
              
throw   new  Exception(e.getMessage());
          }
      }
  }

    Product 类中使用了一个ProductForm 类,这个类是一个ActionForm 类,它的功能是保存客户端提交的数据。关于这个类将在下面详细介绍。Product 类通过构造方法的form 参数将客户端提交的数据传入Product 类的对象实例中,并在构造方法中验证这些数据,如果发现数据不合法,就会抛出一个异常。当客户端提交的数据合法后,成功建立了一个Product 类的对象实例,然后可以通过简单地调用save 方法将数据保存到t_products 表中。
    Product 类似,在 <samples 工程目录 >"src 目录中建立一个 SearchProduct.java 文件,代码如下:

   package  mystruts.model;
  
  
import  java.sql. * ;
  
import  java.util. * ;
  
import  mystruts.actionform. * ;
  
  
public   class  SearchProduct  extends  util.Struts
  {
      
private  ProductForm form;
  
      
public  SearchProduct(ProductForm form)  throws  Exception
      {
          
super ();
          
this .form  =  form;
      }    
      
//  查询产品信息,并通过List返回查询结果
       public  List < String[] >  search()  throws  Exception
      {
          List
< String[] >  result  =   new  LinkedList < String[] > ();
          String sql 
=   " SELECT * FROM t_products WHERE product_name like '% "  
+  form.getProductName()  +   " %' " ;
          PreparedStatement pstmt 
=   conn.prepareStatement(sql);
          ResultSet rs 
=  pstmt.executeQuery();   //  开始执行SELECT语句
           while (rs.next())
          {
              String[] row 
=   new  String[ 3 ];
              row[
0 =  rs.getString( 1 );
              row[
1 =  rs.getString( 2 );
              row[
2 =  rs.getString( 3 );
              result.add(row);
          }
          rs.close();
          conn.close();
          
return  result;
      }
  }

       SearchProduct 类也使用了 ProductForm 类,但在 SearchProduct 中并不会验证 ProductForm 对象实例中的数据,而只是将 ProductForm 对象作为传递查询请求信息(实际上只需要产品名称)的工具而已。

六、实现控制器

   
在这一节要实现的控制器是基于 Struts Web 程序的核心部分之一:控制器实质上也是普通的 Java 类,但这个 Java 类一般要从 org.apache.struts.action.Action 类继承。控制器的主要功能是接受并处理从JSP页面提交的数据、通过模型(Model)和数据库交互以及forward到相应的页面(可以是任何页面,如html、JSP和Servlet等)。在实现控制器之前,需要先实现一个ActionForm类, 这个类的作用是保存JSP页面提交的数据。在<samples工程目录>"src目录中建立一个ProductForm.java文件,代码如下:

   package  mystruts.actionform;
  
  
import  org.apache.struts.action. * ;
  
  
public   class  ProductForm  extends  ActionForm
  {
      
private  String productID;   //  产品ID
       private  String productName;  //  产品名称
       private   float  price;   //  产品价格
       public  String getProductID()
      {
          
return  productID;
      }
      
public   void  setProductID(String productID)
      {
          
this .productID  =  productID;
      }
      
public  String getProductName()
      {
          
return  productName;
      }
      
public   void  setProductName(String productName)
      {
          
this .productName  =  productName;
      }
      
public   float  getPrice()
      {
          
return  price;
      }
      
public   void  setPrice( float  price)
      {
          
this .price  =  price;
      }
  }

    从上面的代码可以看出,ActionForm 类一般从 org.apache.struts.action.ActionForm 类继承,而且在类中需要按着需要保存的数据表字段添加属性。如产品ID的属性是productName。在MyEclipse中可以只定义三个private变量,然后使用MyEclipse的 Source > Generate Getters and Setters... 】功能自动产生getter setter 方法。但在给这些属性取名时要注意,private 变量的名子和数据表的字段名没有直接的关系,但必须和JSP 页面中的<html> 标签的property 属性值一致,如<html:text property="productName" /> 表示输入产品名称的文本框,其中property 属性的值就是ProductForm 类中的productName 变量。如果不一致,将会抛出异常。其他和ProductForm 类的属性对应的<html> 标签可以查看上面的代码。
   
光有ActionForm 类还不够,还需要在struts-config.xml 中的<struts-config> 节点中添加如下的内容:
< form-beans >
     < form-bean  name ="saveProductForm"  type =" mystruts.actionform.ProductForm"   />
     < form-bean  name ="searchProductForm"  type ="mystruts.actionform.ProductForm"   />
</ form-beans >

    上面的代码所配置的两个 ActionForm 实际上指的是同一个 ProductForm 类,但这个 ProductForm 类在后面要讲的两个动作里都要使用,为了更容易理解,为这个 ProductForm 起了两个不同的别名( saveProductForm searchProductForm )。
   
下面来实现 saveProduct 动作的代码。 Struts Action 类必须一般从 org.apache.struts.action.Action 类继承。一般在 Struts Action 类需要覆盖 Action 类的 execute 方法。这个方法有每次客户端访问 Struts Action 时调用。我们可以在方法中处理客户端提交的数据,访问数据库等工作。这个方法返回一个 ActionForward 类型的值,表明在执行完 execute 后,要 forward 到的页面。描述 saveProduct 动作的类叫 SaveProductAction 。代码如下:

   package  mystruts.action;
  
  
import  javax.servlet.http. * ;
  
import  org.apache.struts.action. * ;
  
import  mystruts.actionform. * ;
  
import  mystruts.model. * ;
  
  
public   class  SaveProductAction  extends  Action
  {
      
//  在客户端访问saveProduct动作时执行该方法
       public  ActionForward execute(ActionMapping mapping, ActionForm form,
              HttpServletRequest request, HttpServletResponse response)
      {
          ProductForm saveProductForm 
=  (ProductForm) form;
          
try
          {
              Product product 
=   new  Product(saveProductForm);
              product.save();  
//  保存产品信息
              request.setAttribute( " info " " 保存成功! " );   
          }
          
catch  (Exception e)
          {
              request.setAttribute(
" info " , e.getMessage());
          }
          
return  mapping.findForward( " save " );
      }
  }

    SaveProductAction 类中使用了模型类 Product 验证并保存产品信息。并将操作结果信息保存在 request 的属性中, key 为“ info ”。在 execute 的最后,使用了 ActionMapping 类的 findForward 方法在 struts-config.xml 中寻找一个叫“ save ”的 forward 。这个 forward 是一个 JSP 页,用于显示是否将产品信息保存成功的信息。为了可以在 struts-config.xml 中查找这个 forward ,需要在 struts-config.xml <action-mappings> 节点中加入如下的内容。

< action  name ="saveProductForm"  path ="/saveProduct" scope ="request"  type =" mystruts.action.SaveProductAction" >
    < forward  name ="save"  path ="/mystruts/save.jsp"   />
</ action >

    从上面的代码可以看出,那个用于显示保存状态信息的 JSP 页面叫 save.jsp 。在 <samples 工程目录 >"mystruts 目录中建立一个 save.jsp 文件,代码如下:

   < %@ page  pageEncoding ="GBK" % >
  ${requestScope.info}

    IE 中输入如下的URL

    http://localhost:8080/samples/mystruts/newProduct.jsp

   
在文本框中输入相应的信息后,点“保存”按钮,如果输入的数据是合法的,就会将数据保存在t_products 中,否则会显示出错的原因。    searchProduct动作的实现和saveProduct差不多,也会为三步:实现动作类(SearchProductAction)、在struts-config.xml中添加配置信息和实现用于显示查询结果的JSP文件。下面的代码分别显示了这三步所要编写的代码。

SearchProductAction.java
   package  mystruts.action;
  
  
import  javax.servlet.http. * ;
  
import  org.apache.struts.action. * ;
  
import  mystruts.actionform. * ;
  
import  mystruts.model. * ;
  
import  java.util. * ;
  
  
public   class  SearchProductAction  extends  Action
  {
  
      
public  ActionForward execute(ActionMapping mapping, ActionForm form,
              HttpServletRequest request, HttpServletResponse response)
      {
          ProductForm searchProductForm 
=  (ProductForm) form;
          
try
          {
              SearchProduct searchProduct 
=   new  SearchProduct(searchProductForm);
              List
< String[] >  result  =  searchProduct.search();   //  查询产品信息
               if  (result.size()  >   0 )   //  有符合条件的产品信息
              {
                  request.setAttribute(
" result " , result);
                  request.setAttribute(
" info " " 记录数: "   +  String.valueOf(result.size()));
              }
              
else    //  没有查到任何产品信息
                  request.setAttribute( " info " " 没有符合要求的记录! " );
          }
          
catch  (Exception e)
          {
              request.setAttribute(
" info " , e.getMessage());
          }
          
return  mapping.findForward( " search " );
      }
  }
struts-config.xml 中配置 searchProduct 动作
< action  name ="searchProductForm"  path ="/searchProduct"  scope ="request" type ="mystruts.action.SearchProductAction" >
    < forward  name ="search"  path ="/mystruts/search.jsp"   />
</ action >

search.jsp
   <% @ page pageEncoding = " GBK " %>
  
<% @ taglib uri = " http://struts.apache.org/tags-logic "  prefix = " logic " %>
  
<% @ taglib uri = " http://java.sun.com/jsp/jstl/core "  prefix = " c " %>
  
< html >
      
< body >
<% --  从request的result中取出查询结果  -- %>
          
< c:set  var ="result"  value ="${requestScope.result}"   />
          
< table  width ="100%" >
              
< tr  align ="center" >
                  
< td >
                      ${requestScope.info}
                  
</ td >
              
</ tr >
              
< tr  align ="center" >
                  
< td >
                      
< logic:present  name ="result" >
                          
< table  border ="1" >
                              
< tr  align ="center" >
                                  
< td >  产品ID  </ td >
                                  
< td >  产品名称  </ td >
                                  
< td >  价格  </ td >
                              
</ tr >
                              
< logic:iterate  id ="row"  name ="result" >
                                  
< tr >   < td >  ${row[0]}  </ td >
                                      
< td >  ${row[1]}  </ td >
                                      
< td >  ${row[2]}  </ td >
                                  
</ tr >
                              
</ logic:iterate >
                          
</ table >
                      
</ logic:present >
                  
</ td >
              
</ tr >
          
</ table >
      
</ body >
  
</ html >

    IE 中输入如下的URL

    http://localhost:8080/samples/%20mystruts/searchProduct.jsp

   
在“产品名称”文本框中输入产品名称的一部分,程序就会查询出所有包含输入的产品名称的产品信息,并将结果显示出来。

七、解决 ActionForm 的乱码问题
 
    到现在为止,程序的功能部分已经全部实现完了。但还存在一个问题。当我们在产品名称中输入中文时,虽然将客户端提交的数据成功保存到数据库中,但是在t_products 表中的product_name 字段显示的都是乱码。产生这个问题的原因只有一个,就是客户端提交的数据的编码格式和数据库的编码格式不一致造成的。当然,解决这个问题的方法有很多,但笔者认为最容易的就是使用过滤器。所谓过滤器,就是在客户端提交数据后,在交由服务端处理之前所执行的一段服务端代码(一般为Java 代码)。一个过滤器是一个实现javax.servlet.Filter 接口的类。在本例中要使用的过滤器类叫EncodingFilter ,实现代码如下:

EncodingFilter.java

   package  filter;
  
import  java.io.IOException;
  
import  javax.servlet. * ;
  
public   class  EncodingFilter  implements  Filter
  {
      
public   void  destroy() {  }
      
public   void  doFilter(ServletRequest request, ServletResponse response,
              FilterChain chain) 
throws  IOException, ServletException
      {
          request.setCharacterEncoding(
" GBK " );   //  将客户端提交的数据设为GBK编码格式
          //  继续处理客户端提交的数据,如果不写这条语句,Servlet引擎将不会处理所过滤的页面
          chain.doFilter(request, response);      
      }
      
public   void  init(FilterConfig filterConfig)  throws  ServletException {   }  
  }
    Filter 接口的doFilter 方法是过滤器的核心方法。其中FilterChain 类的doFilter 方法允许继续处理客户端提交的数据。我们还可以使用这个方法来临时关闭Web 站点的某个或全部的页面(根据过滤器的设置而定)。由于本书的数据库使用的是GBK 编码格式,因此,需要使用ServletRequest setCharacterEncoding 方法将客户端提交的数据也设为GBK 编码格式。
    除了实现过滤器类,我们还需要在web.xml 中的<web-app> 节点加入如下的配置信息才能使过滤器生效:
web.xml 中配置过滤器

< filter >
    < filter-name > EncodingFilter </ filter-name >
    < filter-class >
        filter.EncodingFilter
    </ filter-class >
</ filter >
< filter-mapping >
    < filter-name > EncodingFilter </ filter-name >
    < url-pattern > /* </ url-pattern >
</ filter-mapping >

    在重新启动 Tomcat 后,重新输入一条带中文的产品信息,看看是否可以将中文保存在数据库中?

你可能感兴趣的:(MyEclipse,Struts1.X,休闲,第一个,Struts程序)