列表页的动态条件搜索
之前在搞.net的时候,我们可以借助强大的ExpressionTree来解决,之前有一篇是微软的EntityFramework表达式转换:Linq to Entity经验:表达式转换,是将一种表达式转换成数据库组件能够识别的表达式,只不过那篇没有涉及到View中的条件而已。页面动态查询的最简单的方法就是解析View中特定的值来得到后台组件能够识别的查询逻辑。
我们期待View中能够这样指定条件:
<input type="text" name="WHERE.storeName.LIKE" class="form-control" style="width: 180px;" " />
它的意思是查询字段storeName,操作符是like,看起来并不难,但要解决这么几个问题:
参数收集问题,表单域的值以什么样的方式提交到后台?
后台接收参数类型是什么?
如何将表单域中的条件转换成数据库组件能够识别的条件?
我们选择的数据库组件是mybatis+mysql。个人感觉mybatis在处理动态查询时比JPA在前期(技术学习前期,即水平还不太够的时候)要简单些,也可能是我对JPA的认识还不够,总感觉mybatis这种拼SQL的方式比较熟悉一些,也比较容易控制。当然它们的定位本身就不同,这里不多讨论。基于mybatis我们采用了tk.mybatis这个开源的组件,它的功能非常丰富,分页,通用mapper,代码生成等大部分功能都已经包含,大家有兴趣可以去搜索。
注:下面的功能是我的同事完成,这里我做为学习的过程来分享下,可能也有理解不到位的地方,纯属个人学习理解。其中有部分功能未展示出来(比如权限过滤,and or这些分组查询的支持等),只包含最基本的,每个项目的需求不同以及团队环境不同可以会有多种实现方式,选择大家都能接受的就可以了。
我们再分别看下上面的三个问题怎么解决:
参数收集问题,表单域的值以什么样的方式提交到后台方法?
一般做页面查询时请求数据就两种方式,get或者post。get一般是在采用了ajax这类技术,post就复杂一些,分为两种:一种也是采用ajax提交到后台,一种是表单的提交。这里呢,由于我们采用了angularjs,所以很显然只能采用ajax提交,如果查询条件多,可采用ajax的post。由于上面贴的代码片段显示条件的name是动态的,所以我们不可能定义一个具体的后台的业务Model对前台的条件,比如有name,email,phone等等,所以我们采用将表单域整个序列化后的结果传递到后台。
var requestData = $("#"+options.searchFormId+"").serialize(); var url = listUrl+"?"+requestData+"&pageNum="+$scopeLocal.pageRequest.pageNum;$.ajax({type : "POST",url : url,dataType : 'json',async : false,beforeSend:options.beforeSend,error:options.error,success : function(data) {$scopeLocal.pageResponse = data;$scopeLocal.content=data.list;options.callback($scopeLocal,data);}});
2:参数类型是什么?
如果是表单提交方式,我们可以采用HttpServletRequest这个对象来接收所有表单域的值,但上一步我们采用的提交方式并非表单自身的提交,而是ajax的提交,ajax的请求,是不识别HttpServletRequest这个参数类型的,为此我们需要定义一个自定义的公共的对象来接收我们动态View中指定的条件,这里就有个我们的SearchModel,它包含如下内容:
分页信息,当前页,页数据大小
搜索条件信息集合List<SearchFilter>,一堆我们自己定义的条件,主要包含字段名称,操作符以及值,这里是本文的重点。
转换为SQL的逻辑
SearchFilter:
public final class SearchFilter implements Serializable { private String propertyName; private Object value; private Operator operator; private String orGroup;
SearchModel:
public class SearchModel implements Serializable { private List<SearchFilter> searchFilters; private int pageNum = 1; private int pageSize = 10;
上面搜索条件的信息,我们需要从View中获取,这里应用HandlerMethodArgumentResolver来解决,它只有两个方法:
判断是否是支持的参数类型
boolean supportsParameter(MethodParameter parameter);
这个方法实现比较简单,只需要判断下当前的参数类型是否是指定的类型即可:
@Override public boolean supportsParameter(MethodParameter parameter) {Class<?> parameterType = parameter.getParameterType(); return SearchModel.class.isAssignableFrom(parameterType);}
解析数据的详细过程
Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;
这个方法是核心,之前有提到过,因为我们是ajax提交,在后台的controller方法中不能包含HttpServletRequest request 这个参数,但后台要想取表单域的值从哪取呢?其实还是从这个参数中取,只不过我们需要换一种方法,可以从上面接口的的webRequest对象中获取,有了这个对象也就意味着你得到了表单域的所有值了,后台的事情就好办了。
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
下面只需要一个转换类将HttpServletRequest中的表单值填充到我们自定义的SearchModel中就可以了,这里需要一个专业处理转换有类SearchFilterBuilder,首先将表单值转换成一个集合,字符串类型的:
public static SearchFilterBuilder from(final HttpServletRequest request) { return new SearchFilterBuilder(request);} public List<String> buildToStrings() { return buildToStrings(true);} public List<String> buildToStrings(final boolean containsDataAuth) {List<String> searchFilterStrings = Lists.newArrayList();Map<String, String[]> map = request.getParameterMap(); for (Map.Entry<String, String[]> entry : map.entrySet()) {String strKey = entry.getKey(); for (String value : entry.getValue()) { if (!Strings.isNullOrEmpty(value) && !"none".equals(value) && strKey.startsWith(preWhere)) {String filedAndOp = strKey.substring(preWhere.length());searchFilterStrings.add(String.format("%s=%s", filedAndOp, value));}}} if (containsDataAuth) { // to do } return searchFilterStrings;}
基于上面得到的条件集合进一步解析条件,由于我们前端View传递的条件是字符串的,所以这里应用了一个专门的正则表达式的类DefaultSearchFilterStringProcessor去解析数据
public List<SearchFilter> build() {List<String> searchFilterStrings = buildToStrings();List<SearchFilter> searchFilters = Lists.newArrayList();searchFilters.addAll(searchFilterStrings.stream().map(DefaultSearchFilterStringProcessor::from).collect(Collectors.toList())); return searchFilters;}
由于字符串处理的逻辑与本文关联并不大,这里就不贴相关代码了,不会正则的用最笨的人肉解析字符串也是可以的,如前面说的都拿到Request对象了后面都好操作。
3:如果将表单域中的条件转换成数据库组件能够识别的条件?
tk.mybatis或者是官方的mybatis-spring组件都支持动态条件,由于我这采用的是tk.mybatis,所以某些类都是tk.mybatis的,tk是进一步的封装,所以原理大体是相同的,之前有提到过操作mybtis有点像操作原生SQL,感觉就是一种拼SQL的过程,这里我们拼这个动态条件也是类似,简单的话只需要一个静态转换方法就可以了,如果再深入一点可以想办法做成自动识别并转换,有能力的可研究。无非就是如下的转换:
switch (op) { case EQ: this.criteria.andEqualTo(filed, value); break; case NOTEQ: this.criteria.andNotEqualTo(filed, value); break; case LE: this.criteria.andLessThanOrEqualTo(filed, value); break;
解决完上面这些,我们就可以直接这样写后台代码了:
controller:
@RequestMapping(value = "/getAllByPage")@ResponseBody public PageInfo<BcStore> getAllByPage(final SearchModel s1) { this.convertSearchModel(s1); return storeService.select(s1);}
service:有了example对象,分页信息,排序字段,后面的就是tk.mybatis的基本功能了。
@Override public final PageInfo<T> select(final SearchModel searchModel) {Example example = ExampleBuilder.forClass(genericType).fromSearchFilters(searchModel.getSearchFilters()).build(); return select(example, searchModel.getPageNum(), searchModel.getPageSize(), searchModel.getOrderBy());}
基于上面的内容,针对查询条件,我们可以在View中任意指定查语语句所包含的条件,后台的controller以及service基本保持不变,应付普通的管理界面查询足够了。
转载于:https://blog.51cto.com/jsw55667/1928456
列表页的动态条件搜索相关推荐
- Django电商项目(六)商品详情页、列表页分页、商品搜索
Django电商项目 商品详情页 商品列表页 商品搜索 全文检索 安装和配置 索引文件生成 全文检索的使用 改变分词方式 商品详情页 新建detail.html {% extends 'base_de ...
- VUE项目从详情页退回列表页,保留列表页的筛选条件(筛选条件为单选),以及刷新后恢复默认的一个筛选状态
vue项目做的一个"花名册"页面,从"花名册"页面可以点击某一个人的名字,进入这个人的信息详情表格页面,而且这个人的信息详情页面中有一个"直属上级&q ...
- vue 详情页返回列表,过滤查询条件保留
项目场景: 在列表页进行的条件查询出来的数据,点击查看详情之后,再返回,之前的查询条件会清空,如何不清空? 思路 在点击查看的时候,保留查询的条件,等返回的时候,把条件给查询框中的值.这个地方多出用到 ...
- 品优购商城——列表页
效果图: 列表页文件 list.html <!DOCTYPE html> <html lang="en"> <head><meta cha ...
- vue项目列表跳转详情返回列表页保留搜索条件
需求 列表进入详情后,返回详情的时候保留搜索的条件,第几页进入的返回还在第几页 1.在详情页设置定义一个字段 mounted() {sessionStorage.setItem("msgIn ...
- NOW直播Flutter动态搜索列表页实现
作者:腾讯NOW直播 - narutosun (孙帅) 前言 Flutter是Google使用Dart语言开发的移动应用开发框架,使用一套Dart代码就能构建高性能.高保真的iOS和Android应用 ...
- Django项目实战——14—(列表页热销排行、商品搜索、Haystack建立数据索引、渲染商品搜索结果、商品详情页)
1.列表页热销排行 根据路径参数category_id查询出该类型商品销量前二的商品. 使用Ajax实现局部刷新的效果. 查询列表页热销排行数据 请求方式 请求参数:路径参数 响应结果:JSON {& ...
- dede php 调用自定义字段,在dedecms搜索结果列表页调用自定义字段的方法(绝对可用)...
关于在dedecms搜索结果列表页调用自定义字段的方法比较多有些教程都写得比较含糊,经过织梦者的测试今天推荐一个比较有效的方法给大家,方法是从网络中搜到的,但是绝对可用 打开 include/exte ...
- Axure通用web端元件库rplib文件格式+移动端app通用元件库rplib文件格式+电脑端动态可视化图表元件库+数据展示+操作反馈+通用模板+数据录入+列表页+表单页+详情页+通用版布局
作品介绍:Axure通用web端元件库rplib文件格式+移动端app通用元件库rplib文件格式+电脑端动态可视化图表元件库+数据展示+操作反馈+通用模板+数据录入+列表页+表单页+详情页+通用版布 ...
最新文章
- linux makefile 只能使用Tab键进行文本缩进
- ModuleNotFoundError: No module named 'win32api'
- Java使用BTrace动态替换线上class文件
- python温度转换程序_用Python程序温度转换实例
- 绿得发娇的企业即时通讯软件
- IT人 不要一辈子靠技术生存(转)
- 硬核数据研究:年轻人为什么这么喜欢“哈哈哈哈”?
- LeetCode刷题(37)--Edit Distance
- python中如何判断输入的是否是数字_python如何判断输入是不是数字
- 基于Python的淘宝用户行为分析
- gaster字体转换器_哥特字体在线生成,哥特字体在线生成器,哥特字体在线转换
- 银行计算机岗位职称,银行职称有哪些,等级是如何划分的
- 作业(数组)---运行环境winTC(二)
- 【翻译】配置RSVP-signaled LSP
- 区块链技术应用场景之政务链
- 题目 1567: 超级玛丽
- 2018年 中南大学研究生复试机试题(1025~ 1028)
- Python学习笔记——流程控制(拉勾教育数据分析实战训练营学习笔记)
- CEF3如何不加载图片以方便采集信息
- /dev下面存在设备,却open 失败的问题open: No such device or address