首先说明一下,什么是jqGrid的单字段查询。就是只能通过一个字段作为查询条件的查询。虽然大多数情况下,系统功能需要的查询,都是多个字段组合查询的,jqGrid也提供组合多个字段的查询,但是组合多个字段的查询,其实基于的原理还是单字段的查询。所以这里主要介绍一下单字段的查询,以后有机会在来说明一下高级一点的多字段组合查询。
大家可以先看看图片,对单字段查询有一个直观的了解(图片来源于jqGrid的wiki)
这里需要注意的几点分别说明如下:
首先,其实默认jqGrid就有这个功能,所以,向上一篇文章中的例子一样,基本上不需要在js中添加其他的代码。
其次,哪些字段能作为单字段查询,哪些字段不能,这是在jqGrid的colModel属性中可以进行设置的。比如,如果我们对某一个列的数据设置如下:{name:'userName',index:'usrName', width:110,search=false} 那么这个userName列是不会在单字段查询中显示出来的。
第三,点击上面图中的"Find"按钮之后,jqGrid会额外的向服务器发送几个参数过去。想必你已经猜到了,这些参数就是图片中显示的:查询字段searchField,查询操作searchOper以及查询值searchString。这里之所以说是额外的参数,是相对于默认情况下的参数,比如前一篇文章中所说的page,sidx,sord,rows,_search,nd这些参数而言的。当然,这里_search参数的值是true。
第四,最烦处理的,就是如何把我们的searchOper的参数值,转换为对数据库记录的查询操作。
页面上显示的操作,主要包括'equal', 'not equal', 'less', 'less or equal','greater','greater or equal', 'begins with','does not begin with','is in','is not in','ends with','does not end with','contains','does not contain'。相应的,后台获取到的searchOper的值,对应的是'eq','ne','lt','le','gt','ge','bw','bn','in','ni','ew','en','cn','nc'。
最最简单的想法就是像我这养,写一个方法来处理:(以下的例子是基于Hibernate写的)
/**
* 根据参数获取查询HQL的语句(如果是日期字段可能会出问题)
* @param sField 字段名称
* @param sOper 操作名称
* @param sValue 值
* @return
*/
public static String getOperation(String sField, String sOper,String sValue){
if(sValue==null || sValue.trim().length() ==0)
return "";
String result = "";
if(sOper.trim().equals("eq")) //等于 result = sField + "='" + sValue +"' ";
else if(sOper.trim().equals("ne")) //不等于 result = sField + " != '"+ sValue+"' ";
else if(sOper.trim().equals("lt")) //小于 result = sField + " < '"+ sValue+"' ";
else if(sOper.trim().equals("le")) //小于等于 result = sField + " <= '"+ sValue+"' ";
else if(sOper.trim().equals("gt")) //大于 result = sField + " > '"+ sValue+"' ";
else if(sOper.trim().equals("ge")) //大于等于 result = sField + " >= '"+ sValue+"' ";
else if(sOper.trim().equals("bw")) //以...开始 result = sField + " LIKE '"+ sValue+"%' ";
else if(sOper.trim().equals("bn")) //不以...开始 result = sField + " NOT LIKE '"+ sValue+"%' ";
else if(sOper.trim().equals("in")){ //包括 String[] sv = sValue.split(",");
String svString = "";
for(int i = 0 ;i
然后在dao类中,像如下这样进行查询和返回
@SuppressWarnings("unchecked") public PageModel findAll(int page, int rows, String sidx, String sord,String sField,String sValue,String sOper) { Session s = null; try { s = this.getSession(); //根据查询条件封装HQL语句 StringBuilder searchHQL = new StringBuilder (""); if(sField != null && sOper != null){ String cond = SearchOperationUtil.getOperation(sField, sOper, sValue); if(cond != null && cond.trim().length() != 0){ searchHQL.append(" WHERE brand."); searchHQL.append(cond); } } //得到总记录数 String queryCountHql = "select count(*) from MProductBrand brand" + searchHQL.toString(); Query query = s.createQuery(queryCountHql); int records = ((Long)query.uniqueResult()).intValue(); int offset = (page-1) * rows; searchHQL.append(" order by "); searchHQL.append("brand."); searchHQL.append(sidx); searchHQL.append(" "+sord); List datas = s.createQuery("from MProductBrand brand" + searchHQL.toString()) .setFirstResult(offset) .setMaxResults(rows) .list(); //得到结果集 PageModel pm = new PageModel(); int totalPage = records/rows; if(records % rows > 0) totalPage += 1; pm.setTotal(totalPage); pm.setDatas(datas); pm.setPage(page); pm.setRecords(records); return pm; } finally { if(s!=null) s.close(); } }
当然了,在Action类中,我们还需要单独设置几个属性字段,并同时生成其相应的Getter和Setter。
private String searchField; private String searchString; private String searchOper;...... @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); PageModel pm = this.brandService.findAll(Integer.parseInt(page), Integer.parseInt(rows),sidx,sord,searchField,searchString,searchOper); //封装成JSON对象返回 HttpServletResponse response = ServletActionContext.getResponse(); response.setContentType("text/json; charset=UTF-8"); PrintWriter out = response.getWriter(); JSONObject obj = new JSONObject(); obj.put("page", pm.getPage()); obj.put("total",pm.getTotal()); obj.put("records",pm.getRecords()); JSONArray lineitemArray = new JSONArray(); List data = pm.getDatas(); Iterator it = data.iterator(); while (it.hasNext()){ MProductBrand mpb = (MProductBrand)it.next(); lineitemArray.add(mpb.getJSON()); } obj.put("rows", lineitemArray); out.print(obj.toString()); } catch(Exception e) { e.printStackTrace(); } return null; }
看上面这个代码可能会有点糊涂,因为我们的Pojo对象中,还有一个重要的方法,把POJO转换为JSON对象。
//注意,JSON数据插入的顺序是和页面显示的顺序相关的。 public JSONObject getJSON(){ JSONObject json = new JSONObject(); json.put("brandId", brandId); json.put("code", code); json.put("brandName", brandName); json.put("status", status); return json; }
感觉比较丑陋的,还是那个getOperation方法,一个是本身还有很多的bug,比如对空值的处理不够完善;二是对日期类型的处理功能基本上没有等等。呵呵,暂时先这样吧!有兴趣大家也可以帮忙修改一下!
多字段查询,相对于单字段的过滤而言,可以称得上是高级查询了。见下图。
多字段查询的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();
}
}
看起来比以前更简单了。
以上部分代码存在一些隐患,请勿用于您的正式环境!