当前位置:网站首页>Drools规则属性,高级语法
Drools规则属性,高级语法
2022-07-31 01:47:00 【xushiyu1996818】
目录
规则属性 attributes
前面我们已经知道了规则体的构成如下:
rule "ruleName"
attributes
when
LHS
then
RHS
endDrools中提供的属性如下表(部分属性):

enabled属性
enabled属性对应的取值为true和false,默认值为true。
用于指定当前规则是否启用,如果设置的值为false则当前规则无论是否匹配成功都不会触发
package rules
import com.mashibing.drools.entity.AttributesEnabledEntity
/*
用于测试Drools 属性:enabled
*/
//测试enabled
rule "rule_attributes_enabled"
enabled false
when
AttributesEnabledEntity(num > 10)
then
System.out.println("规则rule_attributes_enabled触发");
enddialect属性
dialect属性用于指定当前规则使用的语言类型,取值为java和mvel,默认值为java。
注:mvel是一种基于java语法的表达式语言。
虽然mvel吸收了大量的java语法,但作为一个表达式语言,还是有着很多重要的不同之处,以达到更高的效率,比如:mvel像正则表达式一样,有直接支持集合、数组和字符串匹配的操作符。
除了表达式语言外,mvel还提供了用来配置和构造字符串的模板语言。
mvel2.x表达式包含以下部分的内容:属性表达式,布尔表达式,方法调用,变量赋值,函数定义
salience属性
salience属性用于指定规则的执行优先级,取值类型为Integer。数值越大越优先执行。每个规则都有一个默认的执行顺序,如果不设置salience属性,规则体的执行顺序为由上到下。
drl文件内容如下:
package rules
import com.mashibing.drools.entity.AttributesSalienceEntity
/*
用于测试Drools 属性:salience
*/
rule "rule_attributes_salience_1"
when
AttributesSalienceEntity(flag == true)
then
System.out.println("规则 rule_attributes_salience_1 触发");
end
rule "rule_attributes_salience_2"
when
AttributesSalienceEntity(flag == true)
then
System.out.println("规则 rule_attributes_salience_2 触发");
end
rule "rule_attributes_salience_3"
when
AttributesSalienceEntity(flag == true)
then
System.out.println("规则 rule_attributes_salience_3 触发");
end通过控制台可以看到,由于以上三个规则没有设置salience属性,所以执行的顺序是按照规则文件中规则的顺序由上到下执行的。接下来我们修改一下文件内容:
package rules
import com.mashibing.drools.entity.AttributesSalienceEntity
/*
用于测试Drools 属性:salience
*/
rule "rule_attributes_salience_1"
salience 10
when
AttributesSalienceEntity(flag == true)
then
System.out.println("规则 rule_attributes_salience_1 触发");
end
rule "rule_attributes_salience_2"
salience 20
when
AttributesSalienceEntity(flag == true)
then
System.out.println("规则 rule_attributes_salience_2 触发");
end
rule "rule_attributes_salience_3"
salience 1
when
AttributesSalienceEntity(flag == true)
then
System.out.println("规则 rule_attributes_salience_3 触发");
end通过控制台可以看到,规则文件执行的顺序是按照我们设置的salience值由大到小顺序执行的。
建议在编写规则时使用salience属性明确指定执行优先级。
no-loop属性
no-loop属性用于防止死循环,当规则通过update之类的函数修改了Fact对象时,可能使当前规则再次被激活从而导致死循环。取值类型为Boolean,默认值为false。测试步骤如下:
第一步:编写规则文件
package rules
import com.mashibing.drools.entity.AttributesNoLoopEntity
/*
用于测试Drools 属性:no-loop
*/
rule "rule_attributes_noloop"
//no-loop true
when
$attributesNoLoopEntity:AttributesNoLoopEntity(num > 1)
then
update($attributesNoLoopEntity)
System.out.println("规则 rule_attributes_noloop 触发");
end
第二步:编写单元测试
@Test
public void test(){
KieSession kieSession = kieBase.newKieSession();
AttributesNoLoopEntity attributesNoLoopEntity = new AttributesNoLoopEntity();
attributesNoLoopEntity.setNum(20);
kieSession.insert(attributesNoLoopEntity);
kieSession.fireAllRules();
kieSession.dispose();
}通过控制台可以看到,由于我们没有设置no-loop属性的值,所以发生了死循环。接下来设置no-loop的值为true再次测试则不会发生死循环。
lock-on-active属性
lock-on-active这个属性,可以限制当前规则只会被执行一次,包括当前规则的重复执行不是本身触发的。取值类型为Boolean,默认值为false。测试步骤如下:
第一步:编写规则文件
```java
package rules
import com.mashibing.drools.entity.AttributesLockOnActiveEntity
/*
用于测试Drools 属性:lock-on-active
*/
rule "rule_attributes_lock_on_active_1"
no-loop true
when
$attributesLockOnActiveEntity:AttributesLockOnActiveEntity(num > 1)
then
update($attributesLockOnActiveEntity)
System.out.println("规则 rule_attributes_lock_on_active_1 触发");
end
rule "rule_attributes_lock_on_active_2"
no-loop true
lock-on-active true
when
$attributesLockOnActiveEntity:AttributesLockOnActiveEntity(num > 1)
then
update($attributesLockOnActiveEntity)
System.out.println("规则 rule_attributes_lock_on_active_2 触发");
end
```
第二步:编写单元测试
```java
@Test
public void test(){
KieSession kieSession = kieBase.newKieSession();
AttributesLockOnActiveEntity attributesLockOnActiveEntity = new AttributesLockOnActiveEntity();
attributesLockOnActiveEntity.setNum(20);
kieSession.insert(attributesLockOnActiveEntity);
kieSession.fireAllRules();
kieSession.dispose();
}
```no-loop的作用是限制因为modify等更新操作导致规则重复执行,但是有一个限定条件,是当前规则中进行更新导致当前规则重复执行。而不是防止其他规则更新相同的fact对象,导致当前规则更新,lock-on-active可以看作是no-loop的加强版,不仅能限制自己的更新,还能限制别人的更新造成的死循环。
activation-group属性
activation-group属性是指激活分组,取值为String类型。具有相同分组名称的规则只能有一个规则被触发。
第一步:编写规则文件
```java
package rules
import com.mashibing.drools.entity.AttributesActivationGroupEntity
/*
用于测试Drools 属性: activation-group
*/
rule "rule_attributes_activation_group_1"
activation-group "customGroup"
when
$attributesActivationGroupEntity:AttributesActivationGroupEntity(num > 1)
then
System.out.println("规则 rule_attributes_activation_group_1 触发");
end
rule "rule_attributes_activation_group_2"
activation-group "customGroup"
when
$attributesActivationGroupEntity:AttributesActivationGroupEntity(num > 1)
then
System.out.println("规则 rule_attributes_activation_group_2 触发");
end
```
第二步:编写单元测试
```java
@Test
public void test(){
KieSession kieSession = kieBase.newKieSession();
AttributesActivationGroupEntity attributesActivationGroupEntity = new AttributesActivationGroupEntity();
attributesActivationGroupEntity.setNum(20);
kieSession.insert(attributesActivationGroupEntity);
kieSession.fireAllRules();
kieSession.dispose();
}
```通过控制台可以发现,上面的两个规则因为属于同一个分组,所以只有一个触发了。同一个分组中的多个规则如果都能够匹配成功,具体哪一个最终能够被触发可以通过salience属性确定。
agenda-group属性
agenda-group属性为议程分组,属于另一种可控的规则执行方式。用户可以通过设置agenda-group来控制规则的执行,只有获取焦点的组中的规则才会被触发。
第一步:编写规则文件
```java
package rules
import com.mashibing.drools.entity.AttributesAgendaGroupEntity
/*
用于测试Drools 属性: agenda-group
*/
rule "rule_attributes_agenda_group_1"
agenda-group "customAgendaGroup1"
when
$attributesAgendaGroupEntity:AttributesAgendaGroupEntity(num > 1)
then
System.out.println("规则 rule_attributes_agenda_group_1 触发");
end
rule "rule_attributes_agenda_group_2"
agenda-group "customAgendaGroup1"
when
$attributesAgendaGroupEntity:AttributesAgendaGroupEntity(num > 1)
then
System.out.println("规则 rule_attributes_agenda_group_2 触发");
end
rule "rule_attributes_activation_group_3"
agenda-group "customAgendaGroup2"
when
$attributesAgendaGroupEntity:AttributesAgendaGroupEntity(num > 1)
then
System.out.println("规则 rule_attributes_activation_group_3 触发");
end
rule "rule_attributes_agenda_group_4"
agenda-group "customAgendaGroup2"
when
$attributesAgendaGroupEntity:AttributesAgendaGroupEntity(num > 1)
then
System.out.println("规则 rule_attributes_agenda_group_4 触发");
end
```
第二步:编写单元测试
```java
@Test
public void test(){
KieSession kieSession = kieBase.newKieSession();
AttributesAgendaGroupEntity attributesAgendaGroupEntity = new AttributesAgendaGroupEntity();
attributesAgendaGroupEntity.setNum(20);
kieSession.insert(attributesAgendaGroupEntity);
kieSession.getAgenda().getAgendaGroup("customAgendaGroup2").setFocus();
kieSession.fireAllRules();
kieSession.dispose();
}
```
通过控制台可以看到,只有获取焦点的分组中的规则才会触发。与activation-group不同的是,activation-group定义的分组中只能够有一个规则可以被触发,而agenda-group分组中的多个规则都可以被触发。
auto-focus属性
auto-focus属性为自动获取焦点,取值类型为Boolean,默认值为false。一般结合agenda-group属性使用,当一个议程分组未获取焦点时,可以设置auto-focus属性来控制。
第一步:编写规则文件
```java
package rules
import com.mashibing.drools.entity.AttributesAutoFocusEntity
/*
用于测试Drools 属性: auto-focus
*/
rule "rule_attributes_auto_focus_1"
agenda-group "customAgendaGroup1"
when
$attributesAutoFocusEntity:AttributesAutoFocusEntity(num > 1)
then
System.out.println("规则 rule_attributes_auto_focus_1 触发");
end
rule "rule_attributes_auto_focus_2"
agenda-group "customAgendaGroup1"
when
$attributesAutoFocusEntity:AttributesAutoFocusEntity(num > 1)
then
System.out.println("规则 rule_attributes_auto_focus_2 触发");
end
rule "rule_attributes_auto_focus_3"
agenda-group "customAgendaGroup2"
// auto-focus true
when
$attributesAutoFocusEntity:AttributesAutoFocusEntity(num > 1)
then
System.out.println("规则 rule_attributes_auto_focus_3 触发");
end
rule "rule_attributes_auto_focus_4"
agenda-group "customAgendaGroup2"
when
$attributesAutoFocusEntity:AttributesAutoFocusEntity(num > 1)
then
System.out.println("规则 rule_attributes_auto_focus_4 触发");
end
```
第二步:编写单元测试
```java
@Test
public void test(){
KieSession kieSession = kieBase.newKieSession();
AttributesAutoFocusEntity attributesAutoFocusEntity = new AttributesAutoFocusEntity();
attributesAutoFocusEntity.setNum(20);
kieSession.insert(attributesAutoFocusEntity);
kieSession.fireAllRules();
kieSession.dispose();
}
```timer属性
timer属性可以通过定时器的方式指定规则执行的时间,使用方式有两种:
方式一:timer (int: <initial delay> <repeat interval>?)
此种方式遵循java.util.Timer对象的使用方式,第一个参数表示几秒后执行,第二个参数表示每隔几秒执行一次,第二个参数为可选。
方式二:timer(cron: <cron expression>)
此种方式使用标准的unix cron表达式的使用方式来定义规则执行的时间。
第一步:编写规则文件
package rules
import com.mashibing.drools.entity.AttributesTimerEntity
/*
用于测试Drools 属性: timer
*/
rule "rule_attributes_timer_1"
timer(5s 2s)
when
$attributesTimerEntity:AttributesTimerEntity(num > 1)
then
System.out.println("规则 rule_attributes_timer_1 触发");
end
rule "rule_attributes_timer_2"
timer(cron:0/1 * * * * ?)
when
$attributesTimerEntity:AttributesTimerEntity(num > 1)
then
System.out.println("规则 rule_attributes_timer_2 触发");
end
```
第二步:编写单元测试
```java
@Test
public void test() throws InterruptedException {
KieSession kieSession = kieBase.newKieSession();
AttributesTimerEntity attributesTimerEntity = new AttributesTimerEntity();
attributesTimerEntity.setNum(20);
kieSession.insert(attributesTimerEntity);
kieSession.fireUntilHalt();
Thread.sleep(10000);
kieSession.halt();
kieSession.dispose();
}注意:如果规则中有用到了timer属性,匹配规则需要调用kieSession.fireUntilHalt();这里涉及一个规则引擎的执行模式和线程问题,关于具体细节,我们后续讨论。
date-effective属性
date-effective属性用于指定规则的生效时间,即只有当前系统时间大于等于设置的时间或者日期规则才有可能触发。默认日期格式为:dd-MMM-yyyy。用户也可以自定义日期格式。
第一步:编写规则文件
```java
package rules
import com.mashibing.drools.entity.AttributesDateEffectiveEntity
/*
用于测试Drools 属性: date-effective
*/
rule "rule_attributes_date_effective"
// date-effective "20-七月-2021"
date-effective "2021-02-20"
when
$attributesDateEffectiveEntity:AttributesDateEffectiveEntity(num > 1)
then
System.out.println("规则 rule_attributes_date_effective 触发");
end
```
第二步:编写单元测试
```java
@Test
public void test(){
KieSession kieSession = kieBase.newKieSession();
AttributesDateEffectiveEntity attributesDateEffectiveEntity = new AttributesDateEffectiveEntity();
attributesDateEffectiveEntity.setNum(20);
kieSession.insert(attributesDateEffectiveEntity);
kieSession.fireAllRules();
kieSession.dispose();
}
```注意:需要在VM参数上加上日期格式:-Ddrools.dateformat=yyyy-MM-dd,在生产环境所在规则引擎的JVM设置中,也需要设置此参数,以保证开发和生产的一致性。
date-expires属性
date-expires属性用于指定规则的失效时间,即只有当前系统时间小于设置的时间或者日期规则才有可能触发。默认日期格式为:dd-MMM-yyyy。用户也可以自定义日期格式。
第一步:编写规则文件/resource/rules/dateexpires.drl
```java
package rules
import com.mashibing.drools.entity.AttributesDateExpiresEntity
/*
用于测试Drools 属性: date-expires
*/
rule "rule_attributes_date_expires"
date-expires "2021-06-20"
when
$attributesDateExpiresEntity:AttributesDateExpiresEntity(num > 1)
then
System.out.println("规则 rule_attributes_date_expires 触发");
end
```
第二步:编写单元测试
```java
@Test
public void test(){
KieSession kieSession = kieBase.newKieSession();
AttributesDateExpiresEntity attributesDateExpiresEntity = new AttributesDateExpiresEntity();
attributesDateExpiresEntity.setNum(20);
kieSession.insert(attributesDateExpiresEntity);
kieSession.fireAllRules();
kieSession.dispose();
}
```注意:需要在VM参数上加上日期格式:-Ddrools.dateformat=yyyy-MM-dd,在生产环境所在规则引擎的JVM设置中,也需要设置此参数,以保证开发和生产的一致性。
Drools高级语法
前面章节我们已经知道了一套完整的规则文件内容构成如下:

global全局变量
global关键字用于在规则文件中定义全局变量,它可以让应用程序的对象在规则文件中能够被访问。可以用来为规则文件提供数据或服务。
语法结构为:global 对象类型 对象名称
在使用global定义的全局变量时有两点需要注意:
1、如果对象类型为包装类型时,在一个规则中改变了global的值,那么只针对当前规则有效,对其他规则中的global不会有影响。可以理解为它是当前规则代码中的global副本,规则内部修改不会影响全局的使用。
2、如果对象类型为集合类型或JavaBean时,在一个规则中改变了global的值,对java代码和所有规则都有效。
下面我们通过代码进行验证:
第一步:编写规则文件
```java
package rules
import com.mashibing.drools.entity.GlobalEntity
/*
用于测试Drools 全局变量 : global
*/
global java.lang.Integer globalCount
global java.util.List globalList
rule "rule_global_1"
when
$globalEntity:GlobalEntity(num > 1)
then
System.out.println("规则 rule_global_1 开始...");
globalCount++ ;
globalList.add("张三");
globalList.add("李四");
System.out.println(globalCount);
System.out.println(globalList);
System.out.println("规则 rule_global_1 结束...");
end
rule "rule_global_2"
when
$globalEntity:GlobalEntity(num > 1)
then
System.out.println("规则 rule_global_2 开始...");
System.out.println(globalCount);
System.out.println(globalList);
System.out.println("规则 rule_global_2 结束...");
end
```
第二步:编写单元测试
```java
@Test
public void test(){
KieSession kieSession = kieBase.newKieSession();
GlobalEntity globalEntity = new GlobalEntity();
globalEntity.setNum(20);
ArrayList<Object> globalList = new ArrayList<>();
Integer globalCount = 10;
kieSession.setGlobal("globalCount", 10);
kieSession.setGlobal("globalList", globalList);
kieSession.insert(globalEntity);
kieSession.fireAllRules();
kieSession.dispose();
System.out.println("globalCount=" + globalCount);
System.out.println("globalList=" + globalList);
}
```注意:
1-后面的代码中定义了全局变量以后,前面的test都需要加,不然会出错。
2-属性当中的 关于时间的属性,如果涉及格式问题,也不要忘记,jvm启动参数添加相关配置
query查询
query查询提供了一种查询working memory中符合约束条件的Fact对象的简单方法。它仅包含规则文件中的LHS部分,不用指定“when”和“then”部分并且以end结束。具体语法结构如下:
```
query 查询的名称(可选参数)
LHS
end
```
具体操作步骤:
第一步:编写规则文件
```java
package rules
import com.mashibing.drools.entity.QueryEntity
/*
用于测试Drools 方法: query
*/
//无参查询
query "query_1"
$queryEntity:QueryEntity(age>20)
end
//有参查询
query "query_2"(Integer qAge,String qName)
$queryEntity:QueryEntity(age > qAge && name == qName)
end
```
第二步:编写单元测试
```java
@Test
public void test(){
KieSession kieSession = kieBase.newKieSession();
QueryEntity queryEntity1= new QueryEntity();
QueryEntity queryEntity2= new QueryEntity();
QueryEntity queryEntity3= new QueryEntity();
queryEntity1.setName("张三").setAge(10);
queryEntity2.setName("李四").setAge(20);
queryEntity3.setName("王五").setAge(30);
kieSession.insert(queryEntity1);
kieSession.insert(queryEntity2);
kieSession.insert(queryEntity3);
QueryResults results1 = kieSession.getQueryResults("query_1");
QueryResults results2 = kieSession.getQueryResults("query_2", 1, "张三");
for (QueryResultsRow queryResultsRow : results1) {
QueryEntity queryEntity = (QueryEntity) (queryResultsRow.get("$queryEntity"));
System.out.println(queryEntity);
}
for (QueryResultsRow queryResultsRow : results2) {
QueryEntity queryEntity = (QueryEntity) (queryResultsRow.get("$queryEntity"));
System.out.println(queryEntity);
}
kieSession.fireAllRules();
kieSession.dispose();
}
```function函数
function关键字用于在规则文件中定义函数,就相当于java类中的方法一样。可以在规则体中调用定义的函数。使用函数的好处是可以将业务逻辑集中放置在一个地方,根据需要可以对函数进行修改。
函数定义的语法结构如下:
```java
function 返回值类型 函数名(可选参数){ //逻辑代码}
```
具体操作步骤:
第一步:编写规则文件/resources/rules/function.drl
```java
package rules
import com.mashibing.drools.entity.FunctionEntity
/*
用于测试Drools 方法: function
*/
//定义一个 假发 方法
function Integer add(Integer num){
return num+10;
}
rule "function"
when
$functionEntity:FunctionEntity(num>20)
then
Integer result = add($functionEntity.getNum());
System.out.println(result);
end
```
第二步:编写单元测试
```java
@Test
public void test(){
KieSession kieSession = kieBase.newKieSession();
FunctionEntity functionEntity = new FunctionEntity();
functionEntity.setNum(30);
kieSession.insert(functionEntity);
kieSession.fireAllRules();
kieSession.dispose();
}
```
条件-LHS加强
前面我们已经知道了在规则体中的LHS部分是**介于when和then之间的部分**,主要用于模式匹配,只有匹配结果为true时,才会触发RHS部分的执行。本章节我们会针对LHS部分学习几个新的用法。
复合值限制in/not in
复合值限制是指超过一种匹配值的限制条件,类似于SQL语句中的in关键字。Drools规则体中的LHS部分可以使用in或者not in进行复合值的匹配。具体语法结构如下:
**Object(field in (比较值1,比较值2...))**
举例:
```java
package rules
import com.mashibing.drools.entity.LhsInEntity
/*
用于测试Drools LHS: in not in
*/
rule "lhs_in"
when
$lhsInEntity:LhsInEntity(name in ("张三","李四","王五"))
then
System.out.println("规则 lhs_in 触发");
end
rule "lhs_not_in"
when
$lhsInEntity:LhsInEntity(name not in ("张三","李四","王五"))
then
System.out.println("规则 lhs_not_in 触发");
end
```条件元素eval
eval用于规则体的LHS部分,并返回一个Boolean类型的值。语法结构如下:
**eval(表达式)**
举例:
```
package rules
import com.mashibing.drools.entity.LhsEvalEntity
/*
用于测试Drools LHS: in not in
*/
rule "lhs_eval"
when
$lhsInEntity:LhsEvalEntity(age > 10) and eval(2>1)
then
System.out.println("规则 lhs_eval 触发");
end
```条件元素not
not用于判断Working Memory中是否存在某个Fact对象,如果不存在则返回true,如果存在则返回false。语法结构如下:
**not Object(可选属性约束)**
举例:
```java
package rules
import com.mashibing.drools.entity.LhsNotEntity
/*
用于测试Drools LHS: not
*/
rule "lhs_not"
when
not $lhsNotEntity:LhsNotEntity(age > 10)
then
System.out.println("规则 lhs_not 触发");
end
```条件元素exists
exists的作用与not相反,用于判断Working Memory中是否存在某个Fact对象,如果存在则返回true,不存在则返回false。语法结构如下:
**exists Object(可选属性约束)**
举例:
```
package rules
import com.mashibing.drools.entity.LhsEvalEntity
/*
用于测试Drools LHS: exists
*/
rule "lhs_exists"
when
exists $lhsInEntity:LhsEvalEntity(age > 10)
then
System.out.println("规则 lhs_eval 触发");
end
```
Java代码:
```java
package com.mashibing.drools.client;
import com.mashibing.drools.DroolsApplicationTests;
import com.mashibing.drools.entity.LhsExistsEntity;
import com.mashibing.drools.entity.LhsNotEntity;
import org.junit.jupiter.api.Test;
import org.kie.api.KieBase;
import org.kie.api.runtime.KieSession;
import javax.annotation.Resource;
/**
* @author sunzhiqiang23
* @date 2021-06-17 23:46
*/
public class LhsNotTest extends DroolsApplicationTests {
@Resource
public KieBase kieBase;
@Test
public void test(){
KieSession kieSession = kieBase.newKieSession();
LhsNotEntity lhsNotEntity = new LhsNotEntity();
lhsNotEntity.setAge(1);
kieSession.insert(lhsNotEntity);
kieSession.fireAllRules();
kieSession.dispose();
}
}
```可能有人会有疑问,我们前面在LHS部分进行条件编写时并没有使用exists也可以达到判断Working Memory中是否存在某个符合条件的Fact元素的目的,那么我们使用exists还有什么意义?
两者的区别:当向Working Memory中加入多个满足条件的Fact对象时,使用了exists的规则执行一次,不使用exists的规则会执行多次。
例如:
规则文件(只有规则体):
```java
package rules
import com.mashibing.drools.entity.LhsExistsEntity
/*
用于测试Drools LHS: exists
*/
rule "lhs_exists_1"
when
exists $lhsExistsEntity:LhsExistsEntity(age > 10)
then
System.out.println("规则 lhs_exists_1 触发");
end
rule "lhs_exists_2"
when
$lhsExistsEntity:LhsExistsEntity(age > 10)
then
System.out.println("规则 lhs_exists_2 触发");
end
```
Java代码:
```
@Test
public void test(){
KieSession kieSession = kieBase.newKieSession();
LhsExistsEntity lhsExistsEntity = new LhsExistsEntity();
lhsExistsEntity.setAge(30);
LhsExistsEntity lhsExistsEntity2 = new LhsExistsEntity();
lhsExistsEntity2.setAge(30);
kieSession.insert(lhsExistsEntity);
kieSession.insert(lhsExistsEntity2);
kieSession.fireAllRules();
kieSession.dispose();
}
```上面第一个规则只会执行一次,因为Working Memory中存在两个满足条件的Fact对象,第二个规则会执行两次。
规则继承
规则之间可以使用extends关键字进行规则条件部分的继承,类似于java类之间的继承。
例如:
//方案2
rule "Give 10% discount to customers older than 60_s"
when
$c:Customer(age > 60)
then
System.out.println("方案2:Give 10% discount!");
end
rule "Give free parking to customers older than 60_s"
extends "Give 10% discount to customers older than 60_s"
when
Car(owner == $c.id)
then
System.out.println("方案2:Free parking!");
end方案2:Give 10% discount!
方案2:Free parking!
第二个rule使用extends基于第一个rule扩展了规则条目并配置了更多的数据处理方式,第二个规则依赖于第一规则中的规则条目,第一个规则改动conditions时第二个规则也会同时生效,维护难度的问题得到解决,但是写法上似乎还是比较繁琐。
结果-RHS
规则文件的`RHS`部分的主要作用是通过插入,删除或修改工作内存中的Fact数据,来达到控制规则引擎执行的目的。Drools提供了一些方法可以用来操作工作内存中的数据,操作完成后规则引擎会重新进行相关规则的匹配,原来没有匹配成功的规则在我们修改数据完成后有可能就会匹配成功了。
insert方法
insert方法的作用是向工作内存中插入数据,并让相关的规则重新匹配。
第一步:编写规则文件
```java
package rules
import com.mashibing.drools.entity.RhsInsertEntity
/*
用于测试Drools RHS: insert
*/
rule "rhs_insert_1"
when
$rhsInsertEntity:RhsInsertEntity(age <= 10)
then
RhsInsertEntity rhsInsertEntity = new RhsInsertEntity();
rhsInsertEntity.setAge(15);
insert(rhsInsertEntity);
System.out.println("规则 rhs_insert_1 触发");
end
rule "rhs_insert_2"
when
$rhsInsertEntity:RhsInsertEntity(age <=20 && age>10)
then
RhsInsertEntity rhsInsertEntity = new RhsInsertEntity();
rhsInsertEntity.setAge(25);
insert(rhsInsertEntity);
System.out.println("规则 rhs_insert_2 触发");
end
rule "rhs_insert_3"
when
$rhsInsertEntity:RhsInsertEntity(age > 20 )
then
System.out.println("规则 rhs_insert_3 触发");
end
```
第二步:编写单元测试
```java
public void test(){
KieSession kieSession = kieBase.newKieSession();
RhsInsertEntity rhsInsertEntity = new RhsInsertEntity();
rhsInsertEntity.setAge(5);
kieSession.insert(rhsInsertEntity);
kieSession.fireAllRules();
kieSession.dispose();
}
```通过控制台输出可以发现,3个规则都触发了,这是因为首先进行规则匹配时只有第一个规则可以匹配成功,但是在第一个规则中向工作内存中插入了一个数据导致重新进行规则匹配,此时第二个规则可以匹配成功。在第二个规则中同样向工作内存中插入了一个数据导致重新进行规则匹配,那么第三个规则就出发了。
update方法
update方法的作用是更新工作内存中的数据,并让相关的规则重新匹配。(要避免死循环)
第一步:编写规则文件
```java
package rules
import com.mashibing.drools.entity.RhsUpdateEntity
/*
用于测试Drools RHS: update
*/
rule "rhs_update_1"
when
$rhsUpdateEntity:RhsUpdateEntity(age <= 10)
then
$rhsUpdateEntity.setAge(15);
update($rhsUpdateEntity);
System.out.println("规则 rhs_update_1 触发");
end
rule "rhs_update_2"
when
$rhsUpdateEntity:RhsUpdateEntity(age <=20 && age>10)
then
$rhsUpdateEntity.setAge(25);
update($rhsUpdateEntity);
System.out.println("规则 rhs_update_2 触发");
end
rule "rhs_update_3"
when
$rhsUpdateEntity:RhsUpdateEntity(age > 20 )
then
System.out.println("规则 rhs_update_3 触发");
end
```
第二步:编写单元测试
```java
@Test
public void test(){
KieSession kieSession = kieBase.newKieSession();
RhsUpdateEntity rhsUpdateEntity = new RhsUpdateEntity();
rhsUpdateEntity.setAge(5);
kieSession.insert(rhsUpdateEntity);
kieSession.fireAllRules();
kieSession.dispose();
}
```通过控制台的输出可以看到规则文件中定义的三个规则都触发了。
在更新数据时需要注意防止发生死循环。
modify方法
modify方法的作用跟update一样,是更新工作内存中的数据,并让相关的规则重新匹配。只不过语法略有区别 (要避免死循环)
第一步:编写规则文件
```java
package rules
import com.mashibing.drools.entity.RhsModifyEntity
/*
用于测试Drools RHS: modify
*/
rule "rhs_modify_1"
when
$rhsModifyEntity:RhsModifyEntity(age <= 10)
then
modify($rhsModifyEntity){
setAge(15)
}
System.out.println("规则 rhs_modify_1 触发");
end
rule "rhs_modify_2"
when
$rhsModifyEntity:RhsModifyEntity(age <=20 && age>10)
then
modify($rhsModifyEntity){
setAge(25)
}
System.out.println("规则 rhs_modify_2 触发");
end
rule "rhs_modify_3"
when
$rhsModifyEntity:RhsModifyEntity(age > 20 )
then
System.out.println("规则 rhs_modify_3 触发");
end
```
第二步:编写单元测试
```java
@Test
public void test(){
KieSession kieSession = kieBase.newKieSession();
RhsModifyEntity rhsModifyEntity = new RhsModifyEntity();
rhsModifyEntity.setAge(5);
kieSession.insert(rhsModifyEntity);
kieSession.fireAllRules();
kieSession.dispose();
}
```通过控制台的输出可以看到规则文件中定义的三个规则都触发了。
在更新数据时需要注意防止发生死循环。
retract/delete方法
retract方法的作用是删除工作内存中的数据,并让相关的规则重新匹配。
第一步:编写规则文件
```java
package rules
import com.mashibing.drools.entity.RhsRetractEntity
/*
用于测试Drools RHS: retract
*/
rule "rhs_retract_1"
when
$rhsRetractEntity:RhsRetractEntity(age <= 10)
then
// retract($rhsRetractEntity);
System.out.println("规则 rhs_retract_1 触发");
end
rule "rhs_retract_2"
when
$rhsRetractEntity:RhsRetractEntity(age <= 10)
then
System.out.println("规则 rhs_retract_2 触发");
end
```
第二步:编写单元测试
```java
public void test(){
KieSession kieSession = kieBase.newKieSession();
RhsRetractEntity rhsRetractEntity = new RhsRetractEntity();
rhsRetractEntity.setAge(5);
kieSession.insert(rhsRetractEntity);
kieSession.fireAllRules();
kieSession.dispose();
}
```通过控制台输出可以发现,只有第一个规则触发了,因为在第一个规则中将工作内存中的数据删除了导致第二个规则并没有匹配成功。
RHS加强
RHS部分是规则体的重要组成部分,当LHS部分的条件匹配成功后,对应的RHS部分就会触发执行。一般在RHS部分中需要进行业务处理。
在RHS部分Drools为我们提供了一个内置对象,名称就是drools。本小节我们来介绍几个drools对象提供的方法。
halt
halt方法的作用是立即终止后面所有规则的执行。
```java
package rules
import com.mashibing.drools.entity.RhsHaftEntity
/*
用于测试Drools RHS: haft
*/
rule "rhs_haft_1"
when
$rhsHaftEntity:RhsHaftEntity(age <= 10)
then
drools.halt();
System.out.println("规则 rhs_haft_1 触发");
end
rule "rhs_haft_2"
when
$rhsHaftEntity:RhsHaftEntity(age <= 20)
then
System.out.println("规则 rhs_haft_2 触发");
end
```getWorkingMemory
getWorkingMemory方法的作用是返回工作内存对象。
rule "rhs_get_working_memory_1"
when
$rhsDroolsMethodsEntity:RhsDroolsMethodsEntity(age <= 10)
then
System.out.println(drools.getWorkingMemory());
System.out.println("规则 rhs_get_working_memory_1 触发");
endgetRule
getRule方法的作用是返回规则对象。
rule "rhs_rule_2"
when
$rhsDroolsMethodsEntity:RhsDroolsMethodsEntity(age <=20)
then
System.out.println(drools.getRule());
System.out.println("规则 rhs_rule_2 触发");
end规则文件编码规范(重要)
我们在进行drl类型的规则文件编写时尽量遵循如下规范:
所有的规则文件(.drl)应统一放在一个规定的文件夹中,如:/rules文件夹
书写的每个规则应尽量加上注释。注释要清晰明了,言简意赅
同一类型的对象尽量放在一个规则文件中,如所有Student类型的对象尽量放在一个规则文件中
规则结果部分(RHS)尽量不要有条件语句,如if(...),尽量不要有复杂的逻辑和深层次的嵌套语句
每个规则最好都加上salience属性,明确执行顺序
Drools默认dialect为"Java",尽量避免使用dialect "mvel"
有状态session和无状态session
无状态session
无状态的KIE会话是一个不使用推理来对事实进行反复修改的会话。在无状态的KIE会话中,来自KIE会话先前调用的数据(先前的会话状态)在会话调用之间被丢弃,而在有状态的KIE会话中,这些数据被保留。一个无状态的KIE会话的行为类似于一个函数,因为它产生的结果是由KIE基础的内容和被传入KIE会话以在特定时间点执行的数据决定的。KIE会话对以前传入KIE会话的任何数据都没有记忆。
使用方法类似如下代码:
@Test
public void testStatelessSession() {
StatelessKieSession statelessKieSession = kieBase.newStatelessKieSession();
List<Command> cmds = new ArrayList<>();
KieSessionEntity kieSessionEntity = new KieSessionEntity();
kieSessionEntity.setNum(10);
kieSessionEntity.setValid(false);
cmds.add(CommandFactory.newInsert(kieSessionEntity, "kieSessionEntity"));
statelessKieSession.execute(CommandFactory.newBatchExecution(cmds));
System.out.println(kieSessionEntity);
}简单说来,无状态session执行的时候,不需要调用 fireAllRules(),也不需要执行dispose(),代码执行完execute之后,即销毁所有的数据。
使用场景:比如上述的校验num
验证数据: 比如计算积分,按揭房贷等
路有消息:比如对邮件排序,发送邮件等,行为类的场景
有状态session
有状态的KIE会话是一个使用推理来对事实进行反复修改的会话。在有状态的KIE会话中,来自KIE会话先前调用的数据(先前的会话状态)在会话调用之间被保留,而在无状态的KIE会话中,这些数据被丢弃了。
对比无状态session,有状态session调用fireAllRules()的时候采取匹配规则,就会执行规则匹配,除非遇见dispose()
示例:
数据模型
```java
public class Room {
private String name;
// Getter and setter methods
}
public class Sprinkler {
private Room room;
private boolean on;
// Getter and setter methods
}
public class Fire {
private Room room;
// Getter and setter methods
}
public class Alarm { }
```
规则文件
```java
rule "When there is a fire turn on the sprinkler"
when
Fire($room : room)
$sprinkler : Sprinkler(room == $room, on == false)
then
modify($sprinkler) { setOn(true) };
System.out.println("Turn on the sprinkler for room "+$room.getName());
end
rule "Raise the alarm when we have one or more fires"
when
exists Fire()
then
insert( new Alarm() );
System.out.println( "Raise the alarm" );
end
rule "Cancel the alarm when all the fires have gone"
when
not Fire()
$alarm : Alarm()
then
delete( $alarm );
System.out.println( "Cancel the alarm" );
end
rule "Status output when things are ok"
when
not Alarm()
not Sprinkler( on == true )
then
System.out.println( "Everything is ok" );
end
```
代码
```java
KieSession ksession = kContainer.newKieSession();
String[] names = new String[]{"kitchen", "bedroom", "office", "livingroom"};
Map<String,Room> name2room = new HashMap<String,Room>();
for( String name: names ){
Room room = new Room( name );
name2room.put( name, room );
ksession.insert( room );
Sprinkler sprinkler = new Sprinkler( room );
ksession.insert( sprinkler );
}
ksession.fireAllRules();
```
输出
```none
Console output
> Everything is ok
```
此时还可以继续输入
```
Fire kitchenFire = new Fire( name2room.get( "kitchen" ) );
Fire officeFire = new Fire( name2room.get( "office" ) );
FactHandle kitchenFireHandle = ksession.insert( kitchenFire );
FactHandle officeFireHandle = ksession.insert( officeFire );
ksession.fireAllRules();
```
```
Console output
> Raise the alarm
> Turn on the sprinkler for room kitchen
> Turn on the sprinkler for room office
```
继续输入
```
ksession.delete( kitchenFireHandle );
ksession.delete( officeFireHandle );
ksession.fireAllRules();
```
输出
```
Console output
> Cancel the alarm
> Turn off the sprinkler for room office
> Turn off the sprinkler for room kitchen
> Everything is ok
```使用场景:
- 监测,如监测股票市场并使购买过程自动化
- 诊断,如运行故障查找过程或医疗诊断过程
- 物流,如包裹跟踪和配送供应
- 确保合规性,如验证市场交易的合法性
边栏推荐
- 221. 最大正方形
- 1782. 统计点对的数目 双指针
- 成为比开发硬气的测试人,我都经历了什么?
- Maximum monthly salary of 20K?The average salary is nearly 10,000... What is the experience of working in a Huawei subsidiary?
- 【AcWing 62nd Weekly Game】
- What are the project management tools like MS Project
- Google官方控件ShapeableImageView使用
- 想要写出好的测试用例,先要学会测试设计
- 设置浏览器滚动条样式
- 手把手教你配置Jenkins自动化邮件通知
猜你喜欢
随机推荐
CV-Model [3]: MobileNet v2
Crypto Life, a day in the life of a Web3 project partner
MySQL (6)
计算S=a+aa+…+aa…a
Multiplication, DFS order
PDF 拆分/合并
1782. 统计点对的数目 双指针
数字图像隐写术之卡方分布
VSCode插件:嵌套注释
kotlin中函数作为参数和函数作为返回值实例练习
软件测试报告有哪些内容?
C language applet -- common classic practice questions
leetcode-399: division evaluation
1.非类型模板参数 2.模板的特化 3.继承讲解
Are you still working hard on the limit of MySQL paging?
聚簇索引和非聚簇索引到底有什么区别
I have been working in software testing for 3 years, how did I go from just getting started to automated testing?
Nacos
最高月薪20K?平均薪资近万...在华为子公司工作是什么体验?
PDF split/merge









