续第二天笔记...........
2.6规则体 内置方法
规则文件的RHS
部分的主要作用是通过插入,删除或修改工作内存中的Fact数据,来达到控制规则引擎执行的目的。Drools提供了一些方法可以用来操作工作内存中的数据,操作完成后规则引擎会重新进行相关规则的匹配,原来没有匹配成功的规则在我们修改数据完成后有可能就会匹配成功了。
创建如下实体类:
package com.simia.drools.entity;
import java.util.List;
/**
* 学生
*/
public class Student {
private int id;
private String name;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
2.6.1 update方法
update方法的作用是更新工作内存中的数据,并让相关的规则重新匹配。
第一步:编写规则文件/resources/rules/student.drl,文件内容如下
package student
import com.simia.drools.entity.Student
/*
当前规则文件用于测试Drools提供的内置方法
*/
rule "rule_student_age小于10岁"
when
$s:Student(age < 10)
then
$s.setAge(15);
update($s);//更新数据,导致相关的规则会重新匹配
System.out.println("规则rule_student_age小于10岁触发");
end
rule "rule_student_age小于20岁同时大于10岁"
when
$s:Student(age < 20 && age > 10)
then
$s.setAge(25);
update($s);//更新数据,导致相关的规则会重新匹配
System.out.println("规则rule_student_age小于20岁同时大于10岁触发");
end
rule "rule_student_age大于20岁"
when
$s:Student(age > 20)
then
System.out.println("规则rule_student_age大于20岁触发");
end
第二步:编写单元测试
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
Student student = new Student();
student.setAge(5);
//将数据提供给规则引擎,规则引擎会根据提供的数据进行规则匹配,如果规则匹配成功则执行规则
kieSession.insert(student);
kieSession.fireAllRules();
kieSession.dispose();
通过控制台的输出可以看到规则文件中定义的三个规则都触发了。
在更新数据时需要注意防止发生死循环。
insert方法的作用是向工作内存中插入数据,并让相关的规则重新匹配。
第一步:修改student.drl文件内容如下
package student
import com.simia.drools.entity.Student
/*
当前规则文件用于测试Drools提供的内置方法
*/
rule "rule_student_age等于10岁"
when
$s:Student(age == 10)
then
Student student = new Student();
student.setAge(5);
insert(student);//插入数据,导致相关的规则会重新匹配
System.out.println("规则rule_student_age等于10岁触发");
end
rule "rule_student_age小于10岁"
when
$s:Student(age < 10)
then
$s.setAge(15);
update($s);
System.out.println("规则rule_student_age小于10岁触发");
end
rule "rule_student_age小于20岁同时大于10岁"
when
$s:Student(age < 20 && age > 10)
then
$s.setAge(25);
update($s);
System.out.println("规则rule_student_age小于20岁同时大于10岁触发");
end
rule "rule_student_age大于20岁"
when
$s:Student(age > 20)
then
System.out.println("规则rule_student_age大于20岁触发");
end
第二步:编写单元测试
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
Student student = new Student();
student.setAge(10);
//将数据提供给规则引擎,规则引擎会根据提供的数据进行规则匹配,如果规则匹配成功则执行规则
kieSession.insert(student);
kieSession.fireAllRules();
kieSession.dispose();
通过控制台输出可以发现,四个规则都触发了,这是因为首先进行规则匹配时只有第一个规则可以匹配成功,但是在第一个规则中向工作内存中插入了一个数据导致重新进行规则匹配,此时第二个规则可以匹配成功。在第二个规则中进行了数据修改导致第三个规则也可以匹配成功,以此类推最终四个规则都匹配成功并执行了。
retract方法的作用是删除工作内存中的数据,并让相关的规则重新匹配。
第一步:修改student.drl文件内容如下
package student
import com.simia.drools.entity.Student
/*
当前规则文件用于测试Drools提供的内置方法
*/
rule "rule_student_age等于10岁时删除数据"
/*
salience:设置当前规则的执行优先级,数值越大越优先执行,默认值为0.
因为当前规则的匹配条件和下面规则的匹配条件相同,为了保证先执行当前规则,需要设置优先级
*/
salience 100
when
$s:Student(age == 10)
then
retract($s);//retract方法的作用是删除工作内存中的数据,并让相关的规则重新匹配。
System.out.println("规则rule_student_age等于10岁时删除数据触发");
end
rule "rule_student_age等于10岁"
when
$s:Student(age == 10)
then
Student student = new Student();
student.setAge(5);
insert(student);
System.out.println("规则rule_student_age等于10岁触发");
end
rule "rule_student_age小于10岁"
when
$s:Student(age < 10)
then
$s.setAge(15);
update($s);
System.out.println("规则rule_student_age小于10岁触发");
end
rule "rule_student_age小于20岁同时大于10岁"
when
$s:Student(age < 20 && age > 10)
then
$s.setAge(25);
update($s);
System.out.println("规则rule_student_age小于20岁同时大于10岁触发");
end
rule "rule_student_age大于20岁"
when
$s:Student(age > 20)
then
System.out.println("规则rule_student_age大于20岁触发");
end
第二步:编写单元测试
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
Student student = new Student();
student.setAge(10);
//将数据提供给规则引擎,规则引擎会根据提供的数据进行规则匹配,如果规则匹配成功则执行规则
kieSession.insert(student);
kieSession.fireAllRules();
kieSession.dispose();
通过控制台输出可以发现,只有第一个规则触发了,因为在第一个规则中将工作内存中的数据删除了导致第二个规则并没有匹配成功。
2.7 规则属性
之前已经知道规则体:
rule "ruleName"
attributes
when
LHS
then
RHS
end
此章节针对规则体中的attributes属性....................
属性如下表:
属性名 | 说明 |
---|---|
salience | 指定规则执行优先级 |
dialect | 指定规则使用的语言类型,取值为java和mvel |
enabled | 指定规则是否启用 |
date-effective | 指定规则生效时间 |
date-expires | 指定规则失效时间 |
activation-group | 激活分组,具有相同分组名称的规则只能有一个规则触发 |
agenda-group | 议程分组,只有获取焦点的组中的规则才有可能触发 |
timer | 定时器,指定规则触发的时间 |
auto-focus | 自动获取焦点,一般结合agenda-group一起使用 |
no-loop | 防止死循环 |
2.7.1 enabled属性
取值为true false 默认值为true。
用于指定当前规则是否启用,如果设置值为false,则当前无论是否匹配成功都不触发此规则。
rule "rule_comparison_notMemberOf"
//指定当前规则不可用,当前规则无论是否匹配成功都不会执行
enabled false
when
ComparisonOperatorEntity(names not memberOf list)
then
System.out.println("规则rule_comparison_notMemberOf触发");
end
2.7.2 dialect属性
用于指定当前规则使用的语言类型,取值为java和mvel,默认值为java。
mvel:java语法的表达式语言
像正则表达式一样,有直接支持集合、数组和字符串的操作符。
提供了用来配置和构造字符串的模板语言。
内容包括:属性表达式,布尔表达式,方法调用,变量赋值,函数定义等。
2.7.3 salience属性
用于指定执行优先级,取值为Integer。数值越大越优先执行。每个规则都有一个默认的执行顺序,如果不设置salience属性,规则体的执行顺序由上到下。
举例:
package test.salience
rule "rule_1"
when
eval(true)
then
System.out.println("规则rule_1触发");
end
rule "rule_2"
when
eval(true)
then
System.out.println("规则rule_2触发");
end
rule "rule_3"
when
eval(true)
then
System.out.println("规则rule_3触发");
end
可以从控制台看出执行顺序是按照规则文件中的顺序由上到下执行的。
修改以上drl文件
package testsalience
rule "rule_1"
salience 9
when
eval(true)
then
System.out.println("规则rule_1触发");
end
rule "rule_2"
salience 10
when
eval(true)
then
System.out.println("规则rule_2触发");
end
rule "rule_3"
salience 8
when
eval(true)
then
System.out.println("规则rule_3触发");
end
控制台可以看出是由salience由大到小顺序执行,建议编写规则时使用salience属性明确指定执行优先级。
2.7.4 no-loop属性
用于防止死循环,当规则通过update之类的函数修改了Fact对象时,可能使当前规则再次被激活从而导致死循环。获取类型为Boolean,默认值false。
测试:
第一步:编写resource/rules/noloop.drl
package testnoloop
import com.itheima.drools.entity.Student
/*
此规则文件用于测试no-loop属性
*/
rule "rule_noloop"
when
// no-loop true
$student:Student(age == 25)
then
update($student);//注意此处执行update会导致当前规则重新被激活
System.out.println("规则rule_noloop触发");
end
第二步:编写单元测试
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
Student student = new Student();
student.setAge(25);
//将数据提供给规则引擎,规则引擎会根据提供的数据进行规则匹配,如果规则匹配成功则执行规则
kieSession.insert(student);
kieSession.fireAllRules();
kieSession.dispose();
通过控制台可以看到,由于我们没有设置no-loop属性的值,所以发生了死循环。接下来设置no-loop的值为true再次测试则不会发生死循环。
2.7.5 activation-group属性
activition-group属性是指激活分组,取值为String类型。具有相同分组名称只能有一个规则文件触发。
测试:
编写规则文件
package testactivationgroup
/*
此规则文件用于测试activation-group属性
*/
rule "rule_activationgroup_1"
activation-group "mygroup"
when
then
System.out.println("规则rule_activationgroup_1触发");
end
rule "rule_activationgroup_2"
activation-group "mygroup"
when
then
System.out.println("规则rule_activationgroup_2触发");
end
编写测试单元
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
kieSession.fireAllRules();
kieSession.dispose();
通过控制台可以发现,上面的两个规则因为属于同一个分组,所以只有一个触发了。同一个分组中的多个规则如果都能够匹配成功,具体哪一个最终能够被触发可以通过salience属性确定。
2.7.6 agenda-group属性
agenda-group属性为议程分组,属于另一种可控的规则执行方式。用户可以通过设置agenda-group来控制规则的执行,只有获取焦点的组中的规则才会被触发。
第一步:创建规则文件/resources/rules/agendagroup.drl
package testagendagroup
/*
此规则文件用于测试agenda-group属性
*/
rule "rule_agendagroup_1"
agenda-group "myagendagroup_1"
when
then
System.out.println("规则rule_agendagroup_1触发");
end
rule "rule_agendagroup_2"
agenda-group "myagendagroup_1"
when
then
System.out.println("规则rule_agendagroup_2触发");
end
//========================================================
rule "rule_agendagroup_3"
agenda-group "myagendagroup_2"
when
then
System.out.println("规则rule_agendagroup_3触发");
end
rule "rule_agendagroup_4"
agenda-group "myagendagroup_2"
when
then
System.out.println("规则rule_agendagroup_4触发");
end
第二步:编写单元测试
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
//设置焦点,对应agenda-group分组中的规则才可能被触发
kieSession.getAgenda().getAgendaGroup("myagendagroup_1").setFocus();
kieSession.fireAllRules();
kieSession.dispose();
通过控制台可以看到,只有获取焦点的分组中的规则才会触发。与activation-group不同的是,activation-group定义的分组中只能够有一个规则可以被触发,而agenda-group分组中的多个规则都可以被触发
2.7.7 auto-focus属性
auto-focus属性为自动获取焦点,取值类型为Boolean,默认值为false。一般结合agenda-group属性使用,当一个议程分组未获取焦点时,可以设置auto-focus属性来控制。
第一步:修改/resources/rules/agendagroup.drl文件内容如下
package testagendagroup
rule "rule_agendagroup_1"
agenda-group "myagendagroup_1"
when
then
System.out.println("规则rule_agendagroup_1触发");
end
rule "rule_agendagroup_2"
agenda-group "myagendagroup_1"
when
then
System.out.println("规则rule_agendagroup_2触发");
end
//========================================================
rule "rule_agendagroup_3"
agenda-group "myagendagroup_2"
auto-focus true //自动获取焦点
when
then
System.out.println("规则rule_agendagroup_3触发");
end
rule "rule_agendagroup_4"
agenda-group "myagendagroup_2"
auto-focus true //自动获取焦点
when
then
System.out.println("规则rule_agendagroup_4触发");
end
第二步:编写单元测试
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
kieSession.fireAllRules();
kieSession.dispose();
通过控制台可以看到,设置auto-focus属性为true的规则都触发了。
2.7.8 timer属性
timer属性可以通过定时器的方式指定规则执行的时间,使用方式有两种:
方式一:timer (int: <initial delay> <repeat interval>?)
此种方式遵循java.util.Timer对象的使用方式,第一个参数表示几秒后执行,第二个参数表示每隔几秒执行一次,第二个参数为可选。
方式二:timer(cron: <cron expression>) ----玉米表达式(在Qu)
此种方式使用标准的unix cron表达式的使用方式来定义规则执行的时间。
玉米表达式cron:
- * 表示任何值,如果在分的字段上编写*,表示每分钟都会触发
- , 是个分割符如果秒字段我想20秒和40秒时触发两次就写 20,40
- - 表示一个区间 秒字段5-10 表示 5,6,7,8,9,10
- / 表示递增触发 秒字段 5/10表示5秒开始每隔10秒触发一次
日字段编写1/3表示从每月1日起每隔3天触发一次- ? 表示不确定值, 因为我们在定日期时,一般确定日期就不确定是周几,相反确定周几时就不确定日期
- L 表示last最后的意思,我们可以设置当月的最后一天,就会在日字段用L表示,
周字段使用L表示最后一周,一般会和1-7的数字组合
例如6L表示本月最后一周的周五- W 表示最近的工作日(单纯的周一到周五) 如果日字段编写15W表示
每月15日最近的工作日触发,如果15日是周六就14日触发,如果15日是周日就16日触发 LW通常一起使用,表示本月的最后一个工作日
- # 表示第几个,只能使用在周字段上 6#3表示每月的第三个周五
如果#后面数字写大了,是一个不存在的日期,那就不运行了
适合设计在母亲节或父亲节这样的日期运行
第一步:修改/resources/rules/timer.drl文件内容如下
package testtimer
import java.text.SimpleDateFormat
import java.util.Date
/*
此规则文件用于测试timer属性
*/
rule "rule_timer_1"
timer (5s 2s) //含义:5秒后触发,然后每隔2秒触发一次
when
then
System.out.println("规则rule_timer_1触发,触发时间为:" +
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
end
rule "rule_timer_2"
timer (cron:0/1 * * * * ?) //含义:每隔1秒触发一次
when
then
System.out.println("规则rule_timer_2触发,触发时间为:" +
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
end
第二步:编写单元测试
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
final KieSession kieSession = kieClasspathContainer.newKieSession();
new Thread(new Runnable() {
public void run() {
//启动规则引擎进行规则匹配,直到调用halt方法才结束规则引擎
kieSession.fireUntilHalt();
}
}).start();
Thread.sleep(10000);
//结束规则引擎
kieSession.halt();
kieSession.dispose();
注意:单元测试的代码和以前的有所不同,因为我们规则文件中使用到了timer进行定时执行,需要程序能够持续一段时间才能够看到定时器触发的效果。
2.7.9 date-effctive属性
date-effective属性用于指定规则的生效时间,即只有当前系统时间大于等于设置的时间或者日期规则才有可能触发。默认日期格式为:dd-MMM-yyyy。用户也可以自定义日期格式。
第一步:修改/resources/rules/dateeffctive.drl文件内容如下
package testdateeffective
/*
此规则文件用于测试date-effective属性
*/
rule "rule_dateeffective_1"
date-effective "2020-10-01 10:00"
when
then
System.out.println("规则rule_dateeffective_1触发");
end
第二步:编写单元测试
//设置日期格式
System.setProperty("drools.dateformat","yyyy-MM-dd HH:mm");
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
kieSession.fireAllRules();
kieSession.dispose();
注意:上面的代码需要设置日期格式,否则我们在规则文件中写的日期格式和默认的日期格式不匹配程序会报错。
2.7.10 date-expires属性
date-expires属性用于指定规则的失效时间,即只有当前系统时间小于设置的时间或者日期规则才有可能触发。默认日期格式为:dd-MMM-yyyy。用户也可以自定义日期格式。
第一步:修改/resources/rules/dateexpires.drl文件内容如下
package testdateexpires
/*
此规则文件用于测试date-expires属性
*/
rule "rule_dateexpires_1"
date-expires "2019-10-01 10:00"
when
then
System.out.println("规则rule_dateexpires_1触发");
end
第二步:编写单元测试
//设置日期格式
System.setProperty("drools.dateformat","yyyy-MM-dd HH:mm");
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
kieSession.fireAllRules();
kieSession.dispose();
注意:上面的代码需要设置日期格式,否则我们在规则文件中写的日期格式和默认的日期格式不匹配程序会报错。