web应用的表单查询

3 篇文章 0 订阅
1 篇文章 0 订阅

      在开发web应用程序中,表单查询是一个非常常见的功能,但是需求五花八门,各种各样,更重要的是,后端的SQL/hql要根据页面的输入动态变化。常常形成一个庞大的拼sql的方法,并且各个方法都不一样,为开发,维护带来了不少困难。本人在总结了常用需求的基础上,再结合SSH,想出一个比较通用的方案,以此抛砖引玉。

     实现的思路是这样的,根据页面输入,自动将页面输入转化成一组查询条件,数据访问层再根据这些查询条件自动组装SQL/hql。这个是查询条件类。

import java.io.Serializable;

/**
 * 表单上的查询条件封装类
 * 封装表单的查询条件
 *
 * @auther: XXX
 * Date: 11-4-22
 * Time: 下午2:16
 * @version: 1.0
 */
public class QueryCondition implements Serializable {
    /**
     * 查询主实体名称
     */
    private String enityName;
    /**
     * 实体对应的属性名称
     */
    private String propertyName;
    /**
     * 实体对应的属性类型
     */
    private String propertyType;
    /**
     * 查询关系符号
     */
    private Operator operator;
    /**
     * like查询时候的匹配模式
     */
    private MatchMode matchMode;
    /**
     * 条件的值
     */
    private String value;
    /**
     * 输入的第二个值
     */
    private String value2;
    /**
     * 多个值
     */
    private String[] multipleValue;

    public MatchMode getMatchMode() {
        return matchMode;
    }

    public void setMatchMode(MatchMode matchMode) {
        this.matchMode = matchMode;
    }

    public String getEnityName() {
        return enityName;
    }

    public void setEnityName(String enityName) {
        this.enityName = enityName;
    }

    public String getPropertyName() {
        return propertyName;
    }

    public void setPropertyName(String propertyName) {
        this.propertyName = propertyName;
    }

    public String getPropertyType() {
        return propertyType;
    }

    public void setPropertyType(String propertyType) {
        this.propertyType = propertyType;
    }

    public Operator getOperator() {
        return operator;
    }

    public void setOperator(Operator operator) {
        this.operator = operator;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public String getValue2() {
        return value2;
    }

    public void setValue2(String value2) {
        this.value2 = value2;
    }

    public String[] getMultipleValue() {
        return multipleValue;
    }

    public void setMultipleValue(String[] multipleValue) {
        this.multipleValue = multipleValue;
    }
}

    这个是oprator类,包含了常用的操作符

/**
 * 关系操作符号
 *
 * @author XX
 * @version 1.0
 */
public enum Operator {

	LIKE,    // Like

	LT,     // 小于

	GT,    // 大于

	LE, // 小于等于

	GE,  // 大于等于

	NE,   // 不等于

	IN,    // IN

	BETWEEN, //between

	EQ        //等于
}

   MathcMode 匹配模式类,模仿hibernate

 

/**
 * @auther: XXX
 * Date: 11-4-27
 * Time: 下午4:40
 * @version: 1.0
 */
public enum MatchMode {
	//精确匹配
	EXACT,
	BEFORE,//前匹配
	AFTER, //后匹配
	ANYWHERE //任意匹配
}

   根据查询条件构造criteria

/**
	 * 根据查询条件组件 QueryBuilder
	 * QueryBuilder 的用法请参考hibernate源代码
	 *
	 * @param conditions 查询条件
	 * @return
	 */
	protected Criteria buildCritria(final List<QueryCondition> conditions) {
		if (conditions != null && conditions.size() > 0) {
			Criteria criteria = getSession().createCriteria(entityClass);
			ClassMetadata metadata = getSessionFactory().getClassMetadata(entityClass);
			for (QueryCondition qc : conditions) {
				if (qc == null) continue;
				Type type = metadata.getPropertyType(qc.getPropertyName());
				switch (qc.getOperator()) {
					case LIKE:
						if (StringUtils.isNotEmpty(qc.getValue())) {

							switch (qc.getMatchMode()) {
								case EXACT:
									criteria.add(Restrictions.like(qc.getPropertyName(), qc.getValue(), MatchMode.EXACT));
									break;
								case AFTER:
									criteria.add(Restrictions.like(qc.getPropertyName(), qc.getValue(), MatchMode.END));
									break;
								case BEFORE:
									criteria.add(Restrictions.like(qc.getPropertyName(), qc.getValue(), MatchMode.START));
									break;
								case ANYWHERE:
									criteria.add(Restrictions.like(qc.getPropertyName(), qc.getValue(), MatchMode.ANYWHERE));
									break;
								default:
									criteria.add(Restrictions.like(qc.getPropertyName(), qc.getValue(), MatchMode.ANYWHERE));
									break;
							}

						}
						break;
					case LT:
						if (StringUtils.isNotEmpty(qc.getValue())) {
							Object value = stringToObject(qc.getValue(), type);
							criteria.add(Restrictions.lt(qc.getPropertyName(), value));
						}
						break;
					case GT:
						if (StringUtils.isNotEmpty(qc.getValue())) {
							Object value = stringToObject(qc.getValue(), type);
							criteria.add(Restrictions.gt(qc.getPropertyName(), value));
						}
						break;
					case LE:
						if (StringUtils.isNotEmpty(qc.getValue())) {
							Object value = stringToObject(qc.getValue(), type);
							criteria.add(Restrictions.le(qc.getPropertyName(), value));
						}
						break;
					case GE:
						if (StringUtils.isNotEmpty(qc.getValue())) {
							Object value = stringToObject(qc.getValue(), type);
							criteria.add(Restrictions.ge(qc.getPropertyName(), value));
						}
						break;
					case NE:
						if (StringUtils.isNotEmpty(qc.getValue())) {
							Object value = stringToObject(qc.getValue(), type);
							criteria.add(Restrictions.ne(qc.getPropertyName(), value));
						}
						break;
					case IN:
						if (qc.getMultipleValue() != null && qc.getMultipleValue().length > 0) {
							Object[] values = new Object[qc.getMultipleValue().length];
							for (int i = 0; i < qc.getMultipleValue().length; i++) {
								values[i] = stringToObject(qc.getMultipleValue()[i], type);
							}
							criteria.add(Restrictions.in(qc.getPropertyName(), values));
						}
						break;
					case BETWEEN:
						if (StringUtils.isNotEmpty(qc.getValue()) || StringUtils.isNotEmpty(qc.getValue2())) {
							criteria.add(Restrictions.between(qc.getPropertyName(), qc.getValue(), qc.getValue2()));
						}
						break;
					default:
						if (StringUtils.isNotEmpty(qc.getValue())) {
							Object value = stringToObject(qc.getValue(), type);
							criteria.add(Restrictions.eq(qc.getPropertyName(), value));
						}
						break;
				}
			}
			return criteria;
		}
		return null;
	}

    根据查询条件构造hql

 

/**
	 * 根据查询条件组件 QueryBuilder
	 * QueryBuilder 的用法请参考hibernate源代码
	 *
	 * @param conditions 查询条件
	 * @return
	 */
	protected QueryBuilder buildQuery(final List<QueryCondition> conditions) {
		if (conditions != null && conditions.size() > 0) {
			String entityName = entityClass.getSimpleName();
			QueryBuilder queryBuilder = new QueryBuilder(entityName, StringUtils.uncapitalize(entityName));
			ClassMetadata metadata = getSessionFactory().getClassMetadata(entityClass);
			Parameters parameters = queryBuilder.getRootParameters();
			for (QueryCondition qc : conditions) {
				if (qc == null) continue;
				Type type = metadata.getPropertyType(qc.getPropertyName());
				switch (qc.getOperator()) {
					case LIKE:
						if (StringUtils.isNotEmpty(qc.getValue())) {
							String value = qc.getValue();
							switch (qc.getMatchMode()) {
								case EXACT:
									break;
								case BEFORE:
									value = "%" + value;
									break;
								case AFTER:
									value = value + "%";
									break;
								case ANYWHERE:
									value = "%" + value + "%";
									break;
								default:
									value = "%" + value + "%";
									break;
							}
							parameters.addWhereWithParam(qc.getPropertyName(), "like", value);
						}
						break;
					case LT:
						if (StringUtils.isNotEmpty(qc.getValue())) {
							Object value = stringToObject(qc.getValue(), type);
							parameters.addWhereWithParam(qc.getPropertyName(), "<", value);
						}
						break;
					case GT:
						if (StringUtils.isNotEmpty(qc.getValue())) {
							Object value = stringToObject(qc.getValue(), type);
							parameters.addWhereWithParam(qc.getPropertyName(), ">", value);
						}
						break;
					case LE:
						if (StringUtils.isNotEmpty(qc.getValue())) {
							Object value = stringToObject(qc.getValue(), type);
							parameters.addWhereWithParam(qc.getPropertyName(), "<=", value);
						}
						break;
					case GE:
						if (StringUtils.isNotEmpty(qc.getValue())) {
							Object value = stringToObject(qc.getValue(), type);
							parameters.addWhereWithParam(qc.getPropertyName(), ">=", value);
						}
						break;
					case NE:
						if (StringUtils.isNotEmpty(qc.getValue())) {
							Object value = stringToObject(qc.getValue(), type);
							parameters.addWhereWithParam(qc.getPropertyName(), "!=", value);
						}
						break;
					case IN:
						if (qc.getMultipleValue() != null && qc.getMultipleValue().length > 0) {
							Object[] values = new Object[qc.getMultipleValue().length];
							for (int i = 0; i < qc.getMultipleValue().length; i++) {
								values[i] = stringToObject(qc.getMultipleValue()[i], type);
							}
							parameters.addWhereWithParams(qc.getPropertyName(), "in (", values, ")");
						}
						break;
					case BETWEEN:
						if (StringUtils.isNotEmpty(qc.getValue()) && StringUtils.isEmpty(qc.getValue2())) {
							Object minValue = stringToObject(qc.getValue(), type);
							parameters.addWhereWithParam(qc.getPropertyName(), ">=", minValue);
						} else if (StringUtils.isEmpty(qc.getValue()) && StringUtils.isNotEmpty(qc.getValue2())) {
							Object maxValue = stringToObject(qc.getValue(), type);
							parameters.addWhereWithParam(qc.getPropertyName(), "<=", maxValue);
						} else if (StringUtils.isNotEmpty(qc.getValue()) && StringUtils.isNotEmpty(qc.getValue2())) {
							Object minValue = stringToObject(qc.getValue(), type);
							Object maxValue = stringToObject(qc.getValue2(), type);
							parameters.addWhereWithParam(qc.getPropertyName(), ">=", minValue);
							parameters.addWhereWithParam(qc.getPropertyName(), "<=", maxValue);
						}
						break;
					default:
						if (StringUtils.isNotEmpty(qc.getValue())) {
							Object value = stringToObject(qc.getValue(), type);
							parameters.addWhereWithParam(qc.getPropertyName(), "=", value);
						}
						break;
				}
			}
			return queryBuilder;
		}
		return null;
	}

    由于从前端传来的参数都是String型的,所以需要把String型的数据进行数据类型转换。这里借助了hibernate的实现。

       /**
	 * 从字符串到java对象的转换
	 *
	 * @param value 值
	 * @param type  类型
	 * @return
	 */
	protected Object stringToObject(String value, Type type) {
		Object ret = null;
		if (type instanceof TimestampType) {
			SimpleDateFormat sdf = new SimpleDateFormat();
			if (value.length() > 10) {
				sdf.applyPattern("yyyy-MM-dd HH:mm:ss");
			} else {
				sdf.applyPattern("yyyy-MM-dd");
			}
			try {
				ret = sdf.parse(value);
			} catch (ParseException e) {
				e.printStackTrace();
			}
		} else if (type instanceof AbstractStandardBasicType) {
			ret = ((AbstractStandardBasicType) type).fromString(value);
		}
		return ret;
	}

   调用方法执行 

    /**
	 * 根据条件自动查询
	 *
	 * @param conditions 查询条件
	 * @return
	 */
	public final List<T> findByCondition(final List<QueryCondition> conditions) {
		Criteria criteria = buildCritria(conditions);
		if (criteria != null) {
                       //增加自定义的其他查询条件
			addCustomCriterion(criteria);
			return criteria.list();
		}
		return null;
	}

    为了能将页面上输入值变成查询条件的List,结合了struts2的自动封装数据的特性,在html中只要这样写,就可以自动将输入项目组装成查询条件的List

<td width="9%" class="right">
			用户编号<@splitor/>
</td>
<td width="18%">
	<input type="hidden" name="condition[0].propertyName" value="userCode"/>
	<select id="__select_userCode" name="condition[0].operator" dojoType="dijit.form.Select">
		<option value="LIKE">模糊查询</option>
		<option value="EQ">相等</option>
		<option value="LT">小于</option>
		<option value="GT">大于</option>
		<option value="LE">小于等于</option>
		<option value="GE">大于等于</option>
		<option value="NE">不等于</option>
		<option value="IN">等于多个</option>
		<option value="BETWEEN">范围</option>
	</select>
	<input type="hidden" name="condition[0].matchMode" value="BEFORE"/>
	<input dojoType="dijit.form.TextBox" name="condition[0].value" id="userCode"/>
</td>
<td width="9%" class="right">
			名称<@splitor/>
</td>
<td width="18%">
	<input type="hidden" name="condition[1].propertyName" value="userName"/>
	<select id="__select_userName" name="condition[1].operator" dojoType="dijit.form.Select">
		<option value="LIKE">模糊查询</option>
		<option value="EQ">相等</option>
		<option value="LT">小于</option>
		<option value="GT">大于</option>
		<option value="LE">小于等于</option>
		<option value="GE">大于等于</option>
		<option value="NE">不等于</option>
		<option value="IN">等于多个</option>
		<option value="BETWEEN">范围</option>
	</select>
	<input type="hidden" name="condition[1].matchMode" value="ANYWHERE"/>
	<input dojoType="dijit.form.TextBox" name="condition[1].value" id="userName"/>
</td>

   只要在Action或者Model中定义这样的一个属性,就可以完成自动转换。

/**
 * 查询条件
 */
protected List<QueryCondition> condition;

 当页面提交或者ajax请求时,会自动将上面的输入变成condition,只要将condition传入上述的方法内,就会自动组装查询条件。

 为了让页面书写简单,将上面的输入项目写成一个宏吧,这样使用更方便。

<#--
|查询条件
| property 属性名 必须输入的参数
| index    顺序号 可选参数,但是要保证它不重复
| id       每个输入项的Id,可选参数,但是在使用dojo的情况下,要保证它不重复
| type     输入框的类型,这个类型是指的dojo的类型。
|            大体来说分为,文本,下拉单,日期输入框,数字输入框 ,复选框,单选框 这几种
-->
<#macro seniorQuery property index=0 id="id" type="text">
<label for="${id}"><@s.text name="${property}"/>:</label>
<input type="hidden" name="condition[${index}].propertyName" value="${property}"/>
<select id="__select_${id}" name="condition[${index}].operator" dojoType="dijit.form.Select">
	<option value="LIKE">模糊查询</option>
	<option value="EQ">相等</option>
	<option value="LT">小于</option>
	<option value="GT">大于</option>
	<option value="LE">小于等于</option>
	<option value="GE">大于等于</option>
	<option value="NE">不等于</option>
	<option value="IN">等于多个</option>
	<option value="BETWEEN">范围</option>
</select> &nbsp;
<#--根据类型决定输入控件的类型-->
	<#local controlType = "dijit.form.TextBox">
	<#if type=="number">
		<#local controlType="dijit.form.NumberTextBox">
		<#elseif type="date">
			<#local controlType="dijit.form.DateTextBox">
		<#elseif type="combo">
			<#local controlType="dojo.form.ComboBox">
		<#elseif type="checkbox">
			<#local controlType="dojo.form.CheckBox">
		<#elseif type="radio">
			<#local controlType="dojo.form.Radio">
	</#if>
<input id="${id}" name="condition[${index}].value" dojoType="${controlType}"/>
<script type="text/javascript">
	dojo.ready(function() {
		dojo.connect(dijit.byId("__select_${id}"), "onChange", function() {
			var operator = dijit.byId("__select_${id}").value;
			if (operator == "BETWEEN") {
				var newObj = new ${controlType}({
					id:"${id}Max",
					name:"condition[${index}].value2"});
				newObj.placeAt(dijit.byId("${id}").domNode, "after");
			} else {
				if (typeof(dijit.byId("${id}Max")) != "undefined") {
					dijit.byId("${id}Max").destroy();
				}
			}
		});
	})
</script>
</#macro>

 这样使用起来更方便,你的查询方法会更容易维护,因为所有 表单的查询条件,都被自动创建为criteria了,额外的关联你可以在addCustomCriterion(criteria);里面实现,这个方法是空方法,你只要新建一个子类,重写此方法就可以了。

 

当然你可以扩展这些宏,或者这些方法。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值