分页及查询参数传递问题分享

一、问题:经常碰到查询问题,查询参数需要放在request中,并保证无论是第一次Post,还是以后分页时的get,都能正确取到这些值。于是做了一个小工具,让这种操作更方便,并可用于hql查询(不用hql这功能也可,它还不完善)
二、需要支持:
1、JSON:http://www.json.org/,到这里找forJava的,包名为org.json
2、JDK5
三、代码:


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface QueryParam {}
/**
* JSON 拆解、装配工具
*/

public class JsonUtil {
private static final Log LOG = LogFactory.getLog(JsonUtil.class);
private static final DateFormat dateFormat = DateFormat
.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL);

/**
* 将JSON字符串装配成对象
*
* @param jsonString
* JSON字符串
* @param object
* 被装配的目标对象
*/

public static void assemble(String jsonString, Object object) {
try {
JSONObject json = new JSONObject(jsonString);
Field fields[] = object.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
QueryParam param = field.getAnnotation(QueryParam.class);
if (param == null) {
continue;
}
if (JSONObject.NULL == json.get(field.getName())) {
field.set(object, null);
continue;
}
Type type = field.getType();
//虽然我知道可以用其它形式书写下面这些,不过就这么几个,将就一下吧
if (type == Integer.class) {
field.set(object, json.getInt(field.getName()));
} else if (type == Double.class) {
field.set(object, json.getDouble(field.getName()));
} else if (type == Long.class) {
field.set(object, json.getLong(field.getName()));
} else if (type == Boolean.class) {
field.set(object, json.getBoolean(field.getName()));
} else if (type == Date.class) {
String dateString = json.getString(field.getName());
field.set(object, dateFormat.parse(dateString));
} else {
field.set(object, json.getString(field.getName()));
}
}
} catch (JSONException e) {
LOG.debug(e);
} catch (IllegalArgumentException e) {
LOG.debug(e);
} catch (IllegalAccessException e) {
LOG.debug(e);
} catch (ParseException e) {
LOG.debug(e);
}
}

/**
* 将对象拆解成JSON字符串
*
* @param object
* 被拆解的对象
* @return JSON字符串
*/

public static String disassemble(Object object) {
try {
Field fields[] = object.getClass().getDeclaredFields();
JSONStringer json = new JSONStringer();
JSONWriter writer = json.object();
for (Field field : fields) {
field.setAccessible(true);
QueryParam param = field.getAnnotation(QueryParam.class);
if (param == null) {
continue;
}
Type type = field.getType();
if (type == Date.class) {
Date date = (Date) field.get(object);
writer.key(field.getName()).value(dateFormat.format(date));
} else {
writer.key(field.getName()).value(field.get(object));
}
}
writer.endObject();
return writer.toString();
} catch (JSONException e) {
LOG.debug(e);
} catch (IllegalArgumentException e) {
LOG.debug(e);
} catch (IllegalAccessException e) {
LOG.debug(e);
}
return null;
}
}

web中的一小段代码:

//这些都需要您从request里取得
//QueryComponent queryComponent
//String param
if (params != null) {
// get提交
JsonUtil.assemble(params, queryComponent);
setQueryComponent(queryComponent);
} else {
// post提交
if (queryComponent != null) {
params = JsonUtil.disassemble(queryComponent);
}
}
request.setAttribute(
"params", params);

利用上面的工具,可以在连接中创建查询字串,例如?index=1¶m={"name":"Json"},点击连接,param后的JSON串会被组装成查询对象。
下面这些是给hql查询用的。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Hql {
/**
* hql查询语句
*/

public abstract String hql();
}

//这个是在QueryComponent中字段上设置的注释,不加此注释将不作为查询用。并针对like查询的%问题,left表示左侧有%,no表示此字段不使用like匹配

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface HqlParam {
public enum like {
left, right, all,no;
}

public abstract HqlParam.like like() default HqlParam.like.no;
}


下面是查询方法,所需要的几个类(如Page)找不到的可以用自己的分页方式操作。


public Page<T> queryByPage(final int startRow, final int pageSize,
QueryComponent queryComponent) {
Hql hql = queryComponent.getClass().getAnnotation(Hql.class);
String hqlString = hql.hql();
Query countQuery = createQuery("select count(*) "
+ cutHqlSelect(cutHqlOrder(hqlString)));
Query dataQuery = createQuery(hqlString);
Field[] fields = queryComponent.getClass().getDeclaredFields();
try {
for (Field field : fields) {
HqlParam hqlParam = field.getAnnotation(HqlParam.class);
if (hqlParam == null) {
// 注释为空,跳进下次循环
continue;
}
field.setAccessible(true);
String name = field.getName();
Object value = field.get(queryComponent);
switch (hqlParam.like()) {
case all:
value =
"%" + value + "%";
break;
case left:
value =
"%" + value;
break;
case right:
value = value +
"%";
break;
default:
;
}
countQuery.setParameter(name, value);
dataQuery.setParameter(name, value);
}
} catch (IllegalArgumentException e) {
//TODO debug
} catch (IllegalAccessException e) {
//debug
}
int dataRows = ((Long) countQuery.uniqueResult()).intValue();

dataQuery.setFirstResult(startRow);
dataQuery.setMaxResults(pageSize);
List<T> data = dataQuery.list();
return new Page<T>(data, startRow, dataRows);
}

四、举例:
查询参数类1:

@Hql(hql = "from Account account where account.item like :name")
public class SearchByName implements QueryComponent {
@QueryParam
@HqlParam(like = HqlParam.like.all)
private String name;
//get/set
}

查询参数类2:

@Hql(hql = "from Account account where account.recordTime between :begin and :end")
public class SearchByDate implements QueryComponent {
@QueryParam
@HqlParam
private Date begin;
@QueryParam
@HqlParam
private Date end;
//get/set
}

分页方法不需要区分是哪个查询参数类,统一对待成QueryComponent,如果有特殊要求,可以在service中转换成指定的类型并进行特殊处理(如:需要in查询,此类查询参数暂时无法通过JSON解释成字串,或因过长被截断。SearchByDate searcher=(SearchByDate)queryComponent;searcher.setRoles(...))。
五、不足:
1、JsonUtil在处理Date类型上性能不佳,有做过类似东西的朋友请指点指点。
2、对于一些特殊查询(查询参数不仅在页面上,比如传进name,需要找到此name匹配的集合来做为参数),没想到好办法处理,也望大家指点。