Model1:JSP+JavaBean 1.1 Model1模式简介 Model1设计模式是JSP+JavaBean的结合,在JSP技术发展的初始阶段,Model1模式被广泛的应用。该模式适用于小型的Web应用程序的开发。在该模式中,JavaBean通常用来处理数据,来完成某项具体的业务,例如连接数据库、查询数据等,JSP页面用来表现数据,并且负责接收用户的请求,然后调用相应的JavaBean完成业务逻辑,最后响应用户的请求并将处理结果返回给用户。 该模式具有一个比较清晰的程序结构,它实现了页面显示和业务逻辑的分离,但会因网站功能的扩展而使得程序变得难以维护。因为该模式下的程序流程控制是JSP页面中实现的,这使得JSP页面中嵌入了大量的Java代码,因而使得JSP页面变得更复杂,这对网站后期的维护和管理是非常困难的。所以在大型的网站开发中,这种设计方法会使得项目中的页面数量增多而且内容变得很复杂,是一种低效率、低质量的选择。在小型Web应用的开发中,选择这种模式,能够体现出它的灵活和简便的特点。 1.2 采用Model1模式实现显示论坛主题类别和帖子列表 例001 采用Modell模式实现显示论坛主题类别和帖子列表 下面所讲解的案例是一个简单的论坛,访问首页面后,程序先查询数据库获取论坛主题类别,然后将它们通过JSP页面进行显示,如图1所示。用户可以单击这些论坛主题超链接来查看该主题下的所有帖子,如图2所示。并且可单击这些帖子的标题来查看被选中帖子的详细内容,如图3所示。以上这些功能都是应用Model1模式实现的,其中应用的JavaBean有:对数据库进行操作的JavaBean、表示论坛主题信息的JavaBean、表示帖子信息的JavaBean。下面介绍以上功能的实现过程。
图1 显示论坛主题
图2 查看主题下的帖子
图3 查看帖子的详细内容 (1)创建本例中的3个数据表tb_type、tb_revert和tb_issue,下面分别介绍各数据表的结构。 主题类别表主要用来保存论坛主题信息,其结构如表1所示。 表1 tb_type主题类别表的结构
字段名称 | 类型 | 说明 | 是否主键 |
id | int (4) | 自动编号 | 是 |
type_master | varchar (50) | 主题版主 | 否 |
type_title | varchar (50) | 主题标题 | 否 |
type_num | smallint (2) | 主题代号 | 否 |
type_amount | int (4) | 帖子数 | 否 |
type_order | smallint (2) | 排列次序 | 否 |
字段名称 | 类型 | 说明 | 是否主键 |
id | int (4) | 自动编号 | 是 |
rev_title | varchar (50) | 回复帖的标题 | 否 |
rev_author | varchar (50) | 回复帖的作者 | 否 |
rev_time | datetime (8) | 回复帖的回复时间 | 否 |
rev_content | text (16) | 回复帖的内容 | 否 |
rev_card_type | smallint (2) | 回复帖所属主题类别代号 | 否 |
rev_card_id | int (4) | 回复帖所属的根帖ID值 | 否 |
字段名称 | 类型 | 说明 | 是否主键 |
id | int (4) | 自动编号 | 是 |
iss_title | varchar (50) | 发布帖的标题 | 否 |
iss_author | varchar (50) | 发布帖的作者 | 否 |
iss_time | datetime (8) | 发布帖的发布时间 | 否 |
iss_content | text (16) | 发布帖的内容 | 否 |
iss_card_type | smallint (2) | 发布帖所属主题类别代号 | 否 |
(2)创建对数据库进行操作的JavaBean,名称为“DB”。 该JavaBean用于连接、查询和更新数据库,本实例使用JDBC连接SQL Server 2000,所以首先要把msbase.jar、mssqlserver.jar和msutil.jar3个文件拷贝到应用的WEB-INF\lib目录下。 下面为DB类的关键代码,其中Read()和CreateOrUpdate()方法分别用来查询和更新数据库。
……//省略了获取数据库连接和创建Statement对象的代码 public ResultSet Read(String sql){ if(sql==null)sql=""; getStmed(); //获取Statement对象的方法 ResultSet rs=null; try { rs = stm.executeQuery(sql); } catch (SQLException e) { rs=null; e.printStackTrace(); System.out.println("查询数据库失败!"); } return rs; } public int CreateOrUpdate(String sql){ if(sql==null)sql=""; getStmed(); int i=0; try { i = stm.executeUpdate(sql); } catch (SQLException e) { i=0; e.printStackTrace(); System.out.println("插入或更新数据失败!"); } return i; }
(3)创建表示论坛主题信息的JavaBean,名称为“Single”。 该JavaBean主要用来封装数据。将从数据库中查询出的主题信息保存在该JavaBean中,方便在后面的程序中应用,减少了与数据库的交互操作,Single类的关键代码如下。package com.model.javabean; public class Single { private int id; private String master; //主题版主 private String title; //主题标题 private int amount; //帖子数 private int num; //主题代号 public int getId() { return id; } public void setId(int id) { this.id = id; } ……//省略了其他属性的setXXX()与getXXX()方法 }
(4)创建表示帖子信息的JavaBean,名称为“SubSingle”。 该JavaBean同样用来封装数据。将从数据库中查询出的帖子信息保存在该JavaBean中,方便在后面的程序中应用,SubSingle类的关键代码如下。<span style="font-family:Microsoft YaHei;font-size:14px;">package com.model.javabean; public class SubSingle { private int id; private int typenum; //帖子所属主题的代号 private String subtitle; //帖子标题 private String content; //帖子内容 private String issuer; //帖子发布人 private String time; //帖子发布时间 public int getId() { return id; } public void setId(int id) { this.id = id; } ……//省略了其他属性的setXXX()与getXXX()方法 </span><pre><span style="font-family:Microsoft YaHei;font-size:14px;">} </span><span style="font-family:Microsoft YaHei;font-size:14px;"><strong>(5)实现显示论坛主题功能。</strong></span>
论坛主题应在用户访问本应用时首先显示给用户,如图1所示。应用Model1模式进行编程可以在index.jsp页面中编写代码首先来获取数据库中的论坛主题信息记录,然后在另一个JSP页面中进行显示。 首先创建index.jsp页面,在该页面中调用对数据库进行操作的JavaBean来查询所有的论坛主题,然后将查询出的每条记录对应着一个表示论坛主题信息的JavaBean的实例进行存储,并将该实例存储在集合中,最后将该集合存储到session范围内,并通过<jsp:forward>标签将请求转发到showTypeList.jsp页面进行显示。 index.jsp页面的具体代码如下: <%@page contentType="text/html;charset=gb2312<%@ page contentType="text/html;charset=gb2312"%> <%@ page import="java.sql.ResultSet,com.model.javabean.Single,java.util.ArrayList"%> <jsp:useBean id="mydb" class="com.model.javabean.DB"/> <% String sql="select * from tb_type order by type_order"; ResultSet rs=mydb.Read(sql); ArrayList typelist=new ArrayList(); if(rs.next()){ rs.previous(); while(rs.next()){ Single idtype=new Single(); idtype.setId(rs.getInt("id")); idtype.setMaster(rs.getString("type_master")); idtype.setTitle(rs.getString("type_title")); idtype.setAmount(rs.getInt("type_amount")); idtype.setNum(rs.getInt("type_num")); typelist.add(idtype); } mydb.closed(); session.setAttribute("typelist",typelist); } else session.setAttribute("typelist",null); %> <pre><jsp:forward page="/showTypeList.jsp"/> <span style="font-family:Microsoft YaHei;font-size:14px;"> 然后创建showTypeList.jsp页面,在该页面中将获取存储在session范围内的集合,然后通过JSP的Scriptlet脚本程序输出存储在集合中的论坛主题信息。 </span><span style="font-family:Microsoft YaHei;font-size:14px;">showTypeList.jsp页面的关键代码如下:</span>
<span style="font-family:Microsoft YaHei;font-size:14px;"><%@ page import="java.util.ArrayList,com.model.javabean.Single" %> <% ArrayList typelist=(ArrayList)session.getAttribute("typelist");%> <table> <tr><td colspan="3">====== 简易论坛 ======</td></tr> <tr bgcolor="#76B0C8"> <td align="center"><b><font color="white">斑竹</font></b></td> <td align="center"><b><font color="white">类别</font></b></td> <td align="center"><b><font color="white">贴子数</font></b></td> </tr> <% if(typelist==null){ out.println("<tr><td align='center' colspan='3'>没有类别显示!</td></tr>"); } else{ for(int i=0;i<(typelist.size());i++){ Single single=(Single)(typelist.get(i)); out.println("<tr>"); out.println("<td align='center'>"+single.getMaster()+"</td>"); out.println("<td align='center' style='cursor:hand' onMouseOver='this.bgColor=\"lightyellow\"' onMouseOut='this.bgColor=\"white\"'> <a href='doshowSub.jsp?num="+single.getNum()+"'>" +single.getTitle()+"</a></td>"); out.println("<td align='center'>"+single.getAmount()+"</td>"); out.println("</tr>"); } } %> </span><pre><span style="font-family:Microsoft YaHei;font-size:14px;"></table> </span><span style="font-family:Microsoft YaHei;font-size:14px;"><strong>(6)实现显示主题下存在的帖子的功能。</strong></span>
在showTypeList.jsp页面中单击某个主题后,请求最终会被转发到showSubList.jsp页面显示该主题下存在的帖子,如图2所示。当单击某个主题超链接后,请求首先被转发到doshowSub.jsp页面,在该页面中编写代码首先来获取数据库中属于该主题下的所有帖子信息记录,然后再转到另一个JSP页面中进行显示。 首先创建doshowSub.jsp页面,在该页面中首先查询tb_issue表获取发布的属于该主题下的所有帖子记录,然后将查询出的每条记录对应着一个表示帖子信息的JavaBean的实例进行存储,并将该实例存储在集合中,最后将该集合存储到session范围内,并通过<jsp:forward>标签将请求转发到showSubList.jsp页面进行显示。 doshowSub.jsp页面的具体代码如下:<%@ page contentType="text/html;charset=gb2312"%> <%@ page import="java.sql.ResultSet" %> <%@ page import="java.util.ArrayList" %> <%@ page import="com.model.javabean.SubSingle" %> <jsp:useBean id="mydb" class="com.model.javabean.DB"/> <% String num=request.getParameter("num"); String sql="select * from tb_issue where iss_card_type="+num+ " order by iss_time desc"; ResultSet rs=mydb.Read(sql); ArrayList sublist=new ArrayList(); if(rs.next()){ rs.previous(); while(rs.next()){ SubSingle sub=new SubSingle(); sub.setId(rs.getInt("id")); sub.setSubtitle(rs.getString("iss_title")); sub.setIssuer(rs.getString("iss_author")); sub.setTime(rs.getTimestamp("iss_time").toLocaleString()); sub.setContent(rs.getString("iss_content")); sub.setTypenum(rs.getInt("iss_card_type")); sublist.add(sub); } mydb.closed(); session.setAttribute("sublist",sublist); } else session.setAttribute("sublist",null); %> <jsp:forward page="/showSubList.jsp"/>
然后创建showSubList.jsp页面,在该页面中将获取存储在session范围内的集合,然后通过JSP的Scriptlet脚本程序输出存储在集合中的帖子信息。showSubList.jsp页面的关键代码如下:
<span style="font-family:Microsoft YaHei;font-size:14px;"><%@ page import="java.util.ArrayList" %> <%@ page import="com.model.javabean.SubSingle" %> <%@ page import="com.model.javabean.Single" %> <% String curr_type=""; ArrayList sublist=(ArrayList)session.getAttribute("sublist"); ArrayList typelist=(ArrayList)session.getAttribute("typelist"); String strnum=request.getParameter("num"); if(strnum==null||strnum.equals(""))strnum="-1"; int num=-1; try{ num=Integer.parseInt(strnum); }catch(Exception e){ num=-1; } for(int i=0;i<typelist.size();i++){ if(num==((Single)(typelist.get(i))).getNum()){ curr_type=((Single)(typelist.get(i))).getTitle(); break; } } %> <table> <tr><td align="center" colspan="3">====== 查看帖子列表 ======</td></tr> <tr><td colspan="3">当前论坛主题 -> <%=curr_type%></td></tr> <tr bgcolor="#76B0C8"> <td align="center"><b><font color="white">主题</font></b></td> <td align="center"><b><font color="white">发帖人</font></b></td> <td align="center"><b><font color="white">发帖时间</font></b></td> </tr> <% if(sublist==null){ out.println("<tr><td colspan='3'>没有帖子显示!</td></tr>"); } else{ for(int i=0;i<(sublist.size());i++){ SubSingle subsingle=(SubSingle)(sublist.get(i)); out.println("<tr>"); out.println("<td align='center' style='cursor:hand' onMouseOver='this.bgColor=\"lightyellow\"' onMouseOut='this.bgColor=\"white\"'> <a href='showSingle.jsp?id="+subsingle.getId()+ "&num="+num+"'>"+subsingle.getSubtitle()+"</a></td>"); out.println("<td align='center'>"+subsingle.getIssuer()+"</td>"); out.println("<td align='center'>"+subsingle.getTime()+"</td>"); out.println("</tr>"); } } %> <tr> <td align="right" colspan="3"> <a href="issue.jsp?num=<%=num%>">[发布]</a> <a href="index.jsp">[返回]</a> </td> </tr>
(7)实现显示帖子的详细内容的功能。
在showSubList.jsp页面中单击某个帖子标题后,请求会被转发到showSingle.jsp页面显示该帖子的详细内容,如图3所示。在showSubList.jsp页面中首先从请求中获取要查看帖子的ID值和所属的论坛类别参数,然后根据这两个参数查找已保存在session范围内存储了当前论坛类别下的所有帖子的集合中是否存在要查看的帖子,若存在则输出该帖子的信息,并且查询tb_revert表是否存在该帖的回复帖,若存在则输出这些回复帖。 创建showSubList.jsp页面,先来查看该页面中显示被选中帖子信息的代码。<%@ page import="com.model.javabean.SubSingle" %> <%@ page import="java.util.ArrayList" %> <%@ page import="java.sql.ResultSet" %> <table> <tr><td align="center" colspan="2">====== 查看帖子内容 ======</td></tr> <% int num=-1; int id=-1; String strid=request.getParameter("id"); String strnum=request.getParameter("num"); try{ id=Integer.parseInt(strid); num=Integer.parseInt(strnum); }catch(Exception e){ id=-1; num=-1; } ArrayList sublist=(ArrayList)session.getAttribute("sublist"); if(sublist==null||sublist.size()==0){ out.println("<tr><td colspan='2'>查看帖子内容出错!</td></tr>"); } else{ int i=0; for(;i<sublist.size();i++){ SubSingle subsingle=(SubSingle)(sublist.get(i)); if(id==subsingle.getId()&&num==subsingle.getTypenum()){ %> <tr height="25"> <td align="right" width="20%" bgcolor="#76B0C8">主题:</td> <td><%=subsingle.getSubtitle()%></td> </tr> ……//省略了显示帖子的其他信息的代码 <tr height="20"> <td align="right" colspan="2"> <a href="reversion.jsp?id=<%=id%>&num=<%=num%>">[回复]</a> <a href="showSubList.jsp?num=<%=num%>">[返回]</a> </td> </tr>
以下为显示当前根帖的所有回复帖的具体代码,代码中首先查询tb_revert回复帖表获取所属根帖ID值和所属论坛主题类别代号与当前要查看的根帖的ID值和所属论坛主题类别代号相同的记录,这些记录就是当前根帖的所有回复帖,然后通过JSP的Scriptlet脚本程序输出。
<tr><td align="center" height="25" colspan="2">回复的帖子</td></tr>
<%
String sql="select * from tb_revert where rev_card_type="+num+
" and rev_card_id="+id+" order by rev_time desc";
com.model.javabean.DB mydb=new com.model.javabean.DB();
ResultSet rs=mydb.Read(sql);
if(rs.next()){
rs.previous();
int k=1;
while(rs.next()){
%>
<tr><td align="right" colspan="2">-> 帖子-<%=k++%></td></tr>
<tr>
<td height="25" align="right">回复主题:</td>
<td><%=rs.getString("rev_title")%></td>
</tr>
……//省略了显示回复帖的其他信息的代码
<%
}//while
}//if(rs.next)
else
out.println("<tr><td colspan='2'>没有回复的帖子!</td></tr>");
mydb.closed();
break;
}//if(id==subsingle.getId()&&num==subsingle.getTypenum())
}//for
if(i>=singlist.size()){
out.println("<tr><td colspan='2'>要查看的帖子不存在!</td></tr>");
}
}
%>
Model2:JSP+Servlet+JavaBean 1.1 Model2模式简介 在介绍的JSP+JavaBean设计模式,虽然已经将网站的业务逻辑和显示页面进行分离,但这种模式下的JSP不但要进行程序中大部分的流程控制,又要负责页面的显示,所以仍然不是一种理想的设计模式。 在JSP+JavaBean设计模式的基础上加入Servlet来实现程序中的控制层,是一个很好的选择。在这种模式中,由Servlet来执行业务逻辑并掌握着程序的流程控制,JavaBean组件实现一个业务逻辑来表示数据,充当着模型的角色,JSP用于页面的显示。可以看出这种模式使得程序中的层次关系更明显,各组件的分工也非常明确了。应用Struts框架实现的MVC模式,实际上就是遵循了Model2模式的设计原则,Model2模式适用于大型Web应用的开发
1.2 采用Model2模式实现发表帖子和回复帖子 例002 采用Model2模式实现发表帖子和回复帖子 该应用是在第一个应用的基础上增加了发表帖子和回复帖子的功能,所以本应用中实现的显示论坛主题类别和帖子列表的功能、使用的数据表和JavaBean,都与第一个应用中的相同。 程序开发步骤如下: (1)实现发布帖子的功能。
发布帖子首先要填写帖子信息,填写完成后提交Form表单进行发布,如图1所示。论坛的帖子发布功能是应用了JSP+Servlet+JavaBean来实现的,对于表单数据的验证应用了Servlet中的过滤技术,该模式下的表单应被提交给Servlet,然后在Servlet中调用JavaBean组件来实现帖子的发布。将Form表单提交给Servlet,需要在web.xml文件中进行配置,这在后面将会提到。
首先创建填写发布帖子信息的issue.jsp页面,以下为在showSubList.jsp页面中的代码。 <td align="right"colspan="3"> <ahref="issue.jsp?num=<%=num%>">[发布]</a> <ahref="index.jsp">[返回]</a> </td> 其中“[发布]”超链接用于链接到issue.jsp页面,在该页面中填写要发布帖子的信息,并进行发布,issue.jsp页面的关键代码如下。
<%
String strnum=request.getParameter("num");
int num=-1;
try{
num=Integer.parseInt(strnum);
}catch(Exception e){
num=-1;
}
String mess=(String)request.getAttribute("doResult");
if(mess==null)
mess="<li>请填写发布帖子信息!</li>";
%>
<form action="issue">
<input type="hidden" name="num" value=<%=num%>>
<table>
<tr><td align="center" colspan="2">====== 发布帖子 ======</td></tr>
<tr><td colspan="2"><font color="white"><b><%=mess%></b></font></td></tr>
<tr>
<td align="right">发 布 人:</td>
<td><input type="text" name="issUser" size="84"></td>
</tr>
……//省略了其他信息的代码,如"发布标题"
<tr>
<td align="center" colspan="2">
<input type="submit" value="发布">
<input type="reset" value="重置">
<a href="doshowSub.jsp?num=<%=num%>">[返回]</a>
</td>
</tr>
</table>
</form>
上述代码将Form表单的action指向了一个标志符,该标志符必须已经在web.xml文件中与某个具体存在的Servlet进行了映射,这样该Form表单才会被提交给指定的Servlet,下面创建这个Servlet。 然后创建用于保存帖子信息的Servlet,名称为“IssueCard”。在该Servlet中将获取表单中传递的参数,然后调用JavaBean组件对数据库进行操作,操作完成后将执行结果保存在Http请求中,最后根据执行结果通过RequestDispatcher对象将视图转发到指定的JSP页面显示执行结果,该Servlet的具体代码如下:
package com.model.servlet;
import java.io.IOException;
import java.sql.Timestamp;
import javax.servlet.*
import com.model.javabean.DB;
import com.model.tools.ToChinese;
public class IssueCard extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
ToChinese change=new ToChinese(); //用于解决中文乱码的JavaBean
String num=request.getParameter("num");
String iss_user=request.getParameter("issUser");
String iss_title=request.getParameter("issTitle");
String iss_content=request.getParameter("issContent");
iss_user=change.ToChange(iss_user);
iss_title=change.ToChange(iss_title);
iss_content=change.ToChange(iss_content);
Timestamp iss_time=new Timestamp(new java.util.Date().getTime());
String sql="insert into tb_issue values('"+iss_title+"','"+iss_user+"','"
+iss_time+"','"+iss_content+"',"+num+")";
DB mydb=new DB();
int i=mydb.CreateOrUpdate(sql);
if(i>0){
sql="update tb_type set type_amount=type_amount+1 where type_num="+num;
mydb.CreateOrUpdate(sql);
mydb.closed();
RequestDispatcher rd=request.getRequestDispatcher("doshowSub.jsp?num="+num);
rd.forward(request,response);
}
else{
request.setAttribute("doResult","<li>帖子发布失败!</li>");
RequestDispatcher rd=request.getRequestDispatcher("issue.jsp?num="+num);
rd.forward(request,response);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request,response);
}
}
代码中调用的ToChinese类用于解决中文乱码的问题,它存放在了WEB-INF/classes/com/model/tools/文件夹下。 在tools文件夹下,还提供了两个过滤器来实现表单数据验证,它们都是继承了javax.servlet.Filter接口的类,应用过滤器时需要在web.xml文件中为每个实现javax.servlet.Filter接口的类进行配置,并且为每个过滤器指定要处理请求的模式,这样符合该模式的所有用户请求都会先被提交给这些过滤器进行处理。 最后配置用于保存帖子信息的Servlet,在web.xml文件中配置该Servlet的具体代码如下:
<servlet> <servlet-name>IssServlet</servlet-name> <servlet-class>com.model.servlet.IssueCard</servlet-class> </servlet> <servlet-mapping> <servlet-name>IssServlet</servlet-name> <url-pattern>/issue</url-pattern> </servlet-mapping>
(2)实现回复帖子的功能。回复帖子首先要填写帖子信息,填写完成后提交Form表单进行回复。论坛的帖子回复功能同样应用了JSP+Servlet+JavaBean来实现的,对于表单数据的验证应用了Servlet中的过滤技术,在该模式下的表单应被提交给Servlet,然后在Servlet中调用JavaBean组件来实现帖子的回复。 回复帖子的功能与发布帖子的功能的实现是相同的。 两种模式的比较 目前基于JSP技术的大型Web应用,更多的是采用Model2模式进行开发。因为该模式具有更清晰的程序开发层次,JSP主要用来页面的显示,Servlet用于业务的处理和程序流程的控制,JavaBean作为模式实现了一项具体的业务。该模式下的用户请求应首先被提交给Servlet,然后由Servlet处理请求,并调用JavaBean组件来实现具体的业务,最后Servlet将视图转发到用于显示的JSP页面。在大型项目的开发过程中,Model2模式的优势就会显得尤为突出。应用该模式可以进行明确的分工,界面设计人员可以专注于界面的设计,充分发挥自己的设计才能,程序开发人员可以专注于程序的编码和充分发挥自己的逻辑思维来实现项目中的业务逻辑处理。 在Model1模式中,JSP既作为视图,同时也进行部分的程序流程的控制,JavaBean组件作为模型实现具体的业务。用户的请求会被提交给JSP页面,在该页面中来处理请求,并调用JavaBean组件中的方法来执行业务逻辑,最后JavaBean组件将执行的结果返回给JSP进行显示。Model1模式的主要技术特点就是在JSP页面中嵌入了大量的Java代码,虽然使用很灵活,但这使得页面变的庞大而且复杂,当网站的功能实现后,美工和页面内容设计人员在进行页面的包装时问题就变得严重了,并且对于项目的后期维护和扩展就会显得非常困难。 所以在开发大型项目时,应采用Model2模式,而在小型的应用中可以考虑应用Model1模式。