`
walle1027
  • 浏览: 23007 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

web应用的表单查询

 
阅读更多

      在开发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);里面实现,这个方法是空方法,你只要新建一个子类,重写此方法就可以了。

 

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

 

 

 

1
2
分享到:
评论

相关推荐

    python入门-30.寻找列表中只出现一次的数字-寻找单身狗.py

    python入门-30.寻找列表中只出现一次的数字——寻找单身狗.py

    布尔教育linux优化笔记

    linux优化笔记,配套视频:https://www.bilibili.com/list/474327672?sid=4496133&spm_id_from=333.999.0.0&desc=1

    知识付费系统-直播+讲师入驻+课程售卖+商城系统-v2.1.9版本搭建以及资源分享下载

    知识付费系统-直播+讲师入驻+课程售卖+商城系统-v2.1.9版本搭建以及资源分享下载,CRMEB知识付费分销与直播营销系统是由西安众邦科技自主开发的一款在线教育平台,该系统不仅拥有独立的知识产权,还采用了先进的ThinkPhp5.0框架和Vue前端技术栈,集成了在线直播教学及课程分销等多种功能,旨在为用户提供全方位的学习体验,默认解压密码youyacaocom

    美妆神域-JAVA-基于springBoot美妆神域设计与实现

    美妆神域-JAVA-基于springBoot美妆神域设计与实现

    原生js制作Google粘土logo动画涂鸦代码.zip

    原生js制作Google粘土logo动画涂鸦代码.zip

    golin 扫描工具使用, 检查系统漏洞、web程序漏洞

    golin 扫描工具使用, 检查系统漏洞、web程序漏洞

    原生态纯js图片网格鼠标悬停放大显示特效代码下载.zip

    原生态纯js图片网格鼠标悬停放大显示特效代码下载.zip

    用AWLUM进行灰色编码2^2n-QAM调制的精确率Matlab代码.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。 替换数据可以直接使用,注释清楚,适合新手

    去水印web端独立版web

    去水印web端独立版web

    原生js制作左侧浮动可折叠在线客服代码.zip

    原生js制作左侧浮动可折叠在线客服代码.zip

    Chrome 谷歌浏览器下载

    Chrome 谷歌浏览器下载

    亲测全新完整版H5商城系统源码 附教程

    全新完整版H5商城系统源码 自己花钱买的,亲测可用,需要自行下载 H5商城系统设置是实现商城基本功能的核心部分,涵盖了从网站配置、短信和支付配置,到商品、工单、订单、分站和提现管理等多个模块的设置。以下是详细的设置指南,帮助您快速上手并高效管理商城系统。 测试环境:Nginx+PHP7.0+MySQL5.6 1. 网站配置 设置商城名称、LOGO、标题、联系方式和SEO关键词等,确保商城专业和易于搜索。 2. 短信配置 配置短信接口和模板,用于发送订单通知、验证码等,提升用户体验。 3. 支付接口配置 配置微信、支付宝等支付接口,填写API密钥和回调地址,确保支付流畅。 4. 商品分类管理 对商品进行分类和排序,设置分类名称和图标,便于用户查找商品。 5. 商品管理 添加和管理商品信息、规格、图片等,确保商品信息准确丰富。 6. 工单管理 查看和回复用户工单,记录售后问题,提升用户服务质量。 7. 订单管理 查看订单详情,更新订单状态,支持批量导出,方便订单跟踪。 8. 分站管理 创建不同区域分站,设置权限,统一管理各区域市场。 9. 提现管理

    短信3.141592672893982398674234

    apk安装包

    原生js选项卡插件自定义图片滑动选项卡切换.zip

    原生js选项卡插件自定义图片滑动选项卡切换.zip

    1-宗教信息佛教佛寺寺庙庵堂相关数据-社科数据.zip

    宗教信息佛教佛寺寺庙庵堂相关数据集提供了全国各个地区省市县各个佛教寺庙的详细信息。这些数据不仅包括寺庙的名称和负责人姓名,还涵盖了所属省份、地级市、区县、具体地址、建立日期以及支派类别等关键信息。该数据集整理了超过3万条样本,为研究中国佛教寺庙的分布、历史和文化提供了丰富的第一手资料。这些信息有助于了解佛教在中国的传播和发展,以及寺庙在社会和文化中的作用。数据的整理和提供,对于宗教学、社会学、历史学和文化研究等领域的学者来说,是一个宝贵的资源。

    线性电阻网络的等效电阻计算Matlab代码.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。 替换数据可以直接使用,注释清楚,适合新手

    简单的 Python 版本管理.zip

    简单的 Python 版本管理pyenvpyenv 可让您轻松在多个 Python 版本之间切换。它简单、不引人注目,并遵循 UNIX 传统,即使用单一用途的工具来做好一件事。该项目由rbenv和 ruby​​-build分叉而来,并针对 Python 进行了修改。pyenv 的作用是什么......允许您根据每个用户更改全局 Python 版本。为每个项目的 Python 版本提供支持。允许您使用环境变量覆盖 Python 版本。一次搜索多个 Python 版本的命令。这可能有助于使用tox跨 Python 版本进行测试。与 pythonbrew 和 pythonz 相比,pyenv没有……依赖于Python本身。pyenv由纯shell脚本制作。不存在Python的引导问题。需要加载到你的 shell 中。相反,pyenv 的 shim 方法通过向你的 中添加目录来工作PATH。管理虚拟环境。当然,你可以自己创建虚拟环境 ,或者使用pyenv-virtualenv 来自动化该过程。目录安装获取 PyenvLinux/UNIX自动安装程序基本

    Notepad-v2.20工具,是替代Notepad++的首选工具

    Notepad-v2.20工具,是替代Notepad++的首选工具

    原生js随机图片拖拽排序代码.zip

    原生js随机图片拖拽排序代码.zip

    更快、更好、更稳定的Redis桌面(GUI)管理客户端,兼容Windows、Mac、Linux,性能出众,轻松加载海量键值

    更快、更好、更稳定的Redis桌面(GUI)管理客户端,兼容Windows、Mac、Linux,性能出众,轻松加载海量键值

Global site tag (gtag.js) - Google Analytics