displaytag數據庫分頁方法及導出數據的問題處理
文章分類: Java編程
最近在使用 springside 配合 struts2 + spring2.5 + hibernate3 做項目,為了不重復發明輪子(其實是懶,而且項目時間有限),使用到了 displaytag 作為頁面顯示的組件。 displaytag 具有分頁顯示,導出報表,頁面排序等功能。與其相似的還有另外一個工具叫做 extremecomponent 。雖然 displaytag 功能很多,但是也有很多問題:第一就是他的默認分頁和排序是基于內存分頁的,這個在實際項目中是不太可行的,所以需要修改。第二就是他的導出報表的功能,他主要能導出 5 種格式 csv , xls , xml , pdf , rtf (主要是 pdf 和 excel ),其中默認的 pdf 和 excel 導出都有亂碼問題。
?
本文主要是講述如何解決這 2 個問題。
displaytag 官網: http://displaytag.sourceforge.net/1.2/
基本的配置和使用你可以從官網上了解到,本文不做說明。
本文的解決方法一部分是來自于網絡其他文章。
好了首先是第一個分頁問題,我們要使 displaytag 能夠用數據庫分頁,而不僅僅是內存分頁。 displaytag 其實已經提供了 2 種方法讓你實現數據庫,在其官網中有介紹
http://displaytag.sourceforge.net/1.2/tut_externalSortAndPage.html
我使用的是第一種實現,實現 displaytag 的 PaginatedList 接口:
?
- import ?java.util.List; ??
- ??
- import ?org.displaytag.pagination.PaginatedList; ??
- import ?org.displaytag.properties.SortOrderEnum; ??
- import ?org.springside.modules.orm.Page; ??
- ??
- /** ?
- ?*?實現displaytag的數據庫的分頁接口 ?
- ?*?并集成springside的Page類, ?
- ?*?這樣可以同時使用?springside的數據庫分頁功能及displaytag的分頁顯示功能 ?
- ?*?@author?tianjl ?
- ?* ?
- ?*?@param?<T> ?
- ?*/ ??
- public ? class ?DBPaginatedList<T>? extends ?Page<T>? implements ?PaginatedList?{ ??
- ??
- ???? private ?SortOrderEnum?sortDirection?=?SortOrderEnum.ASCENDING; ??
- ???? private ?String?sortCriterion; ??
- ??
- ???? //?--?構造函數?--// ??
- ???? public ?DBPaginatedList()?{ ??
- ???????? super (); ??
- ????} ??
- ??
- ???? public ?DBPaginatedList( int ?pageSize)?{ ??
- ???????? super (pageSize); ??
- ????} ??
- ??
- ???? public ? int ?getFullListSize()?{ ??
- ???????? return ?( int )?totalCount; ??
- ????} ??
- ??
- ???? public ?List<T>?getList()?{ ??
- ???????? return ?result; ??
- ????} ??
- ??
- ???? public ? int ?getObjectsPerPage()?{ ??
- ???????? return ?pageSize; ??
- ????} ??
- ??
- ???? public ? int ?getPageNumber()?{ ??
- ???????? return ?pageNo; ??
- ????} ??
- ??
- ???? public ? void ?setSortCriterion(String?fieldN)?{ ??
- ???????? this .sortCriterion?=?(fieldN?==? null ?||? "null" .equals(fieldN))??? "1" ??
- ????????????????:?fieldN; ??
- ????} ??
- ??
- ???? public ?String?getSortCriterion()?{ ??
- ???????? return ? this .sortCriterion; ??
- ????} ??
- ??
- ???? public ? void ?setSortDirection(String?dir)?{ ??
- ???????? if ?(dir?==? null ?||? "null" .equals(dir)?||? "asc" .equalsIgnoreCase(dir))?{ ??
- ????????????sortDirection?=?SortOrderEnum.ASCENDING; ??
- ????????}? else ?{ ??
- ????????????sortDirection?=?SortOrderEnum.DESCENDING; ??
- ????????} ??
- ????} ??
- ???? ??
- ???? public ? void ?setSortDirection(SortOrderEnum?sortDirection)?{ ??
- ???????? this .sortDirection?=?sortDirection; ??
- ????} ??
- ??
- ???? public ?String?getSortDirectionStr()?{ ??
- ???????? if ?(sortDirection?==?SortOrderEnum.ASCENDING) ??
- ???????????? return ? "ASC" ; ??
- ???????? else ??
- ???????????? return ? "DESC" ; ??
- ????} ??
- ??
- ???? public ?SortOrderEnum?getSortDirection()?{ ??
- ???????? return ?sortDirection; ??
- ????} ??
- ??
- ???? public ?String?getSearchId()?{ ??
- ???????? return ?Integer.toHexString(pageSize?*? 10000 ?+?pageNo); ??
- ????} ??
- ??
- }??
import java.util.List; import org.displaytag.pagination.PaginatedList; import org.displaytag.properties.SortOrderEnum; import org.springside.modules.orm.Page; /** * 實現displaytag的數據庫的分頁接口 * 并集成springside的Page類, * 這樣可以同時使用 springside的數據庫分頁功能及displaytag的分頁顯示功能 * @author tianjl * * @param <T> */ public class DBPaginatedList<T> extends Page<T> implements PaginatedList { private SortOrderEnum sortDirection = SortOrderEnum.ASCENDING; private String sortCriterion; // -- 構造函數 --// public DBPaginatedList() { super(); } public DBPaginatedList(int pageSize) { super(pageSize); } public int getFullListSize() { return (int) totalCount; } public List<T> getList() { return result; } public int getObjectsPerPage() { return pageSize; } public int getPageNumber() { return pageNo; } public void setSortCriterion(String fieldN) { this.sortCriterion = (fieldN == null || "null".equals(fieldN)) ? "1" : fieldN; } public String getSortCriterion() { return this.sortCriterion; } public void setSortDirection(String dir) { if (dir == null || "null".equals(dir) || "asc".equalsIgnoreCase(dir)) { sortDirection = SortOrderEnum.ASCENDING; } else { sortDirection = SortOrderEnum.DESCENDING; } } public void setSortDirection(SortOrderEnum sortDirection) { this.sortDirection = sortDirection; } public String getSortDirectionStr() { if (sortDirection == SortOrderEnum.ASCENDING) return "ASC"; else return "DESC"; } public SortOrderEnum getSortDirection() { return sortDirection; } public String getSearchId() { return Integer.toHexString(pageSize * 10000 + pageNo); } }
?
?
這里要講一下, Page 類是 springside 提供的對分頁功能的封裝。這里你應該去掉繼承 Page 類,并配合你自己的數據庫分頁功能。在 Page 類中,有一個內部的 List 對象,存放著真實的結果集數據。 displaytag 在分析時,會判斷你傳給他的對象是否實現了 PaginatedList 接口,如果實現了,它會從該對象中調用 getList() 方法,獲取結果集。所以在你的使用中,你應該注意你的數據庫分頁代碼和 PaginatedList 接口的實現類的配合。
- public ? interface ?PaginatedList{ ??
- ???? /** ?
- ?????*?獲取分頁的結果集 ?
- ?????*/ ??
- ????List?getList(); ??
- ??
- ???? /** ?
- ?????*?獲取當前頁數 ?
- ?????*/ ??
- ???? int ?getPageNumber(); ??
- ??
- ???? /** ?
- ?????*?獲取每頁的個數 ?
- ?????*/ ??
- ???? int ?getObjectsPerPage(); ??
- ??
- ???? /** ?
- ?????*?獲取整個結果集的個數 ?
- ?????*/ ??
- ???? int ?getFullListSize(); ??
- ??
- ???? /** ?
- ?????*?獲取根據哪一列排序 ?
- ?????*/ ??
- ????String?getSortCriterion(); ??
- ??
- ???? /** ?
- ?????*?升序還是降序 ?
- ?????*/ ??
- ????SortOrderEnum?getSortDirection(); ??
- ??
- ???? /** ?
- ?????*?返回一個查詢的id ?
- ?????*/ ??
- ????String?getSearchId(); ??
- }??
public interface PaginatedList{ /** * 獲取分頁的結果集 */ List getList(); /** * 獲取當前頁數 */ int getPageNumber(); /** * 獲取每頁的個數 */ int getObjectsPerPage(); /** * 獲取整個結果集的個數 */ int getFullListSize(); /** * 獲取根據哪一列排序 */ String getSortCriterion(); /** * 升序還是降序 */ SortOrderEnum getSortDirection(); /** * 返回一個查詢的id */ String getSearchId(); }
?
?
這里請注意,你的數據庫分頁代碼和 displaytag 的分頁其實是 2 個不同的實現,也就是說,你在之后的使用中,要同時為你數據庫分頁的代碼設置分頁值,排序值,并且也要為 PaginatedList 接口的實現類設置分頁值及排序值。 當然你如果設計的好,能將 2 者柔和在一起,你可以只設置一次。我這里還是設置 2 次值的。 我這里寫了個工具類還是設置 2 次值的。他從request中獲取分頁參數,放到 PaginatedList 的實現類中,實際的數據庫分頁參數由struts2自動封裝到action中的Page對象中,其實這個類寫的有些多余,完全可以在 PaginatedList 的實現類內部解決問題,根本不需要引入HttpServletRequest 。
- /** ?
- ?*?分頁工具類 ?
- ?*? ?
- ?*?@author?tianjl ?
- ?*? ?
- ?*/ ??
- public ? class ?PaginationUtil?{ ??
- ??
- ???? /** ?
- ?????*?該方法用于displaytag的參數到springside的page的分頁參數做轉換 ?
- ?????*? ?
- ?????*?@param?request ?
- ?????*????????????(HttpServletRequest)?用于獲取請求參數 ?
- ?????*?@param?page ?
- ?????*????????????(DBPaginatedList)?springside的Page對象 ?
- ?????*/ ??
- ???? public ? static ? void ?pageInfoPopulate(HttpServletRequest?request, ??
- ????????????DBPaginatedList?page)?{ ??
- ???????? if ?(request?==? null ?||?page?==? null ) ??
- ???????????? return ; ??
- ???????? else ?{ ??
- ????????????String?pagestr?=?request.getParameter( "page" ); ??
- ????????????String?sort?=?request.getParameter( "sort" ); ??
- ????????????String?dir?=?request.getParameter( "dir" ) ??
- ???????????? if ?(!StringUtils.isBlank(pagestr))?{ ??
- ????????????????page.setPageNo(NumberUtils.toInt(pagestr)); ??
- ????????????} ??
- ???????????? if ?(!StringUtils.isBlank(sort))?{ ??
- ????????????????page.setOrderBy(sort); ??
- ????????????????page.setSortCriterion(sort); ??
- ????????????} ??
- ???????????? if ?(!StringUtils.isBlank(dir))?{ ??
- ???????????????? if ?(StringUtils.equals(Page.ASC,?dir))?{ ??
- ????????????????????page.setOrder(Page.ASC); ??
- ????????????????}? else ?{ ??
- ????????????????????page.setOrder(Page.DESC); ??
- ????????????????} ??
- ????????????????page.setSortDirection(dir); ??
- ????????????} ??
- ????????} ??
- ????} ??
- }??
/** * 分頁工具類 * * @author tianjl * */ public class PaginationUtil { /** * 該方法用于displaytag的參數到springside的page的分頁參數做轉換 * * @param request * (HttpServletRequest) 用于獲取請求參數 * @param page * (DBPaginatedList) springside的Page對象 */ public static void pageInfoPopulate(HttpServletRequest request, DBPaginatedList page) { if (request == null || page == null) return; else { String pagestr = request.getParameter("page"); String sort = request.getParameter("sort"); String dir = request.getParameter("dir") if (!StringUtils.isBlank(pagestr)) { page.setPageNo(NumberUtils.toInt(pagestr)); } if (!StringUtils.isBlank(sort)) { page.setOrderBy(sort); page.setSortCriterion(sort); } if (!StringUtils.isBlank(dir)) { if (StringUtils.equals(Page.ASC, dir)) { page.setOrder(Page.ASC); } else { page.setOrder(Page.DESC); } page.setSortDirection(dir); } } } }
?然后頁面中寫上displaytag的標簽:
- <display:table?id= "content" ?name= "pageList" ?requestURI= "/security/user.do" ? ??
- ????size= "totalCount" ?pagesize= "${pageList.pageSize?}" ?partialList= "true" > ??
- ????<display:column?property= "id" ?sortable= "true" ?style= "width:2%" /> ??
- ????<display:column?property= "name" ?title= "姓名" ?sortable= "true" ?/> ??
- ????<display:column?property= "loginName" ?title= "登錄名" ?sortable= "true" ?/> ??
- ????<display:column?property= "email" ?title= "郵箱" ?sortable= "true" ?/> ??
- </display:table>??
<display:table id="content" name="pageList" requestURI="/security/user.do" size="totalCount" pagesize="${pageList.pageSize }" partialList="true"> <display:column property="id" sortable="true" style="width:2%"/> <display:column property="name" title="姓名" sortable="true" /> <display:column property="loginName" title="登錄名" sortable="true" /> <display:column property="email" title="郵箱" sortable="true" /> </display:table>
?
?
這里有幾個點要注意, size 屬性中必須指定一個總頁數,而且必須是 int 型的, springside 中 Page 類的總頁數是 long 型的,所以我又自己定義了個 totalCount ,將值給 size 屬性。 partialList 屬性必須指定為 true ,這樣 displaytag 才會使用你的分頁。
到這里已經可以使 displaytag 使用你提供的數據庫分頁了。
然后,我們來解決導出數據的問題。導出數據是個很常用的功能,基本上是個系統都要這個功能。 displaytag 是提供了該功能的,但是問題太多。一個是當你讓 displaytag 使用了數據庫分頁后,只能導出當前頁的數據;另一個問題是導出數據中文亂碼的問題。
第一個問題需要修改源代碼,給與實際解決方案文章在此:
http://superwing.iteye.com/blog/427407
你需要在 org.displaytag.tags.TableTag.java 中,將 1065 以及 1066 兩行進行替換:
將
- PaginationHelper?paginationHelper?=? new ?PaginationHelper(pageNumber,?pagesize); ??
- this .tableIterator?=?paginationHelper.getIterator( this .list);???
PaginationHelper paginationHelper = new PaginationHelper(pageNumber, pagesize); this.tableIterator = paginationHelper.getIterator(this.list);
?替換為:
- if (MediaTypeEnum.HTML.equals( this .currentMediaType)){ ??
- ????PaginationHelper?paginationHelper?=? new ?PaginationHelper(pageNumber,?pagesize); ??
- ???? this .tableIterator?=?paginationHelper.getIterator( this .list); ??
- } else ?{ ??
- ???? this .tableIterator?=?IteratorUtils.getIterator( this .list); ??
- }???
if(MediaTypeEnum.HTML.equals(this.currentMediaType)){ PaginationHelper paginationHelper = new PaginationHelper(pageNumber, pagesize); this.tableIterator = paginationHelper.getIterator(this.list); }else { this.tableIterator = IteratorUtils.getIterator(this.list); }
?
在你的 action 種的代碼中,你需要知道用戶是否點擊了導出超鏈接,通過以下代碼可以獲取該值:
?
- String?exportValue?=?request.getParameter(TableTagParameters.PARAMETER_EXPORTING);??
String exportValue = request.getParameter(TableTagParameters.PARAMETER_EXPORTING);
?
?
如果該值不為空,則表示用戶按了導出,否則就是沒有。你應該根據此值來選擇是查詢部分內容還是全部內容。比如:
- if ?(exportValue?==? null ?||?exportValue.equals( "" ))?{ ??
- ???? //非導出,根據查詢條件filters進行分頁查詢 ??
- ????pageList?=?securityEntityManager.searchUser(pageList,?filters); ??
- }? else ?{ ??
- ???? //導出,根據查詢條件filters查詢出所有結果集 ??
- ????List<User>?userList?=?securityEntityManager.getAllUser(pageList,?filters); ??
- ????pageList.setResult(userList); ??
- }??
if (exportValue == null || exportValue.equals("")) { //非導出,根據查詢條件filters進行分頁查詢 pageList = securityEntityManager.searchUser(pageList, filters); } else { //導出,根據查詢條件filters查詢出所有結果集 List<User> userList = securityEntityManager.getAllUser(pageList, filters); pageList.setResult(userList); }?
好,第一個導出問題解決完畢。
接下去是亂碼問題,我這里只說明 excel 和 pdf 的處理。解決方法也是來自于網絡。
excel 的導出處理相對簡單,在你項目的 classpath 中加入 displaytag.properties 文件,加入以下代碼即可。
- export.excel. class =org.displaytag.export.excel.ExcelHssfView??
export.excel.class=org.displaytag.export.excel.ExcelHssfView
? 注意該屬性表示使displaytag使用poi導出excel,所以你需要加入displaytag-export-poi-1.2.jar和poi的jar包,而且只能使用poi3.2版本的jar包,其他版本都會報錯說NoSuchMethod,但是我看poi源碼,明明是有該方法的,不知道什么原因。(我使用過poi3.5和poi3.0都報了該錯)
pdf的亂碼處理,需要下載iTextAsian.jar,然后加入你的classpath,再修改源代碼org.displaytag.export.PdfView的115行處:
- //將 ??
- smallFont?=?FontFactory.getFont(FontFactory.HELVETICA,? 7 ,?Font.NORMAL,? new ?Color( 0 ,? 0 ,? 0 )); ??
- //替換為: ??
- smallFont?=?FontFactory.getFont( "STSong-Light" , "UniGB-UCS2-H" ,?Font.DEFAULTSIZE);??
//將 smallFont = FontFactory.getFont(FontFactory.HELVETICA, 7, Font.NORMAL, new Color(0, 0, 0)); //替換為: smallFont = FontFactory.getFont("STSong-Light","UniGB-UCS2-H", Font.DEFAULTSIZE);
?這樣pdf亂碼也可以解決了。
最后再引申幾個我遇到的其他問題:
excel導出的內容標題看不清,這個需要修改源碼,找到org.displaytag.export.excel.ExcelHssfView的101到107行處是設置標題樣式的,主要是把102行:
- //將 ??
- headerStyle.setFillPattern(HSSFCellStyle.FINE_DOTS); ??
- //替換為: ??
- headerStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);??
//將 headerStyle.setFillPattern(HSSFCellStyle.FINE_DOTS); //替換為: headerStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
?這樣,導出的標題就不會看不清了。
另外一個問題,是因為displaytag和struts2一起使用導致的,由于displaytag生成的參數中帶“-”,而struts2中接受的參數中默認又不允許有“-”,控制臺會報錯,但是卻不影響使用。不過控制臺老報錯,看著也不爽。
在 http://hi.baidu.com/j2ee_cn/blog/item/688a03db5a48846cd1164e66.html
說修改xwork源碼可以解決該問題,我照做確實解決了,但是又出現了其他問題,struts2不會替我自動封裝參數到action的對象中了,所以我又改了回來。大家如果用的也是struts2可以試試,是不是有這樣的問題。
另一個方法是只要將,devMode設置為false就不會報這個錯了,這個的確可以解決該問題。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
