在這一節(jié)中,我們?yōu)榫W(wǎng)上書店添加一個留言板,讓讀者可以發(fā)表圖書的讀后感想。通常留言板在顯示用戶留言的時候,都會采用分頁顯示,因為數(shù)據(jù)庫中可能存儲了上千條、甚至上萬條留言,如果在一個頁面中顯示,肯定會讓用戶看得頭暈眼花。更重要的是,一次顯示上千條記錄,需要大量的處理時間,這會讓用戶等待較長的時間,這是用戶無法忍受的。在本例中,我們將給讀者介紹一種高效的分頁查詢技術(shù)。實例的開發(fā)主要有下列步驟。
Step1:創(chuàng)建guestbook表
首先在bookstore數(shù)據(jù)庫中建立存放用戶留言的數(shù)據(jù)庫表guestbook。在本例中,使用MySQL數(shù)據(jù)庫,讀者也可以選擇其他的數(shù)據(jù)庫,打開命令提示符窗口,輸入:
mysql -uroot -p12345678 bookstore
進入mysql客戶程序,訪問bookstore數(shù)據(jù)庫。輸入創(chuàng)建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表的結(jié)構(gòu)如表12-4所示。
表12-4 guestbook表的結(jié)構(gòu)
字段
描 述
gst_id bookinfo表的主鍵,整型,設(shè)置AUTO_INCREMENT屬性,
讓該列的值自動從1開始增長
gst_user 字符串類型,留言的用戶名,不能為空
gst_title 字符串類型,留言的標(biāo)題,不能為空
gst_content 文本串類型,留言的內(nèi)容,可以為空
gst_time TIMESTAMP類型,留言的時間
gst_ip 字符串類型,用戶的IP地址
Step2:配置留言板程序的運行目錄和JDBC數(shù)據(jù)源
在%CATALINA_HOME%\conf\Catalina\localhost目錄下,新建ch12.xml文件,編輯此文件,內(nèi)容如例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>內(nèi)容:</td>
<td>
<textarea name="content" rows="10"cols="40"></textarea>
</td>
</tr>
<tr>
<td><inputtype="submit"value="提交"></td>
<td><inputtype="reset"value="重填"></td>
</tr>
</table>
</form>
</center>
Step4:編寫util.jsp
util.jsp中包含了一個靜態(tài)的工具方法toHtml(),用于對HTML中的保留字符和一些特殊字符進行轉(zhuǎn)換。將編寫好的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();
}
%>
用戶在留言的時候,可能會輸入一些特殊的字符,如果我們不對這些字符做相應(yīng)的轉(zhuǎn)換,那么用戶輸入的數(shù)據(jù)將不能正常顯示。例如,用戶輸入了下面的數(shù)據(jù):
<?xml version="1.0"encoding="gb2312"?>
如果這個XML聲明沒有經(jīng)過轉(zhuǎn)換,瀏覽器將不會顯示這些數(shù)據(jù)。
在一些需要用戶在線提交數(shù)據(jù)的網(wǎng)絡(luò)應(yīng)用程序中,例如論壇,都應(yīng)該對保留字符和一些特殊字符做相應(yīng)的轉(zhuǎn)換,一方面可以保證數(shù)據(jù)的正常顯示,另一方面也保證了Web應(yīng)用程序的安全性(參見23.5節(jié))。
Step5:編寫process.jsp
process.jsp用于向數(shù)據(jù)庫中插入用戶的留言。編輯process.jsp,將編寫好的JSP文件放到F:\JSPLesson\ch12\gst目錄下。完整的源代碼如例12-7所示。
例12-7 process.jsp
1. <%@ page contentType="text/html;charset=gb2312"%>
2. <%@ pageimport="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. DataSourceds=(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屬性設(shè)置頁面的MIME類型是text/html,字符編碼是gb2312。第2行,利用page指令import屬性導(dǎo)入在頁面中需要用到的Java類。第3行,利用include指令包含util.jsp,這樣,在頁面中就可以使用toHtml()方法了。
第6行,設(shè)置請求正文使用的字符編碼是gb2312。
第12~16行,判斷name,title和content參數(shù)對象是否為空,一般情況下,用戶都是訪問say.html頁面而間接調(diào)用process.jsp。name,title和content參數(shù)對象不會為空,但是為了防止用戶直接訪問process.jsp頁面,從而導(dǎo)致空指針異常,所以在這里做一個判斷,如果有任何一個參數(shù)對象為null,則將客戶端重定向到index.jsp頁面。
第18~19行,首先去掉用戶名和標(biāo)題前后的空格,然后調(diào)用toHtml()方法對用戶名和標(biāo)題中的特殊字符做處理。第20~24行,判斷用戶名和標(biāo)題是否為空,如果為空,則讓用戶重新輸入留言。因為在我們的留言板程序中,要求用戶名和標(biāo)題不能為空,而內(nèi)容可以為空,所以在這里只判斷用戶名和標(biāo)題是否為空。
第26行,調(diào)用請求對象的getRemoteAddr()方法,得到客戶端的IP地址,如果用戶是通過代理服務(wù)器上網(wǎng),那么此處得到的IP地址將是代理服務(wù)器的IP地址。
第28~30行,利用數(shù)據(jù)源對象建立數(shù)據(jù)庫的連接。
第32~39行,將用戶留言的內(nèi)容和客戶端的IP地址存儲到數(shù)據(jù)庫的guestbook表中。注意,在這里,我們并沒有插入留言的時間,這是因為存儲留言時間的字段gst_time的類型是TIMESTAMP,在MySQL中,如果在一個TIMESTAMP列中插入NULL,或者在插入新行時,沒有給TIMESTAMP列賦值,那么MySQL會自動將該列設(shè)置為當(dāng)前的日期和時間。這樣我們在插入一條記錄的時候,就不用考慮插入當(dāng)前時間的問題了。通過這種方式,可以保證留言的當(dāng)前日期和時間被正確地記錄下來。不過要注意的是,這是利用了MySQL本身提供的特性,如果讀者使用其他的數(shù)據(jù)庫,則要采用另外的方法來插入當(dāng)前日期和時間。
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. <%@ pageimport="java.sql.*,javax.sql.*,javax.naming.*"%>
3.
4. <html>
5. <head>
6.<title>網(wǎng)上書店留言板</title>
7. </head>
8. <body>
9. <ahref="/say.html">我要留言</a><br>
10. <%
11. Context ctx=new InitialContext();
12. DataSourceds=(DataSource)ctx.lookup("java:comp/env/jdbc/bookstore");
13. Connection conn=ds.getConnection();
14.
15. //創(chuàng)建可滾動的結(jié)果集。
16. Statement stmt=conn.createStatement(
17. ResultSet.TYPE_SCROLL_INSENSITIVE,
18. ResultSet.CONCUR_READ_ONLY);
19. ResultSet rs=stmt.executeQuery("select * from guestbook orderby gst_time desc");
20.
21. //移動游標(biāo)至結(jié)果集的最后一行。
22. rs.last();
23.
24. //得到當(dāng)前行的行數(shù),也就得到了數(shù)據(jù)庫中留言的總數(shù)。
25. int rowCount=rs.getRow();
26. if(rowCount==0)
27. {
28. out.println("當(dāng)前沒有任何留言!");
29. return;
30. }
31.
32. String strCurPage=request.getParameter("page");
33.
34. //表示當(dāng)前的頁數(shù)。
35. int curPage;
36.
37. if(strCurPage==null)
38. curPage=1;
39. else
40. curPage=Integer.parseInt(strCurPage);
41.
42. //定義每頁顯示的留言數(shù)。
43. int countPerPage=5;
44.
45. //計算顯示所有留言需要的總頁數(shù)。
46. int pageCount=(rowCount+countPerPage-1)/countPerPage;
47.
48. //移動游標(biāo)至結(jié)果集中指定的行。如果顯示的是第一頁,curPage=1,
49. //游標(biāo)移動到第1行。
50. rs.absolute((curPage-1)*countPerPage+1);
51.
52. //如果是第1頁,則顯示不帶鏈接的文字,如果不是第1頁,
53. //則給用戶提供跳轉(zhuǎn)到第一頁和上一頁的鏈接。
54. if(curPage==1)
55. {
56. %>
57. 第一頁
58. 上一頁
59. <%
60. }
61. else
62. {
63. %>
64. <ahref="index.jsp?page=<%=1%>">第一頁</a>
65.
66. <ahref="index.jsp?page=<%=curPage-1%>">上一頁</a>
67.
68. <%
69. }
70. //如果當(dāng)前頁是最后一頁,則顯示不帶鏈接的文字,如果不是最后一頁,
71. //則給用戶提供跳轉(zhuǎn)到最后一頁和下一頁的鏈接。
72. if(curPage==pageCount)
73. {
74.
75. %>
76. 下一頁
77. 最后頁
78. <%
79. }
80. else
81. {
82. %>
83. <ahref="index.jsp?page=<%=curPage+1%>">下一頁</a>
84.
85. <ahref="index.jsp?page=<%=pageCount%>">最后頁</a>
86.
87. <%
88. }
89.
90. int i=0;
91.
92. //以循環(huán)的方式取出每頁要顯示的數(shù)據(jù),因為在前面針對要顯示的頁數(shù),
93. //調(diào)用了rs.absolute((curPage-1)*countPerPage+1);
94. //所以是從游標(biāo)所在的位置取出當(dāng)前頁要顯示的數(shù)據(jù)。
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("內(nèi)容:"+rs.getString("gst_content"));
112. i++;
113. rs.next();
114. }
115. rs.close();
116. stmt.close();
117. conn.close();
118. %>
119. </body>
120.</html>
在這個頁面中實現(xiàn)了留言板的分頁功能。主要思路就是利用可滾動的結(jié)果集,根據(jù)要顯示的頁數(shù)和每頁顯示的留言數(shù)量,將游標(biāo)移動到相應(yīng)的位置,然后讀取每頁顯示留言數(shù)量的記錄數(shù)。在實現(xiàn)過程中,主要就是邏輯的組織,例如,如何計算總的頁數(shù),如何判斷用戶要查看哪一頁的留言(通過在URL后附加查詢參數(shù)),什么時候應(yīng)該讓第一頁、上一頁、下一頁和最后頁的鏈接生效等。讀者可仔細體會這段代碼。
這段代碼添加了注釋,在這里我們就不再詳細講述了。不過,有一個地方需要提醒讀者注意,代碼的第101~104行,我們在取出留言時間后,做了一些轉(zhuǎn)換。首先調(diào)用Timestamp類的getTime()方法返回從January1, 1970, 00:00:00GMT開始的毫秒數(shù),然后利用這個毫秒數(shù)構(gòu)造java.sql.Date對象(表示留言的日期)和java.sql.Time對象(表示留言的時間),最后用這兩個對象來共同輸出留言的時間。那為什么不直接使用Timestamp對象來輸出時間呢?這是因為如果直接用ts.toString()來輸出時間,將會得到下列形式的時間值:
2005-04-05 19:35:04.0
注意在秒數(shù)后面還有一個“.0”,這是Java語言顯示時間本身的問題。如果你不希望看到最后的“.0”,一種方式是通過字符串操作,從時間字符串中去掉“.0”,另外一種方式就是筆者在上面給讀者提供的方法。
Step7:運行留言板程序
啟動Tomcat服務(wù)器,打開IE瀏覽器,在地址欄中輸入http://localhost:8080/ch12/gst/index.jsp,將看到如圖12-2所示的頁面。
圖12-2 顯示留言的頁面-當(dāng)前沒有留言
單擊“我要留言”的鏈接,將看到如圖12-3所示的頁面。
填寫留言的內(nèi)容,單擊“提交”按鈕,將看到如圖12-4所示的頁面。
圖12-3 用戶留言的頁面 圖12-4 顯示留言的頁面——有1條留言
讀者可以繼續(xù)留言,當(dāng)有6條以上留言的時候,“下一頁”和“最后頁”的文字將變成超鏈接,如圖12-5所示。
圖12-5 顯示留言的頁面——有6條留言
如果單擊“下一頁”的鏈接,將進入下一頁面,此時“第一頁”和“上一頁”的文字將變成超鏈接。
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請
點擊舉報。