【JEECG技术博文】Jeecg高级查询器

一、背景 

      对于用户来讲查询功能按易用性分三个层次:

        1. 最简单查询操作是一个输入框,全文检索,如百度,后台实现技术使用搜索引擎,需要设计和建立索引,技术较为复杂,适用于文档和信息数据库检索,但是结果很难精确控制。

        2. 其次是定义字段查询,很多企业信息系统大多用的是这种查询,针对模块特定字段的查询有针对性、使用门坎低,适用于企业内部信息管理系统模块定制。

        3. 最后一种是专门针对数据模型灵活的查询编辑器,使用难度最高,但是查询结果可以灵活和精确的控制,适用于有一定IT知识并对数据相当了解的用户,同时又可以避免直接将数据库暴露给用户带来的不安全隐患。

        大家不难发现一个好的系统软件的查询基本会涵盖上述三种类型的查询功能

二、jeecg实现原理

        Jeecg系统中模块主要使用第二种方式的查询功能,使用hibernate的QBC来封装前端查询条件,针对字段的定制过滤条件,最后转换为sql执行数据库查询。

        现有方案优点:

            1)默认无须复杂开发即可实现字段检索,支持范围、模糊、精确匹配查询

            2)内置简单表达式支持:!*,实现非、模糊、数组等

        现有方案缺点:

            1)不支持or条件

            2)不支持字段间操作field1=field2

            3)不支持非hibernate关联表联合查询

            4)单一字段条件只能出现一次

            5)不支持sql嵌套

            6)要支持上述功能需要额外开发定制工作

        某些特定场景下,用户想要通过模块获得相关信息必须借助于第三方报表模块功能或求助于开发人员,无形中对报表模块开发带来一定压力。比如,用户要从模块中查询特殊的部分数据进行操作,现有查询功能无法做到,报表模块又不能操作编辑,这个时候就是高级查询器定制查询功能派上用场的时候了。

三、类似应用举例

        我们来看看Outlook邮件查询设计:

1.全文检索型

 

2.字段定制型

 

3.高级查找

 

 微软Team Foundation Server查询编辑器:

 

四、Jeecg查询器设计与实现

        UI设计:首先要实现高级查询,必须要对数据表元信息metadata进行封装才可以通用化,我们利用datagrid标签中的columnList来自动生成字段下拉列表。作为高级查询,从易用的性和使用频率的角度功能所占UI比例不能太多,采用弹出窗口实现,如图:

        这里的表格我使用了treegrid,因为更适合表达sql语法树,但没有实现树形结构,条件只加了大于、小于、包含等常用操作符,有待后续扩展sql嵌套。

        右侧有查询历史,将每次查询的json条件保存在一个数组中实现快速重查,这个历史查询记录使用HTML5的localstorage保存在客户端缓存中,下次登录仍然有效,对于不支持localstorage的浏览器将使用cookie存储。

        前后端交互:从到向前兼容性和对框架升级改动最小的因素考虑,将查询器组装的所有内容封装到一个json字符串,作为一个参数_sqlbuidler传递到后台,由后台控制条件组装,更加安全,防止sql注入等漏洞

        json格式示例:

[java]  view plain  copy
  1. [{“id”:101,”field”:”user_name”,”condition”:”like”,”value”:”%王%”,”relation”:”and”},{“id”:101,”field”:”user_name”,”condition”:”like”,”value”:”%王%”,”relation”:”and”}]  

        后台封装处理java对象QueryCondition

[java]  view plain  copy
  1. package org.jeecgframework.web.demo.entity.test;  
  2. import java.util.List;  
  3. public class QueryCondition {  
  4.     String field;  
  5.     String type;  
  6.     String condition;  
  7.     String value;  
  8.     String relation;  
  9.     List<QueryCondition> children;  
  10.         public List<QueryCondition> getChildren() {  
  11.         return children;  
  12.     }  
  13.     public void setChildren(List<QueryCondition> children) {  
  14.         this.children = children;  
  15.     }  
  16.     public String getField() {  
  17.         return field;  
  18.     }  
  19.     public void setField(String field) {  
  20.         this.field = field;  
  21.     }  
  22.     public String getType() {  
  23.         return type;  
  24.     }  
  25.     public void setType(String type) {  
  26.         this.type = type;  
  27.     }  
  28.     public String getCondition() {  
  29.         return condition;  
  30.     }  
  31.     public void setCondition(String condition) {  
  32.         this.condition = condition;  
  33.     }  
  34.     public String getValue() {  
  35.         return value;  
  36.     }  
  37.     public void setValue(String value) {  
  38.         this.value = value;  
  39.     }  
  40.     public String getRelation() {  
  41.         return relation;  
  42.     }  
  43.     public void setRelation(String relation) {  
  44.         this.relation = relation;  
  45.     }  
  46.     public String toString(){  
  47.         StringBuffer sb =new StringBuffer();  
  48.         sb.append(this.relation).append(" ");  
  49.         sb.append(this.field).append(" ")  
  50.         .append(this.condition).append(" ");  
  51.         if("java.util.Date".equals(this.type)){  
  52.             sb.append("to_date('").append(this.value).append("','yyyy-MM-dd')");  
  53.         }else if("java.lang.Number".equals(this.type)  
  54.                 ||this.condition.indexOf("in")>0){//TODO 需要按类型处理  
  55.             sb.append(this.value);  
  56.         }else{  
  57.             sb.append("'").append(this.value).append("'");//TODO 需要处理特殊字符  
  58.         }  
  59.         return sb.toString();  
  60.     }  
  61. }  
  62. 后台解析处理代码,修改org.jeecgframework.core.extend.hqlsearch.HqlGenerateUtil:  
  63.     public static void installHql(CriteriaQuery cq, Object searchObj,  
  64.             Map<String, String[]> parameterMap) {  
  65.         installHqlJoinAlias(cq, searchObj, getRuleMap(), parameterMap, "");  
  66. //      --增加一个特殊sql参数处理----------------------------------   
  67.         try{            if(StringUtil.isNotEmpty(parameterMap.get("_sqlbuilder"))){  
  68.                 List<QueryCondition> list  = JSONHelper.toList(  
  69.                         parameterMap.get("_sqlbuilder")[0]  
  70.                          , QueryCondition.class);  
  71.                 String sql=getSql(list,"");  
  72.                 System.out.println("DEBUG sqlbuilder:"+sql);  
  73.                 cq.add(Restrictions.sqlRestriction(sql));  
  74.             }  
  75.         }catch(Exception e){  
  76.             e.printStackTrace();  
  77.         }  
  78. //      --增加一个特殊sql参数处理----------------------------------   
  79.         cq.add();  
  80.     }  

五、实现约束

        1)只支持标准命名的表名,因为是通过java驼峰命名转换带下划线的数据库表名,如果表名不是这个规则字段名会转换错误;
        2)Sql前端界面在输入时并没有做太多约束和控制,因此非专业用户使用时会出现非法的语句而查询无果,建议在懂sql的人员指导下使用不要直接交给用户。

 六、标签使用

        Datagrid标签中已经对高级查询器做了封装,js变量也根据每个模块的id做了命名处理防止冲突。

        使用时只需在datagrid标签加一个属性queryBuilder="true",例如

[java]  view plain  copy
  1. <t:datagrid name="jeecgDemoList" title="DEMO示例列表"   
[java]  view plain  copy
  1. autoLoadData="true" actionUrl="jeecgDemoController.do?datagrid"   
[java]  view plain  copy
  1. sortName="userName" fitColumns="true"  
  2. ield="id" fit="true" queryMode="group" checkbox="true"   
[java]  view plain  copy
  1. queryBuilder="true">  

        模块UI效果如下,重置后面会多一个按钮:

 

七、TODO

        UI方面:易用性还可以提升,比如选中某字段和条件=时,值自动根据数据库表中该列实际值枚举一部分候选项;字段为date类型时,值编辑框变成date控件;字段为整形时,值编辑框有校验或只能输入整数的spinbox来防呆

        功能:List<QueryCondition>对象转换为sql getSql()函数中仅仅实现拼装原生sql,这块还有很大的改进空间,可以增加字段类型(int,string,date)的识别和处理、操作符(正则匹配)、内置表达式和函数(类似TFS)等扩展。

       安全:模块按钮还没有跟权限绑定,只是通过标签属性来开关,应该加一个动态权限由系统配置来控制


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JEECG是一个基于代码生成器的开源企业级快速开发平台,提供了代码生成器、单配置、工作流等一系列功能,能够快速构建高质量的企业级应用系统。 在JEECG中,单配置是一个非常重要的功能。通过单配置,我们可以快速创建各种类型的单,如普通单、树形单等。本文主要介绍如何在JEECG中创建树形单。 1. 新建单 首先,我们需要新建一个单。在JEECG平台中,可以通过“代码生成器”中的“单生成器”来创建单。 2. 配置单属性 在新建单后,需要对单进行一些基本的配置,如单名称、单类型等。 3. 设置树形单 在单属性配置完成后,我们需要将单设置为树形单。在单设计器中,可以通过“控件箱”中的“Tree”控件来创建树形控件。 4. 配置树形单属性 创建树形控件后,需要对树形控件进行一些属性配置,比如节点名称、节点类型等。 5. 添加子节点 在完成树形控件的属性配置后,我们需要添加树形控件的子节点。在树形控件中,可以通过“右键菜单”中的“添加子节点”来添加子节点。 6. 保存单配置 在完成树形控件的配置后,需要保存单配置。保存后,我们就可以在生成的代码中使用树形控件了。 总结: 通过JEECG单配置功能,我们可以快速创建各种类型的单,包括树形单。使用树形单能够提高用户体验,使用户更方便地进行数据操作。如果您需要快速构建一个高质量的企业级应用系统,JEECG是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值