在上一節中配置好了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的東西,所以不介紹了。