Displaytag使用與應用displaytag完成大資料量分頁顯示的例子
介個是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最常用的功能,更多功能請參考
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格式,在
<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檔案。很煩人!
指定檔名後就可以選擇儲存和打開了。