第2-4-5章 规则引擎Drools高级语法-业务规则管理系统-组件化-中台 [复制链接]
发表于 2022-11-26 08:18:39 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
目录

6. Drools高级语法

全套代码及资料全部完整提供,点此处下载
前面章节我们已经知道了一套完整的规则文件内容构成如下:
关键字描述package包名,只限于逻辑上的管理,同一个包名下的查询或者函数可以直接调用import用于导入类或者静态方法global全局变量function自定义函数query查询rule end规则体本章节我们就来学习其中的几个关键字。
6.1 global全局变量

global关键字用于在规则文件中定义全局变量,它可以让应用程序的对象在规则文件中能够被访问。可以用来为规则文件提供数据或服务。
语法结构为:global 对象类型 对象名称
在使用global定义的全局变量时有两点需要注意:
1、如果对象类型为包装类型时,在一个规则中改变了global的值,那么只针对当前规则有效,对其他规则中的global不会有影响。可以理解为它是当前规则代码中的global副本,规则内部修改不会影响全局的使用。
2、如果对象类型为集合类型或JavaBean时,在一个规则中改变了global的值,对java代码和所有规则都有效。
下面我们通过代码进行验证:
第一步:创建UserService类
  1. package com.itheima.drools.service;
  2. public class UserService {
  3.     public void save(){
  4.         System.out.println("UserService.save()...");
  5.     }
  6. }
复制代码
第二步:编写规则文件/resources/rules/global.drl
  1. package testglobal
  2. /*
  3.     此规则文件用于测试global全局变量
  4. */
  5. global java.lang.Integer count //定义一个包装类型的全局变量
  6. global com.itheima.drools.service.UserService userService //定义一个JavaBean类型的全局变量
  7. global java.util.List gList //定义一个集合类型的全局变量
  8. rule "rule_global_1"
  9.     when
  10.     then
  11.         count += 10; //全局变量计算,只对当前规则有效,其他规则不受影响
  12.         userService.save();//调用全局变量的方法
  13.         gList.add("itcast");//向集合类型的全局变量中添加元素,Java代码和所有规则都受影响
  14.         gList.add("itheima");
  15.         System.out.println("count=" + count);
  16.         System.out.println("gList.size=" + gList.size());
  17. end
  18. rule "rule_global_2"
  19.     when
  20.     then
  21.         userService.save();
  22.         System.out.println("count=" + count);
  23.         System.out.println("gList.size=" + gList.size());
  24. end
复制代码
第三步:编写单元测试
  1. KieServices kieServices = KieServices.Factory.get();
  2. KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
  3. KieSession kieSession = kieClasspathContainer.newKieSession();
  4. //设置全局变量,名称和类型必须和规则文件中定义的全局变量名称对应
  5. kieSession.setGlobal("userService",new UserService());
  6. kieSession.setGlobal("count",5);
  7. List list = new ArrayList();//size为0
  8. kieSession.setGlobal("gList",list);
  9. kieSession.fireAllRules();
  10. kieSession.dispose();
  11. //因为在规则中为全局变量添加了两个元素,所以现在的size为2
  12. System.out.println(list.size());
复制代码
6.2 query查询

query查询提供了一种查询working memory中符合约束条件的Fact对象的简单方法。它仅包含规则文件中的LHS部分,不用指定“when”和“then”部分并且以end结束。具体语法结构如下:
  1. query 查询的名称(可选参数)
  2.         LHS
  3. end
复制代码
具体操作步骤:
第一步:编写规则文件/resources/rules/query.drl
  1. package testquery
  2. import com.itheima.drools.entity.Student
  3. /*
  4.     此规则文件用于测试query查询
  5. */
  6. //不带参数的查询
  7. //当前query用于查询Working Memory中age>10的Student对象
  8. query "query_1"
  9.     $student:Student(age > 10)
  10. end
  11. //带有参数的查询
  12. //当前query用于查询Working Memory中age>10同时name需要和传递的参数name相同的Student对象
  13. query "query_2"(String sname)
  14.     $student:Student(age > 20 && name == sname)
  15. end
复制代码
第二步:编写单元测试
  1. KieServices kieServices = KieServices.Factory.get();
  2. KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
  3. KieSession kieSession = kieClasspathContainer.newKieSession();
  4. Student student1 = new Student();
  5. student1.setName("张三");
  6. student1.setAge(12);
  7. Student student2 = new Student();
  8. student2.setName("李四");
  9. student2.setAge(8);
  10. Student student3 = new Student();
  11. student3.setName("王五");
  12. student3.setAge(22);
  13. //将对象插入Working Memory中
  14. kieSession.insert(student1);
  15. kieSession.insert(student2);
  16. kieSession.insert(student3);
  17. //调用规则文件中的查询
  18. QueryResults results1 = kieSession.getQueryResults("query_1");
  19. int size = results1.size();
  20. System.out.println("size=" + size);
  21. for (QueryResultsRow row : results1) {
  22.     Student student = (Student) row.get("$student");
  23.     System.out.println(student);
  24. }
  25. //调用规则文件中的查询
  26. QueryResults results2 = kieSession.getQueryResults("query_2","王五");
  27. size = results2.size();
  28. System.out.println("size=" + size);
  29. for (QueryResultsRow row : results2) {
  30.     Student student = (Student) row.get("$student");
  31.     System.out.println(student);
  32. }
  33. //kieSession.fireAllRules();
  34. kieSession.dispose();
复制代码
6.3 function函数

function关键字用于在规则文件中定义函数,就相当于java类中的方法一样。可以在规则体中调用定义的函数。使用函数的好处是可以将业务逻辑集中放置在一个地方,根据需要可以对函数进行修改。
函数定义的语法结构如下:
  1. function 返回值类型 函数名(可选参数){
  2.         //逻辑代码
  3. }
复制代码
具体操作步骤:
第一步:编写规则文件/resources/rules/function.drl
  1. package testfunction
  2. import com.itheima.drools.entity.Student
  3. /*
  4.     此规则文件用于测试function函数
  5. */
  6. //定义一个函数
  7. function String sayHello(String name){
  8.     return "hello " + name;
  9. }
  10. rule "rule_function_1"
  11.     when
  12.         $student:Student(name != null)
  13.     then
  14.         //调用上面定义的函数
  15.         String ret = sayHello($student.getName());
  16.         System.out.println(ret);
  17. end
复制代码
第二步:编写单元测试
  1. KieServices kieServices = KieServices.Factory.get();
  2. KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
  3. KieSession kieSession = kieClasspathContainer.newKieSession();
  4. Student student = new Student();
  5. student.setName("小明");
  6. kieSession.insert(student);
  7. kieSession.fireAllRules();
  8. kieSession.dispose();
复制代码
6.4 LHS加强

前面我们已经知道了在规则体中的LHS部分是介于when和then之间的部分,主要用于模式匹配,只有匹配结果为true时,才会触发RHS部分的执行。本章节我们会针对LHS部分学习几个新的用法。
6.4.1 复合值限制in/not in

复合值限制是指超过一种匹配值的限制条件,类似于SQL语句中的in关键字。Drools规则体中的LHS部分可以使用in或者not in进行复合值的匹配。具体语法结构如下:
Object(field in (比较值1,比较值2...))
举例:
  1. $s:Student(name in ("张三","李四","王五"))
  2. $s:Student(name not in ("张三","李四","王五"))
复制代码
6.4.2 条件元素eval

eval用于规则体的LHS部分,并返回一个Boolean类型的值。语法结构如下:
eval(表达式)
举例:
  1. eval(true)
  2. eval(false)
  3. eval(1 == 1)
复制代码
6.4.3 条件元素not

not用于判断Working Memory中是否存在某个Fact对象,如果不存在则返回true,如果存在则返回false。语法结构如下:
not Object(可选属性约束)
举例:
  1. not Student()
  2. not Student(age < 10)
复制代码
6.4.4 条件元素exists

exists的作用与not相反,用于判断Working Memory中是否存在某个Fact对象,如果存在则返回true,不存在则返回false。语法结构如下:
exists Object(可选属性约束)
举例:
  1. exists Student()
  2. exists Student(age < 10 && name != null)
复制代码
可能有人会有疑问,我们前面在LHS部分进行条件编写时并没有使用exists也可以达到判断Working Memory中是否存在某个符合条件的Fact元素的目的,那么我们使用exists还有什么意义?
两者的区别:当向Working Memory中加入多个满足条件的Fact对象时,使用了exists的规则执行一次,不使用exists的规则会执行多次。
例如:
规则文件(只有规则体):
  1. rule "使用exists的规则"
  2.     when
  3.             exists Student()
  4.     then
  5.             System.out.println("规则:使用exists的规则触发");
  6. end
  7. rule "没有使用exists的规则"
  8.     when
  9.             Student()
  10.     then
  11.             System.out.println("规则:没有使用exists的规则触发");
  12. end
复制代码
Java代码:
  1. kieSession.insert(new Student());
  2. kieSession.insert(new Student());
  3. kieSession.fireAllRules();
复制代码
上面第一个规则只会执行一次,因为Working Memory中存在两个满足条件的Fact对象,第二个规则会执行两次。
6.4.5 规则继承

规则之间可以使用extends关键字进行规则条件部分的继承,类似于java类之间的继承。
例如:
  1. rule "rule_1"
  2.     when
  3.             Student(age > 10)
  4.     then
  5.             System.out.println("规则:rule_1触发");
  6. end
  7. rule "rule_2" extends "rule_1" //继承上面的规则
  8.     when
  9.             /*
  10.             此处的条件虽然只写了一个,但是从上面的规则继承了一个条件,
  11.             所以当前规则存在两个条件,即Student(age < 20)和Student(age > 10)
  12.             */
  13.             Student(age < 20)
  14.     then
  15.             System.out.println("规则:rule_2触发");
  16. end
复制代码
6.5 RHS加强

RHS部分是规则体的重要组成部分,当LHS部分的条件匹配成功后,对应的RHS部分就会触发执行。一般在RHS部分中需要进行业务处理。
在RHS部分Drools为我们提供了一个内置对象,名称就是drools。本小节我们来介绍几个drools对象提供的方法。
6.5.1 halt

halt方法的作用是立即终止后面所有规则的执行。
  1. package testhalt
  2. rule "rule_halt_1"
  3.     when
  4.     then
  5.             System.out.println("规则:rule_halt_1触发");
  6.                 drools.halt();//立即终止后面所有规则执行
  7. end
  8. //当前规则并不会触发,因为上面的规则调用了halt方法导致后面所有规则都不会执行
  9. rule "rule_halt_2"
  10.     when
  11.     then
  12.             System.out.println("规则:rule_halt_2触发");
  13. end
复制代码
6.5.2 getWorkingMemory

getWorkingMemory方法的作用是返回工作内存对象。
  1. package testgetWorkingMemory
  2. rule "rule_getWorkingMemory"
  3.     when
  4.     then
  5.             System.out.println(drools.getWorkingMemory());
  6. end
复制代码
6.5.3 getRule

getRule方法的作用是返回规则对象。
  1. package testgetRule
  2. rule "rule_getRule"
  3.     when
  4.     then
  5.             System.out.println(drools.getRule());
  6. end
复制代码
6.6 规则文件编码规范

我们在进行drl类型的规则文件编写时尽量遵循如下规范:

  • 所有的规则文件(.drl)应统一放在一个规定的文件夹中,如:/rules文件夹
  • 书写的每个规则应尽量加上注释。注释要清晰明了,言简意赅
  • 同一类型的对象尽量放在一个规则文件中,如所有Student类型的对象尽量放在一个规则文件中
  • 规则结果部分(RHS)尽量不要有条件语句,如if(...),尽量不要有复杂的逻辑和深层次的嵌套语句
  • 每个规则最好都加上salience属性,明确执行顺序
  • Drools默认dialect为"Java",尽量避免使用dialect "mvel"
6.7 以上代码均在drools_quickstart项目中


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表