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

08-06-28 freebox
         

一、问题:经常碰到查询问题,查询参数需要放在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;
	}
}
<p>

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);
<p>

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

下面这些是给hql查询用的。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Hql {
	/**
	 * hql查询语句
	 */
	public abstract String hql();
}
<p>

//这个是在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;
}
<p>

         

freebox
2008-06-28 22:55

下面是查询方法,所需要的几个类(如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);
	}
<p>

四、举例:

查询参数类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
}
<p>

查询参数类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
}
<p>

分页方法不需要区分是哪个查询参数类,统一对待成QueryComponent,如果有特殊要求,可以在service中转换成指定的类型并进行特殊处理(如:需要in查询,此类查询参数暂时无法通过JSON解释成字串,或因过长被截断。SearchByDate searcher=(SearchByDate)queryComponent;searcher.setRoles(...))。

五、不足:

1、JsonUtil在处理Date类型上性能不佳,有做过类似东西的朋友请指点指点。

2、对于一些特殊查询(查询参数不仅在页面上,比如传进name,需要找到此name匹配的集合来做为参数),没想到好办法处理,也望大家指点。