limt 小结

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. Serviceservice需要和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. DAOdao为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) {    r eturn 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) ";  }        Limit数据库端分页   package com.sinovs.saler.webapp.action;   import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.ActionForm; import org.apache.struts.action.DispatchAction; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.apache.struts.action.ActionMessage; import org.apache.struts.action.ActionMessages; import org.extremecomponents.table.context.Context; import org.extremecomponents.table.context.HttpServletRequestContext; import org.extremecomponents.table.core.Preferences; import org.extremecomponents.table.core.PreferencesConstants; import org.extremecomponents.table.core.TableConstants; import org.extremecomponents.table.core.TableModelUtils; import org.extremecomponents.table.core.TableProperties; import org.extremecomponents.table.limit.Limit; import org.extremecomponents.table.limit.LimitFactory; import org.extremecomponents.table.limit.TableLimit; import org.extremecomponents.table.limit.TableLimitFactory; import com.sinovs.saler.model.Saler; import com.sinovs.saler.service.SalerManager;   public final class SalerAction extends DispatchAction {       public ActionForward search(ActionMapping mapping, ActionForm form,            HttpServletRequest request, HttpServletResponse response)            throws Exception {          //从spring中得到的bean        SalerManager mgr = (SalerManager) getBean("salerManager");        /**         * controller首先需要创建一个Limit         */        String tableId = "ec";        Context context = new HttpServletRequestContext(request);        LimitFactory limitFactory = new TableLimitFactory(context, tableId);        Limit limit = new TableLimit(limitFactory);        /**           * 得到系统默认的页面显示大小         */        Preferences preferences = new TableProperties();        preferences.init(null, TableModelUtils.getPreferencesLocation(context));        String crdStr = tableId + "_" + TableConstants.CURRENT_ROWS_DISPLAYED;       /**         * 注意:此处取值时,如果后台配置文件里有配置,就从里面取,反之取15         */        int defaultRowsDisplayed = Integer.parseInt(preferences               .getPreference(PreferencesConstants.TABLE_ROWS_DISPLAYED));        if (request.getParameter(crdStr) != null) {            defaultRowsDisplayed = Integer.parseInt(request                   .getParameter(crdStr));        }        /**         * limit需要得到所有的行来得到行的信息         */        int totalRows = mgr.getTotalSalers(limit);        limit.setRowAttributes(totalRows, defaultRowsDisplayed);        /**         * 需要从services得到Collection数据         */        List list = mgr.getSalers(limit);        /**         * 将Collections和totalRow这些信息传送回JSP以便eXtremeTable知道如何显示这些信息         */        request.setAttribute("list", list);        request.setAttribute("totalRows", new Integer(totalRows));        return mapping.findForward("list");     } }   SalerManagerImpl   /**      * controller需要到第一条信息就是总行数。      */       private final static String salersQuery = "FROM Saler ";     private final static String totalSalersQuery = "SELECT count(*) FROM Saler ";     public int getTotalSalers(Limit limit) {        String totalQuery = totalSalersQuery;          String modTotalQuery = filterQuery(limit.getFilterSet(), totalQuery);        int totalRows = dao.getTotalSalers(modTotalQuery);        return totalRows;     }     public List getSalers(Limit limit) {        String query = salersQuery;        String modSalersQuery = filterQuery(limit.getFilterSet(), query);        modSalersQuery = sortQuery(limit.getSort(), modSalersQuery);        return dao.getSalers(modSalersQuery, limit);     }     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 = filterQuery(query, property, value);        }        return query;       }     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        }          result.append(" AND " + property + " like '%" + value + "%'");        return result.toString();     }       private String sortQuery(Sort sort, String query) {        if (!sort.isSorted()) {            return query;        }        String property = sort.getProperty();        String sortOrder = sort.getSortOrder();        return sortQuery(query, property, sortOrder);     }       public String sortQuery(String query, String property, String sortOrder) {        StringBuffer result = new StringBuffer(query + " ORDER BY ");        result.append(property + " " + sortOrder);        return result.toString();     } SalerDaoHibernate   /**      * 得到数据总行数      */     public int getTotalSalers(String query) {        int totalRows = 0;        List list = getHibernateTemplate().find(query);        if (list.size() > 0) {            totalRows = ((Integer) list.get(0)).intValue();        }        return totalRows;     }       /**      * 根据HQL语句的到纪录列表      */     public List getSalers(String query, Limit limit) {        Session session = this.getSession();        Query q = session.createQuery(query);        q.setFirstResult((limit.getPage() - 1)               * limit.getCurrentRowsDisplayed());        q.setMaxResults(limit.getCurrentRowsDisplayed());        return q.list();     }   页面   <ec:table        items="salerList"        var="saler"        action="${pageContext.request.contextPath}/saler.do?method=search"        imagePath="${pageContext.request.contextPath}/images/table/*.gif"        title="saler.list.title"        width="80%"        retrieveRowsCallback="org.extremecomponents.table.callback.LimitCallback" filterRowsCallback="org.extremecomponents.table.callback.LimitCallback" sortRowsCallback="org.extremecomponents.table.callback.LimitCallback"          rowsDisplayed="16" >        <ec:exportXls fileName="presidents.xls" tooltip="Export Excel"/>        <ec:row highlightRow="true">            <ec:column property="rowcount" cell="rowCount" sortable="false" filterable="false" title="ID" width="60"/>            <ec:column property="salerName" title="saler.salername"/>            <ec:column property="salerPhone" title="saler.salerphone"/>            <ec:column property="id" title="saler.button.mod" filterable="false" viewsAllowed="html" sortable="false">            <a href="${pageContext.request.contextPath}/saler.do?method=edit&amp;id=${saler.id}"><bean:message key="saler.button.mod"/></a>            </ec:column>            <ec:column property="id" title="saler.button.delete" filterable="false" sortable="false" viewsAllowed="html">            <a href="${pageContext.request.contextPath}/saler.do?method=delete&amp;id=${saler.id}"><bean:message key="saler.button.delete"/></a>            </ec:column>          </ec:row>       </ec:table>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值