低码框架 json-script-rule 高级查询说明_pagehelper_row_id_九天流云的博客-程序员秘密

技术标签: spring boot  maven  mybatis  低码  json  sql  

说明

高级查询是前面查询功能的进阶篇,这里将会介绍一些更为复杂的查询方式,按照复杂性依次为排序,分组,函数,视图,以及子查询等,接下来上一个比较复杂的json,而后进行全面说明,如下所示

{
    "rule": {
        "name": "group",
        "actions": [
            {
                "name": "test_group",
                "define":{
                    "viewParams":{"view.ZsTestView":["kkk","%3%"]},
                    "function":{
                        "sum":["sumSalary"],"substr":{"view.neibu.ZsTestPOView.test_field":["#","1","2"],"view.ZsTestView.test_field":["#","1","3"],
                        "[email protected]":["#","1","8"],"[email protected]":["#","1","5"]},
                        "decode":{"view.neibu.ZsTestPOView.name":["#","xxx","!!!!!!"]},"avg":["view.neibu.ZsTestPOView.qian"]
                    }
                },
                "get": {
                    "classes":["view.ZsTestView","view.neibu.ZsTestPOView"],
                    "relations": {"type":"left","main":"view.neibu.ZsTestPOView"},
                    "fields": ["view.ZsTestView.test_field","view.neibu.ZsTestPOView.test_field","substr->[email protected]","sum->sumSalary","avg->view.neibu.ZsTestPOView.qian"],
                    "order":{"view.neibu.ZsTestPOView.test_field":"asc","substr->view.neibu.ZsTestPOView.test_field":"desc"},
                    "group":{
                        "by":["substr->[email protected]","view.neibu.ZsTestPOView.test_field","view.ZsTestView.test_field"],
                        "having":{"in":{"view.neibu.ZsTestPOView.test_field":["tyu"]},"bracket":[{"equal":{"substr->[email protected]":["zzz","sss"]}}]},
                        "relation":"and"
                    }
                }
            }
        ]
    }
}

上面的json看着虽然很复杂,但是只要稍微梳理一下便能明白其中的含义,而在这里面有很多包的前缀,例如像view.neibu,这个是包的名字,并没有什么其它的含义

排序

排序使用order属性,key为字段,value为排序方式,这里后面的key使用了函数,意在说明排序时依然可以使用函数,而这主要取决于你的sql

分组

分组对应JSRuleGroups这个对象,目前只有3个属性,by、having和andOr属性

  • by:顾名思义就是group by,按照哪些字段进行分组查询,这里不仅可以写字段,而且同样也是可以使用函数的,例如按照时间进行分组,每年每月每周,可以substr截取时间的字符串进行分组
  • having:需要注意的是having这个属性的类型其实是JSRuleMatches,也就是前面查询功能篇所提到的查询条件这个对象模型,这里是复用了查询条件这个对象,因此里面的写法规则可以参照前面所说的
  • andOr:表示逻辑关系符and或or。

生成的sql:

select suibian.test_field as "view_ZsTestView-test_field",zs_test_view_po.test_field as "view_neibu_ZsTestPOView-asddsa",substr(zs_test_view_po.test_field,'1','8') as "[email protected]",sum(sum1_salary) as "sum$lbv_salary",avg(zs_test_view_po.qian) as "avg$view_neibu_ZsTestPOView-qian" from (select * from zs_test) zs_test_view_po left join (select * from zs_test_son1 where oh_no='kkk' and id like '%3%') suibian on zs_test_view_po.id=suibian.zs_test_id group by substr(zs_test_view_po.test_field,'1','8'),zs_test_view_po.test_field,suibian.test_field having   zs_test_view_po.test_field in  ('tyu') and (  substr(suibian.test_field,'1','5')='zzz'  or  substr(suibian.test_field,'1','5')='sss') order by zs_test_view_po.test_field asc,substr(zs_test_view_po.test_field,'1','2') desc

对比json和sql可以更为有效的理解语法的含义

这里需要注意的是:不同的数据库有不同的语法,比如说mysql的group by 后面的having里如果要使用字段,譬如上面条件里的view.ZsTestView.test_field

"bracket":[{"equal":{"substr->[email protected]":["zzz","sss"]}}]

如果要使用这个字段,就必须要在select中存在这个字段(其它数据库没有这个问题),因此如果你用的是mysql数据库,那么上面json中的fields属性应该有view.ZsTestView.test_field字段,否则sql就会报错

除此之外,一些数据库的差异简单的罗列几个

  1. 例如Postgresql的substr函数是从0开始计算的,而mysql是从1开始计算的
  2. 同样的更新语句,在postgresql执行不通过,在其它数据库都可以,因为在Postgresql中更新语句后面的字段不能有前缀

update zs_test set zs_test.name='lkjhg' where   name='ccc'  or name='mmm' 
  1. postgresql插入时没有对字符串进行自动转化其它类型的处理,因此在插入时会报错,不过这一点在框架里已经解决了,不需要开发者关注这一点,例如:salary这个字段是double类型,在json中写成双引号的字符串类型依然可以成功插入,后台会根据你Po中字段定义的类型进行转化
  2. oracle数据库在使用pagehelper插件时会额外多返回PAGEHELPER_ROW_ID或ROW_ID字段

因此综上所述,不同的数据库需要不同的对待,可将sql先行打印出来再在数据库里执行一下,对比结果便不难得出结论

函数

这里再贴上一个新的示例,json如下:

{
    "rule": {
        "name": "function",
        "actions": [
            {
                "name": "test_function",
                "define":{
                    "viewParams":{"view.ZsTestView":["kkk","%1%"]},
                    "function":{
                        "ifnull":{"ZsTestPO.salary":["#","#name"]},
                        "length":["name"],"concat":{"ZsTestPO.name":["#","vnvn","kook"]},
                        "substr":{"ZsTestPO.name":["#","1","3"],"[email protected]":["#","1","5"],"view.ZsTestView.test_field":["#","1","3"],
                        "ZsTestPO.test_field":["#","1","4"],"[email protected]":["#substr->[email protected]","1","#length->name"]}
                    }
                },
                "get": {
                    "classes":["ZsTestPO","view.ZsTestView"],
                    "andOr":"or",
                    "matches":{
                        "bracket":[
                            {"bracket":[{"in":{"substr->ZsTestPO.name":["qqq"]},"like":{"substr->[email protected]":["%x%","%p%"]}}]},
                            {"bracket":[{"equal":{"substr->view.ZsTestView.test_field":["zzz"],"view.ZsTestView.test_field":["sss"]}}]}
                        ]
                    },
                    "fields": ["length->name","view.ZsTestView.test_field","substr->ZsTestPO.name","substr->[email protected]","substr->ZsTestPO.test_field",
                    "ifnull->ZsTestPO.salary","substr->[email protected]"]
                }
            }
        ]
    }
}

首先函数对应的顶级类为JSRuleFunctions,代码如下:

/**此类中的jackson注解可以控制其子类的相关jackson配置,如去掉ignoreUnknown,则同时影响当前以及子类的jackson装配*/
@JsonIgnoreProperties(ignoreUnknown=true)
public class JSRuleFunctions implements IJSRuleCustomModel{

    /**String为字段名,String[]为函数参数,无参类型为String[],有参类型为Map<String,String[]>*/
    public String[] length;
    public Map<String,String[]> substr;
    /**聚合函数*/
    public String[] sum;
    public String[] min;
    public String[] max;
    public String[] avg;
    public String[] count;
    
    public List<Field> getFunctions() {
        List<Field> fields = new ArrayList<Field>();
        for (Field f:ZSReflect.getFields(this.getClass(),JSRuleFunctions.class)) {
            try {
                if (ZSObject.sizeIsNotEmpty(f.get(this))) {
                    fields.add(f);
                }
            } catch (IllegalArgumentException | IllegalAccessException e) {
                throw new JSRuleException(e);
            }
        }
        return fields;
    }
}

这里定义了各个数据库共有的函数

  1. 要使用函数首先需要在类中定义函数类型,函数名为字段名,函数分为无参函数和有参函数,无参函数例如length(字段),计算出字段值的字符长度,以及有参函数substr(字段,1,5),截取字符长度,无参类型为String[]数组,可以同时在length函数中定义多个字段,有参类型为Map<String,String[]>

提示:属性中只存在String[](单体参数)和Map<String,String[]>(多体参数),单体参数通常指的是参数只包含字段本身,比如length(字段名),sum(字段名),多体参数表示参数有多个,例如substr(字段名,0,3)

  1. 接下来需要在json中定义函数变量,如上面的json中的

"function":{
    "ifnull":{"ZsTestPO.salary":["#","#name"]},
    "length":["name"],"concat":{"ZsTestPO.name":["#","vnvn","kook"]},
    "substr":{"ZsTestPO.name":["#","1","3"],
              "[email protected]":["#","1","5"],"view.ZsTestView.test_field":["#","1","3"],
              "ZsTestPO.test_field":["#","1","4"],"[email protected]":
              ["#substr->[email protected]","1","#length->name"]}
}

这里是定义本次action请求所可能用到的函数,也可能没有用到,所以多写几个也没关系,这里的"#"表示的就是前面key所对应的表的字段名,后面则是多个参数,有几个写几个,例如"substr":{"ZsTestPO.name":["#","1","3"]},转换成sql就是substr(zs_test.name,'1','3')。

补充说明:"ifnull":{"ZsTestPO.salary":["#","#name"]},这里的#name是指字段名而不是字符串'name',等同于ifnull(zs_test.salary,name),另外如果你的函数是嵌套函数,同样可以用#开头,例如上面的"[email protected]":["#substr->[email protected]","1","#length->name"],最终转化为substr(substr(zs_test.name,'1','5'),'1',length(name)),这个例子已经非常详细了,但需要注意的是嵌套函数引用的函数必须在它之前定义,因此这里的嵌套函数的例子写在了函数定义的最后

需要注意的是:ifnull函数是mysql的函数,decode函数是oracle的函数,不同的数据库需要集成不同的函数自定义类,他们分别是JSRuleMysqlFunctions,JSRulePostgresqlFunctions,JSRuleOracleFunctions,JSRuleKingbaseFunctions,这4个扩展函数类库继承了JSRuleFunctions,如果你需要自己定义自己的函数则对应的需要继承这些类,将类注册成spring bean,加@Component等注解即可,这个在后面的自定义开发中将会详细的说明

  1. 最后在json主体中引用函数的定义,截取上面的json片段

"matches":{
    "bracket":[
        {"bracket":[{"in":{"substr->ZsTestPO.name":["qqq"]},"like":{"substr->[email protected]":["%x%","%p%"]}}]},
        {"bracket":[{"equal":{"substr->view.ZsTestView.test_field":["zzz"],"view.ZsTestView.test_field":["sss"]}}]}
    ]
},
"fields": ["length->name","view.ZsTestView.test_field","substr->ZsTestPO.name","substr->[email protected]","substr->ZsTestPO.test_field",
"ifnull->ZsTestPO.salary","substr->[email protected]"]

这里列举了两个示例,一个是查询条件里使用函数,一个是在查询字段上使用函数,它们的语法都是以函数名开头+指向箭头符号(->)+字段名,注意,substr所指向的ZsTestPO.name必须是在上面所示的json主体的function中定义的,如果没有定义则会提示报错

{
    "code": "6000",
    "msg": "操作失败",
    "result": null,
    "log": "未定义的表达式:substr->ZsTestPOa.name"
}

函数别名:由于多体参数的参数值有可能不同,因此如果当你的函数多次对同一个字段进行定义时,比如substr(name,1,3),substr(name,1,5),则可以使用别名,语法为加后缀符号@,参考"[email protected]":["#","1","5"]此别名只针对多体参数的函数,单体函数不可能有不同的参数存在,因此用不到

最后附上此json最后生成的sql语句:

select length(name) as "length$name",suibian.test_field as "view_ZsTestView-test_field",substr(zs_test.name,'1','3') as "substr$ZsTestPO-name",substr(zs_test.name,'1','5') as "[email protected]",substr(zs_test.test_field,'1','4') as "substr$ZsTestPO-sum_test_field",ifnull(zs_test.salary,name) as "ifnull$ZsTestPO-salary",substr(substr(zs_test.name,'1','5'),'1',length(name)) as "[email protected]" from zs_test,(select * from zs_test_son1 where oh_no='kkk' and id like '%1%') suibian where   zs_test.id=suibian.zs_test_id and (((  substr(zs_test.name,'1','3') in  ('qqq')   or  substr(zs_test.name,'1','5') like '%x%'   or  substr(zs_test.name,'1','5') like '%p%' ) and (  substr(suibian.test_field,'1','3')='zzz'  or  suibian.test_field='sss')))

总结:函数表达式可作用于查询字段、查询条件、分组、排序等。

视图

视图说通俗一点就是一个sql语句而并非是一个表,它可能是由一个表组成也可能是由多个表组成,总之就是一个查询结果集

说明:

配置视图需要在Po类上进行配置,如

@JSRuleRelationTable(tableName="suibian",view="select # from zs_test_son1 where oh_no=? and id like ?")
public class ZsTestView {

    @JSRuleRelationField(pk=true)
    public String id;
    @JSRuleRelationField(fieldName= "zs_test_id",fk="ZsTestPO")
    public String zs_test_id;
    @JSRuleRelationField(fieldName= "zs_test_id",fk="view.neibu.ZsTestPOView")
    public String zs_test_id2;
    @JSRuleRelationField(fieldName= "oh_no")
    public String ohNo;
    public Double qian;
    public String test_field;
    @JSRuleRelationField(fieldName= "zs_test_son2_id")
    public String zs_test_son2_id;
}

这里我们只看上面的@JSRuleRelationTable注解,这里的tableName是为视图起的别名(如果不是视图这里需要写表的名字),后面的view是视图的查询sql,可以单表也可以多个表,里面的#符号表示字段动态传参,参数取决于json上action中的定义对象JSRuleDefinition里的viewParams和viewFields,如果你不需要动态传参,这里直接写sql原语法select xx或* from table就可以了,系统会把这个sql作为这个对象ZsTestView进行关联处理,对应的sql含义进行对比,如下:
表:select * from tableName
视图:select * from (select * from xx) suibian
后面的?符号表示的是查询参数动态传参,参数取决于json,这里也可以直接写死参数是什么。
接下来是一个足够复杂的json主体

{
    "rule": {
        "name": "view",
        "actions": [
            {
                "name": "poAction",
                "get": {
                    "classes":["ZsTestPO"],
                    "isExecute":false,
                    "fields":["test_field"]
                }
            },
            {
                "define":{
                    "viewParams":{"view.ZsTestView":["kkk","%1%"]},
                    "viewFields":{"view.ZsTestView":[],"view.ZSTestUpdateSon":[],"view.neibu.ZsTestPOView":["name"]}
                },
                "name": "test_view_left",
                "get": {
                    "classes":["view.ZSTestUpdateSon","ZsTestPO","view.ZsTestView"],
                    "relations": {"type":"left","main":"ZsTestPO"},
                    "andOr":"or",
                    "fields":[],
                    "matches":{
                        "in":{"ZsTestPO.name":"view->view.neibu.ZsTestPOView",
                        "ZsTestPO.test_field":"action->poAction"}
                    },
                    "order":{"ZsTestPO.id":"asc"},
                    "page":{"pageNum":"1","pageSize":"10"}
                }
            }
        ]
    }
}

上面提到的动态传参,这里对应的地方是

"define":{
    "viewParams":{"view.ZsTestView":["kkk","%1%"]},
    "viewFields":{"view.ZsTestView":[],"view.ZSTestUpdateSon":[],"view.neibu.ZsTestPOView":["name"]}
}

viewParams对应的是?符号,里面的参数个数应该与视图中?符号的个数相同,按照顺序进行赋值
viewFields对应的是#符号,如果没有定义对应视图的动态字段数组,或字段数组是个空[]数组,则表示的动态字段是所有字段
这里值得注意的是:view.neibu.ZsTestPOView并没有出现在classes中,但在in查询的时候却用到了这个动态视图,因此说明viewParams属性中定义的并非仅仅是classes中的类,而是所有通过po类配置过的视图类,这个in所用到的->指向符号表达的含义是指向性操作,也就是sql语法 in (view.neibu.ZsTestPOView对应的视图sql,也可以理解为一个子查询),这样一个in (子查询)就完成了。
生成的sql:

select zs_test_son1_son1.zs_test_id as "view_ZSTestUpdateSon-zs_test_id",zs_test_son1_son1.zs_test_son1_id as "view_ZSTestUpdateSon-zs_test_son1_id2",zs_test_son1_son1.zs_test_son1_id as "view_ZSTestUpdateSon-zs_test_son1_id",zs_test_son1_son1.oh_oh_no as "view_ZSTestUpdateSon-wolegequ",zs_test_son1_son1.id as "view_ZSTestUpdateSon-id",zs_test_son1_son1.zs_test_id as "view_ZSTestUpdateSon-id2_dawangba",zs_test.remark as "ZsTestPO-remark",zs_test.test_field as "ZsTestPO-sum_test_field",zs_test.birth_day as "ZsTestPO-birthDay",zs_test.id as "ZsTestPO-id",zs_test.bonus as "ZsTestPO-bonus",zs_test.name as "ZsTestPO-name",zs_test.salary as "ZsTestPO-salary",zs_test.qian as "ZsTestPO-qian",zs_test.create_date as "ZsTestPO-create_date",zs_test.sum1_salary as "ZsTestPO-lbv_salary",suibian.zs_test_id as "view_ZsTestView-zs_test_id",suibian.oh_no as "view_ZsTestView-ohNo",suibian.zs_test_id as "view_ZsTestView-zs_test_id2",suibian.id as "view_ZsTestView-id",suibian.qian as "view_ZsTestView-qian",suibian.test_field as "view_ZsTestView-test_field",suibian.zs_test_son2_id as "view_ZsTestView-zs_test_son2_id" from zs_test left join (select * from zs_test_son1_son1) zs_test_son1_son1 on zs_test.id=zs_test_son1_son1.zs_test_id left join (select * from zs_test_son1 where oh_no='kkk' and id like '%1%') suibian on zs_test.id=suibian.zs_test_id where   zs_test.name in (select name from zs_test)  or zs_test.test_field in (select test_field as "sum_test_field" from zs_test)  order by zs_test.id asc

生成的sql有点长,因为没有指定查询字段fields属性的缘故,默认就是查询所有字段

指向性查询

还是上面的json,我们先来看看第一个action,这个action非常简单就是从zs_test表中查询test_field字段,这里的属性"isExecute":false,表示这个action并不需要执行,它只为第二个action的子查询sql,因此虽然是两个action,但却只执行了第二个。
我们再看下面的json片段

"matches":{
    "in":{"ZsTestPO.name":"view->view.neibu.ZsTestPOView",
    "ZsTestPO.test_field":"action->poAction"}
}

这里第二行表达式action->poAction指向了上面第一个action的名字,也就是将第一个action查询的sql作为子查询放进了in的参数里,这与先前的in (视图) 子查询其实是一个意思,指向性操作目前只有view以及action两种方式

注意:作为子查询的action一定要写在前面


总结:高级查询其实可以满足绝大部分应用场景了,如果有极个别的特殊需求也可以通过扩展接口来变相实现,这些自定义开发规则将在后面的介绍中详细说明

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/ying1dudu/article/details/125448996

智能推荐

stm32f103vet6不能下载程序_stm32f103vet6下载程序_casdfxx的博客-程序员秘密

先上结论:BOOT0没有接地。BOOT0接地就解决了问题。看看是不是电路板问题。具体如下:stm32f103vet6可以识别到芯片ID,但是不能下载程序,点击下载时出现问题截图如下:原理图:最后发现是板子的问题。我跟同事说我画板子几个小时就搞定了,他们不信。由于想赶在月底前将板子打出去,所以匆匆茫茫总是出现问题。所以说,画板子是个细致活。...

VMware虚拟机按ctrl+alt+delete登录和物理机有冲突 跳出两个任务管理器窗口的解决办法_fj3704的博客-程序员秘密

本文转自:https://www.fengjunzi.com/blog-18914.html 在虚拟机中直接按ctrl+alt+delete时会和主机有冲突,弹出两个任务管理器窗口。如图: 我们可以用ctrl+alt+insert组合键来代替。insert按键在键盘上的位置: 也可以在虚拟机里用鼠标点击“发送ctrl+alt+delete": ...

《Windows程序设计》读书笔记------------->>GDI的简单基础<<_roybinux的博客-程序员秘密

关于GDI GDI全称为Graphics Device Interface(图形设备接口)图形设备接口(GDI:Graphics Device Interface)是Windows的子系统,它负责在视讯显示器和打印机上显示图形。正如您所认为的那样,GDI是Windows非常重要的部分。不只您为Windows编写的应用系统在显示视觉信息时使用GDI,就连Windows本身也使用GDI来显

华清大学、京北大学联合发现:学习Python可抑制新型冠状病毒(纯属娱乐)_49.99%的博客-程序员秘密

本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理以下文章来源于腾讯云 作者:Python编程与实战( 想要学习Python?Python学习交流群:1039649593,满足你的需求,资料都已经上传群文件流,可以自行下载!还有海量最新2020python学习资料。 )阅读文本大概需要 2 分钟。(洋葱社消息,纯属娱乐)31日从华清北联合研究院获悉,该所研究初步发现,传统艺能#学习Python可抑制新型冠状病毒#。此前,华清北联合

【第九周项目1- 猴子选大王(数组版)】_猴子选大王用数组怎么写_Cube_007的博客-程序员秘密

问题及代码:/* * Copyright (c) 2016, 烟台大学计算机与控制工程学院 * All rights reserved. * 文件名称:Cube007.cpp * 作 者:刘小楠 * 完成日期:2016年10月27日 * * 问题描述:输入m和n,输出猴子离开圈子的顺序,从中也可以看出最后为大王是几号猴子。 * 输入描述:m,n * 输出描述:猴子号码

手把手教你部署k8s(kubeadm方式)-------详解_kubeode_yjssjm的博客-程序员秘密

1.Kubernetes架构与组件示意图2.部署准备准备三台虚拟机,master节点至少2核2Gmaster: 192.168.13.141 node1: 192.168.13.142 node2: 192.168.13.143 所有机器:关闭防火墙:systemctl stop firewalldsystemctl disable firewalld关闭selin...

随便推点

Linux下 (Centos/ Ubuntu) 操作防火墙 / 开放关闭端口 方法_linux开放端口ubuntu_Frost_216的博客-程序员秘密

提供了Linux下包括Centos与Ubuntu操作防火墙以及开放端口的详细操作

cancase vector_基于Vector总线设备的CAN总线测试方法概述_昂图的博客-程序员秘密

3.3采样点位置测试测试设备:CANoe、CANStressDR。测试系统架构中需将CANScope旁路处理。测试设备正确接线后(CANStressDR串接在回路中),CANoe发送报文,CANStressDR采用位干扰的方式进行干扰,具体是采用CANStressDR从后往前逐位干扰某一报文,直至CANoe监测出现错误帧,如图5所示,然后用公式计算出采样点位置SamplePoint=m/nx 10...

手把手教你接入网站微信支付_北极象的博客-程序员秘密

小摊小贩们在线下交易一般无需接入微信支付,只需要向别人出示自己的付款码就行。因为一手交钱,一手交货,你不付钱,人家不会给你商品。但在网上,要实现无人值守,别人扫码付了款,平台得自动知道,然后再发货。所以必须接入微信支付。申请地址:https://mp.weixin.qq.com。关联一个appId,搜索appId, 然后提交申请。首先你要有一个微信账号,然后申请服务号或公众号。微信支付必须是企业主体,要有营业执照才能申请。在公众号侧,菜单”微信支付“里确认申请。本文简单整理了一下该过程,备忘。

xxd命令中文参数说明_xxd -r_LuckyZZR的博客-程序员秘密

xxd命令英文参数说明:Usage:       xxd [options] [infile [outfile]]    or       xxd -r [-s [-]offset] [-c cols] [-ps] [infile [outfile]]Options:    -a          toggle autoskip: A single '*' replaces nul-lines. ...

Python之OO 面向对象_xuqing-ICT的博客-程序员秘密

Python之面向对象在C++和Python中,都有面向对象的概念,但是两者有哪些重要的区别呢?用最简单的语言便是,C++把每一个用户(这里当然值的是使用代码的程序员)当做是潜在的可能的犯罪分子,可以提供给你的部分已经规定好了,不可逾越。Python则是个比较柔和的管理方式,它认为所有的用户都具有高度的自觉性,用户可以很自自觉的使用代码。更加生动的比喻见这里类与对象

推荐文章

热门文章

相关标签