示用户留言的时候,都会采用分页显示,因为数据库中可能存储了上千条、甚至上万条留言,如果在一个页面中显示,肯定会让用户看得头晕眼花。更重要的是,一次显示上千条记录,需要大量的处理时间,这会让用户等待较长的时间,这是用户无法忍受的。在本例中,我们将给读者介绍一种高效的分页查询技术。实例的开发主要有下列步骤。


Step1:创建guestbook表


首先在bookstore数据库中建立存放用户留言的数据库表guestbook。在本例中,使用MySQL数据库,读者也可以选择其他的数据库,打开命令提示符窗口,输入:


mysql -uroot -p12345678 bookstore


进入mysql客户程序,访问bookstore数据库。输入创建guestbook表的SQL语句,如下:


create table guestbook(

gst_id INT AUTO_INCREMENT not null primary key,

gst_user VARCHAR(10) not null,

gst_title VARCHAR(100) not null,

gst_content TEXT,

gst_time TIMESTAMP not null,

gst_ip VARCHAR(15) not null);


guestbook表的结构如表12-4所示。


表12-4 guestbook表的结构



字段                          描 述


gst_id                bookinfo表的主键,整型,设置AUTO_INCREMENT属性,

                   让该列的值自动从1开始增长


gst_user          字符串类型,留言的用户名,不能为空


gst_title            字符串类型,留言的标题,不能为空


gst_content        文本串类型,留言的内容,可以为空


gst_time            TIMESTAMP类型,留言的时间


gst_ip                字符串类型,用户的IP地址





Step2:配置留言板程序的运行目录和JDBC数据源


在%CATALINA_HOME%\conf\Catalina\localhost目录下,新建ch12.xml文件,编辑此文件,内容如例12-4所示。


例12-4 ch12.xml

<Context path="/ch12" docBase="F:\JSPLesson\ch12" reloadable="true">
<Resource name="jdbc/bookstore" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000"
username="root" password="12345678"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/bookstore?autoReconnect=true"/>
</Context>

Step3:编写say.html

say.html页面用于填写留言信息。将编写好的say.html文件放到F:\JSPLesson\ch12\gst目录下。完整的代码如例12-5所示。


例12-5 say.html

<center>
<form action="process.jsp" method="post">
<table bgcolor="#B3B3FF">
<caption>欢迎访问留言板</caption>
<tr>
<td>用户名:</td>
<td><input type="text" name="name"></td>
</tr>
<tr>
<td>主题:</td>
<td><input type="text" name="title" size="40"></td>
</tr>
<tr>
<td>内容:</td>
<td>
<textarea name="content" rows="10" cols="40"></textarea>
</td>
</tr>
<tr>
<td><input type="submit" value="提交"></td>
<td><input type="reset" value="重填"></td>
</tr>
</table>
</form>
</center>

Step4:编写util.jsp

util.jsp中包含了一个静态的工具方法toHtml(),用于对HTML中的保留字符和一些特殊字符进行转换。将编写好的util.jsp文件放到F:\JSPLesson\ch12\gst目录下。完整的源代码如例12-6所示。


例12-6 util.jsp

<%!
public static String toHtml(String str)
{
if(str==null)
return null;
StringBuffer sb = new StringBuffer();
int len = str.length();
for (int i = 0; i < len; i++)
{
char c = str.charAt(i);
switch(c)
{
case ' ':
sb.append(" ");
break;
case '\n':
sb.append("<br>");
break;
case '\r':
break;
case '\'':
sb.append("'");
break;
case '<':
sb.append("<");
break;
case '>':
sb.append(">");
break;
case '&':
sb.append("&");
break;
case '"':
sb.append(""");
break;
case '\\':
sb.append("\");
break;
default:
sb.append(c);
}
}
return sb.toString();
}
%>

用户在留言的时候,可能会输入一些特殊的字符,如果我们不对这些字符做相应的转换,那么用户输入的数据将不能正常显示。例如,用户输入了下面的数据:

<?xml version="1.0" encoding="gb2312"?>


如果这个XML声明没有经过转换,浏览器将不会显示这些数据。


在一些需要用户在线提交数据的网络应用程序中,例如论坛,都应该对保留字符和一些特殊字符做相应的转换,一方面可以保证数据的正常显示,另一方面也保证了Web应用程序的安全性(参见23.5节)。


Step5:编写process.jsp

process.jsp用于向数据库中插入用户的留言。编辑process.jsp,将编写好的JSP文件放到F:\JSPLesson\ch12\gst目录下。完整的源代码如例12-7所示。


例12-7 process.jsp

1. <%@ page contentType="text/html;charset=gb2312" %>
2. <%@ page import="java.sql.*,javax.sql.*,javax.naming.*" %>
3. <%@ include file="util.jsp" %>
4.
5. <%
6. request.setCharacterEncoding("gb2312");
7.
8. String name=request.getParameter("name");
9. String title=request.getParameter("title");
10. String content=request.getParameter("content");
11.
12. if(null==name || null==title || null==content)
13. {
14. response.sendRedirect("index.jsp");
15. return;
16. }
17.
18. name=toHtml(name.trim());
19. title=toHtml(title.trim());
20. if(name.equals("") || title.equals(""))
21. {
22. response.sendRedirect("say.html");
23. return;
24. }
25. content=toHtml(content.trim());
26. String fromIP=request.getRemoteAddr();
27.
28. Context ctx=new InitialContext();
29. DataSource ds=(DataSource)ctx.lookup("java:comp/env/jdbc/bookstore");
30. Connection conn=ds.getConnection();
31.
32. PreparedStatement pstmt=conn.prepareStatement(
33. "insert into guestbook(gst_user,gst_title,gst_content,gst_ip) values(?,?,?,?)");
34. pstmt.setString(1,name);
35. pstmt.setString(2,title);
36. pstmt.setString(3,content);
37. pstmt.setString(4,fromIP);
38.
39. pstmt.executeUpdate();
40. pstmt.close();
41. conn.close();
42. response.sendRedirect("index.jsp");
43.%>


代码的第1行,利用page指令contentType属性设置页面的MIME类型是text/html,字符编码是gb2312。第2行,利用page指令import属性导入在页面中需要用到的Java类。第3行,利用include指令包含util.jsp,这样,在页面中就可以使用toHtml()方法了。


第6行,设置请求正文使用的字符编码是gb2312。


第12~16行,判断name,title和content参数对象是否为空,一般情况下,用户都是访问 say.html页面而间接调用process.jsp。name,title和content参数对象不会为空,但是为了防止用户直接访问 process.jsp页面,从而导致空指针异常,所以在这里做一个判断,如果有任何一个参数对象为null,则将客户端重定向到index.jsp页面。


第18~19行,首先去掉用户名和标题前后的空格,然后调用toHtml()方法对用户名和标题中的特殊字符做处理。第20~24行,判断用户名和标题是否为空,如果为空,则让用户重新输入留言。因为在我们的留言板程序中,要求用户名和标题不能为空,而内容可以为空,所以在这里只判断用户名和标题是否为空。


第26行,调用请求对象的getRemoteAddr()方法,得到客户端的IP地址,如果用户是通过代理服务器上网,那么此处得到的IP地址将是代理服务器的IP地址。


第28~30行,利用数据源对象建立数据库的连接。


第32~39行,将用户留言的内容和客户端的IP地址存储到数据库的guestbook表中。注意,在这里,我们并没有插入留言的时间,这是因为存储留言时间的字段gst_time的类型是TIMESTAMP,在MySQL中,如果在一个TIMESTAMP列中插入NULL,或者在插入新行时,没有给TIMESTAMP列赋值,那么MySQL会自动将该列设置为当前的日期和时间。这样我们在插入一条记录的时候,就不用考虑插入当前时间的问题了。通过这种方式,可以保证留言的当前日期和时间被正确地记录下来。不过要注意的是,这是利用了MySQL本身提供的特性,如果读者使用其他的数据库,则要采用另外的方法来插入当前日期和时间。


Step6:编写index.jsp

index.jsp是留言板的首页,用于显示用户的留言。编辑index.jsp,将编写好的JSP文件放到F:\JSPLesson\ch12\gst目录下。完整的源代码如例12-8所示。


例12-8 index.jsp

1. <%@ page contentType="text/html;charset=gb2312" %>
2. <%@ page import="java.sql.*,javax.sql.*,javax.naming.*" %>
3.
4. <html>
5. <head>
6. <title>网上书店留言板</title>
7. </head>
8. <body>
9. <a href="/say.html">我要留言</a><br>
10. <%
11. Context ctx=new InitialContext();
12. DataSource ds=(DataSource)ctx.lookup("java:comp/env/jdbc/bookstore");
13. Connection conn=ds.getConnection();
14.
15. //创建可滚动的结果集。
16. Statement stmt=conn.createStatement(
17. ResultSet.TYPE_SCROLL_INSENSITIVE,
18. ResultSet.CONCUR_READ_ONLY);
19. ResultSet rs=stmt.executeQuery("select * from guestbook order by gst_time desc");
20.
21. //移动游标至结果集的最后一行。
22. rs.last();
23.
24. //得到当前行的行数,也就得到了数据库中留言的总数。
25. int rowCount=rs.getRow();
26. if(rowCount==0)
27. {
28. out.println("当前没有任何留言!");
29. return;
30. }
31.
32. String strCurPage=request.getParameter("page");
33.
34. //表示当前的页数。
35. int curPage;
36.
37. if(strCurPage==null)
38. curPage=1;
39. else
40. curPage=Integer.parseInt(strCurPage);
41.
42. //定义每页显示的留言数。
43. int countPerPage=5;
44.
45. //计算显示所有留言需要的总页数。
46. int pageCount=(rowCount+countPerPage-1)/countPerPage;
47.
48. //移动游标至结果集中指定的行。如果显示的是第一页,curPage=1,
49. //游标移动到第1行。
50. rs.absolute((curPage-1)*countPerPage+1);
51.
52. //如果是第1页,则显示不带链接的文字,如果不是第1页,
53. //则给用户提供跳转到第一页和上一页的链接。
54. if(curPage==1)
55. {
56. %>
57. 第一页   
58. 上一页   
59. <%
60. }
61. else
62. {
63. %>
64. <a href="index.jsp?page=<%=1%>">第一页</a>
65.   
66. <a href="index.jsp?page=<%=curPage-1%>">上一页</a>
67.   
68. <%
69. }
70. //如果当前页是最后一页,则显示不带链接的文字,如果不是最后一页,
71. //则给用户提供跳转到最后一页和下一页的链接。
72. if(curPage==pageCount)
73. {
74.
75. %>
76. 下一页   
77. 最后页   
78. <%
79. }
80. else
81. {
82. %>
83. <a href="index.jsp?page=<%=curPage+1%>">下一页</a>
84.   
85. <a href="index.jsp?page=<%=pageCount%>">最后页</a>
86.   
87. <%
88. }
89.
90. int i=0;
91.
92. //以循环的方式取出每页要显示的数据,因为在前面针对要显示的页数,
93. //调用了rs.absolute((curPage-1)*countPerPage+1);
94. //所以是从游标所在的位置取出当前页要显示的数据。
95. while(i<countPerPage && !rs.isAfterLast())
96. {
97. out.println("<hr color=\"blue\" size=\"2\"><br>");
98. out.println("用户名:"+rs.getString("gst_user"));
99. out.println(" ");
100.
101. Timestamp ts=rs.getTimestamp("gst_time");
102. long lms=ts.getTime();
103. Date date=new Date(lms);
104. Time time=new Time(lms);
105.
106. out.println("留言时间:"+date+" "+time);
107.
108. out.println(" ");
109. out.println("用户IP:"+rs.getString("gst_ip")+"<br>");
110. out.println("主题:"+rs.getString("gst_title")+"<br>");
111. out.println("内容:"+rs.getString("gst_content"));
112. i++;
113. rs.next();
114. }
115. rs.close();
116. stmt.close();
117. conn.close();
118. %>
119. </body>
120.</html>

在这个页面中实现了留言板的分页功能。主要思路就是利用可滚动的结果集,根据要显示的页数和每页显示的留言数量,将游标移动到相应的位置,然后读取每页显示留言数量的记录数。在实现过程中,主要就是逻辑的组织,例如,如何计算总的页数,如何判断用户要查看哪一页的留言(通过在URL后附加查询参数),什么时候应该让第一页、上一页、下一页和最后页的链接生效等。读者可仔细体会这段代码。


这段代码添加了注释,在这里我们就不再详细讲述了。不过,有一个地方需要提醒读者注意,代码的第101~104行,我们在取出留言时间后,做了一些转换。首先调用Timestamp类的getTime()方法返回从January 1, 1970, 00:00:00 GMT开始的毫秒数,然后利用这个毫秒数构造java.sql.Date对象(表示留言的日期)和java.sql.Time对象(表示留言的时间),最后用这两个对象来共同输出留言的时间。那为什么不直接使用Timestamp对象来输出时间呢?这是因为如果直接用ts.toString()来输出时间,将会得到下列形式的时间值:

2005-04-05 19:35:04.0

注意在秒数后面还有一个“.0”,这是Java语言显示时间本身的问题。如果你不希望看到最后的“.0”,一种方式是通过字符串操作,从时间字符串中去掉“.0”,另外一种方式就是笔者在上面给读者提供的方法。

Step7:运行留言板程序

启动Tomcat服务器,打开IE浏览器,在地址栏中输入http://localhost:8080/ch12/gst/index.jsp,将看到如图12-2所示的页面。

使用jquery留言板 jsp实现留言板代码界面_数据库


图12-2 显示留言的页面-当前没有留言

单击“我要留言”的链接,将看到如图12-3所示的页面。

填写留言的内容,单击“提交”按钮,将看到如图12-4所示的页面。

使用jquery留言板 jsp实现留言板代码界面_数据库


使用jquery留言板 jsp实现留言板代码界面_数据库


图12-3 用户留言的页面 图12-4 显示留言的页面——有1条留言

读者可以继续留言,当有6条以上留言的时候,“下一页”和“最后页”的文字将变成超链接,如图12-5所示。

使用jquery留言板 jsp实现留言板代码界面_数据库


图12-5 显示留言的页面——有6条留言

如果单击“下一页”的链接,将进入下一页面,此时“第一页”和“上一页”的文字将变成超链接。