目录
前面我们已经知道了规则体的构成如下:
- rule "ruleName"
- attributes
- when
- LHS
- then
- RHS
- end
Drools中提供的属性如下表(部分属性):

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触发");
- end
dialect属性用于指定当前规则使用的语言类型,取值为java和mvel,默认值为java。
注:mvel是一种基于java语法的表达式语言。
虽然mvel吸收了大量的java语法,但作为一个表达式语言,还是有着很多重要的不同之处,以达到更高的效率,比如:mvel像正则表达式一样,有直接支持集合、数组和字符串匹配的操作符。
除了表达式语言外,mvel还提供了用来配置和构造字符串的模板语言。
mvel2.x表达式包含以下部分的内容:属性表达式,布尔表达式,方法调用,变量赋值,函数定义
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属性用于防止死循环,当规则通过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这个属性,可以限制当前规则只会被执行一次,包括当前规则的重复执行不是本身触发的。取值类型为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属性是指激活分组,取值为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来控制规则的执行,只有获取焦点的组中的规则才会被触发。
- 第一步:编写规则文件
-
-
-
- ```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属性为自动获取焦点,取值类型为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 (int:
此种方式遵循java.util.Timer对象的使用方式,第一个参数表示几秒后执行,第二个参数表示每隔几秒执行一次,第二个参数为可选。
方式二:timer(cron:
此种方式使用标准的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属性用于指定规则的生效时间,即只有当前系统时间大于等于设置的时间或者日期规则才有可能触发。默认日期格式为: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属性用于指定规则的失效时间,即只有当前系统时间小于设置的时间或者日期规则才有可能触发。默认日期格式为: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设置中,也需要设置此参数,以保证开发和生产的一致性。
前面章节我们已经知道了一套完整的规则文件内容构成如下:

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
-
- 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查询提供了一种查询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关键字用于在规则文件中定义函数,就相当于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部分是**介于when和then之间的部分**,主要用于模式匹配,只有匹配结果为true时,才会触发RHS部分的执行。本章节我们会针对LHS部分学习几个新的用法。
复合值限制是指超过一种匹配值的限制条件,类似于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用于规则体的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用于判断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的作用与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`部分的主要作用是通过插入,删除或修改工作内存中的Fact数据,来达到控制规则引擎执行的目的。Drools提供了一些方法可以用来操作工作内存中的数据,操作完成后规则引擎会重新进行相关规则的匹配,原来没有匹配成功的规则在我们修改数据完成后有可能就会匹配成功了。
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方法的作用是更新工作内存中的数据,并让相关的规则重新匹配。(要避免死循环)
- 第一步:编写规则文件
-
- ```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方法的作用跟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方法的作用是删除工作内存中的数据,并让相关的规则重新匹配。
- 第一步:编写规则文件
-
- ```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部分是规则体的重要组成部分,当LHS部分的条件匹配成功后,对应的RHS部分就会触发执行。一般在RHS部分中需要进行业务处理。
在RHS部分Drools为我们提供了一个内置对象,名称就是drools。本小节我们来介绍几个drools对象提供的方法。
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方法的作用是返回工作内存对象。
- 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 触发");
- end
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
无状态的KIE会话是一个不使用推理来对事实进行反复修改的会话。在无状态的KIE会话中,来自KIE会话先前调用的数据(先前的会话状态)在会话调用之间被丢弃,而在有状态的KIE会话中,这些数据被保留。一个无状态的KIE会话的行为类似于一个函数,因为它产生的结果是由KIE基础的内容和被传入KIE会话以在特定时间点执行的数据决定的。KIE会话对以前传入KIE会话的任何数据都没有记忆。
使用方法类似如下代码:
- @Test
- public void testStatelessSession() {
- StatelessKieSession statelessKieSession = kieBase.newStatelessKieSession();
- List
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
name2room = new HashMap(); - 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
- ```
使用场景:
- 监测,如监测股票市场并使购买过程自动化
- 诊断,如运行故障查找过程或医疗诊断过程
- 物流,如包裹跟踪和配送供应
- 确保合规性,如验证市场交易的合法性