在上一节中配置好了DataTable的配置,接下来介绍关于后台实现分页、条件查询、排序的方法。
DataTable后台分页、条件查询、排序
1.DataTable参数的接收
后台代码需要接收的参数有:
- 当前记录数
- 每页显示记录
- 排序的列(可能有多个)
- 排序列的方向 ASC/DESC(可能有多个)
- 当前请求次数
- 排序列的数量
- 自定义查询的参数
然后再来看上一节中DataTable的基础配置中,有以下代码:
function retrieveData( sSource, aoData, fnCallback ) {
//查询条件称加入参数数组
var rentRuleId =document.getElementById('rentRuleId').value;
//alert(rentRuleId);
$.ajax( {
type: "POST",
url: sSource,
dataType:"json",
data: "jsonParam="+JSON.stringify(aoData)+"&isHistory=0&rentRuleId="+rentRuleId,
success: function(data) {
//$("#url_sortdata").val(data.aaData);
fnCallback(data); //服务器端返回的对象的returnObject部分是要求的格式
}
});
}
这段代码是是关键,它定义了关于DataTable的自己的参数适用jsonParam来接收,而关于自定义的查询参数如isHistory、rentRuleId都适用相应的属性来接收。但是也有一些方法是将这些参数放在aoData里面,然后用同一个对象接收所有的数据。这里先不考虑。
这时,相应的Controller就应该写成这样:
dataTableTestController.java
@RequestMapping(value = "/list", method = {RequestMethod.POST })
protected void list(String jsonParam,ChargeRuleModel model,HttpServletResponse response){
xxxx;//这里避免干扰先不写
}
第一,通过ChargeRuleModel来接收自定义查询的参数。
第二,用jsonParam来接受相应的jsonString。jsonParam的格式如下:
[{"name":"sEcho","value":1}, //请求次数
{"name":"iColumns","value":5}, //列数
{"name":"sColumns","value":",,,,"}, //这个没查到
{"name":"iDisplayStart","value":0}, //记录起始,0表示第一条
{"name":"iDisplayLength","value":10}, //每页显示的记录数
{"name":"mDataProp_0","value":"key"}, //第一列名称
{"name":"bSortable_0","value":false}, //第一列是否可以排序
{"name":"mDataProp_1","value":"rentRuleId"},//第二列名称
{"name":"bSortable_1","value":true}, //第二列是否可以排序
{"name":"mDataProp_2","value":"ruleName"},
{"name":"bSortable_2","value":true},
{"name":"mDataProp_3","value":"isEnable"},
{"name":"bSortable_3","value":true},
{"name":"mDataProp_4","value":"id"},
{"name":"bSortable_4","value":true},
{"name":"iSortCol_0","value":1},//第一个排序,value值得是第几列
{"name":"sSortDir_0","value":"asc"},//排序是正序还是倒叙
{"name":"iSortingCols","value":1}] //一共有几列排序
看出来了么,其实是个JsonArray,所以常见的方法是这样:
JSONArray ja = (JSONArray) JSONArray.parse(aoData);
//分别为关键的参数赋值
for (int i = 0; i < ja.size(); i++) {
JSONObject obj = (JSONObject) ja.get(i);
if (obj.get("name").equals("sEcho"))
sEcho = obj.get("value").toString();
if (obj.get("name").equals("iDisplayStart"))
iDisplayStart = obj.get("value").toString();
if (obj.get("name").equals("iDisplayLength"))
iDisplayLength = obj.get("value").toString();
if (obj.get("name").equals("sSearch"))
sSearch = obj.get("value").toString();
}
对于一个JsonArray操作多麻烦,而且格式那么统一,“name”、“value”让人想起什么?
protected Map<String,Object> covertJsonStringToHashMap(String jsonParam){
JSONArray jsonArray = JSONArray.parseArray(jsonParam);
Map<String,Object> map = Maps.newHashMap();
for(int i=0;i<jsonArray.size();i++){
JSONObject jsonObj = jsonArray.getJSONObject(i);
map.put(jsonObj.getString("name"), jsonObj.get("value"));
}
return map;
}
这时候考虑到可能有实现多个列的排序,另外不要再service再去判断参数中第一列到底对应的是数据库那个字段,所以就需要对于Map再进一步操作。这时引入一个对象 DataTableParameter。
public class DataTableParameter {
private int sEcho; //请求服务器端次数
private int iDisplayStart;//其实记录,第一条为0
private int iDisplayLength;
private int iColumns;
private List<String> mDataProps; //列的Name列表
private List<Boolean> bSortables;//列对应是否能排序
private int iSortingCols;
private List<Integer> iSortCols; //排序列的编号
private List<String> iSortColsName; //排序列的名称
private List<String> sSortDirs; //排布列排序形式 Asc/Desc
......
//seter and getter
}
然后引入一个方法
protected DataTableParameter getDataTableParameterByJsonParam(String jsonParam){
Map<String,Object> map = covertJsonStringToHashMap(jsonParam);
int sEcho = (int) map.get("sEcho");
int iDisplayStart = (int) map.get("iDisplayStart");
int iDisplayLength = (int) map.get("iDisplayLength");
int iColumns = (int)map.get("iColumns");
int iSortingCols = (int)map.get("iSortingCols");
List<String> mDataProps = Lists.newArrayList();
List<Boolean> bSortables = Lists.newArrayList();
for(int i=0;i<iColumns;i++){
String dataProp = (String) map.get("mDataProp_"+i);
Boolean sortable = (Boolean) map.get("bSortable_"+i);
mDataProps.add(dataProp);
bSortables.add(sortable);
}
List<Integer> iSortCols = Lists.newArrayList();
List<String> sSortDirs = Lists.newArrayList();
List<String> iSortColsName = Lists.newArrayList();
for(int i=0;i<iSortingCols;i++){
Integer sortCol = (Integer) map.get("iSortCol_"+i);
String sortColName = mDataProps.get(sortCol);
String sortDir = (String) map.get("sSortDir_"+i);
iSortCols.add(sortCol);
sSortDirs.add(sortDir);
iSortColsName.add(sortColName);
}
return new DataTableParameter(sEcho, iDisplayStart, iDisplayLength, iColumns, mDataProps, bSortables, iSortingCols, iSortCols, sSortDirs,iSortColsName);
}
这时Controller就变成这样了:
@RequestMapping(value = "/list", method = {RequestMethod.POST })
protected void list(String jsonParam,ChargeRuleModel model,HttpServletResponse response) {
DataTableParameter dataTableParam = getDataTableParameterByJsonParam(jsonParam);
//......
}
参数接收处理完毕。
2.DataTable返回页面参数
DataTable.java
public class DataTable<T>{
private List<T> aaData;//数据
private int iTotalDisplayRecords;//得到的记录数
private int iTotalRecords;//数据库中记录数
private int sEcho; //请求服务器端次数
//getter and setter
}
aaData接收相应的查询返回结果,sEcho在请求一次后就加1.
完整的Controller方法如下:
@RequestMapping(value = "/list", method = {RequestMethod.POST })
protected void list(String jsonParam,ChargeRuleModel model,HttpServletResponse response) {
DataTableParameter dataTableParam = getDataTableParameterByJsonParam(jsonParam);
List<ChargeRuleModel> aaData = chargeRuleService.list(model, dataTableParam);
DataTable<ChargeRuleModel> dt = new DataTable<ChargeRuleModel>();
int sEcho = dataTableParam.getsEcho()+1;
dt.setAaData(aaData);
dt.setsEcho(sEcho);
dt.setiTotalDisplayRecords(aaData.size());
dt.setiTotalRecords(aaData.size());
response.setCharacterEncoding("utf-8");
response.getWriter().write(JSONObject.toJSONString(dt));
}
3.动态查询与分页
Service方法如下:
public List<ChargeRuleModel> list(ChargeRuleModel model, DataTableParameter dataTableParam) {
Pageable pageRequest = buildPageRequest(dataTableParam,ChargeRule.class);
Specification<ChargeRule> spec = buildSpecification(model);
List<ChargeRule> chargeRules = chargeRuleDao.findAll(spec, pageRequest).getContent();
List<ChargeRuleModel> chargeRuleModels = null;
// PO到VO的转化
Map<String, String> map = Maps.newHashMap();
map.put("sourcePro1", "ruleAppliType.id");
map.put("targetPro1", "ruleAppliTypePK");
map.put("sourcePro2", "ruleAppliType.name");
map.put("targetPro2", "ruleAppliTypeName");
chargeRuleModels = BeanUtils.copyPropertiesBylist(chargeRules, ChargeRuleModel.class, map);
return chargeRuleModels;
}
private Specification<ChargeRule> buildSpecification(ChargeRuleModel model) {
String rentRuleId = model.getRentRuleId();
boolean isHistory = model.getIsHistory();
String historyRentRuleId = model.getHistoryRentRuleId();
return new Specification<ChargeRule>() {
@Override
public Predicate toPredicate(Root<ChargeRule> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
List<Predicate> list = new ArrayList<Predicate>();
list.add(cb.equal(root.get("isHistory"), isHistory));
if (StringUtils.isNotNullAndEmpty(historyRentRuleId)) {
list.add(cb.equal(root.get("rentRuleId"), historyRentRuleId));
}
if (StringUtils.isNotNullAndEmpty(rentRuleId)) {
list.add(cb.like(root.get("rentRuleId"), "%" + rentRuleId + "%"));
}
Predicate[] p = new Predicate[list.size()];
return cb.and(list.toArray(p));
}
};
}
protected PageRequest buildPageRequest(DataTableParameter dataTableParam,Class<T> entityClass){
int iDisplayStart = dataTableParam.getiDisplayStart();
int iDisplayLength = dataTableParam.getiDisplayLength();
List<String> iSortColsName = dataTableParam.getiSortColsName();
List<String> sSortDirs = dataTableParam.getsSortDirs();
Sort sort = null;
if(!ListUtils.isNullOrEmpty(iSortColsName)){
validatorSortColumns(iSortColsName,entityClass);
List<Order> orders = Lists.newArrayList();
for(int i=0;i<iSortColsName.size();i++){
String sortCol = iSortColsName.get(i);
String dir = sSortDirs.get(i);
orders.add(new Order(getDirectionByDirString(dir),sortCol));
}
sort = new Sort(orders);
}
return new PageRequest(iDisplayStart / iDisplayLength, iDisplayLength, sort);
}
因为经过之前封装成DataTableParam之后,分页以及排序的方法都是相同的。所以实际上我是把buildPageRequest(..)方法放在BaseService中的。PageRequest、Specification是Spring data jpa的东西,所以不介绍了。