1. 程式人生 > >Displaytag使用與應用displaytag完成大資料量分頁顯示的例子

Displaytag使用與應用displaytag完成大資料量分頁顯示的例子

Display Tag Lib是一個標籤庫,用來處理jsp網頁上的Table,功能非常強,可以對的Table進行分頁、資料匯出、分組、對列排序等等,反正我在做專案時需要的功能它都給我提供了,而且使用起來非常的方便。能夠大大減少程式碼量。
    介個是Display Tag的官方網站http://displaytag.sourceforge.net

    首先當然是要下載它的jar包了,這裡可以下載到最新的版本。將jar包放到WEB-INF的lib資料夾下。另外還需要兩個輔助包:apache的commons-lang和standard包,更多的輔助包可以在這裡下載

    在web.xml下新增一個filter
    <filter>
        <filter-name>exportFilter</filter-name>
        <filter-class>org.displaytag.filter.ResponseOverrideFilter</filter-class>
    </filter>

    在jsp頁面做一個引用:
<%@ taglib uri="http://displaytag.sf.net/el" prefix="display" %>

    首先我們定義一個list
<%
 List test = new ArrayList( 6 );
 test.add( "Test String 1" );
 test.add( "Test String 2" );
 test.add( "Test String 3" );
 test.add( "Test String 4" );
 test.add( "Test String 5" );
 test.add( "Test String 6" );
 request.setAttribute( "test", test );
%>

    當我們想在jsp頁面上顯示這個list時,我們只需要寫一句話
    <display:table name="test" />
    display tag會自動生成一個table

    如果list是從控制層丟擲來的,name可使用EL表示式表示
    <display:table name="${test}" />

    這是最簡單的display tag的使用,我們可以給它加上樣式等,也可以定義顯示的列,下面的table顯示覆雜一些
<display:table name="test" styleClass="list" cellspacing="0" cellpadding="0">
  <display:column property="id" title="ID" class="idcol"/>
  <display:column property="name" />
  <display:column property="email" />
  <display:column property="description" title="Comments"/>
</display:table>

    如果想要給它加個連結也很簡單,下面的程式碼給name加了連線,並附帶id引數,email也自動連線到mailto:XXX
<display:table name="test" styleClass="list" cellspacing="0" cellpadding="0">
  <display:column property="id" title="ID" class="idcol"/>
  <display:column property="name" url="detail.jsp" paramId="id" paramProperty="id"/>
  <display:column property="email" autolink="true"/>
  <display:column property="description" title="Comments"/>
</display:table>

下面介紹幾個Display最常用的功能,更多功能請參考

http://displaytag.homeip.net/displaytag-examples-1.1/
1. 分頁
    如果想對程式碼分頁,只需在display:table標籤中新增一項pagesize="每頁顯示行數",如
<display:table name="test" pagesize="10"/>

2. 對列排序
    display tag可對列進行排序,就是點選列名,對該列的資料進行排序。你只需對想要排序的列新增 sort="true" 就OK,如下面的程式碼可對前三列進行排序。在display:table中新增defaultsort="列數",可預設對指定的列排序。
<display:table name="test" styleClass="list" cellspacing="0" cellpadding="0" defaultsort="1">
  <display:column property="id" title="ID" class="idcol" sort="true"/>
  <display:column property="name" url="detail.jsp" paramId="id" paramProperty="id" sort="true"/>
  <display:column property="email" autolink="true" sort="true"/>
  <display:column property="description" title="Comments"/>
</display:table>
   如果table有分頁,Display Tag預設只對當前頁進行排序,如果想對整個list排序,可以在display:table之間新增一段程式碼:
<display:setProperty name="sort.amount" value="list"/>

3. 匯出資料
    在display:table中新增export="true",看看會出現什麼!Display Tag預設會提供三種資料匯出方式:CSV、Excel、XML 。
    另外Display Tag還可以匯出為PDF格式,在

http://prdownloads.sourceforge.net/itext/下載一個輔助包iText.jar,copy到lib目錄下,然後在display:table之間新增一段程式碼:
<display:setProperty name="export.pdf" value="true"/>,大功告成。

4. Display Tag的屬性設定
    前面所說的display:setProperty 是一種改變Display Tag屬性的方法,但是在每個jsp中都要寫太麻煩了。
    Display Tag中設定了很多預設的屬性,它有一個專門的屬性檔案,是在它的jar包中的displaytag/properties/TableTag.properties
    想要改變它的預設屬性,我們可以在WEB-INF\classes下新建一個檔案displaytag.properties,仿照TableTag.properties中屬性的格式設定需要修改的屬性。
    TableTag.properties中的# messages中設定的是顯示在頁面上的提示資訊。預設是英文的,我們可以把它改為中文的。不過這裡只能使用unicode,就是說中文字元必須轉換為 unicode碼,這個可以使用jdk自帶的native2ascii.exe進行轉換。

5. 其它功能
    DisplayTag還有一些很實用的小功能,這裡提兩個。一個是對資料的Format,這是1.1版本新增的新功能,可以使用標籤的方式格式化時間、數 字、字串。比如日期,在需要格式化的column標籤中新增format="{0,date,yyyy-MM-dd}",第一個引數為格式化的資料序 號,第二個引數是資料型別,數字為number,第三個引數為資料格式。
    另外一個功能是對table資料的合計功能。在table標籤中新增 decorator="org.displaytag.decorator.TotalTableDecorator",然後在想要進行合計的資料列的 column標籤中新增 total="true",該列就可以被計算總數了。但這個功能有個缺點,不能用在有分頁的時候,它只能合計第一頁的資料。

DisplayTag的不足
    初次使用DisplayTag的人可能會覺得驚喜,但是用久了會發現很多問題,最大的問題是對中文的支援不好,比如如果查詢條件中有中文,就無法翻頁,無 法對中文排序,將中文匯出為指定檔案時出現亂碼等等。這些問題有時候會讓人很鬱悶,有時候逼得你要去修改它的原始碼。下面是對以上幾個問題的解決方法:
    1. 對於中文無法翻頁、排序,最簡單的辦法是修改Tomcat下的server.xml檔案。找到HTTP的Connector標籤,在裡面新增一項 URIEncoding="...",引號裡面的內容取決於你的頁面編碼,比如可以是GBK,UTF8等。這樣上面兩個問題就可以解決了。

tomcat/conf/server.xml中找到 Connector 在後面加上 URIEncoding="UTF-8" 即可

connectionTimeout="20000"
redirectPort="8443" URIEncoding="UTF-8"/>
    2. 匯出為檔案:其實這個功能除了中文支援外還有很多其它問題,比如它會將Html標籤一起匯出、只匯出顯示的內容,但如果對table進行了 decorator,decorator後的內容無法匯出。如果想要將中文正確匯出,需要修改DisplayTag原始碼。
    下載相同版本的原始碼,在org.displaytag.export.ExcelView.java檔案中找到getMimeType()方法,將此方 法修改為 return "application/vnd.ms-excel;charset=GB2312";,修改後匯出資料的速度會慢很多,不過將就吧。
    3. 新版的DisplayTag1.1添加了對一次取部分資料的支援,相關的標籤包括partialList和size,需要設定partialList="true"和size的大小。具體怎麼用偶還沒研究。

首先,墨守成規地把這幾天的學習內容羅列一下。

然後,根據displaytag具有的缺陷以及解決辦法提出自己的無限失望。

好,開始列了,

displaytag 學習筆記總結(1)

6,列表的子集
 <display:table name="mylist" offset="m" length="n"/>
 offset 為第一個資料在mylist中的序號。
 length 為顯示的記錄條數。

7,自動設定連結
 能夠把此列的內容作為連結,連線的url和內容一致。
    當然必須格式有效的url,否則寫了也沒有連線。

 (
  自認為這個功能一般。
  因為連線的內容和顯示的內容通常不一樣。
  )
 當然可以在<display:column href="">在這裡給某列指定所有的連結為同一個連線。
 或者<display:column ><a href="">sdfs</a></display:column >兩者效果是一樣的。

 !!!那麼,如果連線的url不是列內容,而且每行的連結都不相同,這個時候怎麼辦?
  就好像廣告列表有商戶名稱,商戶名稱是一個連線,連線到商戶的具體資訊頁面。
  連線的url肯定不同。如何處理?
 
 現在還不知道,接著往下看。

8,使用裝飾類來轉換資料。
 

 <display:table name="test" decorator="org.displaytag.sample.Wrapper" >
 用來轉換資料內容的。一般用來格式化資訊。
 此類必繼承TableDecorator。
 順便介紹一個格式化時間的包  org.apache.commons.lang.time.FastDateFormat;
 格式化金錢的包  java.text.DecimalFormat;
 
 思路就是覆蓋List中具體型別的getXXX方法。
 this.getCurrentRowObject() 得到當前行的資料。
 this.getListIndex() 得到當前行的序號。
 之後就轉化為List中存放的具體型別,之後得到某列的資料。
 返回格式化後的資料。

 !!!!另外我看到,裝飾類增加了一些getXXX方法,這些方法並沒有在ListObject中定義。
 還不知道這樣做有什麼好處?
 
  <display:column property="date" decorator="org.displaytag.sample.LongDateWrapper" />
 這樣僅僅對一列資料進行格式化,需要繼承ColumnDecorator
 需要實現:String decorate(Object columnValue)方法。此方法同樣接受一個Object引數,
  此引數就是物件的某個具體屬性值,直接轉化為對應型別,然後格式化,返回String。


9,建立動態連結。
 兩種方式可以建立動態連結。
    第一種方式直接在頁面上使用
  <display:column href="baseurl" paramId="paramid" />
  http://baseurl/paramid=columnvalue
  <display:column href="baseurl" paramId="paramid"  paramName=“name” paramScope="scope"/>
  http://baseurl/paramid=scope範圍內的名字為name的值
  <display:column href="baseurl" paramId="paramid"  paramProperty=“propertyname”/>
  http://baseurl/paramid=名字為propertyname的屬性值

 這種方式處理簡單的連結十分有效,但是連結引數值如果通過查詢資料庫等比較複雜的方式得到的話,這種方式
 就不適應了。這時候就必須使用第二種方法。

 第二種方法,在裝飾類中獲得連線。
  裝飾類多定義一些getXXX方法,那麼頁面上就可以使用<display:column propety="XXX" ..>來使用裝飾類de
  getXXX方法。好,這樣一來,在裝飾類的getXXX方法裡,得到此行物件,這樣物件的各個屬性就都能得到,
  之後去查資料庫也好,去完成負責的判斷邏輯也好,都很容易實現,別忘了,然後拼裝起來,並寫成一個
  <a href="....">這樣的字串返回。
  好,頁面直接一句話就可得到此拼裝的《a》了。<display:column propety="XXX" ..>

 第二種方法比較好些,因為可以顯得更靈活。想怎麼寫就怎麼寫。第一種方法就是簡單地實現。
 
10,翻頁。
 1,怎麼得到每行的序號?
  <display:table id="xxx" ...>
  使用<%=pageContext.getAttribute("xxx_rowNum")%>肯定好用。
   但是<c:out value="${row_rowNum}"/>這種方式就不好用。也不知道差哪了。

  翻頁很簡單。就是在<display:table  pagesize="m">這樣來指定每頁的數量就可以了。
  自動出現換頁的索引,上下頁等等東西。
  實在是方便。但是問題就是一次傳下來很多東西,效能不好。等一會看看如何改良。


11,自動設定排序
  <display:table name="sessionScope.stest" defaultsort="1" defaultorder="descending">
  <display:column property="id" title="ID" sortable="true" headerClass="sortable" />
  可以設定預設時按照哪列排序,是升序還是降序。本例設定第一列預設時降序。
  每列都得物件都必須實現了Comparable接口才能被設定成sortable="true".
  如果沒有實現Comparable,那麼必須寫一個裝飾類。
  另外需要注意,
   他只對當前頁面進行排序。而不是全部。在翻頁時特別需要注意。
   要想實現全部的排序,那麼必須重新寫Action往網頁傳新的List了。

12,如何分組
  按照某列進行分組顯示。
  這個功能真不錯。
  <display:column property="city" title="CITY" group="1"/>
  <display:column property="project" title="PROJECT" group="2"/>
  這樣不僅僅結果可以分組,而且還可以省略掉重複的資料,例如city列,project列都有
  A ,B ,A,B 兩行那麼第二行這兩列就不顯示了。

  同樣只能對當前頁進行分組。

  注意:第一列一定要1,第二列一定是2,否則出NUllPoint 錯誤。

13,統計
  好東西啊。可以對分組進行統計,也可以對所有行進行統計。
  主要靠TableDecorator類finishRow()返回統計結果,放到頁面去顯示。

  TableDecorator類方法getDecoratedObject()得到整個結果集。一般把它轉化成List,因為大多數情況下結果
  是存放在List中的。
  public final String finishRow() {}當一行結束時執行此方法。所以用它來判斷是否需要計算城市統計,
   抑或是全部統計。
  相當於事件處理。返回的字串也將在頁面上顯示。本例返回了<tr><td>...</td><tr>這樣的三份。
  這樣就在表中巢狀進了三行。用來顯示統計是足夠了。

14,匯出資料
  很簡單。talbe裡設定export=“true”就行了。配置檔案也要設定好export.xml = true , 這樣才能匯出xml。
  類推pdf、excel、html、csv。
  每個列能配置是否在某種格式中顯示,語法<display:column media="csv excel" 。。
  不配置就在所有格式中都顯示。

  另外需要注意:
   被包含的檔案不能使用這個功能,非要使用,就的用過濾器。以後再深研究吧。

15,配置DisplayTag.
  在應用的classpath路徑上拷貝一個TableTag.properties,並命名為displaytag.properties.
  這樣就預設取displaytag.properties裡的配置了。想漢化,很簡單。就需要把displaytag_zh.properties放到
  classpath路徑下就可以了。

  標題像漢化:因為預設是jstl的資原始檔使用方式。所以得先學習jstl的
  我知道了jstl.fmt如何使用資原始檔。好了,jstl 首先必須用fmt:bunlle指定一個資原始檔,然後才能
  在他的body部分使用此資原始檔,頗麻煩。

  而displaytag呢? 沒有這麼麻煩,如果與struts搭配使用,適用struts的資原始檔當然最合理了。
  你必須在displaytag.properties裡定義好一個
  引數,#locale.provider=org.displaytag.localization.I18nJstlAdapter
   locale.provider=org.displaytag.localization.I18nStrutsAdapter
  就這麼簡單,就能夠使用struts配置檔案了。我今天竟然看了一下午。哎,苦於沒有好點的資料啊。

  問題是:如果不合struts搭配使用。如果僅僅在jstl環境下,displaytag又該如何使用jstl的資原始檔呢?
  你必須象在jstl環境裡一樣使用<fmt:bundl>指定好資原始檔,之後把displaytag標籤放到他的body
  後,就可以使用jstl的資原始檔了!!!!(幸虧我首先看了jstl使用資原始檔的方法)


16,一個頁面兩個以上的表格.
  很簡單,只需要每個表格配置不同的id.

17,表格裡面還有表格.
  很簡單.只要在外層表的
  <dispaly:column>
   ( 在這加<display:table ...>。。。
   </display:table>即可)
  </display:column>
 
  
  
18,表頭表尾
  很簡單:<dispaly:caption><display:footer>想<display:column>一樣使用就行了.
  主要注意一下: <display:footer>內容必須是<tr>.....</tr>

19,表格裡的值截斷,與顯示空白.
 <display:column property="nullValue" nulls="false"/>
   <display:column property="longDescription" maxLength="10" style="whitespace: nowrap;"/>
 簡單得很.

ok,差不多到了關鍵的時候了。!!!!!

=============================================
關鍵:
20,如何分頁.
 
 因為displaytag的分頁機制需要一次把所有紀錄都傳到裡面.所以對紀錄很多的專案並不合適.如果還非得用
 displaytag那隻好放棄他的一些功能了.比較理智的方法是僅僅讓displaytag獲得一頁的list.這樣就不用displaytag
 的分頁index,而使用自己的,或者寫customertag,或者使用jsppager tag.

 有很多人質疑這種方法,因為這使得displaytag的光芒至少減少了一半以上,因為他的很多功能因此而無法使用,
 例如排序功能,可以針對所有list進行全域性排序,如果每次只傳給一頁的資料,全域性排序就和當頁排序沒有區別了.
 displaytag的全域性排序功能宣佈廢掉了.

 <display:table name="mylist" offset="m" length="n"/>這樣的功能基本上也沒有什麼用處。


 那麼還使用它幹什麼呢? displaytag即使廢掉了一些武功仍然有它的優點.例如
 1,支援表格的巢狀.
 2,支援css這樣就能寫少量地程式碼使得程式更簡潔.只好定義好一套css,就能全域性範圍內使用了.
 3,另外還支援截斷長的字串,這樣就不用在頁面使用自己的方法了. 
 4,能夠實現比較複雜的邏輯從而產生動態連結。這也勉強所以個好處吧。
 5,能輕鬆地使用struts的資原始檔。使得它可以很好的國際化。
 6,能夠過濾空值。


 缺點: 
 1,分組就使用不了了,統計也無法使用了。只統計當前頁是沒有意義的。
 2,排序也變成當前頁排序了。沒多少意思。
 3,匯出也變成當前頁匯出了,沒多少意思了。

應用displaytag在struts中完成大資料量分頁顯示,Oracle資料庫

JSP檔案:

<% ... @ taglib uri = " /WEB-INF/displaytag.tld "  prefix = " disp "   %>

 < disp:table  name ="resultList"  export ="true"  pagesize ="100"  requestURI ="logQueryAction.do"  sort ="external"  id ="element"  partialList ="true"  size ="resultSize" >
            < disp:column  property ="operdate"  title ="操作時間"   ></ disp:column >
            < disp:column  property ="pername"  title ="操作人員"   ></ disp:column >
            < disp:column  property ="opertype"  title ="操作型別"   ></ disp:column >
 </ disp:table > 
 
name="resultList" 將記錄集存在session或者request中的鍵值
export="true" 是否顯示匯出選項
pagesize="100" 每頁顯示100條資料
requestURI="logQueryAction.do"  struts中action的名稱,如果記錄少,可以直接分頁
sort="external"  外部排序
id="element"   表格id值,用於程式得相關的引數
partialList="true"  分段從資料庫中讀資料
size="resultSize"  記錄的總條數,用於計算總頁數

struts action:

String pageIndexName  =   new  org.displaytag.util.ParamEncoder( " element " ).encodeParameterName(org.displaytag.tags.TableTagParameters.PARAMETER_PAGE);   // 頁數的引數名
          int  pageSize  =   100 ;   //每頁顯示的條數
          int  pageIndex  =  GenericValidator.isBlankOrNull(request.getParameter(pageIndexName)) ? 0 :(Integer.parseInt(request.getParameter(pageIndexName))  -   1 );  //當前頁數
 String sqlCount  =   " select count(*)  from user_log a  " ;  //用於統計總記錄數的sql語句
         String sql  =   "  select * from (select rownum as rid, t1.* from (select b.pername as pername,to_char(a.operdate,'yyyy-mm-dd hh24:mi:ss') as operdate, "   +
                  "  decode(a.opertype,'D','刪除','M','修改','其他 ') as opertype, a.hphm as hphm from user_log a,  "   +
                  "  (select asuser.userid as userid,nvl(asempmsg.pername,asuser.loginname) as pername from asuser,ASEMPMSG where asuser.perid=ASEMPMSG.perid(+)) b "   +
                  "  where a.userid=b.userid  " ;   // 查詢語句
            //構造查詢條件
         StringBuffer sb  =   new  StringBuffer();
          if (logQueryForm.getCzrqStart() != null   &&   ! "" .equals(logQueryForm.getCzrqStart())) ... {
             sb.append( "  and a.operdate > to_date(' " + logQueryForm.getCzrqStart() + " ','yyyy-mm-dd') " );
         }
          if (logQueryForm.getCzrqEnd() != null   &&   ! "" .equals(logQueryForm.getCzrqEnd())) ... {
             sb.append( "  and a.operdate <= to_date(' " + logQueryForm.getCzrqEnd() + " ','yyyy-mm-dd') " );
         }
          if (logQueryForm.getCzlx() != null   &&   ! "" .equals(logQueryForm.getCzlx())) ... {
             sb.append( "  and a.opertype = ' " + logQueryForm.getCzlx() + " ' " );
         }
          if (logQueryForm.getCzry() != null   &&   ! "" .equals(logQueryForm.getCzry())) ... {
             sb.append( "  and a.userid = ' " + logQueryForm.getCzry() + " ' " );
         }
      
         sqlCount  +=  sb.toString();
         sql  +=  sb.toString() + "  order by a.operdate desc) t1 where rownum<= "
                          +  (pageIndex  +   1 )  *  pageSize  +   "  ) t2 where t2.rid> " +  pageIndex  *  pageSize;  //分頁讀取語句
        
          // System.out.println(sb.toString());
         DBBean db  =   new  DBBean();
         ResultSet rs  =   null ;
         PreparedStatement prep  =   null ;
          try ... {
             List resultList  =  db.getResultList(sql);   //將ResultSet儲存在List裡返回
             request.setAttribute( " resultList " ,resultList);  //把結果存入request
             prep  =  db.getConnection().prepareStatement(sqlCount);   
             rs  =  prep.executeQuery();
              if (rs.next()) ... {
                 request.setAttribute( " resultSize " , new  Integer(rs.getInt( 1 )));     //將總記錄數儲存成Intger例項儲存在request中
             }
         }
          catch (Exception ex) ... {
             ex.printStackTrace();
         }
          finally ... {
              if (db != null ) ... {
                 db.closeConnection();    //關閉連線
             } 
         }
其中的頁面導航是英文的,只要修改org.displaytag.properties.TableTag.properties配置檔案就可以把英文改成中文。同時還可以指定匯出檔案的型別以及檔名。

export.excel = true
 export.excel.label = <span class = " export excel " >Excel </span>
 export.excel.include_header = true
 export.excel.filename = export.xls
如果不指定檔名,在點選匯出excel的時候,就會在ie視窗中開啟excel檔案。很煩人!
指定檔名後就可以選擇儲存和打開了。

< type="text/javascript"><!-- google_ad_client = "pub-9648238652058580"; google_ad_width = 468; google_ad_height = 60; google_ad_format = "468x60_as_rimg"; google_cpa_choice = "CAAQiYaYhAIaCJ2wcuQYTrQ_KOm293M"; google_ad_channel = ""; //-->< type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js">