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的寫法.

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