首先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 文件中的下列內容
- 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
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 標籤裏 指定
- <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代碼
- <ec:table
- retrieveRowsCallback="limit"
- sortRowsCallback="limit"
- filterRowsCallback="limit"
- ...... >
- ......
- </ec:table>
<ec:table
retrieveRowsCallback="limit"
sortRowsCallback="limit"
filterRowsCallback="limit"
...... >
......
</ec:table>
在上面的JSP代碼中,指定了所有的RSF操作都使用基於數據庫層的.
action的代碼
- //默認每頁顯示的記錄條數
- 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");
- }
//默認每頁顯示的記錄條數
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裏的代碼
- 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;
- }
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的寫法.