JWorld@TW the best professional Java site in Taiwan
      註冊 | 登入 | 全文檢索 | 排行榜  

» JWorld@TW » JDBC/SQL討論區 » JDBC 應用  

按列印兼容模式列印這個話題 列印話題    把這個話題寄給朋友 寄給朋友    訂閱主題
reply to topicthreaded modego to previous topicgo to next topic
本主題所含的標籤
無標籤
作者 【心得】由AJAX設計導入來談資料庫資料分頁實作
Jao

用盡全力來過生活...



發文: 87
積分: 1
於 2006-09-15 20:25 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
談到分頁這個問題,其實算是個討論到爛的設計,目前說法大抵可分「一次讀完」,與「建立索引」 這兩派。

一次讀完再慢慢地分,簡單來說就是一口氣將資料庫的屬於該邏輯的所有資料讀到記憶體中,再依使用者所要求的區間來呈現資料表內容。這方法的好處是
可以降低平均整體查詢資料庫的時間,也就是降低與資料庫之間存取的頻率。而缺點就是必須提供大量的記憶體來儲存整個資料表內容,而且不能即時反應
資料庫的變化。

建立索引的方式則是取得資料庫的資料表內含的索引,再讓使用者依照分頁索引來查詢出資料庫內的資料表該區間的資料。這方法的好處是僅需在記憶體內
提供一些位置給使用者做資料表更新與索引表物件,缺點就是對資料庫的存取頻率較高,連結資料庫的動作所消耗的時間較多。

當然,有些額外的手段可以使資料庫查詢的動作簡化,就好比說是Connection Pool,這幾乎可以讓建立連結的時間忽略不計,而只需考慮到資料庫本身處
理SQL語法的速度。但仍有些部分是無解(也不能這麼說,因為阿扁言:能用金錢解決的問題就不是問題。),那就是記憶體的使用量。

正因為金錢的來源是大問題,所以實際上要設計分頁還是要回歸使用者的使用習慣和查詢功能。若使用者的查詢習慣是只看前幾筆最新的內容,則一次讀完
的方式顯然太浪費記憶體。又若使用者的習慣是將所有每筆資料逐筆查詢做運算,則建立索引的方式就顯得無用武之地。

既然使用者的習慣捉摸不定,那麼設計者就應要在介面上來隱性導引使用者習慣。好比說頁面僅呈現該區間資料的簡略屬性,若需要詳細資料,可再繼續按
下按鈕調出詳細的屬性。而要調閱及時反應的最新資料,僅需使用者按下了分頁的第一頁,整批索引便立即更新,即可調閱最新一批資料。這些都是目前各
開源的討論區作法的一部份。

而如今,有賴於AJAX的流行,我們更可以將所有資料作更分散式的取得,讓使用者有者近似於「一次讀完」的速度,又可以達到「建立索引」的記憶體節約。

以MySQL舉例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
doPost(....){
// 摘錄
                ArrayList pageList = null;
                try{
                    pageList = etdao.getEventPageNums(account, 10);
                    session.setAttribute("pageList", pageList);
                }catch(java.sql.SQLException e){
                    System.out.println(e + "Get Page List Fail!");
                }
                
                try{
                    if(pageList.size() > 0){
                        etdao.SearchAllinPageDesc(account, (new Integer((String)pageList.get(0))).intValue(), 10, "");
                        if(pageList.size() == 1){
                            request.setAttribute("lastPage", true);
                        }
                    }
 
                }catch(java.sql.SQLException e){
                    System.out.println("Search Page Events Fail!");
                }
                
                Iterator eventIt = etdao.getEventIndexList().iterator();
                Iterator zigbeeIt = etdao.getBeeNameList().iterator();
                Iterator gatewayIt = etdao.getBoxLocationList().iterator();
                Iterator dateIt = etdao.getCreateDateList().iterator();
                ArrayList tempList = new ArrayList();
                for(int i = 1; eventIt.hasNext(); i++) {
                    String[] elms = new String[4];
                    elms[0] = (String) eventIt.next();
                    elms[1] = (String) zigbeeIt.next();
                    elms[2] = (String) gatewayIt.next();
                    elms[3] = (String) dateIt.next();
                    tempList.add(elms);
                }
                request.setAttribute("eventList", tempList);
                request.setAttribute("pageindex", 0);
                if(pageList.size() > 0){
                    request.setAttribute("pageSize", pageList.size());
                }                
  
  //未完
 
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// In Database Access Object...
public ArrayList getEventPageNums(String account, int limit) throws SQLException{
        ArrayList pageList = new ArrayList();
        PreparedStatement stmt = null;
        try {
            conn = connMgr.getConnection("obdb");
            stmt = conn.prepareStatement(getAllEventNum_stmt);
            stmt.setString(1, account);
            rs = stmt.executeQuery();
            int i = 0;
            while(rs.next()) {
                if((i%10) == 0){
                    pageList.add(rs.getString("EventIndex"));
                }
                i++;
            }
        } finally {
            stmt.close();
            connMgr.freeConnection("obdb", conn);
        }
        return pageList;
    }    
private final static String getAllEventNum_stmt = "SELECT EventIndex FROM EventTable WHERE Account = ? "+
                                                      "ORDER BY CreateDate DESC";
 
public int SearchAllinPageDesc(String account, int start, int limit, String dateStart) throws SQLException {      
        PreparedStatement stmt = null;
        int nums = 0;
        try {
            conn = connMgr.getConnection("obdb");
            stmt = conn.prepareStatement(getAllinPage_stmt);
            stmt.setString(1, account);
            stmt.setInt(2, start);
            stmt.setString(3, dateStart);
            stmt.setInt(4, limit);
            rs = stmt.executeQuery();
            while (rs.next()) {
                nums++;
                eventIndexList.add(rs.getString("EventIndex"));
                beeNameList.add(rs.getString("BeeName"));
                boxLocationList.add(rs.getString("BoxLocation"));
                createDateList.add(rs.getString("CreateDate"));
            }   
        } finally {
            stmt.close();
            connMgr.freeConnection("obdb", conn);
        }
        return nums;
    }
    private final static String getAllinPage_stmt = "SELECT EventIndex, BeeName, BoxLocation, CreateDate "+
                                                    "FROM EventTable " +
                                                    "WHERE (Account = ?) AND (EventIndex <= ?) AND (CreateDate >= ?)" +
                                                    "ORDER BY CreateDate DESC LIMIT ?";
 


http://www.wretch.cc/album/show.php?i=hougzou&b=4&f=1333974463&p=7

由以上幾行簡單的Code,配合JSP頁面的JSTL與EL語法,可以容易地產生上圖的資料表。此內容為也就是「建立索引」方式的實踐,搭配了Connection Pool
之後,分頁的速度也頗為迅速。由於此服務的使用者習慣為檢閱最新資料以及指定時間區間的資料,因此在資料呈現上提供了最新十筆資料,並在右下角要
求使用者一頁頁往前翻查,再不然也可以使用日期輸入器指定日期區間來做查詢。

接著,若按下了事件旁的按鈕,則會啟動AJAX function,將指定的單筆資料撈回做處理。以下的Code就是個最原始的AJAX Servlet實踐,完全將Table用手
工製造。但要製造出相同的表格,用document.createElement(XXX)會讓人瘋掉,因此在其他Lib尚未不熟悉的狀況下,就先將就著寫上去。

http://www.wretch.cc/album/show.php?i=hougzou&b=4&f=1333974464&p=8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
        ArrayList tempList = null;
        StringBuffer sb = new StringBuffer(2048);
        sb.append("<table bgcolor='#EEEEEE'><tr><td>");
        try{
            tempList = etdao.SearchSimpleLog(eventIndex, account);
        }catch(SQLException e){
            tempList = new ArrayList();
            System.out.println(e);
        }
        if(tempList.get(0) != null){
            sb.append("<table border='0' cellpadding='1' width='400' class='n12-b'><tr><td colspan='2'>簡訊內容</td></tr><tr><td width='25'></td><td width='375' valign='top' align='left' class='n12-bb'>");
            sb.append((String)tempList.get(0)).append("</td></tr>");
            if(tempList.get(2) != null){
                sb.append("<tr><td width='25'></td><td width='375' valign='top' align='left' class='n12-b'>").append("簡訊聯絡人: ").append((String)tempList.get(2)).append("</td></tr>");
            }
            sb.append("</table>");
        }
        if(tempList.get(1) != null){
            ContextAnalyzer ca = new ContextAnalyzer(account, eventIndex, logPath);
            sb.append("<table border='0' cellpadding='1' width='400' class='n12-b'>");
            sb.append("<tr><td colspan='3'>郵件內容</td></tr>");
            sb.append("<tr><td width='25'></td><td width='30' align='left' valign='top'>主旨</td><td width='345' valign='top' align='left' class='n12-bb'>").append(ca.GetMailSubject()).append("</td></tr>");
            sb.append("<tr><td width='25'></td><td width='30' align='left' valign='top'>本文</td><td width='345' valign='top' align='left' class='n12-bb'>").append(ca.GetMailBody()).append("</td></tr>");
            List attList = ca.GetMailAttachment();
            if(attList != null){
                sb.append("<tr><td width='25'></td><td width='30' align='left' valign='top'>附件</td><td width='345' valign='top' align='left'>");
                sb.append("<table class='n12-bb'><tr>");
                for(int i=0; i < attList.size(); i++){
                    sb.append("<td valign='top'>");
                    sb.append("<div style='cursor:hand' onClick=\"attwindow('").append(i).append("');\">").append("附件").append(i+1).append(" </div>");
                    sb.append("</td>");
                } 
                sb.append("</tr></table></td></tr>");
            }
            if(tempList.get(3) != null){
                sb.append("<tr><td width='25'></td><td width='375' align='left' valign='top' colspan='2'>郵件聯絡人: ").append((String)tempList.get(3)).append("</td></tr>");
            }          
            sb.append("</table>");
            session.setAttribute("attPath", attList);
        }
        sb.append("</td></tr></table>");
        response.setContentType("text/xml");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Cache-Control", "no-cache");
        try{
            response.getWriter().write(sb.toString());
        }catch(java.io.IOException e){
            System.out.println(e + " Write message fail!");
        }
    }
 


因為我們是要將已完成的表格直接印出至頁面,因此不採用req.responseXML.xml 這個function。雖然在Servlet裡頭產生的well form XML,這但這裡不採用
的原因其實是因為Firefox不支援這個動作。所以我們改採以下的function。

1
req.responseText;


http://www.wretch.cc/album/show.php?i=hougzou&b=4&f=1333974465&p=9

由於不用換頁,因此此分頁的內容無須在重新reload,也無須重新查詢一次整個頁面的事件數。 即使系統忙碌,使用者也可藉由讀取中的小icon來得知目前
正在等待事件事件內容資料,而不像目前一般討論區在閱讀文章遇到系統忙碌時,使用者只能看著瀏覽器底下的進度欄緩慢地前進,頁面空白一片,不知所
謂。

當然,若要更狠一些,還可以將整個頁面都以JavaScript配合XMLHttpRequest來呈現出來,但我想那已經已經算是「純」Web 2.0 網站(Beta都應該還不算
是吧!)的範圍了吧。

註: 「純」web 2.0,我想每個人心中都有一把尺,我認為像是Google ig、start.com、以及Windows Live Mail beta 的純度應該比Yahoo奇摩那些大得多。


Jao edited on 2006-09-15 20:29
reply to postreply to post
The River with Turbidness
http://www.turbidness.net/
» JWorld@TW »  JDBC/SQL討論區 » JDBC 應用

reply to topicthreaded modego to previous topicgo to next topic
  已讀文章
  新的文章
  被刪除的文章
Jump to the top of page

JWorld@TW 本站商標資訊

Powered by Powerful JuteForum® Version Jute 1.5.8