Datatables是一款jquery表格插件。它是一个高度灵活的工具,可以将任何HTML表格添加高级的交互功能。

分页,即时搜索和排序
几乎支持任何数据源:DOM, javascript, Ajax 和 服务器处理
支持不同主题 DataTables, jQuery UI, Bootstrap, Foundation
各式各样的扩展: Editor, TableTools, FixedColumns ……
丰富多样的option和强大的API
支持国际化
免费开源 ( MIT license )! 商业支持

因此这一款非常值得推荐的表格插件。今天借此机会我们来记录一下使用服务器端(SpringMVC)分页的方法。

首先,来看客户端的JavaScript代码:

var self_order_table = $("#self_order_table").DataTable({
            "processing": true,
            "serverSide": true,
            "paging": true,
            language : QINGE._set.table_language,
             "ajax": {
                   "url": CONFIG.mec_datable.index + "otm/order/list?p=self",
                   "type": "POST",
                   "data": function (d) {
                      //删除多余请求参数
                      for(var key in d){
                          if(key.indexOf("columns")==0||key.indexOf("order")==0||key.indexOf("search")==0){ //以columns开头的参数删除
                              delete d[key];
                          }
                      }
                      var searchParams= {
                              page : d.start == 0 ? 1 : (d.start/d.length + 1),
                              rows : d.length,
                       };
                      //附加查询参数
                      if(searchParams){
                          $.extend(d,searchParams); //给d扩展参数
                      }
                   },
                   "dataType" : "json",
                   "dataFilter": function (response) {//json是服务器端返回的数据
                       var json = QINGE.jsonEval(response);

                        if (json[QINGE.keys.statusCode] == QINGE.statusCode.error) {
                            if (json[QINGE.keys.message])
                                $.showErr(json[QINGE.keys.message]);
                        } else {
                               var returnData = {};
                               returnData.draw = json.result.vo.mo.draw;
                               returnData.recordsTotal = json.result.count;//返回数据全部记录
                               returnData.recordsFiltered = json.result.count;//后台不实现过滤功能,每次查询均视作全部结果
                               returnData.data = json.result.list;//返回的数据列表
                               return JSON.stringify(returnData);//这几个参数都是datatable需要的,必须要
                        }

                   }
               },
            deferRender : true,
            "scrollX" : true,
            scrollCollapse : true,
            fixedColumns : {
                leftColumns : 1,
            },
            "columnDefs" : [
                    {
                        targets : 0,
                        data : "scode",
                        title : "商品",
                        render : function(data, type, row) {
                            return "</div><div style='color: #676b6e;font-size: 10px; '>" + row.scode + "</div>";
                        }
                    }, {
                        targets : 1,
                        "data" : "order_count",
                        title : "委托量",
                    }, {
                        targets : 2,
                        "data" : "chengjiao_count",
                        title : "成交量",
                    }, {
                        targets : 3,
                        "data" : "order_price",
                        title : "委托价",
                    } ],
            "rowCallback" : function(row, data, index) {
            },
        });

需要注意的内容就是,如果使用服务器端分页,需要开启:

"processing": true,
"serverSide": true,
"paging": true,

然后借助ajax选项来定制发往服务器端的分页参数和服务器端返回的分页参数,先来看分页请求参数:

   "data": function (d) {
  //删除多余请求参数
  for(var key in d){
      if(key.indexOf("columns")==0||key.indexOf("order")==0||key.indexOf("search")==0){ //以columns开头的参数删除
          delete d[key];
      }
  }
  var searchParams= {
          page : d.start == 0 ? 1 : (d.start/d.length + 1),
          rows : d.length,
   };
  //附加查询参数
  if(searchParams){
      $.extend(d,searchParams); //给d扩展参数
      }
   },

其中page为第几页,rows为一页显示多少行,这两个是必须参数。这个时候,发往服务器端的参数都有哪一些呢?看下图。

DataTables的服务器端(SpringMVC)分页模式_DataTables的服务器端

①、其中draw是DataTables一个必须参数,是记录第几次分页的一个关键参数,服务器端还需要返回。
②、通过start和length参数可以计算出当前请求是第几页,比如说上图中的start为40,length为10,即page等于5(计算公式为:d.start == 0 ? 1 : (d.start/d.length + 1)
③、rows和length是相同的,只不过本次我们的SpringMVC端只认rows,不认length,所以需要转换一下。

再来看服务器端返回的数据处理,此时需要用到Ajax的dataFilter:

"dataFilter": function (response) {//json是服务器端返回的数据
 var json = QINGE.jsonEval(response);

if (json[QINGE.keys.statusCode] == QINGE.statusCode.error) {
    if (json[QINGE.keys.message])
        $.showErr(json[QINGE.keys.message]);
    } else {
        var returnData = {};
          returnData.draw = json.result.vo.mo.draw;
          returnData.recordsTotal = json.result.count;//返回数据全部记录
          returnData.recordsFiltered = json.result.count;//后台不实现过滤功能,每次查询均视作全部结果
          returnData.data = json.result.list;//返回的数据列表
          return JSON.stringify(returnData);//这几个参数都是datatable需要的,必须要
    }
}

服务器端返回的是json字符串,因此可以通过jsonEval函数来转换成json对象。然后从中取出datatables需要的关键数据:

var returnData = {};
returnData.draw = json.result.vo.mo.draw;
returnData.recordsTotal = json.result.count;//返回数据全部记录
returnData.recordsFiltered = json.result.count;//后台不实现过滤功能,每次查询均视作全部结果
returnData.data = json.result.list;//返回的数据列表
return JSON.stringify(returnData);//这几个参数都是datatable需要的,必须要

QINGE.jsonEval(response)函数的具体内容如下:

jsonEval : function(data) {
    try {
        if ($.type(data) == 'string')
            return eval('(' + data + ')');
        else
            return data;
    } catch (e) {
        return {};
    }
},

这样的话,针对客户端传递到服务器端的参数和接收服务器端返回的数据就处理完成了,接下来我们来看Java端(也就是SpringMVC)来如何接收分页请求和响应分页数据。

首先来看controller:

@SuppressWarnings({ "rawtypes", "unchecked" })
@RequestMapping(value = "list")
public void list(HttpServletResponse response) {
    Map result = new HashMap();

    // 获取列表参数
    BaseConditionVO vo = getBaseConditionVOForTable();
    vo.addParams("uid", InfoEL.getMemberUid());

    String p = getPara("p", "trade");
    if ("self".equals(p)) {
        result.put("vo", vo);
        result.put("count", selfOrderService.countTotal(vo));
        result.put("list", selfOrderService.getList(vo, vo.createRowBounds()));
        renderJsonDone(response, result);
    }
}

其中BaseConditionVO 为分页的请求参数,里面包含如下属性:

public static int PAGE_SHOW_COUNT = 50;// 默认一页为50行
private int pageNum = 1;//第几页
private int numPerPage = 0;// 一页显示多少行数据
private long totalCount = 0;// 总页数
private String orderField = "";// 排序字段
private String orderDirection = "";// 排序方式

/**
 * @Fields ps : 对参数类型进行封装,同时方便存储其他参数
 */
private Map<String, Object> mo = new HashMap<String, Object>();

其中renderJsonDone为返回json字符串的方法:

    private void renderJson(HttpServletResponse response, String jsonText) {
        PrintWriter writer = null;
        try {
            response.setHeader("Pragma", "no-cache");
            response.setHeader("Cache-Control", "no-cache");
            response.setDateHeader("Expires", 0);

            response.setContentType(contentType);
            writer = response.getWriter();
            writer.write(jsonText);
            writer.flush();
        } catch (IOException e) {
            throw new OrderException(e.getMessage());
        } finally {
            if (writer != null)
                writer.close();
        }
    }

    protected void renderJsonDone(HttpServletResponse response, final Object value) {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("statusCode", 200);
        map.put("result", value);
        String jsonText = JSON.toJSONString(map);

        renderJson(response, jsonText);
    }

那么,SpringMVC是怎么分页的呢?关键方法就在:

result.put("count", selfOrderService.countTotal(vo));// 计算总页数
result.put("list", selfOrderService.getList(vo, vo.createRowBounds()));// 获取分页

对于SpringMVC来说,其强大的集成功能就在于此,我们只需要按照这种方式将RowBounds:vo.createRowBounds()对象传递给Mybatis,Mybatis就会自己帮我们处理好分页,我们并不需要关注分页的具体细节。
先来来看Mybatis的处理:

    <!--列 -->
    <sql id="Base_Column_List">
        so.*
    </sql>

    <sql id="queryJoins">
    </sql>

    <sql id="Base4List">
        from otm_self_order so
        <include refid="queryJoins"/>
        where so.del_flag = 0
        <if test="mo.status != null">
            and so.status = #{mo.status}
        </if>
        <if test="mo.uid != null">
            and so.uid = #{mo.uid}
        </if>
        <if test="mo.cancel != null">
            and so.cancel = #{mo.cancel}
        </if>
    </sql>

    <select id="getList" resultMap="BaseResultMap" parameterType="map">
        select
        <include refid="Base_Column_List" />
        <include refid="Base4List" />
        <choose>
            <when test="orderField !=null and orderField !=''">
                ORDER BY ${orderField}
                <if test="orderDirection != null and orderDirection != ''">${orderDirection}</if>
            </when>
            <otherwise>
                order by so.update_date DESC
            </otherwise>
        </choose>
    </select>

    <select id="countTotal" resultType="java.lang.Integer" parameterType="map">
        select
            count(0)
        <include refid="Base4List" />
    </select>

好了,说完了客户端和服务器端的处理细节,我们来看一下具体的服务器端返回数据形式,通过Chrome浏览器的network面板就可以观察到,如下图:

DataTables的服务器端(SpringMVC)分页模式_DataTables的服务器端_02

另外,分页的效果图如下:

DataTables的服务器端(SpringMVC)分页模式_DataTables的服务器端_03