displaytag数据库分页方法及导出数据的问题处理

系统 1937 0

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 接口:

 

Java代码 复制代码
  1. import  java.util.List;   
  2.   
  3. import  org.displaytag.pagination.PaginatedList;   
  4. import  org.displaytag.properties.SortOrderEnum;   
  5. import  org.springside.modules.orm.Page;   
  6.   
  7. /**  
  8.  * 实现displaytag的数据库的分页接口  
  9.  * 并集成springside的Page类,  
  10.  * 这样可以同时使用 springside的数据库分页功能及displaytag的分页显示功能  
  11.  * @author tianjl  
  12.  *  
  13.  * @param <T>  
  14.  */   
  15. public   class  DBPaginatedList<T>  extends  Page<T>  implements  PaginatedList {   
  16.   
  17.      private  SortOrderEnum sortDirection = SortOrderEnum.ASCENDING;   
  18.      private  String sortCriterion;   
  19.   
  20.      // -- 构造函数 --//   
  21.      public  DBPaginatedList() {   
  22.          super ();   
  23.     }   
  24.   
  25.      public  DBPaginatedList( int  pageSize) {   
  26.          super (pageSize);   
  27.     }   
  28.   
  29.      public   int  getFullListSize() {   
  30.          return  ( int ) totalCount;   
  31.     }   
  32.   
  33.      public  List<T> getList() {   
  34.          return  result;   
  35.     }   
  36.   
  37.      public   int  getObjectsPerPage() {   
  38.          return  pageSize;   
  39.     }   
  40.   
  41.      public   int  getPageNumber() {   
  42.          return  pageNo;   
  43.     }   
  44.   
  45.      public   void  setSortCriterion(String fieldN) {   
  46.          this .sortCriterion = (fieldN ==  null  ||  "null" .equals(fieldN)) ?  "1"   
  47.                 : fieldN;   
  48.     }   
  49.   
  50.      public  String getSortCriterion() {   
  51.          return   this .sortCriterion;   
  52.     }   
  53.   
  54.      public   void  setSortDirection(String dir) {   
  55.          if  (dir ==  null  ||  "null" .equals(dir) ||  "asc" .equalsIgnoreCase(dir)) {   
  56.             sortDirection = SortOrderEnum.ASCENDING;   
  57.         }  else  {   
  58.             sortDirection = SortOrderEnum.DESCENDING;   
  59.         }   
  60.     }   
  61.        
  62.      public   void  setSortDirection(SortOrderEnum sortDirection) {   
  63.          this .sortDirection = sortDirection;   
  64.     }   
  65.   
  66.      public  String getSortDirectionStr() {   
  67.          if  (sortDirection == SortOrderEnum.ASCENDING)   
  68.              return   "ASC" ;   
  69.          else   
  70.              return   "DESC" ;   
  71.     }   
  72.   
  73.      public  SortOrderEnum getSortDirection() {   
  74.          return  sortDirection;   
  75.     }   
  76.   
  77.      public  String getSearchId() {   
  78.          return  Integer.toHexString(pageSize *  10000  + pageNo);   
  79.     }   
  80.   
  81. }  
      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 接口的实现类的配合。

Java代码 复制代码
  1. public   interface  PaginatedList{   
  2.      /**  
  3.      * 获取分页的结果集  
  4.      */   
  5.     List getList();   
  6.   
  7.      /**  
  8.      * 获取当前页数  
  9.      */   
  10.      int  getPageNumber();   
  11.   
  12.      /**  
  13.      * 获取每页的个数  
  14.      */   
  15.      int  getObjectsPerPage();   
  16.   
  17.      /**  
  18.      * 获取整个结果集的个数  
  19.      */   
  20.      int  getFullListSize();   
  21.   
  22.      /**  
  23.      * 获取根据哪一列排序  
  24.      */   
  25.     String getSortCriterion();   
  26.   
  27.      /**  
  28.      * 升序还是降序  
  29.      */   
  30.     SortOrderEnum getSortDirection();   
  31.   
  32.      /**  
  33.      * 返回一个查询的id  
  34.      */   
  35.     String getSearchId();   
  36. }  
      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

Java代码 复制代码
  1. /**  
  2.  * 分页工具类  
  3.  *   
  4.  * @author tianjl  
  5.  *   
  6.  */   
  7. public   class  PaginationUtil {   
  8.   
  9.      /**  
  10.      * 该方法用于displaytag的参数到springside的page的分页参数做转换  
  11.      *   
  12.      * @param request  
  13.      *            (HttpServletRequest) 用于获取请求参数  
  14.      * @param page  
  15.      *            (DBPaginatedList) springside的Page对象  
  16.      */   
  17.      public   static   void  pageInfoPopulate(HttpServletRequest request,   
  18.             DBPaginatedList page) {   
  19.          if  (request ==  null  || page ==  null )   
  20.              return ;   
  21.          else  {   
  22.             String pagestr = request.getParameter( "page" );   
  23.             String sort = request.getParameter( "sort" );   
  24.             String dir = request.getParameter( "dir" )   
  25.              if  (!StringUtils.isBlank(pagestr)) {   
  26.                 page.setPageNo(NumberUtils.toInt(pagestr));   
  27.             }   
  28.              if  (!StringUtils.isBlank(sort)) {   
  29.                 page.setOrderBy(sort);   
  30.                 page.setSortCriterion(sort);   
  31.             }   
  32.              if  (!StringUtils.isBlank(dir)) {   
  33.                  if  (StringUtils.equals(Page.ASC, dir)) {   
  34.                     page.setOrder(Page.ASC);   
  35.                 }  else  {   
  36.                     page.setOrder(Page.DESC);   
  37.                 }   
  38.                 page.setSortDirection(dir);   
  39.             }   
  40.         }   
  41.     }   
  42. }  
      /**
 * 分页工具类
 * 
 * @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的标签:

Java代码 复制代码
  1. <display:table id= "content"  name= "pageList"  requestURI= "/security/user.do"     
  2.     size= "totalCount"  pagesize= "${pageList.pageSize }"  partialList= "true" >   
  3.     <display:column property= "id"  sortable= "true"  style= "width:2%" />   
  4.     <display:column property= "name"  title= "姓名"  sortable= "true"  />   
  5.     <display:column property= "loginName"  title= "登录名"  sortable= "true"  />   
  6.     <display:column property= "email"  title= "邮箱"  sortable= "true"  />   
  7. </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 两行进行替换:

Java代码 复制代码
  1. PaginationHelper paginationHelper =  new  PaginationHelper(pageNumber, pagesize);   
  2. this .tableIterator = paginationHelper.getIterator( this .list);   
      PaginationHelper paginationHelper = new PaginationHelper(pageNumber, pagesize);
this.tableIterator = paginationHelper.getIterator(this.list); 
    

 替换为:

Java代码 复制代码
  1. if (MediaTypeEnum.HTML.equals( this .currentMediaType)){   
  2.     PaginationHelper paginationHelper =  new  PaginationHelper(pageNumber, pagesize);   
  3.      this .tableIterator = paginationHelper.getIterator( this .list);   
  4. } else  {   
  5.      this .tableIterator = IteratorUtils.getIterator( this .list);   
  6. }   
      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 种的代码中,你需要知道用户是否点击了导出超链接,通过以下代码可以获取该值:

 

Java代码 复制代码
  1. String exportValue = request.getParameter(TableTagParameters.PARAMETER_EXPORTING);  
      String exportValue = request.getParameter(TableTagParameters.PARAMETER_EXPORTING);
    

 

 

如果该值不为空,则表示用户按了导出,否则就是没有。你应该根据此值来选择是查询部分内容还是全部内容。比如:

Java代码 复制代码
  1. if  (exportValue ==  null  || exportValue.equals( "" )) {   
  2.      //非导出,根据查询条件filters进行分页查询   
  3.     pageList = securityEntityManager.searchUser(pageList, filters);   
  4. else  {   
  5.      //导出,根据查询条件filters查询出所有结果集   
  6.     List<User> userList = securityEntityManager.getAllUser(pageList, filters);   
  7.     pageList.setResult(userList);   
  8. }  
      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 文件,加入以下代码即可。

Java代码 复制代码
  1. 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行处:

Java代码 复制代码
  1. //将   
  2. smallFont = FontFactory.getFont(FontFactory.HELVETICA,  7 , Font.NORMAL,  new  Color( 0 0 0 ));   
  3. //替换为:   
  4. 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行:

Java代码 复制代码
  1. //将   
  2. headerStyle.setFillPattern(HSSFCellStyle.FINE_DOTS);   
  3. //替换为:   
  4. 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就不会报这个错了,这个的确可以解决该问题。

displaytag数据库分页方法及导出数据的问题处理


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论