多字段查询,相对于单字段的过滤而言,可以称得上是高级查询了。见下图。
多字段查询的jqGrid调用方式如下:
$(document).ready(function(){ $("#grid").jqGrid({ url:'queryAllBrand.action', datatype: "json", mtype: 'POST', colNames:['品牌ID','品牌代码', '品牌名称', '品牌状态','最后修改时间'], colModel:[ {name:'brandId',index:'brandId', width:90}, {name:'code',index:'code', width:110}, {name:'brandName',index:'brandName', width:100}, {name:'status',index:'status', width:80}, {name:'lastModifiedDatetime',index:'lastModifiedDatetime', width:100} ], rowNum:30, rowList:[30,40,50], pager: '#nav', sortname: 'brandId', viewrecords: true, width: 500, height: 500, sortorder: "ASC", jsonReader: { repeatitems : false, id: "0" }, caption: "品牌信息" }).navGrid('#nav',{edit:false,add:false,del:false}) .searchGrid({multipleSearch:true}); });
大家可以看到最后一句话:.searchGrid({multipleSearch:true}),对,就是这么简单。(不过这种拼接调用的方式有一个问题。页面进入的时候,默认就会出来一个查询框,就像上面的图显示的一样,这感觉很不爽!暂时还没有找到好的办法!!??)
那么这种方式和单字段查询的区别在什么地方呢?
单字段查询,jqGrid向服务器传递的参数是searchField、searchString、searchOper这三个参数。多字段查询的时候,jqGrid不再采用这种方式,因为多个查询条件,参数数目就不是固定的了。这种情况下,jqGrid向服务器传递的参数是一个名字叫做filters的json字符串。类似于如下的形式:
filters = {"groupOp":"AND", "rules":[ {"field":"invdate","op":"ge","data":"2007-10-06"}, {"field":"invdate","op":"le","data":"2007-10-20"}, {"field":"name","op":"bw","data":"Client 3"} ] }
知道了这一点,我们就可以在Action类中定义一个变量是filters,然后用json-lib包提供的JSONObject类来进行json参数的解析。
json-lib包不仅提供了把Bean或容器类List、Map等转换为json格式数据的方法,而且提供了把json格式数据转换为容器类或者Bean类对象的方法。
为了方便转换,我们这里定义了两个Transfer Object对象,分别是JqGridSearchTo对象以及JqGridSearchDetailTo对象。
JqGridSearchTo对象的定义如下:(为了方便,我们把一些其他jqGrid上传的参数也封装了进去)
public class JqGridSearchTo implements java.io.Serializable{ private String groupOp; //多字段查询时分组类型,主要是AND或者OR private List rules; //多字段查询时候,查询条件的集合 private int page; //当前第几页 private int rows; //每页显示多少条数据 private String sidx; //排序字段 private String sord; //排序类型 ASC或者DESC private boolean _search; //是否是查询 true 或者 false private String nd; //暂时不清楚啥用的 private String searchField; //单字段查询的时候,查询字段名称 private String searchString; //单字段查询的时候,查询字段的值 private String searchOper; //单字段查询的时候,查询的操作 public JqGridSearchTo(){ } ......//省略Getter和Setter方法定义 }
然后定义jqGridSearchDetailTo对象,这个对象只是为多字段查询的rules中的查询条件设计的,相对非常简单:
public class JqGridSearchDetailTo implements java.io.Serializable{ private String field; //查询字段 private String op; //查询操作 private String data; //选择的查询值 public JqGridSearchDetailTo(){ } ......//省略Getter和Setter方法定义 }
这里需要说明一下,大家应该看到了,这两个类定义的属性和获取的json数据格式是一样的。这就方便json-lib进行转换。
然后大家看看Action中的执行方法类的定义:
@SuppressWarnings("unchecked") public String queryAllBrand() { try { if(page == null ) page= "1"; if(sidx==null) sidx ="brandId"; if(rows==null) rows ="30"; if(sord==null) sord ="asc"; if(_search == null) _search=""; if(nd == null) nd = "0"; log.info("Page="+page+";sidx="+sidx+";rows="+rows+";sord="+sord+";_search="+_search+";nd="+nd); log.info("searchField="+searchField+";searchValue="+searchString+";searchOper="+searchOper); log.info("filters="+filters); JqGridSearchTo to = new JqGridSearchTo(); if(_search.equals("true") && filters != null ){ JSONObject filt = JSONObject.fromObject(filters); Map m = new HashMap(); m.put("rules", JqGridSearchDetailTo.class); to = (JqGridSearchTo)JSONObject.toBean(filt, JqGridSearchTo.class, m); log.info(to.toString()); } to.setPage(Integer.parseInt(page)); to.setRows(Integer.parseInt(rows)); to.setSidx(sidx); to.setSord(sord); to.setSearchField(searchField); to.setSearchOper(searchOper); to.setSearchString(searchString); to.set_search(Boolean.parseBoolean(_search)); PageModel pm = this.brandService.findAll(to); //封装成JSON对象返回 HttpServletResponse response = ServletActionContext.getResponse(); response.setContentType("text/json; charset=UTF-8"); PrintWriter out = response.getWriter(); JSONObject ja = JSONObject.fromObject(pm); out.print(ja.toString()); } catch(Exception e) { e.printStackTrace(); } return null; }
这里需要说明一下的是:我们在转换jqGrid上传的json数据的时候,没有直接用JSONObject.toBean(filt,JqGridSearchTo.class)这种方式,而是首先定义了一个Map对象,并指定了rules转换的目标对象。这是因为如果不用Map指定rules的List包含的对象类,那么,json-lib默认会把rules的List中包含的对象转换成一个叫做ezmorphDynaBean的对象,而不会按照我们的定义转换成JqGridSearchDetailTo对象。
另外,相比于我上一篇文章中的转换对象为Json数据的方式,本文中直接使用了JSONObect.fromObject方法,是不是更简单了!!
然后就该重新设定一个方法来解析我们获取到的查询条件了。
/** * 根据表名称的别名以及JqGridSearchTo的查询条件组合HQL语句 * @param alias 查询对象对应的别名 * @param to * @return */ public static String getCombOperation(String alias,JqGridSearchTo to){ StringBuilder result = new StringBuilder(""); if (to != null) { if(!to.get_search()){ //不是查询,则直接组合排序条件即可 result.append(" order by "); result.append(alias); result.append("."); result.append(to.getSidx()); result.append(" "+to.getSord()); } else if (to.get_search() && to.getSearchField() != null && to.getSearchOper() != null ) { //只是单字段的查询 String cond = SearchOperationUtil.getOperation(to.getSearchField(), to.getSearchOper(), to.getSearchString()); if(cond != null && cond.trim().length() != 0){ result.append(" WHERE "); result.append(alias); result.append("."); result.append(cond); } result.append(" order by "); result.append(alias); result.append("."); result.append(to.getSidx()); result.append(" "+to.getSord()); } else if (to.get_search() && to.getGroupOp() != null && to.getRules()!= null && to.getRules().size() != 0){ //多字段的组合查询 String groupOp = to.getGroupOp(); Iterator it = to.getRules().iterator(); result.append(" WHERE "); int i = 0; while (it.hasNext()) { //循环处理所有的查询条件 i++; JqGridSearchDetailTo dto = (JqGridSearchDetailTo) it.next(); result.append(alias); result.append("."); result.append(SearchOperationUtil.getOperation(dto .getField(), dto.getOp(), dto.getData())); if (i < to.getRules().size()) result.append(" " + groupOp + " "); } result.append(" ORDER BY "); result.append(alias); result.append("."); result.append(to.getSidx()); result.append(" "+to.getSord()); } } log.info(result.toString()); return result.toString(); }
OK,在最后看一下我们的Dao实现类是如何实现的吧。
@SuppressWarnings("unchecked") public PageModel findAll(JqGridSearchTo to) { if (to == null ) return null; Session s = null; try { s = this.getSession(); int page = to.getPage(); int rows = to.getRows(); String whereCond = SearchOperationUtil.getCombOperation("brand", to); //得到总记录数 String queryCountHql = "select count(*) from MProductBrand brand" + whereCond; Query query = s.createQuery(queryCountHql); int records = ((Long)query.uniqueResult()).intValue(); int offset = (page-1) * rows; List datas = s.createQuery("from MProductBrand brand" + whereCond) .setFirstResult(offset) .setMaxResults(rows) .list(); //得到结果集 PageModel pm = new PageModel(); int totalPage = records/rows; if(records % rows > 0) totalPage += 1; pm.setTotal(totalPage); pm.setRows(datas); pm.setPage(page); pm.setRecords(records); return pm; } finally { if(s!=null) s.close(); } }
看起来比以前更简单了。
OK,That’s all !
以上部分代码存在一些隐患,请勿用于您的正式环境!