ecside展现列表、排序、过滤

首先ecside展现列表、排序、过滤(该三种操作以下简称为 RSF )的实现原理完全和原版EC一样,
如果您对原版EC的retrieveRowsCallback、sortRowsCallback、filterRowsCallback 非常熟悉,那么可以忽略此文.

先来简单介绍一下RSF操作方式.
ecside对数据的展现操作有三种:分页展现,按列排序(目前只支持单列),按列过滤(支持多列联合过滤)
(该三种操作以下简称为 RSF )

ecside提供了两种方式来实现RSF操作 : 基于java collection层 和 基于数据库层,下面先简单介绍一下原理:

=========================================================
一:基于java collection层:
这是ec的默认实现方式, 最简单易用.

您要做的就是将整个列表所要展现的全部数据放入collection 内,并交给EC来处理.
其中RSF操作,全部由EC在内存中完成,由于您已经将全部数据放入了collection中,
所以排序 过滤都是基于全部数据的.

您要在DAO中做的就是一个 查询操作,SQL语句中不需要加入 关于排序 分页 过滤的代码.

这种方式的优点非常明显:实现简单.

缺点同样明显,而且在很大程度上是致命的: 数据量大的时候速度慢,而且很可能outofmemery.

这时候我们就需要第二种方式了:

方式一您所要做的工作:
1 通过DAO,查询出所有的数据,放入collection内
2 将collection传给列表页面



二:基于数据库层:

在这种方式下,EC的角色发生了一点点变化.
此时,EC负责把 collection 里的内容展现出来, 同时会向您提供RSF相关的参数.
而这些参数需要您自己手动取得 并传入到DAO中(当然EC提供了很多方便的方法来帮助您取得这些参数),
具体功能的实现需要您自己在DAO中组织相应的SQL语句.

方式二您所要做的工作:
1 查询出所有的数据的总数
2 取得一个ECSide提供的Limit对象
3 在该对象的帮助下取得RSF操作的相关信息(如 数据的起止行数,排序的列和顺序,过滤的列和内容)
4 将RSF操作的相关信息传入DAO内,来进行SQL语句的拼装(或者其他的操作,如使用ORM工具时)
5 通过DAO,查询出当前页所要显示的数据,放入collection内
6 将collection传给列表页面


这种方式的优缺点正好和方式一相反.

关于两种方式的配置可以看一下 ecside.properties 文件中的下列内容

Java代码 复制代码
  1. table.filterRowsCallback.process=org.extremecomponents.table.callback.ProcessRowsCallback      
  2. table.filterRowsCallback.limit=org.extremecomponents.table.callback.LimitCallback      
  3.      
  4. table.sortRowsCallback.process=org.extremecomponents.table.callback.ProcessRowsCallback      
  5. table.sortRowsCallback.limit=org.extremecomponents.table.callback.LimitCallback      
  6.      
  7. table.retrieveRowsCallback.process=org.extremecomponents.table.callback.ProcessRowsCallback      
  8. table.retrieveRowsCallback.limit=org.extremecomponents.table.callback.LimitCallback     
table.filterRowsCallback.process=org.extremecomponents.table.callback.ProcessRowsCallback   
table.filterRowsCallback.limit=org.extremecomponents.table.callback.LimitCallback   
  
table.sortRowsCallback.process=org.extremecomponents.table.callback.ProcessRowsCallback   
table.sortRowsCallback.limit=org.extremecomponents.table.callback.LimitCallback   
  
table.retrieveRowsCallback.process=org.extremecomponents.table.callback.ProcessRowsCallback   
table.retrieveRowsCallback.limit=org.extremecomponents.table.callback.LimitCallback   



其中 ProcessRowsCallback 采用了方式一 ,LimitCallback采用了方式二
而table.retrieveRowsCallback.default会告诉EC您默认使用的是哪个.
同样您也可以在 ec:table 标签里 指定

Java代码 复制代码
  1. <ec:table filterRowsCallback="process/limit"  sortRowsCallback="process/limit"  retrieveRowsCallback="process/limit" ... >     
<ec:table filterRowsCallback="process/limit"  sortRowsCallback="process/limit"  retrieveRowsCallback="process/limit" ... >   



大家可能还看到了 org.ecside.table.callback.CommonLimitCallback 这个是我自己随意组合出来的一个rowcallback
他在执行分页的时候,使用了方式二,而排序 过滤的时候使用了方式一
所以,大家可能会发现,DEMO中的排序 过滤方法只对当前页有效.
我这么做其实是一种偷懒的方式,但多数情况下,正如网友所说"只排当页数据毫无意义",您们说的没错,的确如此:)


==========================================
下面看一个简单的例子: (以 struts + dao 的方式演示,例子中采用的是ECSide 2.0 RC1版本)

jsp代码

Java代码 复制代码
  1.   
  2. <ec:table    
  3. retrieveRowsCallback="limit"    
  4. sortRowsCallback="limit"    
  5. filterRowsCallback="limit"    
  6. ...... >   
  7. ......   
  8. </ec:table>  
<ec:table 
retrieveRowsCallback="limit" 
sortRowsCallback="limit" 
filterRowsCallback="limit" 
...... >
......
</ec:table>



在上面的JSP代码中,指定了所有的RSF操作都使用基于数据库层的.

action的代码

Java代码 复制代码
  1.   
  2. //默认每页显示的记录条数   
  3.     protected static int DEFAULT_PAGE_SIZE = 20;   
  4.   
  5.        
  6. // 数据库端分页,适合数据量较大的情况   
  7.     public ActionForward doQuery(ActionMapping mapping, ActionForm form,   
  8.     HttpServletRequest request, HttpServletResponse response)   
  9.     throws Exception {   
  10.         UserInfoDAO userInfoDAO=(UserInfoDAO)getBean("userInfoDAO");   
  11.        
  12. // 当列表的分页 过滤 排序等操作是基于数据库时,必须要用到Limit对象.   
  13. // 注意,当页面有多个ec的时候,需要使用带 tableId参数的同名方法.   
  14.     //Limit limit=RequestUtils.getLimit(request,"ecGird的Id");   
  15.                    
  16.         Limit limit=RequestUtils.getLimit(request);   
  17. // 基于数据库的排序.   
  18. // ECSide会帮助开发人员取得排序的相关信息:当前是按哪个(目前只支持单列排序)column排序的,以及排序的方式desc或asc,   
  19. // 这个信息以key-value方式存放在map里.   
  20. // 但是至于如果处理这些信息(如组装成sql语句),则是由开发人员自己在DAO里完成的.   
  21.         Sort sort=limit.getSort();   
  22.         Map sortValueMap = sort.getSortValueMap();   
  23.                    
  24. // 基于数据库过滤.   
  25. // ECSide会帮助开发人员取得过滤的相关信息:当前是对哪些column进行过滤,以及过滤的内容是什么,这个信息以key-value方式存放在map里.   
  26. // 但是至于如果处理这些信息(如组装成sql语句),则是由开发人员自己在DAO里完成的.   
  27.         FilterSet filterSet =limit.getFilterSet();   
  28.         Map filterPropertyMap=filterSet.getPropertyValueMap();   
  29.                    
  30. // 在本例中, sort 和 filter 相关信息将被传入 DAO,用于拼装sql语句.   
  31. // 其实,此时的排序 过滤就和我们以前的传统的查询非常类似:从查询页面取得查询条件,传入DAO.                  
  32.                    
  33.   
  34. // RequestUtils.getTotalRowsFromRequest(request);是一个工具类,用来从ECSIDE的列表中取得上次计算出的总行数   
  35. // 如果您不希望每次翻页都重新计算总行数,那么建议参考下面几行代码的做法.     
  36.   
  37.         int totalRows = RequestUtils.getTotalRowsFromRequest(request);   
  38.         if (totalRows < 0) {   
  39. // TODO :  userInfoDAO.getUserInfoNumber()为能够取得总行数的方法,请替换为实际的实现。   
  40. // 取得记录总条数时,不要忘了把filter作为参数传入,因为要取得的总行数也是要接受条件限制的.   
  41.             totalRows = userInfoDAO.getUserInfoNumber(filterPropertyMap);   
  42.         }   
  43.            
  44.            
  45.            
  46. // DEFAULT_PAGE_SIZE ==0 时, 每页记录数会使用 properties文件内的默认设置   
  47. // DEFAULT_PAGE_SIZE <0 时, 每页记录数会等于全部记录数    
  48.         limit.setRowAttributes(totalRows, DEFAULT_PAGE_SIZE);   
  49.   
  50.            
  51.            
  52. //取得当前要查询的页面的记录起止行号   
  53. // offset表示数据编号的起始号. ORACLE数据库一般是从1开始的,HSQLDB是从0开始,默认是从0开始计数,在这里我们使用从0开始.   
  54.         int offset=0;   
  55.         int[] rowStartEnd =new int[] { limit.getRowStart() + offset, limit.getRowEnd() + offset };   
  56.            
  57.   
  58.   
  59. // TODO :  userInfoDAO.getSomeUserInfo(rowStartEnd[0], rowStartEnd[1])   
  60. // 为查询记录的方法,请替换为实际的实现。rowStartEnd[0], rowStartEnd[1]为起止行   
  61. // rowStartEnd[0], rowStartEnd[1] 左闭 右开   
  62.         List rslist = userInfoDAO.getSomeUserInfo(rowStartEnd[0], rowStartEnd[1],sortValueMap,filterPropertyMap);   
  63.   
  64.         request.setAttribute("recordList", rslist);   
  65.        
  66. // 字典数据. 一个Map,存放的是  "编号" 到 "显示的文字" 的映射    
  67.         request.setAttribute("GENDER_MAP", CommonDictionary.GENDER);   
  68.         request.setAttribute("USERROLE_MAP", CommonDictionary.USERROLE);   
  69.            
  70.         return mapping.findForward("listPage");   
  71.     }  
//默认每页显示的记录条数
	protected static int DEFAULT_PAGE_SIZE = 20;

	
// 数据库端分页,适合数据量较大的情况
	public ActionForward doQuery(ActionMapping mapping, ActionForm form,
	HttpServletRequest request, HttpServletResponse response)
	throws Exception {
		UserInfoDAO userInfoDAO=(UserInfoDAO)getBean("userInfoDAO");
	
// 当列表的分页 过滤 排序等操作是基于数据库时,必须要用到Limit对象.
// 注意,当页面有多个ec的时候,需要使用带 tableId参数的同名方法.
	//Limit limit=RequestUtils.getLimit(request,"ecGird的Id");
				
		Limit limit=RequestUtils.getLimit(request);
// 基于数据库的排序.
// ECSide会帮助开发人员取得排序的相关信息:当前是按哪个(目前只支持单列排序)column排序的,以及排序的方式desc或asc,
// 这个信息以key-value方式存放在map里.
// 但是至于如果处理这些信息(如组装成sql语句),则是由开发人员自己在DAO里完成的.
		Sort sort=limit.getSort();
		Map sortValueMap = sort.getSortValueMap();
				
// 基于数据库过滤.
// ECSide会帮助开发人员取得过滤的相关信息:当前是对哪些column进行过滤,以及过滤的内容是什么,这个信息以key-value方式存放在map里.
// 但是至于如果处理这些信息(如组装成sql语句),则是由开发人员自己在DAO里完成的.
		FilterSet filterSet =limit.getFilterSet();
		Map filterPropertyMap=filterSet.getPropertyValueMap();
				
// 在本例中, sort 和 filter 相关信息将被传入 DAO,用于拼装sql语句.
// 其实,此时的排序 过滤就和我们以前的传统的查询非常类似:从查询页面取得查询条件,传入DAO.				
				

// RequestUtils.getTotalRowsFromRequest(request);是一个工具类,用来从ECSIDE的列表中取得上次计算出的总行数
// 如果您不希望每次翻页都重新计算总行数,那么建议参考下面几行代码的做法.	

		int totalRows = RequestUtils.getTotalRowsFromRequest(request);
		if (totalRows < 0) {
// TODO :  userInfoDAO.getUserInfoNumber()为能够取得总行数的方法,请替换为实际的实现。
// 取得记录总条数时,不要忘了把filter作为参数传入,因为要取得的总行数也是要接受条件限制的.
			totalRows = userInfoDAO.getUserInfoNumber(filterPropertyMap);
		}
		
		
		
// DEFAULT_PAGE_SIZE ==0 时, 每页记录数会使用 properties文件内的默认设置
// DEFAULT_PAGE_SIZE <0 时, 每页记录数会等于全部记录数	
		limit.setRowAttributes(totalRows, DEFAULT_PAGE_SIZE);

		
		
//取得当前要查询的页面的记录起止行号
// offset表示数据编号的起始号. ORACLE数据库一般是从1开始的,HSQLDB是从0开始,默认是从0开始计数,在这里我们使用从0开始.
		int offset=0;
		int[] rowStartEnd =new int[] { limit.getRowStart() + offset, limit.getRowEnd() + offset };
		


// TODO :  userInfoDAO.getSomeUserInfo(rowStartEnd[0], rowStartEnd[1])
// 为查询记录的方法,请替换为实际的实现。rowStartEnd[0], rowStartEnd[1]为起止行
// rowStartEnd[0], rowStartEnd[1] 左闭 右开
		List rslist = userInfoDAO.getSomeUserInfo(rowStartEnd[0], rowStartEnd[1],sortValueMap,filterPropertyMap);

		request.setAttribute("recordList", rslist);
	
// 字典数据. 一个Map,存放的是  "编号" 到 "显示的文字" 的映射 
		request.setAttribute("GENDER_MAP", CommonDictionary.GENDER);
		request.setAttribute("USERROLE_MAP", CommonDictionary.USERROLE);
		
		return mapping.findForward("listPage");
	}




其实上面的代码和代码里的注释已经说的比较明白了.


DAO里的代码

Java代码 复制代码
  1. public List getSomeUserInfo(int startRow,int endRow,Map sortValueMap,Map filterPropertyMap){   
  2.     StringBuffer bufSql = new StringBuffer();   
  3.     int size=endRow-startRow;   
  4.        
  5. // 使用传统JDBC时,根据不同条件拼装不同的SQL一向是非常恼人的事情.   
  6. // ECSide当然不能够帮助您解决这个问题.   
  7. // 再次重申一遍,当翻页 过滤 排序 均基于数据库时,ECSide只是能够帮助开发者在后台更方便的取得操作相关的数据.   
  8. // 而如何使用他们,仍然需要开发人员自己来决定.   
  9.        
  10.     bufSql.append("SELECT * FROM ");   
  11.        
  12.     StringBuffer whereSql = new StringBuffer(" WHERE 1=1 ");   
  13.        
  14.     if (filterPropertyMap!=null && !filterPropertyMap.isEmpty()){   
  15. // 根据过滤条件进行sql语句的拼装.   
  16.        
  17. // 在本例中,只有 USERROLE USERNAME GENDER 是可以进行过滤的.   
  18. // 在这里偷下懒,就不用 PreparedStatement 的方式了,而是直接把过滤项拼装进sql语句内.   
  19.         String filterProperty;   
  20.         String filterValue;   
  21.            
  22.         filterProperty="USERROLE";   
  23.         filterValue=(String)filterPropertyMap.get(filterProperty);   
  24.         if (filterValue!=null){   
  25.             whereSql.append(" AND ").append(filterProperty).append(" = '").append(StringEscapeUtils.escapeSql(filterValue)).append("' ");   
  26.         }   
  27.   
  28.         filterProperty="USERNAME";   
  29.         filterValue=(String)filterPropertyMap.get(filterProperty);   
  30.         if (filterValue!=null){   
  31.             whereSql.append(" AND ").append(filterProperty).append(" like '").append(StringEscapeUtils.escapeSql(filterValue)).append("' ");   
  32.         }   
  33.            
  34.         filterProperty="GENDER";   
  35.         filterValue=(String)filterPropertyMap.get(filterProperty);   
  36.         if (filterValue!=null){   
  37.             whereSql.append(" AND ").append(filterProperty).append(" = '").append(StringEscapeUtils.escapeSql(filterValue)).append("' ");   
  38.         }   
  39.     }   
  40.        
  41.     if (sortValueMap!=null && !sortValueMap.isEmpty()){   
  42.         bufSql.append("( SELECT * FROM USER_INFO  ");   
  43.         bufSql.append(whereSql);   
  44.         bufSql.append(ECSideUtils.getDefaultSortSQL(sortValueMap));   
  45.         bufSql.append(" ) ");    
  46.     }else{   
  47.         bufSql.append(" USER_INFO ");   
  48.         bufSql.append(whereSql);   
  49.     }   
  50.        
  51.     bufSql.append(" LIMIT ? OFFSET ?");   
  52.        
  53.        
  54.     Connection conn=null;   
  55.     PreparedStatement pstmt = null;   
  56.     ResultSet rest = null;   
  57.     List userList=null;   
  58.        
  59.     try {   
  60.         conn = getConnection();   
  61.         pstmt = ConnectionUtils.prepareStatement(conn,bufSql.toString());   
  62.         int prarameterIndex=0;   
  63.            
  64.         pstmt.setInt(++prarameterIndex, size);   
  65.         pstmt.setInt(++prarameterIndex, startRow);   
  66.            
  67.         rest = pstmt.executeQuery();   
  68.         String[] columnName=getColumnName(rest);   
  69.         userList=new ArrayList();   
  70.         Map userInfo=null;   
  71.         while (rest.next()) {   
  72.             userInfo=new HashMap();   
  73.             buildRecord(rest,columnName,userInfo);   
  74.             userList.add(userInfo);   
  75.         }   
  76.     } catch (Exception e) {   
  77.         LogHandler.errorLog(logger, e);   
  78.         userList=null;   
  79.     }finally{   
  80.         close(rest, pstmt, conn);   
  81.     }   
  82.        
  83.     return userList;   
  84. }  
public List getSomeUserInfo(int startRow,int endRow,Map sortValueMap,Map filterPropertyMap){
	StringBuffer bufSql = new StringBuffer();
	int size=endRow-startRow;
	
// 使用传统JDBC时,根据不同条件拼装不同的SQL一向是非常恼人的事情.
// ECSide当然不能够帮助您解决这个问题.
// 再次重申一遍,当翻页 过滤 排序 均基于数据库时,ECSide只是能够帮助开发者在后台更方便的取得操作相关的数据.
// 而如何使用他们,仍然需要开发人员自己来决定.
	
	bufSql.append("SELECT * FROM ");
	
	StringBuffer whereSql = new StringBuffer(" WHERE 1=1 ");
	
	if (filterPropertyMap!=null && !filterPropertyMap.isEmpty()){
// 根据过滤条件进行sql语句的拼装.
	
// 在本例中,只有 USERROLE USERNAME GENDER 是可以进行过滤的.
// 在这里偷下懒,就不用 PreparedStatement 的方式了,而是直接把过滤项拼装进sql语句内.
		String filterProperty;
		String filterValue;
		
		filterProperty="USERROLE";
		filterValue=(String)filterPropertyMap.get(filterProperty);
		if (filterValue!=null){
			whereSql.append(" AND ").append(filterProperty).append(" = '").append(StringEscapeUtils.escapeSql(filterValue)).append("' ");
		}

		filterProperty="USERNAME";
		filterValue=(String)filterPropertyMap.get(filterProperty);
		if (filterValue!=null){
			whereSql.append(" AND ").append(filterProperty).append(" like '").append(StringEscapeUtils.escapeSql(filterValue)).append("' ");
		}
		
		filterProperty="GENDER";
		filterValue=(String)filterPropertyMap.get(filterProperty);
		if (filterValue!=null){
			whereSql.append(" AND ").append(filterProperty).append(" = '").append(StringEscapeUtils.escapeSql(filterValue)).append("' ");
		}
	}
	
	if (sortValueMap!=null && !sortValueMap.isEmpty()){
		bufSql.append("( SELECT * FROM USER_INFO  ");
		bufSql.append(whereSql);
		bufSql.append(ECSideUtils.getDefaultSortSQL(sortValueMap));
		bufSql.append(" ) "); 
	}else{
		bufSql.append(" USER_INFO ");
		bufSql.append(whereSql);
	}
	
	bufSql.append(" LIMIT ? OFFSET ?");
	
	
	Connection conn=null;
	PreparedStatement pstmt = null;
	ResultSet rest = null;
	List userList=null;
	
	try {
		conn = getConnection();
		pstmt = ConnectionUtils.prepareStatement(conn,bufSql.toString());
		int prarameterIndex=0;
		
		pstmt.setInt(++prarameterIndex, size);
		pstmt.setInt(++prarameterIndex, startRow);
		
		rest = pstmt.executeQuery();
		String[] columnName=getColumnName(rest);
		userList=new ArrayList();
		Map userInfo=null;
		while (rest.next()) {
			userInfo=new HashMap();
			buildRecord(rest,columnName,userInfo);
			userList.add(userInfo);
		}
	} catch (Exception e) {
		LogHandler.errorLog(logger, e);
		userList=null;
	}finally{
		close(rest, pstmt, conn);
	}
	
	return userList;
}



这个DAO同样没什么好说的了 非常好理解.


以上就是一个简单的实现,最有参考价值的就是ACTION的写法.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章