文章目录

1、背景

此处介绍一下​​drools​​​中​​then​​​部分的写法,以及一些内置的方法,比如​​insert/delete/modify​​​等等。同时也介绍一下​​rule​​​的继承,和在​​when​​​中实现​​if else if ​​等操作。

2、支持的方法

drools提供了一些内置的方法,这些方法会修改drools的​​工作内存​​​中​​Fact​​对象的值。从而会导致重新进行模式匹配。

2.1 insert 插入对象到工作内存中

​insert​​是向工作内存中插入对象,同时会导致重新进行规则的模式匹配。同时当规则不满足时,不会自动删除。

2.1.1 需求

当发生火灾​​Fire​​​时,向工作内存中插入一个​​Alarm​​​对象,告警发生后,删除工作内存中的​​Fire​​​对象,然后检测​​Alarm​​对象是否还存在。

2.1.2 drl文件编写

package rules

import com.huan.drools.insertmethod.Fire
import com.huan.drools.insertmethod.Alarm

rule "insert_发生火灾时,往工作内存中插入告警对象"
when
$fire: Fire()
then
System.out.println("1、发生火灾时,往工作内存中插入告警对象");
insert(new Alarm($fire));
end

rule "insert_当规则内存中存在告警对象,进行告警,然后删除火灾对象"
when
$fire: Fire()
$alarm: Alarm( fire == $fire )
then
System.out.println("2、进行告警,然后删除对应的火灾对象");
end

rule "insert_检测告警对象是否还是存在-01"
when
Alarm()
then
System.out.println("3、insert 插入的告警对象还存在");
// 删除工作内存中的Fire对象
delete($fire);
end

rule "insert_检测告警对象不存在"
when
not Alarm()
then
System.out.println("3、insert 插入的告警对象不存在");
end

此处使用的是 ​​insert​​进行插入

2.1.3 部分java代码编写

// 将火灾对象插入到工作内存中
kieSession.insert(new Fire());
// 只触发规则名称以 insert_ 开头的规则
kieSession.fireAllRules(new RuleNameStartsWithAgendaFilter("insert_"));

2.1.4 运行结果

1、发生火灾时,往工作内存中插入告警对象
2、进行告警,然后删除对应的火灾对象
3、insert 插入的告警对象还存在

2.1.5 结论

  1. ​insert​​​可以向工作内存中插入​​Fact​​对象。
  2. ​insert​​方法调用后,会导致模式的重新匹配,导致之前不会执行的规则,重新执行。
  3. ​insert​​​方法插入到工作内存的对象,在规则不成立时,不会自动删除,需要手动删除,注意和​​insertLogical​​的区别

2.2 insertLogical 插入对象到工作内存中

​insert​​是向工作内存中插入对象,同时会导致重新进行规则的模式匹配。同时当规则不满足时,会自动删除。

2.2.1 需求

当发生火灾​​Fire​​​时,向工作内存中插入一个​​Alarm​​​对象,告警发生后,删除工作内存中的​​Fire​​​对象,然后检测​​Alarm​​对象是否还存在。

2.2.2 drl文件编写

package rules

import com.huan.drools.Fire
import com.huan.drools.Alarm

rule "insertLogical_发生火灾时,往工作内存中插入告警对象"
when
$fire: Fire()
then
System.out.println("1、发生火灾时,往工作内存中插入告警对象");
insertLogical(new Alarm($fire));
end

rule "insertLogical_当规则内存中存在告警对象,进行告警,然后删除火灾对象"
when
$fire: Fire()
$alarm: Alarm( fire == $fire )
then
System.out.println("2、进行告警,然后删除对应的火灾对象");
delete($fire);
end

rule "insertLogical_检测告警对象是否还是存在-01"
when
Alarm()
then
System.out.println("3、insertLogical 插入的告警对象还存在");
end

rule "insertLogical_检测告警对象不存在"
when
not Alarm()
then
System.out.println("3、insertLogical 插入的告警对象不存在");
end

此处使用的是​​insertLogical​​插入

2.2.3 部分java代码编写

kieSession.insert(new Fire());
kieSession.fireAllRules(new RuleNameStartsWithAgendaFilter("insertLogical_"));

2.2.4 运行结果

1、发生火灾时,往工作内存中插入告警对象
2、进行告警,然后删除对应的火灾对象
3、insertLogical 插入的告警对象不存在

2.2.5 结论

  1. ​insertLogical​​​可以向工作内存中插入​​Fact​​对象。
  2. ​insertLogical​​方法调用后,会导致模式的重新匹配,导致之前不会执行的规则,重新执行。
  3. ​insertLogical​​​方法插入到工作内存的对象,在规则不成立时,会自动删除注意和​​insert​​的区别

2.3、update 更新工作内存中的对象

​update:​​​ 使用它来指定要更新的字段和整个相关Fact,并将更改通知 Drools 引擎。 Fact发生更改后,您必须在更改可能受更新值影响的另一个事实之前调用 update。 ​​为避免此添加步骤,请改用 modify 方法。​

2.3.1 需求

规则一: 当工作内存中存在火灾对象​​Fire​​​,并且名字name为空时触发规则,同时在设置火灾的名字为​​大火灾​​​。
规则二: 当火灾存在名字时,输出火灾名字

2.3.2 drl 文件编写

package rules

import com.huan.drools.Fire
import com.huan.drools.Alarm

rule "update_当存在火灾对象时,设置一个火灾的名字"
when
$fire: Fire(name == null)
then
System.out.println("1、设置火灾名字");
$fire.setName("大火灾");
update($fire)
end

rule "update_当火灾对象存在名字时触发"
when
$fire: Fire(name != null)
then
System.out.println("2、火灾对象的名字为: " + $fire.getName());
end

2.3.3 部分java文件编写

kieSession.insert(new Fire());
kieSession.fireAllRules(new RuleNameStartsWithAgendaFilter("update_"));

2.3.4 运行结果

1、设置火灾名字
2、火灾对象的名字为: 大火灾

2.3.4 结论

  1. ​update​​会导致模式的重新匹配。
  2. ​update​​会修改工作对象内存中的值。

2.4、modify 更新工作内存中的对象

​modify:​​ 使用它来指定要为Fact对象修改的字段并将更改通知 Drools 引擎。 此方法提供了一种结构化的事实更新方法。 它将更新操作与 setter 调用相结合以更改对象字段。

2.4.1 需求

规则一: 当工作内存中存在火灾对象​​Fire​​​,并且名字name为空时触发规则,同时在设置火灾的名字为​​大火灾​​​。
规则二: 当火灾存在名字时,输出火灾名字

2.4.2 drl 文件编写

package rules

import com.huan.drools.Fire
import com.huan.drools.Alarm

rule "modify_当存在火灾对象时,设置一个火灾的名字"
when
$fire: Fire(name == null)
then
System.out.println("1、设置火灾名字");
modify($fire){
setName("大火灾")
}
end

rule "modify_当火灾对象存在名字时触发"
when
$fire: Fire(name != null)
then
System.out.println("2、火灾对象的名字为: " + $fire.getName());
end

2.4.3 部分java文件编写

kieSession.insert(new Fire());
kieSession.fireAllRules(new RuleNameStartsWithAgendaFilter("modify_"));

2.4.4 运行结果

1、设置火灾名字
2、火灾对象的名字为: 大火灾

2.4.5 结论

  1. ​modify​​会导致模式的重新匹配。
  2. ​modify​​会修改工作对象内存中的值。
  3. 一般情况下使用​​modify​​​,不要使用​​update​​。

2.5 delete 删除工作内存中的对象

用法:​​delete(<object>)​

​retract​​​也是和​​delete​​​一样的效果,但是推荐使用​​delete​​。

3、drools变量的简单使用

package rules

rule "drools_变量的使用"
when
eval(true)
then
System.out.println("Match激活的当前触发规则: " + drools.getMatch());
System.out.println("当前触发规则的名字: " + drools.getRule().getName());
// System.out.println("终止规则执行fireUntilHalt(): " + drools.getKieRuntime().halt());
// System.out.println("激活AgendaGroup组: " + drools.getKieRuntime().getAgenda().getAgendaGroup( "CleanUp" ).setFocus());

System.out.println("获取所有全局变量: " + drools.getKieRuntime().getGlobals());
// System.out.println("设置全局变量:" + drools.getKieRuntime().setGlobal("username","huan"); );
// System.out.println("获取查询结果:" + drools.getKieRuntime().getQueryResults());
end

4、规则的继承

4.1 需求

规则一: 如果用户(​​customer​​​)的年龄(​​age​​​)大于60岁,则打​​0.9​​​折。
规则二: 在规则一的基础上,如果用户有车(​​car​​​),则可以免费停车(​​freeParking​​)。

4.2 drl文件编写

package rules

import com.huan.drools.Customer
import com.huan.drools.Car

rule "rule_extends_规则一"
when
$c: Customer(age > 60)
then
modify($c){
setDiscount(0.9)
}
System.out.println("触发规则一:用户年龄>60岁,打0.9折");
end

// 规则二继承规则一的条件
rule "rule_extends_规则二" extends "rule_extends_规则一"
when
$car: Car()
then
modify($car){
setFreeParking(true)
}
System.out.println("触发规则二:用户有车,免费停车");
end

此处​​rule_extends_规则二​​​继承了​​rule_extends_规则一​​,所以规则一的条件也继承了。

4.3 部分java代码

Car car = new Car();
Customer customer = new Customer();
customer.setAge(65);
kieSession.insert(customer);
kieSession.insert(car);
kieSession.fireAllRules(new RuleNameStartsWithAgendaFilter("rule_extends_"));

客户有车,并且年龄是65岁,满足上方的规则一和规则二

4.4 运行结果

触发规则一:用户年龄>60岁,打0.9折
触发规则二:用户有车,免费停车

4.5 结论

可以看到在​​rule​​​上使用​​extends​​关键字,可以实现规则的继承。

5、使用do[…]语法重写上方继承的例子

5.1 需求

只要用户大于60岁,直接打0.9折,如果还有车,则可以免费停车。

5.2 drl文件编写

package rules

import com.huan.drools.Customer
import com.huan.drools.Car

rule "命名结果_rule"
when
$c: Customer(age > 60)
do[giveDiscount] // 当上方的条件成立时执行 then [giveDiscount]
$car: Car() // 此条件成立时,执行默认的 then
then
modify($car){
setFreeParking(true)
};
System.out.println("用户有车,免费停车");
then [giveDiscount]
modify($c){
setDiscount(0.9)
};
System.out.println("用户年龄>60岁,打0.9折");
end

​解释:​​ 见上方的规则文件里的注视

5.3 部分java代码编写

Car car = new Car();
Customer customer = new Customer();
customer.setAge(65);
kieSession.insert(customer);
kieSession.insert(car);
kieSession.fireAllRules(new RuleNameStartsWithAgendaFilter("命名结果_"));

5.4 运行结果

用户年龄>60岁,打0.9折
用户有车,免费停车

也实现了需求

5.5 结论

通过​​when​​​中使用 ​​do[名字]​​​ 然后 ​​then​​​ ​​then 名字​​ 也可以实现。

6、实现 if else if 的效果

6.1 需求

完成类似 ​​if else if else​​效果。见下方的各种执行结果

6.2 规则文件

rule "if else-if"
when
$customer: Customer(age > 60) // 规则内存中存在Customer对象,并且age>60
if($customer.getLevel() == 1) do[level1] // 用户的级别是1,执行then[level1],然后继续执行下方的条件
else if ($customer.getLevel() == 2) break[level2] // 用户的级别是2,执行then[level2],不在执行下方的条件
else do[levelOther] // 其他的level级别,执行then[levelOther],然后在执行下方的条件
Car()
then
System.out.println("我执行了");
then[level1]
System.out.println("level1");
then[level2]
System.out.println("level2");
then[levelOther]
System.out.println("levelOther");
end

6.3 执行结果图

drools中then部分的写法_drools修改工作内存对象

6.4 各种执行结果-也就是上图的解释

1、​​Customer​​​的​​age​​​小于​​60​​​。
​​​输出:​​​ 没有输出。
2、​​​Customer​​​的​​age​​​大于​​60​​​并且​​level=1​​​,没有​​Car​​​。
​​​输出:​​​ level1
3、​​​Customer​​​的​​age​​​大于​​60​​​并且​​level=1​​​,有​​Car​​​。
​​​输出:​​​ level1 我执行了
4、​​​Customer​​​的​​age​​​大于​​60​​​并且​​level=2​​​,没有​​Car​​​。
​​​输出:​​​ level2
5、​​​Customer​​​的​​age​​​大于​​60​​​并且​​level=2​​​,有​​Car​​​。
​​​输出:​​​ level2
6、​​​Customer​​​的​​age​​​大于​​60​​​并且​​level=3​​​,没有​​Car​​​。
​​​输出:​​​ levelOther
7、​​​Customer​​​的​​age​​​大于​​60​​​并且​​level=3​​​,有​​Car​​​。
​​​输出:​​ levelOther 我执行了

6.5 do和 break 有什么区别

​do​​​:执行完之后,还会继续判断后面的执行条件。 (​​即还会执行后面的Car判断,根据是否有Car获取不同的结果​​​)
​​​break​​​:执行完之后,不会在判断后面的执行条件。(​​即忽略了后面的Car判断,rule执行完了​​)

7、完整代码

​https://gitee.com/huan1993/spring-cloud-parent/tree/master/drools/drools-drl-then​

8、参考文档

1、​​https://docs.drools.org/7.69.0.Final/drools-docs/html_single/index.html#drl-rules-THEN-con_drl-rules​