在这一节我们来编写用于操作数据库的模型类。由于本例子是Web程序,因此,建议在连接数据库时使用数据库连接池。在<Tomcat安装目录>"conf"Catalina"localhost目录中打开samples.xml文件(如果没有该文件,则建立一个samples.xml文件),在<Context>节点中加入如下的内容:
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"/>
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文件,代码所示:
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());
}
}
}
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文件,代码如下:
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类还不够,还需要在struts-config.xml中的<struts-config>节点中添加如下的内容:
<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。代码如下:
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>节点中加入如下的内容。
<forward name="save" path="/mystruts/save.jsp" />
</action>
从上面的代码可以看出,那个用于显示保存状态信息的JSP页面叫save.jsp。在<samples工程目录>"mystruts目录中建立一个save.jsp文件,代码如下:
${requestScope.info}
http://localhost:8080/samples/mystruts/newProduct.jsp
在文本框中输入相应的信息后,点“保存”按钮,如果输入的数据是合法的,就会将数据保存在t_products中,否则会显示出错的原因。 searchProduct动作的实现和saveProduct差不多,也会为三步:实现动作类(SearchProductAction)、在struts-config.xml中添加配置信息和实现用于显示查询结果的JSP文件。下面的代码分别显示了这三步所要编写的代码。
SearchProductAction.java
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");
}
}
<forward name="search" path="/mystruts/search.jsp" />
</action>
<%@ 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>
http://localhost:8080/samples/%20mystruts/searchProduct.jsp
在“产品名称”文本框中输入产品名称的一部分,程序就会查询出所有包含输入的产品名称的产品信息,并将结果显示出来。
EncodingFilter.java
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-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后,重新输入一条带中文的产品信息,看看是否可以将中文保存在数据库中?