幸せのちから

          平凡的世界
          看似平常實崎嶇
          成如容易卻艱辛

          Limit指南

          Limit指南


          1. 引言

          在你需要處理大量數據時你應該考慮使用eXtremeTable的Limit特性。Limit這個名字來自MySQL的limit 命令,Limit接口的目的就是如何對表的結果集進行limit處理。Limit實現知道當排序、過濾、分頁、導出時,用戶如何與表互相作用。有了這些信息你 將能夠使用可能是最有效的方式顯示正確的過濾、排序后的請求頁面。

          為了示范Limit特性,我將要做的工作將分解為JSP、Controller、Service和DAO。這示范了一種使用分層的方式來處理 Limit。你可以根據自己的需要來增加或減少層。本示例也使用了Spring框架來重新得到使用Spring的JDBC取得的數據,因此你的代碼看起來可能有點不同。eXtremeTable的一個特點就是不依賴任何框架和容器。

          1.1. JSP

          為了使用Limit特性,eXtremeTable需要使用limit特定的RetrieveRowsCallback、 FilterRowsCallback和SortRowsCallback接口。eXtremeComponents提供了每個接口的一個實現,可以簡單地通過設置每個屬性值為limit來簡單來使用。

          <ec:table 
          items="presidents"
          retrieveRowsCallback="limit"
          filterRowsCallback="limit"
          sortRowsCallback="limit"
          view="limit"
          >
          ...

          另外視圖屬性參照一個名為limit的定制視圖。這是一個簡單修改默認eXtremeTable視圖,不包含最后頁工具條的實現。這僅僅關系到你是否能取得確切需要的行。 一些數據庫例如Oracle和MySQL都提供了一種得到確定行的特性,但是,其他的數據庫例如:Sybase沒有提供特性。在我的示例中我考慮最壞的情況你的數據庫不支持這種特性。

          即使你的數據庫不提供取得特定行的特性,當你考慮用戶如何和表協同工作時,Limit仍然非常有意義。用戶通常會對一些數據進行排序、過濾和分頁。 這個例子中15條數據構成一頁,第一頁需要15條數據,第二頁需要30條數據,第三頁需要45條數據,以此類推。在經過一段時間分頁后,他們常常使用過濾來提煉數據。 即使他們不這樣做,他們也必須在此之前對大量的數據進行分頁,這將影響效率。當然如果允許用戶點擊最后頁,那么所有的數據都將被取出,這將非常影響效率。

          1.2. Controller

          提示:Spring框架的Controller和Struts框架的Action非常相像。

          controller首先需要創建一個Limit。為了完成這個你需要得到一些關于Context和LimitFactory的幫助。

          Context context = new HttpServletRequestContext(request);
          LimitFactory limitFactory = new TableLimitFactory(context);
          Limit limit = new TableLimit(limitFactory);

          Context是一個處理取得屬性的接口,LimitFactory使用Context來找出用戶如何和eXtremeTable交互。 然后Limit使用LimitFactory來組裝自己。

          為了初始化Limit,它將包含所有的有用的信息。這些信息包括數據將被如何排序和過濾,哪一頁將被顯示和是否允許被導出。

          然而,Limit仍然需要得到行的信息,這樣正確的信息頁面才能被顯示給用戶。行信息包括開始行、結束行、當前顯示行。 controller必須從service得到這些信息,而Service將從dao中得到這些信息。這里我只給出Controller端的代碼。

          int totalRows = presidentsService.getTotalPresidents(limit.getFilterSet(), limit.isExported());
          limit.setRowAttributes(totalRows, defaultRowsDisplayed);

          limit需要得到所有的行來得到行的信息。service需要知道那些被過濾,不管這些數據是否要導出。為了設置行信息,默認的一頁顯示的行數需要被設置。 這可以通過對TableTag的rowsDisplayed屬性設置一個確定的數值來實現。

          現在我們只需要從services得到Collection數據。

          Collection presidents = presidentsService.getPresidents(limit.getFilterSet(), limit.getSort(), limit.getRowEnd());

          因為limit已經包含所有信息,這將十分容易。所有需要做的就是傳入過濾器,排序和最后行的信息。 最后要做的是將Collections和totalRow這些信息傳送回JSP以便eXtremeTable知道如何顯示這些信息。

          request.setAttribute("presidents", presidents);
          request.setAttribute("totalRows", new Integer(totalRows));

          1.3. Service

          service需要和dao進行交互來得到總行數和Collection。

          1.3.1. 取得總行數

          controller需要到第一條信息就是總行數。

          public int getTotalPresidents(FilterSet filterSet, boolean isExported) {
          String totalQuery = presidentsDao.getTotalPresidentsQuery();
          String modTotalQuery = filterQuery(filterSet, totalQuery);
          int totalRows = presidentsDao.getTotalPresidents(modTotalQuery);
          if (isExported && totalRows > maxExportRows) {
          totalRows = maxExportRows;
          }
          return totalRows;
          }

          service和dao一起來過濾結果集,它的工作方式是在Where語句后面增加更多的AND語句來修改查詢字符串。為此,你需要和Limit FilterSet一起工作。

          FilterSet是一個過濾器對象數組,一個過濾器包括一個bean property和這個過濾器的值?;蛘?,簡單的說就是用戶想要過濾的行和他們輸入的值。這使得它非常容易交互。service只需要迭代所有的 FilterSet并調用dao來拼接查詢語句。(譯者注:過濾的實現方式是:在Where后面增加And語句來改變查詢語句以達到對數據進行過濾的效果)

          private String filterQuery(FilterSet filterSet, String query) {
          if (!filterSet.isFiltered() || filterSet.isCleared()) {
          return query;
          }

          Filter filters[] = filterSet.getFilters();
          for (int i = 0; i < filters.length; i++) {
          Filter filter = filters[i];
          String property = filter.getProperty();
          String value = filter.getValue();
          query = presidentsDao.filterQuery(query, property, value);
          }

          return query;
          }

          query修改包括了filter信息,總行數。在一些情況下這就足夠,但是當用戶導出數據時仍然存在一個潛在的問題。為了保持高效 service不允許導出超出一個最大行數的數據。

          1.3.2. 取得Collection

          controller需要到第二條信息就是Collection。

          public Collection getPresidents(FilterSet filterSet, Sort sort, int rowEnd) {
          String patientsQuery = presidentsDao.getPresidentsQuery();
          String modPatientsQuery = filterQuery(filterSet, patientsQuery);
          modPatientsQuery = sortQuery(sort, modPatientsQuery);
          modPatientsQuery = presidentsDao.limitQuery(rowEnd, modPatientsQuery);
          return presidentsDao.getPresidents(modPatientsQuery);
          }

          和前面一樣,service和dao一起來過濾結果集。

          另外query字符串需要擴展ORDER BY語句以便數據按照正確的方式進行排序。Sort包含一個bean property和 sortOrder值(正序還是逆序)。service僅僅需要使用Sort來調用dao。

          private String sortQuery(Sort sort, String query) {
          if (!sort.isSorted()) {
          String defaultSortOrder = presidentsDao.getDefaultSortOrder();
          if (StringUtils.isNotBlank(defaultSortOrder)) {
          return query + defaultSortOrder;
          }

          return query;
          }

          String property = sort.getProperty();
          String sortOrder = sort.getSortOrder();

          return presidentsDao.sortQuery(query, property, sortOrder);
          }

          query字符串最后需要的修改就是增加數據庫特別的指令來limit將要被返回的結果集。這就是limitQuery() 方法的作用。

          1.4. DAO

          dao為service負責底層數據工作。

          1.4.1. 定義Query字符串

          為了真正理解dao,query字符串需要被展示。

          這就是得到數據的presidents query字符串:

          private final static String presidentsQuery = 
          " SELECT " +
          " president_id presidentId, " +
          " first_name firstName, " +
          " last_name lastName, " +
          " nick_name nickName, " +
          " concat(first_name, ' ',last_name) fullName, " +
          " term, " +
          " born, " +
          " died, " +
          " education, " +
          " career, " +
          " political_party politicalParty " +
          " FROM presidents ";

          這是得到總行數的query字符串:

          private final static String totalPresidentsQuery = 
          " SELECT count(*) FROM presidents ";

          1.4.2. Filter 和 Sort Query 字符串

          兩個最有趣的方法就是過濾和排序。

          filter看起來像這樣:

          public String filterQuery(String query, String property, String value) {
          StringBuffer result = new StringBuffer(query);

          if (query.indexOf("WHERE") == -1) {
          result.append(" WHERE 1 = 1 "); //stub WHERE clause so can just append AND clause
          }

          if (property.equals("fullName")) {
          result.append(" AND concat(first_name, ' ',last_name) like '%" + value + "%'");
          } else if (property.equals("nickName")) {
          result.append(" AND nick_name like '%" + value + "%'");
          } else {
          result.append(" AND " + property + " like '%" + value + "%'");
          }

          return result.toString();
          }

          filterQuery()方法需要增加正確的AND語句到query字符串。

          sort看起來非常類似:

          public String sortQuery(String query, String property, String sortOrder) {
          StringBuffer result = new StringBuffer(query + " ORDER BY ");

          if (property.equals("fullName")) {
          result.append("concat(first_name, ' ',last_name) " + sortOrder);
          } else {
          result.append(property + " " + sortOrder);
          }

          return result.toString();
          }

          sortQuery()方法需要增加正確的ORDER BY語句到query字符串。

          1.4.3. Limit Query String

          現在query字符串修改能夠正確的進行filter和sort,它還需要修改以便只取頁面顯示相關的數據。MySQL為s the limit命令。

          public String limitQuery(int rowEnd, String query) {
          return query + " limit " + rowEnd;
          }

          1.4.4. 取回總行數和Collection.

          service需要的唯一東西就是:總行數和Collection。

          public Collection getPresidents(final String query) {
          return jdbcTemplate.query(query, new ResultReader() {
          List results = new ArrayList();
          public List getResults() {
          return results;
          }

          public void processRow(ResultSet rs)
          throws SQLException {
          President president = new President();
          president.setPresidentId(new Integer(rs.getInt("presidentId")));
          president.setFirstName(rs.getString("firstName"));
          president.setLastName(rs.getString("lastName"));
          president.setNickName(rs.getString("nickName"));
          president.setFullName(rs.getString("fullName"));
          president.setTerm(rs.getString("term"));
          president.setBorn(rs.getDate("born"));
          president.setDied(rs.getDate("died"));
          president.setEducation(rs.getString("education"));
          president.setCareer(rs.getString("career"));
          president.setPoliticalParty(rs.getString("politicalParty"));
          results.add(president);
          }
          });
          }

          public int getTotalPresidents(final String query) {
          return jdbcTemplate.queryForInt(query);
          }

          ResultReader是一個幫助處理JDBC查詢的Spring特殊類,作為一個callback來處理JDBC ResultSet。jdbcTemplate是對JDBC連接的抽象。

          1.4.5. 默認的Sort順序

          最后,這是service需要的默認sort順序:

          public String getDefaultSortOrder() {
          return " ORDER BY concat(first_name, ' ', last_name) ";
          }

          posted on 2006-02-23 10:34 Lucky 閱讀(2159) 評論(8)  編輯  收藏 所屬分類: extremeComponents

          評論

          # re: Limit指南 2006-05-16 13:48 悠哥

          版本:eXtremeComponents-1.0.1-RC2
          環境:JSF

          文件:departments.jsp
          <f:view>
          <f:loadBundle var="text" basename="#{departmentList.bundleName}"/>
          <h:form id="editDepartment">
          <ec:table
          items="departmentList.departments"
          action="${pageContext.request.contextPath}/hr/departments.html"
          imagePath="${pageContext.request.contextPath}/images/table/*.gif"
          title="${text['userList.title']}"
          rowsDisplayed="10"
          form="editDepartment" var="department"
          retrieveRowsCallback="limit"
          filterRowsCallback="limit"
          sortRowsCallback="limit"
          view="limit"
          autoIncludeParameters="false">
          <ec:row highlightRow="true">
          <ec:column title="${text['department.name']}" property="name"/>
          </ec:row>
          </ec:table>
          </h:form>
          </f:view>

          文件:DepartmentList.java
          public List getDepartments() {
          Context context = new HttpServletRequestContext(getRequest());
          LimitFactory limitFactory = new TableLimitFactory(context);
          Limit limit = new TableLimit(limitFactory);

          //int pageSize = limit.getCurrentRowsDisplayed();
          //可能是一個bug,生成 limit 的時候,并沒有給 currentRowsDisplayed 賦值
          int pageSize = limitFactory.getCurrentRowsDisplayed(0, 10);

          FilterSet filter = limit.getFilterSet();

          Page page = new PageHibernate(limit.getPage(), pageSize);
          page.setFilters(FilterUtil.convert(filter));

          page = departmentManager.getDepartments(page);

          List list = page.getThisPageElements();

          limit.setRowAttributes(page.getTotalNumberOfElements(), page.getPageSize());

          getRequest().setAttribute("totalRows", new Integer(page.getTotalNumberOfElements()));
          return list;
          }

          結果:
          1.從limit中取不到pagesize,可能是一個bug,跟蹤TableLimit原碼,初始化的時候,并沒有給 currentRowsDisplayed 賦值
          2.沒有顯示“最后一頁”,這個按鈕  回復  更多評論   

          # re: Limit指南 2006-05-16 14:03 悠哥

          3.不能生成 excel 文件  回復  更多評論   

          # re: Limit指南 2006-05-16 14:14 xplucky

          我沒用過JSF,你可以sina的免費郵箱中找到extremesite實例,用戶名、密碼: extremetable。  回復  更多評論   

          # re: Limit指南 2006-09-20 20:56 Jkallen

          呵呵 我正在看英文的tutoails而頭疼呢!

          支持!以后常來  回復  更多評論   

          # re: Limit指南 2006-09-20 21:03 Jkallen

          對了,這個ResultReader是spring哪個版本的啊?

          現在我們公司的都是:
          ecTableRowMapper implements RowMapper
          這樣來將resultset中的結果放到map中(當然也可以放到一個DTO中)  回復  更多評論   

          # re: Limit指南 2006-10-16 18:23 孫建彬

          在創建limit之前:
          你是在哪里定義defaultRowsDisplayed的,然后怎么樣取得的?
          發個e-mail:volcano_hosan@sina.com,多謝!  回復  更多評論   

          # re: Limit指南 2006-10-17 16:23 sLAmbBkiNg

          如何解決使用LIMIT后“最后一頁”按鈕不顯示問題?
          M5版本據說解決了導出EXCEL和PDF中文文件名問題,M5版本在哪里下載?
          BLOG上的連接不能下  回復  更多評論   

          # re: Limit指南 2007-07-20 10:25 assd

          去掉頁面中的 view="limit"  回復  更多評論   

          <2006年2月>
          2930311234
          567891011
          12131415161718
          19202122232425
          2627281234
          567891011

          導航

          隨筆分類(125)

          文章分類(5)

          日本語

          搜索

          積分與排名

          最新隨筆

          最新評論

          主站蜘蛛池模板: 张掖市| 朝阳区| 宁河县| 南澳县| 中超| 竹溪县| 淮滨县| 榕江县| 馆陶县| 定兴县| 华宁县| 石棉县| 章丘市| 桦南县| 开鲁县| 仁怀市| 闽清县| 同心县| 紫金县| 安阳市| 察哈| 浮山县| 东兰县| 清苑县| 农安县| 广元市| 库尔勒市| 阆中市| 姚安县| 临夏市| 收藏| 拜泉县| 武隆县| 塔城市| 二手房| 元朗区| 荔波县| 南漳县| 双鸭山市| 阳原县| 通州区|