此文章來自於新浪部落格:娟子的部落格http://blog.sina.com.cn/liulijuan500
在這一節中,我們為網上書店添加一個留言板,讓讀者可以發表圖書的讀後感想。通常留言板在顯示使用者留言的時候,都會採用分頁顯示,因為資料庫中可能儲存了上千條、甚至上萬條留言,如果在一個頁面中顯示,肯定會讓使用者看得頭暈眼花。更重要的是,一次顯示上千條記錄,需要大量的處理時間,這會讓使用者等待較長的時間,這是使用者無法忍受的。在本例中,我們將給讀者介紹一種高效的分頁查詢技術。執行個體的開發主要有下列步驟。
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 指標異常,所以在這裡做一個判斷,如果有任何一個參數對象為null,則將用戶端重新導向到index.jsp頁面。
第18~19行,首先去掉使用者名稱和標題前後的空格,然後調用toHtml()方法對使用者名稱和標題中的特殊字元做處理。第20~24行,判斷使用者名稱和標題是否為空白,如果為空白,則讓使用者重新輸入留言。因為在我們的留言板程式中,要求使用者名稱和標題不可為空,而內容可以為空白,所以在這裡只判斷使用者名稱和標題是否為空白。
第26行,調用請求對象的getRemoteAddr()方法,得到用戶端的IP地址,如果使用者是通過Proxy 伺服器上網,那麼此處得到的IP地址將是Proxy 伺服器的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所示的頁面。
圖12-2 顯示留言的頁面-當前沒有留言
單擊“我要留言”的連結,將看到12-3所示的頁面。
填寫留言的內容,單擊“提交”按鈕,將看到12-4所示的頁面。
圖12-3 使用者留言的頁面 圖12-4 顯示留言的頁面——有1條留言
讀者可以繼續留言,當有6條以上留言的時候,“下一頁”和“最後頁”的文字將變成超連結,12-5所示。
圖12-5 顯示留言的頁面——有6條留言
如果單擊“下一頁”的連結,將進入下一頁面,此時“第一頁”和“上一頁”的文字將變成超連結。
(http://cache.baiducontent.com/c?m=9d78d513d99b00ed4fece4697c65c0111f43f0622ba3d4027ea4843990732f475010e0ac51240705a3d20c6016de4f4beb802103311456c48cb9825dabcd866f6fd6286e365ac45613a004b29b18789437902da8f445b4adf045c2f38dc4df2302964e4467d0aed60f1713be68f5433ae0f6ca48175c12b8bd312ebf4e775ec5651fe8&p=8979c64ad7c02de043bd9b7e0c14c123&newp=c36f8316d9c242b908e2947f0f4993315c5bc4387ebad51679c2&user=baidu&fm=sc&query=jsp%CA%E4%B3%F6%C1%F4%D1%D4%B5%C4%C4%DA%C8%DD&qid=&p1=1)