语法基础
package
package定义在规则文件的首行,是规则文件的三大模块之一。
package表示规则的逻辑路径。建议在定义的时候和物理路径相同。但这并不是必须的。可以不同。不过强烈建议。
package包含 import、global、funcation、query、rule、EOF。
rule是规则文件的核心。前面学习的规则属性就是rule中的内容。
在前面的示例中,我们配置过kmodule.xml这样的一个文件,在这个文件中KieBase元素设置了packages路径,来表示当前路径下的所有规则文件,像规则文件、决策表、领域语言文件等等都会被加入到规则库中。但是当前路径下子文件夹的规则文件并没有包含在当前的规则库中。
package参数实际上是一个命名空间,没有去关联文件或者文件夹。因此,可以有多个规则目录为规则库构建源组合规则。有一个顶级的package配置。所有的规则都在其控制之下。
虽然生命在不同名称下的资源不可能合并成一个包,但是单个规则库可以用多个包来构建它。
packages可以设置多个路径,通过逗号来分割。
创建规则内容:
1 package rules.isPackage 2 rule "testRuleNameOnly" 3 when 4 eval(true); 5 then 6 System.out.println("testRuleNameOnly say hello"); 7 end 8 rule "testRuleNameOnly" 9 when 10 eval(true); 11 then 12 System.out.println("testRuleNameOnly say hello"); 13 end
在配置文件中继续添加:
<kbase name="isPackage" packages="rules.isPackage"> <ksession name="isPackge"/> </kbase>
看下运行结果:
2019-08-07 22:58:12 [ERROR]org.drools.....KieProject - Unable to build KieBaseModel:isPackage [8,0]: Duplicate rule name: testRuleNameOnly Rule Compilation error : [Rule name='testRuleNameOnly'] rules/isPackage/Rule_testRuleNameOnly670538843.java (3:109) : The type Rule_testRuleNameOnly670538843 is already defined java.lang.RuntimeException: Error while creating KieBase[Message [id=1, kieBase=isPackage, level=ERROR, path=D:\JavaDev\workspace\DroolsProject\BaseProject\target\classes\rules.isPackage\package.drl, line=8, column=0 text=Duplicate rule name: testRuleNameOnly], Message [id=2, kieBase=isPackage, level=ERROR, path=D:\JavaDev\workspace\DroolsProject\BaseProject\target\classes\rules.isPackage\package.drl, line=8, column=0 text=Rule Compilation error The type Rule_testRuleNameOnly670538843 is already defined]] at org.drools.compiler.kie.builder.impl.KieContainerImpl.getKieBase(KieContainerImpl.java:363) at org.drools.compiler.kie.builder.impl.KieContainerImpl.newKieSession(KieContainerImpl.java:519) at org.drools.compiler.kie.builder.impl.KieContainerImpl.newKieSession(KieContainerImpl.java:487) at com.packages.RulePackage.RulePackage(RulePackage.java:14) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
很是明显的一个错误,提示规则文件代码rule名称重复了。修改名称后就可以正常运行。这个时候再添加一个规则文件,内容如下:
package rules.isPackage2 rule "testRuleNameOnly" when eval(true); then System.out.println("testRuleNameOnly say hello"); end
和之前的例子中区别就是package参数是不相同的。继续看下结果:
2019-08-07 23:02:41 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES testRuleNameOnly say hello testRuleNameOnly say hello testRuleNameOnly say hello 2019-08-07 23:02:41 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING 2019-08-07 23:02:41 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE 总共执行了3条规则 2019-08-07 23:02:41 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED
从结果中可以看出,同一个物理路径下的规则相关文件都会被加载到规则苦衷,不同规则文件中不同的package会影响规则名称的定义。
继续创建一个规则文件,测试一下子目录下的规则文件是不是会被加载。
创建规则内容:
package rules.isPackage.package2; rule "testRuleNameOnly" when eval(true); then System.out.println("testRuleNameOnly say hello"); end
看下结果:
2019-08-07 23:06:32 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES testRuleNameOnly say hello testRuleNameOnly say hello testRuleNameOnly say hello 2019-08-07 23:06:32 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING 2019-08-07 23:06:32 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE 总共执行了3条规则 2019-08-07 23:06:32 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED
结果是没有任何变化的。说明了规则库不会去加载子目录下的规则相关文件。
global
全局变量,由关键字global class name组成。 class是很随意的,能够为规则提供操作数据或者服务等功能。特别实在规则RHS部分中使用程序提供的服务,例如,添加日志,发送邮件,操作数据表等。
global全局变量与Fact对象有区别,不会因为值的改变而影响到规则的再次激活。
创建规则内容如下:
package rules.isPackage rule "testRuleNameOnly" when eval(true); then System.out.println("testRuleNameOnly say hello"); end rule "testRuleNameOnly2" when eval(true); then System.out.println("testRuleNameOnly say hello"); end
创建调用代码:
@Test public void testActivationGroup() { KieServices kss = KieServices.Factory.get(); KieContainer kc = kss.getKieClasspathContainer(); KieSession ks = kc.newKieSession("isGlobal"); ks.setGlobal("count", 2019); int count = ks.fireAllRules(); System.out.println("总共执行了" + count + "条规则"); ks.dispose(); }
看下执行结果:
1 globalupdate1 count:2019 2 globalupdate1 count:10 3 globalupdate2 count:2019 4 2019-08-08 23:19:55 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING 5 2019-08-08 23:19:55 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE 6 总共执行了2条规则
结论:当前规则体中修改的值包装类的全局变量只会影响到当前规则体。
如果多个package使用相同标识声明的全局变量,那么他们的类型必须是相同的,并且它们所有引用都是相同的全局变量。
创建规则:
package rules.isGlobal; global java.lang.Boolean count; rule "global2" when then System.out.println("global2 count:" + count); end
执行结果会报错如下:
ule Compilation error : [Rule name='globalupdate1'] rules/isGlobal/Rule_globalupdate1174728079.java (8:445) : Type mismatch: cannot convert from int to Boolean
重新编写文件内容如下:
package rules.isGlobal; //global java.lang.Boolean count; global java.lang.Integer count rule "global2" when then System.out.println("global2 count:" + count); end
再次执行后,不会报错。
global功能:
1.全局变量定义成常量或者包装类型时,这个值对于整个规则而言是不变的。但是如果在同一段规则代码中改变了global值,那么其作用只是针对这段规则代码而言,使用的则是被修改后的global值。对其他规则代码或者元素节点中的global都不会有任何的影响。也可以这样去说,他是当前规则代码或其他元素节点中的global副本。规则内部不会影响全局的使用。
global变量作为全局变量放在规则中,虽然说全局变量的值是不可以变化的。但是并不建议用于数据之间的共享。因为,针对不同类型全局变量中的内容也可能会发生变化。
2.全局变量定义为集合类或者JavaBean时,在规则体RHS部分中进行修改,则规则库或Java代码中的值都是会发生变化的。这说明了全局变量并非不可变的值。如果在多个地方使用并修改了全局变量,可能就会导致结果非常奇怪了。
query查询
以query开头,以end结束。其中包含query name,查询参数是可以选择的,多个参数以逗号为分隔符。需要注意的是查询是一种条件匹配的方式,所以它只包含LHS部分。所以不需要when 或者then。如何判断条件是否正确,可以通过Java代码进行获取。查询中的name在当前规则库的逻辑路径下是唯一的,和规则名称的约束是一样的。
创建规则示例:
package rules.isQuery; import com.entity.Person; query "person age is 30" person:Person(age == 30); end
添加配置文件:
<kbase name="isQuery" packages="rules.isQuery"> <ksession name="isQuery"/> </kbase>
创建测试调用的代码:
@Test public void testQuery() { KieServices kss = KieServices.Factory.get(); KieContainer kc = kss.getKieClasspathContainer(); KieSession ks = kc.newKieSession("isQuery"); Person person1 = new Person("张三", "35"); Person person2 = new Person("李四", "30"); Person person3 = new Person("王五", "50"); ks.insert(person1); ks.insert(person2); ks.insert(person3); QueryResults queryResults = ks.getQueryResults("person age is 30"); for (QueryResultsRow q : queryResults) { Person person = (Person) q.get("person"); System.out.println("输出符合查询条件的对象的name是:" + person.getName()); } ks.dispose(); }
看一下输出的结果:
2019-08-09 22:27:08 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now EXECUTING_TASK 2019-08-09 22:27:08 [DEBUG]org.drools...DefaultAgenda - State was EXECUTING_TASK is now INACTIVE 2019-08-09 22:27:08 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now EXECUTING_TASK 2019-08-09 22:27:08 [DEBUG]org.drools...DefaultAgenda - State was EXECUTING_TASK is now INACTIVE 2019-08-09 22:27:08 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now EXECUTING_TASK 2019-08-09 22:27:08 [DEBUG]org.drools...DefaultAgenda - State was EXECUTING_TASK is now INACTIVE 输出符合查询条件的对象的name是:李四 2019-08-09 22:27:08 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED
是符合预期的。规则文件中没有使用任何的规则体,query的目的只是为了判断insert到规则中的Fact对象是否满足条件。query非常简单,判断Person属性是否满足age==30,如果满足,就返回一个集合。
另外,query是可以添加参数的。
创建规则内容如下:
package rules.isQuery; import com.entity.Person; query "person age is 30 and name is 张小三" (String $name) person:Person(name == $name, age == 30); end
创建调用端的测试代码示例:
@Test public void testQuery2() { KieServices kss = KieServices.Factory.get(); KieContainer kc = kss.getKieClasspathContainer(); KieSession ks = kc.newKieSession("isQuery"); Person person1 = new Person("张三", "35"); Person person2 = new Person("李四", "30"); Person person3 = new Person("王五", "50"); Person person4 = new Person("张小三", "30"); ks.insert(person1); ks.insert(person2); ks.insert(person3); ks.insert(person4); Object[] objects = new Object[]{"张小三"}; QueryResults queryResults = ks.getQueryResults("person age is 30 and name is 张小三", objects); for (QueryResultsRow q : queryResults) { Person person = (Person) q.get("person"); System.out.println("输出符合查询条件的对象的name是:" + person.getName()); } ks.dispose(); }
看下运行的结果:
2019-08-09 22:38:00 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now EXECUTING_TASK 2019-08-09 22:38:00 [DEBUG]org.drools...DefaultAgenda - State was EXECUTING_TASK is now INACTIVE 2019-08-09 22:38:00 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now EXECUTING_TASK 2019-08-09 22:38:00 [DEBUG]org.drools...DefaultAgenda - State was EXECUTING_TASK is now INACTIVE 2019-08-09 22:38:00 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now EXECUTING_TASK 2019-08-09 22:38:00 [DEBUG]org.drools...DefaultAgenda - State was EXECUTING_TASK is now INACTIVE 输出符合查询条件的对象的name是:张小三 2019-08-09 22:38:00 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED
符合预期。
function
function函数的写法是和JavaScript中的函数类似的,规则函数是Java类方法的一种变形,它的参数是非必填的信息。返回值也是非必填的。一个规则使用函数的好处是可以在同一个地方保持所有的逻辑,同一个逻辑路径下的函数是一个全局的函数,如果规则函数发生变化,就意味着所有调用该函数的规则体都发生了变化。
规则中的函数有两种形式,一种是import,可以去引用Java中的静态方法,另一种需要在规则中添加关键字function.
创建规则体内容:
package rules.isFunction; rule "function" when then function01(); System.out.println("函数function02()的返回值" + function02()); function03("张小三"); System.out.println("函数function04()的返回值" + function04("李小四")); end function void function01() { System.out.println("一个无参无返回值的函数"); } function String function02() { System.out.println("一个无参有返回值的函数"); return "Hello"; } function void function03(String name) { System.out.println("一个有参无返回值的函数,输出入参是:" + name); } function String function04(String name) { System.out.println("一个有参没有返回值的函数,输出入参是" + name); return name; }
在kmodule.xml文件中继续添加节点:
<kbase name="isFunction" packages="rules.isFunction"> <ksession name="isFunction"/> </kbase>
创建调用规则的Java客户端代码:
@Test public void testFunction() { KieServices kss = KieServices.Factory.get(); KieContainer kc = kss.getKieClasspathContainer(); KieSession ks = kc.newKieSession("isFunction"); int count = ks.fireAllRules(); System.out.println("总共执行了" + count + "条规则"); ks.dispose(); }
运行结果如下:
2019-08-10 20:49:20 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES 一个无参无返回值的函数 一个无参有返回值的函数 函数function02()的返回值Hello 一个有参无返回值的函数,输出入参是:张小三 一个有参没有返回值的函数,输出入参是李小四 函数function04()的返回值李小四 2019-08-10 20:49:20 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING 2019-08-10 20:49:20 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE 总共执行了1条规则 2019-08-10 20:49:20 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED
function函数和一个Java方法并没有什么区别。
最后来验证下同一个逻辑路径下或者不同路径下函数的访问范围。
1.同一个逻辑路径下
创建规则文件:
package rules.isFunction; rule "function2" when then function01(); System.out.println("函数function02()的返回值" + function02()); function03("张小小"); System.out.println("函数function04()的返回值" + function04("李小小")); end
看下调用的结果:
一个无参无返回值的函数
一个无参有返回值的函数
函数function02()的返回值Hello
一个有参无返回值的函数,输出入参是:张小小
一个有参没有返回值的函数,输出入参是李小小
函数function04()的返回值李小小
一个无参无返回值的函数
一个无参有返回值的函数
函数function02()的返回值Hello
一个有参无返回值的函数,输出入参是:张小三
一个有参没有返回值的函数,输出入参是李小四
函数function04()的返回值李小四
证明了function函数在同逻辑路径下是全局性的。
相反,如果不在同一逻辑路径下,是不可用的。
function函数的第一种形式,是通过Java静态方法的方式。
创建FunctionStatic.java文件
package com.rules.isFunction;
public class FunctionStatic {
public static void testStatic1() {
System.out.println("一个无参无返回值的Java静态方法");
}
public static String testStatic2() {
System.out.println("一个无参有返回值的Java静态方法");
return "Hello";
}
public static void testStatic3(String name) {
System.out.println("一个有参有返回值的Java静态方法,参数是:" + name);
}
public static String testStatic4(String name) {
System.out.println("一个有参有返回值的Java静态方法, 参数是" + name);
return name;
}
}
调用代码:
package rules.isFunction; import function com.rules.isFunction.FunctionStatic.testStatic1; import function com.rules.isFunction.FunctionStatic.testStatic2; import function com.rules.isFunction.FunctionStatic.testStatic3; import function com.rules.isFunction.FunctionStatic.testStatic4; rule "function3" when then testStatic1(); System.out.println("testStatic2()的返回值" + testStatic2()); testStatic3("张小三"); System.out.println("testStatic4()的返回值" + testStatic4("李小四")); end
结果如下:
2019-08-10 21:11:00 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES
一个无参无返回值的Java静态方法
一个无参有返回值的Java静态方法
testStatic2()的返回值Hello
一个有参有返回值的Java静态方法,参数是:张小三
一个有参有返回值的Java静态方法, 参数是李小四
declare声明
declare在规则引擎中的功能主要有两个:一个是声明新类型,二是声明元数据类型。
1.声明新类型 和JavaBean功能一样,但是方式比JavaBean简单。在之前的例子里面,在规则中操作Fact对象都是通过Java代码insert到规则中进行处理。但是有时候,我们不希望去编写JavaBean。在这个时候,使用declare再好不过。
2.声明元数据类型,fact对象中包含了一些特性,这些特性称之为类元信息。如果需要当前的属性长度是固定的,那么就可以在属性声明前添加元数据。一般元数据用于在查询中,复杂的事件处理和属性字段的约束最多。
创建规则内容:
package rules.isDeclare; declare Person name:String age:int end rule "declareInsert" when then insert(new Person("张三", 20)); end rule "declareTest" when $p:Person(name=="张三") then System.out.println("使用declare测试insert后进行操作"); end
在kmodule.xml中添加节点:
<kbase name="isDeclare" packages="rules.isDeclare"> <ksession name="isDeclare"/> </kbase>
创建测试代码:
@Test public void testDeclare() { KieServices kieServices = KieServices.Factory.get(); KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer(); KieSession session = kieClasspathContainer.newKieSession("isDeclare"); int count = session.fireAllRules(); System.out.println("总共执行了" + count + "条规则"); session.dispose(); }
运行结果:
2019-08-11 17:39:37 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES 使用declare测试insert后进行操作 2019-08-11 17:39:37 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING 2019-08-11 17:39:37 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE 总共执行了2条规则 2019-08-11 17:39:37 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED
例子中声明了一个名称是Person的新的Fact类型。定义声明属性类型可以是任意的Java有效类型,并且包括自定义创建的任何其他类,甚至可以是当前规则中已有的声明类型。
在声明一个新的事实类型时,Drools会在编译时生成实现表示事实类型的一个Java类的字节码。生成的Java类,将是一个一对一JavaBean类型定义的映射。
因为生成的类是一个简单的Java类,相当于在当前规则中import一个对象,所以在同一个逻辑路径下的规则体中都是可以使用的。和全局变量,函数查询的共享范围都是一致的。
如果在规则文件中声明的类型和import引用的类名相同时,系统会报错。
创建规则内容:
package rules.isDeclare; import com.entity.Person; declare Person name:String age:int end
运行测试代码结果:
New declaration of com.entity.Person does not include field className New declaration of com.entity.Person redeclared field age : existing : java.lang.String vs declared : int New declaration of com.entity.Person can't declare a different set of fields existing : [age, className, name] declared : [age, name] diff : [--className] Rule Compilation error : [Rule name='declareInsert'] rules/isDeclare/Rule_declareInsert725960459.java (7:390) : The constructor Person(String, int) is undefined java.lang.RuntimeException: Error while creating KieBase[Message [id=1, kieBase=isDeclare, level=ERROR, path=D:\JavaDev\workspace\DroolsProject\BaseProject\target\classes\rules.isDeclare\declare.drl, line=4, column=0 text=New declaration of com.entity.Person does not include field className], Message [id=2, kieBase=isDeclare, level=ERROR, path=D:\JavaDev\workspace\DroolsProject\BaseProject\target\classes\rules.isDeclare\declare.drl, line=4, column=0 text=New declaration of com.entity.Person redeclared field age : existing : java.lang.String vs declared : int], Message [id=3, kieBase=isDeclare, level=ERROR, path=D:\JavaDev\workspace\DroolsProject\BaseProject\target\classes\rules.isDeclare\declare.drl, line=4, column=0 text=New declaration of com.entity.Person can't declare a different set of fields existing : [age, className, name] declared : [age, name] diff : [--className]], Message [id=4, kieBase=isDeclare, level=ERROR, path=D:\JavaDev\workspace\DroolsProject\BaseProject\target\classes\rules.isDeclare\declare.drl, line=9, column=0 text=Rule Compilation error The constructor Person(String, int) is undefined]] at org.drools.compiler.kie.builder.impl.KieContainerImpl.getKieBase(KieContainerImpl.java:363) at org.drools.compiler.kie.builder.impl.KieContainerImpl.newKieSession(KieContainerImpl.java:519) at org.drools.compiler.kie.builder.impl.KieContainerImpl.newKieSession(KieContainerImpl.java:487) at com.com.rulesDeclare.RulesDeclare.testDeclare(RulesDeclare.java:14) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
将声明类型中的属性删除,再次执行,就不会报错。原因是规则文件编译冲突,规则并不知道自己到底需要用哪一种引用类型,所以只能是系统报错。
声明类型中还有一个和Java继承相似得功能。也是通过关键字extends。
创建规则内容:
package rules.isDeclare declare PersonEx extends com.entity.Person type : String end rule "declareInsertExt" when then PersonEx p = new PersonEx(); p.setType("1"); p.setAge("20"); p.setName("张三"); insert(p); end rule "declareTestExtends" when $p:PersonEx(name=="张三") then System.out.println("hello" + $p.getName()); end
看下运行的结果:
2019-08-11 18:05:17 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES 使用declare测试insert后进行操作 hello张三 2019-08-11 18:05:17 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING 2019-08-11 18:05:17 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE 总共执行了4条规则 2019-08-11 18:05:17 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED
规则when
规则文件中处理业务的核心在于规则体的灵活使用,简称规则,它的书写规范,和Java语言是相似的。LHS部分是when与then之间的语法。规则属性判断过程,只有在规则LHS中匹配结果是true时,才认为规则成立,才会跳转到RHS部分。如果说LHS部分是空,就表示这个规则永远都是true。当工作空间中Fact对象发生变化时,这个规则呢可能会再次被激活,我们可以使用之前学习过的no-loop属性得到解决。
pattern匹配模式中,pattern表示一种约束条件。看一下它的组织结构:
patternBinding:patternType ( constraints )
匹配模式由零个或者多个约束组成,并且有一个可以选择的模式绑定,在最简单的格式中,没有内部的约束条件,通过一个给定类型的事实就可以进行模式匹配了。要注意下,不必是一些事实对象的实际类,模式可以引用子类,接口,函数等。
引用匹配的对象,可以为一个模式绑定变量,例如:$c,这个前缀符号$其实不是必须的,但是在复杂的规则中非常有用,可以有助于去区别变量和字段。例如:$p:Person(age==30);
模式中的括号内部是所有匹配的地方,约束可以是一个字段,或者是一个约束组,也可以是一个其内部的计算。约束可以使用, && || 符号分割。
和Java中是一样的,比较运算符是有优先级的。进行与或非等运算时要注意短路机制。
对象属性有3种类型的限制,单值限制,复合值限制和多限制。单值限制就是上面所说个那种样子,复合限制类似于数据库中查询语句中的in和not in。
1.复合值限制in not in
创建规则内容:
1 package rules.isIn 2 import com.entity.Person; 3 import com.entity.School; 4 5 rule "inTest" 6 when 7 School($cn:className) 8 Person(className in (5, 6, $cn)) 9 then 10 System.out.println("in test"); 11 end 12 13 rule "notinTest" 14 when 15 School($cn:className) 16 Person(className not in (5, 6, $cn)) 17 then 18 System.out.println("not in test"); 19 end
修改配置文件,继续添加节点:
<kbase name="isIn" packages="rules.isIn"> <ksession name="isIn"/> </kbase>
创建测试调用代码:
1 @Test 2 public void testIn() { 3 4 KieServices kss = KieServices.Factory.get(); 5 KieContainer kc = kss.getKieClasspathContainer(); 6 KieSession ks = kc.newKieSession("isIn"); 7 8 Person person = new Person(); 9 person.setClassName("3"); 10 ks.insert(person); 11 School school = new School(); 12 school.setClassName("3"); 13 ks.insert(school); 14 Person person2 = new Person(); 15 person2.setClassName("4"); 16 ks.insert(person2); 17 int count = ks.fireAllRules(); 18 System.out.println("总共执行了" + count + "条规则"); 19 ks.dispose(); 20 21 22 }
最后看下运行的效果:
1 2019-08-12 23:32:14 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES 2 in test 3 not in test 4 2019-08-12 23:32:14 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING 5 2019-08-12 23:32:14 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE 6 总共执行了2条规则 7 2019-08-12 23:32:14 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED
2.条件元素eval
格式:eval(expression)
条件元素eval是在最开始的示例中就列举过的。它可以代表任何语义的代码。并且返回一个boolean类型,也可以绑定在规则LHS中的变量,函数或者直接写常量进行比较。但是在实际编码的过程中,尽量少用eval作为比较符,因为会导致引擎的性能问题。
3.条件元素not
格式:not(conditionalElement)
条件元素not是判断在工作内存中是否还存在某一个值,当not ec成立时就表示当前工作内存中不存在ec,可以认为是一定没有这个值。
创建规则体内容:
package rules.isNot import com.entity.Person; rule "testNot" when not Person(); then System.out.println("测试Person一定不再工作内存中"); end rule "testNotx2" when not(not Person()); then System.out.println("测试Person一定在工作内存中"); end
在配置文件中节点中添加:
<kbase name="isNot" packages="rules.isNot"> <ksession name="isNot"/> </kbase>
测试代码:
@Test public void testIn() { KieServices kss = KieServices.Factory.get(); KieContainer kc = kss.getKieClasspathContainer(); KieSession ks = kc.newKieSession("isIn"); Person person = new Person(); person.setClassName("3"); ks.insert(person); School school = new School(); school.setClassName("3"); ks.insert(school); Person person2 = new Person(); person2.setClassName("4"); ks.insert(person2); int count = ks.fireAllRules(); System.out.println("总共执行了" + count + "条规则"); ks.dispose(); }
看下运行结果:
2019-08-13 22:31:02 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES 测试Person一定在工作内存中 2019-08-13 22:31:02 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING 2019-08-13 22:31:02 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE 总共执行了1条规则 2019-08-13 22:31:02 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED
符合预期。
4.条件元素 exists
格式:exists(conditionalElement)
条件元素exists的功能是和not相反的,指的是工作内存中是否存在某一个东西,可以认为是至少有一个。
创建规则内容:
package rules.isExists import com.entity.Person; rule "testExists" when exists Person(); then System.out.println("测试Person一定在工作内存中"); end rule "testExistsx2" when not (exists Person()); then System.out.println("测试Person一定不在工作内存中"); end
创建客户端调用代码:
@Test public void testExists() { KieServices kss = KieServices.Factory.get(); KieContainer kc = kss.getKieClasspathContainer(); KieSession ks = kc.newKieSession("isNot"); Person person = new Person(); person.setClassName("1"); person.setAge("35"); ks.insert(person); int count = ks.fireAllRules(); System.out.println("总共执行了" + count + "条规则"); ks.dispose(); }
看下运行结果:
2019-08-13 22:39:43 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES 测试Person一定在工作内存中 2019-08-13 22:39:43 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING 2019-08-13 22:39:43 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE 总共执行了1条规则 2019-08-13 22:39:43 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED
符合预期。
条件元素from
结构:pattern from expression
条件元素from很有意思,可以指定任意的资源,用于LHS部分的数据匹配,也可以用来对集合进行遍历。还可以用来对Java服务进行访问并且对结果进行遍历。还有global全局变量也可以提供Java服务。
创建规则内容:
package rules.isFrom import com.entity.Person import com.entity.School rule "testFrom" when $p:Person($ps:school) $s:School(className=="1") from $ps then System.out.println("test from"); end
修改配置文件:
<kbase name="isFrom" packages="rules.isFrom"> <ksession name="isFrom"/> </kbase>
测试代码:
@Test public void testFrom() { KieServices kss = KieServices.Factory.get(); KieContainer kc = kss.getKieClasspathContainer(); KieSession ks = kc.newKieSession("isFrom"); Person person = new Person(); person.setAge("30"); person.setName("张三"); person.setSchool(new School("1")); ks.insert(person); ks.insert(new School("2")); int count = ks.fireAllRules(); System.out.println("总共执行了" + count + "条规则"); ks.dispose(); }
运行结果:
2019-08-14 22:37:36 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES test from 2019-08-14 22:37:36 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING 2019-08-14 22:37:36 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE 总共执行了1条规则 2019-08-14 22:37:36 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED
上面的规则内容其实可以用下面的这种写法来替代:
rule "testFrom2" when $p:Person() $s:School(className=="1") from $p.school then System.out.println("test from2"); end
条件元素from支持对象源,会返回一个对象集合,在这种情况下,from将会遍历集合中所有的对象,并分别匹配它们中每一个对象值。
创建规则内容:
rule "testFrom3" when $s:School() $p:Person(className=="1") from $s.classNameList then System.out.println("testFrom3" + $p.getName()); end
创建调用端:
@Test public void testFromList() { KieServices kss = KieServices.Factory.get(); KieContainer kc = kss.getKieClasspathContainer(); KieSession ks = kc.newKieSession("isFrom"); Person person = new Person(); person.setName("张三"); person.setClassName("1"); Person person2 = new Person(); person2.setName("李四"); person2.setClassName("1"); Person person3 = new Person(); person3.setName("王五"); person3.setClassName("2"); Person person4 = new Person(); person4.setName("赵六"); person4.setClassName("1"); School school = new School(); List<Person> classNameList = new ArrayList(); classNameList.add(person); classNameList.add(person2); classNameList.add(person3); classNameList.add(person4); school.setClassNameList(classNameList); ks.insert(school); int count = ks.fireAllRules(); System.out.println("总共执行了" + count + "条规则"); ks.dispose(); }
运行后结果:
2019-08-14 22:56:37 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES testFrom3赵六 testFrom3李四 testFrom3张三 2019-08-14 22:56:37 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING 2019-08-14 22:56:37 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE 总共执行了3条规则 2019-08-14 22:56:37 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED
此外,要注意在使用属性时,特别是与lock-on-active规则属性联合使用时,因为这个属性可以产生不一样的效果。
最后,from不仅可以在对象,集合中应用,还可以在函数中应用。
条件元素collect
pattern 'from' 'collect' (pattern from collect accumulate)
条件元素collect需要结合from元素来使用。from是用来遍历的,而 from collect是用来汇总的。collect后的参数中还可以匹配 遍历 收集 统计的功能。
创建规则体内容:
package rules.isCollect import com.entity.Person import com.entity.School import java.util.ArrayList rule "test from collect" when $al: ArrayList() from collect($p:Person(className=="1")) then System.out.println("test from collect array list size:" + $al.size()); end rule "test from collect pattern" when $al: ArrayList(size>=3) from collect($p:Person(className=="1")) then System.out.println("test from collect array list size:" + $al.size()); end
修改kmodule.xml配置文件:
<kbase name="isCollect" packages="rules.isCollect"> <ksession name="isCollect"/> </kbase>
创建测试代码:
@Test public void testFromCollect() { KieServices kss = KieServices.Factory.get(); KieContainer kc = kss.getKieClasspathContainer(); KieSession ks = kc.newKieSession("isCollect"); Person person = new Person(); person.setName("张三"); person.setClassName("1"); ks.insert(person); Person person2 = new Person(); person2.setName("李四"); person2.setClassName("1"); ks.insert(person2); Person person3 = new Person(); person3.setName("王五"); person3.setClassName("2"); ks.insert(person3); Person person4 = new Person(); person4.setName("赵流"); person4.setClassName("1"); ks.insert(person4); int count = ks.fireAllRules(); System.out.println("总共执行了" + count + "条规则"); ks.dispose(); }
结果:
2019-08-15 23:38:12 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES test from collect array list size:3 test from collect array list size:3 2019-08-15 23:38:12 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING 2019-08-15 23:38:12 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE 总共执行了2条规则 2019-08-15 23:38:12 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED
collect参数中使用from。添加规则体:
rule "test from collect from" when $s:School() $al: ArrayList(size>=3) from collect($p:Person(className=="1") from $s.classNameList) then System.out.println("test from collect array list size:" + $al.size()); end
创建测试端调用代码:
@Test public void testFromCollectFrom() { KieServices kss = KieServices.Factory.get(); KieContainer kc = kss.getKieClasspathContainer(); KieSession ks = kc.newKieSession("isCollect"); Person person = new Person(); person.setName("张三"); person.setClassName("1"); Person person2 = new Person(); person2.setName("李四"); person2.setClassName("1"); Person person3 = new Person(); person3.setName("王五"); person3.setClassName("2"); Person person4 = new Person(); person4.setName("赵流"); person4.setClassName("1"); School school = new School(); List classNameList= new ArrayList(); classNameList.add(person); classNameList.add(person2); classNameList.add(person3); classNameList.add(person4); school.setClassNameList(classNameList); ks.insert(school); int count = ks.fireAllRules(); System.out.println("总共执行了" + count + "条规则"); ks.dispose(); }
运行结果:
2019-08-15 23:43:16 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES test from collect array list size:0 test from collect array list size:3 2019-08-15 23:43:16 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING 2019-08-15 23:43:16 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE 总共执行了2条规则 2019-08-15 23:43:16 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED
符合预期。
◆ 条件元素规则的继承
条件元素规则继承与java相似。因为Drools是以规则引擎是基于java开发的开源技术。在编写规则时,大部分的操作都离不开使用Java脚本对一类fact对象进行操作。同时Drools中语法也存在继承的逻辑关系,其目的是为了减少相同代码的出现,从而方便代码的优化和管理。
创建规则内容:
package rules.isExtends import com.entity.Person import com.entity.School rule "test extends No1" when $p:Person(name == "张小三") then System.out.println("输出张小三"); end rule "test extends No2" extends "test extends No1" when $s:School(className == "1") then System.out.println("输出张三和1"); end
修改配置文件:
<kbase name="isExtends" packages="rules.isExtends"> <ksession name="isExtends"/> </kbase>
创建测试代码:
@Test public void testExtends() { KieServices kss = KieServices.Factory.get(); KieContainer kc = kss.getKieClasspathContainer(); KieSession ks = kc.newKieSession("isExtends"); Person person = new Person(); person.setName("张小三"); School school = new School(); school.setClassName("1"); ks.insert(person); ks.insert(school); int count = ks.fireAllRules(); System.out.println("总共执行了" + count + "条规则"); ks.dispose(); }
运行结果:符合预期
2019-08-16 08:45:11 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES 输出张小三 输出张三和1 2019-08-16 08:45:11 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING 2019-08-16 08:45:11 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE 总共执行了2条规则 2019-08-16 08:45:11 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED
kmodule配置说明
kmodule是非常重要的配置文件。它关系到规则库是否可以成功的创建以及KieSession的正确使用。将kmodule.xml文件放到src/main/resources/META-INF/文件夹下,内容如下:
<kbase name="rule" packages="rules.rulesHello"> <ksession name="testHelloWorld"/> </kbase>
name:
必填,值类型String
用于从KieContainer检索这个KieBase的名称。是规则库的名称,又是唯一的强制属性。最好规范命名,见名知意。
packages:
非必填,值类型 String
默认的情况下,加载资源文件夹中所有Drools相关文件,这个属性目的是将当前路径下的规则文件在KieBase中编译。仅仅限于指定的软件包路径下的所有文件。这个值是可以多个的。中间用逗号分隔即可。如果路径是子文件,就必须用小数点进行区别,类似Java包。
includes:
非必填,值类型
指一个KieBase被另一个KieBase2的配置中设置了includes='KieBase1',那么KieBase2就有了KieBase1所有资源了,当然这个值也可以是多个,中间任然使用逗号分隔开,有点类似继承的意思。
default:
非必填,值类型 true/false
用来表示当前KieBase是否是次模块的默认值,因此可以从KieContainer中创建,而不会传递任何名称。每个模块中最多只能有一个默认的KieBase,默认值是false.
equalsBehavior
非必填 值类型 Indentity/equality
将新Fact事实对象插入到工作内存中时,定义了Drools的操作,使用了身份属性,那么它会创建一个新的FactHandle,除非相同的对象不存在与工作内存中,而只有新插入的对象不存在与工作内从中,而只有新插入的对象与已存在的事实对象不相等时才会相同。
eventProcessingMode
非必填 值类型 Cloud/stream
当以云模式编译时,KieBase将事实视为正常事实,而在流模式下可以对其进行实践推理。
declarativeAgenda
菲必填 值类型Disabled enabled
定义声明议程是否启用。
KieSession属性声明
name
必填 值类型 string
KieSession的名称。用于从KieContainer中获取KieSession。唯一一个强制必填的属性。用于指定操作规则的会话。
type
非必填 值类型 stateful/stateless 指当前KieSession的类型是有状态还是没有状态的,默认不指定为有状态的。
default
非必填 值类型 true/false
定义这个KieSession是否是个模块的默认值,如果这个值是true,就可以从KieContainer中创建,而不会传递任何名称。在每个模块中,每个类型最多可以有一个默认的KieSession,且是有状态。
clockType
非必填 realtime/seudo
定义事件时间戳是有系统时钟还是由应用程序控制的seudo时钟确定。这个时钟对于单元测试时间规则特别有用。
beiefSystem
非必填 simple/tms/defeasible
定义KieSession使用的beiefSystem的类型。
Spring整合Drools
1.在pom.xml文件中引入相应的jar包
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>DroolsProject</artifactId> <groupId>com.sunlei</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>DroolsSpring</artifactId> <properties> <spring.version>4.2.6.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>org.kie</groupId> <artifactId>kie-spring</artifactId> <version>7.10.0.Final</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.1</version> </dependency> </dependencies> <build> <resources> <resource> <directory> ${project.basedir} /src/main/resources </directory> </resource> </resources> </build> </project>
2.创建spring 配置文件,注意其中的节点配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:kie="http://drools.org/schema/kie-spring" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd http://drools.org/schema/kie-spring http://drools.org/schema/kie-spring.xsd"> <kie:kmodule id = "kmodule"> <kie:kbase name="kbase" packages="rules.isString"> <kie:ksession name="ksession"/> </kie:kbase> </kie:kmodule> <bean id="kiePostProcessor" class="org.kie.spring.annotations.KModuleAnnotationPostProcessor"/> </beans>
3.创建规则文件
package rules.isString; rule "测试Spring + Drools规则" when then System.out.println("调用规则" + drools.getRule().getName()); end;
4.创建调用文件
package com.ruleSpring; import org.junit.Test; import org.junit.runner.RunWith; import org.kie.api.cdi.KSession; import org.kie.api.runtime.KieSession; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration({"classpath:Spring.xml"}) public class TestSpring { @KSession("ksession") KieSession ksession; @Test public void runRules() { int count = ksession.fireAllRules(); System.out.println("总共执行了" + count + "条规则"); ksession.dispose(); } }
5.运行结果
2019-08-22 23:11:47 [DEBUG]org.drools.....ClasspathKieProject - KieModule URL type=file url=/D:/JavaDev/workspace/DroolsProject/DroolsSpring/target/classes 2019-08-22 23:11:47 [INFO ]org.kie..KModuleBeanFactoryPostProcessor - Adding KieModule from /D:/JavaDev/workspace/DroolsProject/DroolsSpring/target/classes to repository. 2019-08-22 23:11:47 [INFO ]org.drools.....KieRepositoryImpl - KieModule was added: FileKieModule[releaseId=com.sunlei:DroolsSpring:1.0-SNAPSHOT,file=\JavaDev\workspace\DroolsProject\DroolsSpring\target\classes] 2019-08-22 23:11:47 [DEBUG]org.drools.....KieRepositoryImpl - Cannot load a KieRepositoryScanner, using the DummyKieScanner 2019-08-22 23:11:47 [ERROR]org.drools.....KieRepositoryImpl - Cannot load artifact com.sunlei:DroolsSpring:1.0-SNAPSHOT. You need kie-ci on the classpath to perform this operation 2019-08-22 23:11:48 [WARN ]org.drools.....KieProject - No files found for KieBase kbase, searching folder \JavaDev\workspace\DroolsProject\DroolsSpring\target\classes 2019-08-22 23:11:48 [ERROR]org.drools.....KieRepositoryImpl - Cannot load artifact com.sunlei:DroolsSpring:1.0-SNAPSHOT. You need kie-ci on the classpath to perform this operation 2019-08-22 23:11:48 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES 2019-08-22 23:11:48 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING 2019-08-22 23:11:48 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE 总共执行了0条规则 2019-08-22 23:11:48 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED
能够成功运行,但是运行结果是错误的。暂时还不知道为什么。。。。。。
规则then
rule是then 与end之间的部分,是规则体中的重要组成部分之一。主要用来处理相关的业务。RHS是真正做事的部分,业务操作时,基本都是通过操作Fact事实对象,并将修改过或已经得到的结果返回Java代码,并进行处理。
RHS部分是规则的结果或行动部分的通用名称,这个部分包含要执行操作的列表,RHS部分如果再次进行条件判断或执行其他语法显然是不好的。LHS部分提供了相关的判断。
一般来说,RHS部分是规则体中的最小操作。只有规则体LHS部分全部满足条件时才会执行这部分。规则体的RHS部分也应保持较小,从而去提高规则的可读性和可维护性,如果发现RHS部分中需要调用其他语言或添加条件,那么就应该将这个规则分解成多个规则。RHS部分的主要目的是插入,删除,修改工作内存数据(Fact事实对象)。
update(Fact事实对象):告诉引擎一个对象已经改变了(一个绑定在LHS部分上的引用,即$p:Person中的$p),修改成功后在工作内存中会已经发生变化。可能会导致规则再次被激活。注意只有在真正将工作内存中的值改变时,其他规则体才能正常对变化后的Fact事实对象进行判断操作。
insert(Fact事实对象):将一个新的Fact事实对象放入到工作内存中,它不仅可以操作引用的Fact事实对象,还可以操作declare声明的Fact事实对象。
insertLogical(new Object()):和insert是相似的,但是当没有更多的事实来支持当前触发规则的LHS部分时,这个Fact事实对象将会被自动删除。
delete(handle):从工作内存中删除一个Fact事实对象,和update是相似的,都是通过引用LHS部分上绑定的值,
update是去修改Fact事实对象的一种方式,Drools还提供了另外的一种方式,modify,它和update的功能是一样的。但是写法上有很大的不同。
SpringBoot + Drools(1)
maven项目创建,pom.xml中文件如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <parent> 6 <artifactId>Code</artifactId> 7 <groupId>com.sunlei</groupId> 8 <version>1.0-SNAPSHOT</version> 9 </parent> 10 <modelVersion>4.0.0</modelVersion> 11 12 <artifactId>DroolsSpringBootTest</artifactId> 13 14 <properties> 15 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 16 <drools.version>7.10.0.Final</drools.version> 17 </properties> 18 19 <dependencies> 20 <!--start drools --> 21 <dependency> 22 <groupId>org.drools</groupId> 23 <artifactId>drools-core</artifactId> 24 <version>${drools.version}</version> 25 </dependency> 26 27 <dependency> 28 <groupId>org.drools</groupId> 29 <artifactId>drools-compiler</artifactId> 30 <version>${drools.version}</version> 31 </dependency> 32 33 <dependency> 34 <groupId>org.kie</groupId> 35 <artifactId>kie-spring</artifactId> 36 <version>${drools.version}</version> 37 </dependency> 38 39 <dependency> 40 <groupId>org.kie</groupId> 41 <artifactId>kie-internal</artifactId> 42 <version>${drools.version}</version> 43 </dependency> 44 45 <dependency> 46 <groupId>org.drools</groupId> 47 <artifactId>drools-templates</artifactId> 48 <version>${drools.version}</version> 49 </dependency> 50 <!--end drools --> 51 52 <dependency> 53 <groupId>junit</groupId> 54 <artifactId>junit</artifactId> 55 <version>4.12</version> 56 </dependency> 57 58 <!--Spring boot start --> 59 <dependency> 60 <groupId>org.springframework.boot</groupId> 61 <artifactId>spring-boot-starter-web</artifactId> 62 <!-- 如果要配置log4j2, 就要先去除logging包 --> 63 <exclusions> 64 <exclusion> 65 <groupId>org.springframework.boot</groupId> 66 <artifactId>spring-boot-starter-logging</artifactId> 67 </exclusion> 68 </exclusions> 69 </dependency> 70 71 <dependency> 72 <groupId>org.springframework.boot</groupId> 73 <artifactId>spring-boot-starter-test</artifactId> 74 <scope>test</scope> 75 </dependency> 76 77 <dependency> 78 <groupId>org.springframework.boot</groupId> 79 <artifactId>spring-boot-starter-data-redis</artifactId> 80 </dependency> 81 82 <dependency> 83 <groupId>org.springframework.boot</groupId> 84 <artifactId>spring-boot-starter-log4j2</artifactId> 85 </dependency> 86 87 <!--Spring boot end --> 88 89 </dependencies> 90 91 <build> 92 <plugins> 93 <plugin> 94 <groupId>org.springframework.boot</groupId> 95 <artifactId>spring-boot-maven-plugin</artifactId> 96 <configuration> 97 <fork>true</fork> 98 </configuration> 99 </plugin> 100 </plugins> 101 </build> 102 </project>
在resources文件夹下创建规则文件:mytest.drl文件如下:
1 package rules 2 import com.entity.Person 3 rule "测试SpringBootDrools" 4 when 5 then 6 System.out.println("测试SpringBootDrools"); 7 end 8 9 rule "测试insertPerson值" 10 when 11 $p:Person(name=="张三的歌") 12 then 13 System.out.println("测试insertPerson值"); 14 end
创建配置文件DroolsConfiguration.java
1 package com.config; 2 3 import org.kie.api.KieBase; 4 import org.kie.api.KieServices; 5 import org.kie.api.builder.*; 6 import org.kie.api.runtime.KieContainer; 7 import org.kie.api.runtime.KieSession; 8 import org.kie.internal.io.ResourceFactory; 9 import org.kie.spring.KModuleBeanFactoryPostProcessor; 10 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 11 import org.springframework.context.annotation.Bean; 12 import org.springframework.context.annotation.Conditional; 13 import org.springframework.context.annotation.Configuration; 14 import org.springframework.core.io.Resource; 15 import org.springframework.core.io.support.PathMatchingResourcePatternResolver; 16 import org.springframework.core.io.support.ResourcePatternResolver; 17 18 import java.io.IOException; 19 20 /** 21 * drools config 22 */ 23 @Configuration 24 public class DroolsConfiguration { 25 26 private static final String RULES_PATH = "rules/"; 27 28 private KieServices getKieServices() { 29 return KieServices.Factory.get(); 30 } 31 32 private Resource[] getRuleFiles() throws IOException { 33 ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); 34 return resourcePatternResolver.getResources(String.format("classpath*:%s**/*.*", RULES_PATH)); 35 } 36 37 38 @Bean 39 @ConditionalOnMissingBean(KieFileSystem.class) 40 public KieFileSystem kieFileSystem() throws IOException { 41 KieFileSystem kieFileSystem = getKieServices().newKieFileSystem(); 42 for (Resource file : getRuleFiles()) { 43 kieFileSystem.write(ResourceFactory.newClassPathResource( 44 RULES_PATH + file.getFilename(), "UTF-8")); 45 return kieFileSystem; 46 } 47 return null; 48 } 49 50 @Bean 51 @ConditionalOnMissingBean(KieContainer.class) 52 public KieContainer kieContainer() throws IOException { 53 final KieRepository kieRepository = getKieServices().getRepository(); 54 kieRepository.addKieModule( 55 new KieModule() { 56 @Override 57 public ReleaseId getReleaseId() { 58 return kieRepository.getDefaultReleaseId(); 59 } 60 } 61 ); 62 63 KieBuilder kieBuilder = getKieServices().newKieBuilder(kieFileSystem()); 64 kieBuilder.buildAll(); 65 return getKieServices().newKieContainer(kieRepository.getDefaultReleaseId()); 66 } 67 68 @Bean 69 @ConditionalOnMissingBean(KieBase.class) 70 public KieBase kieBase() throws IOException { 71 return kieContainer().getKieBase(); 72 } 73 74 @Bean 75 @ConditionalOnMissingBean(KieSession.class) 76 public KieSession kieSession() throws IOException { 77 return kieContainer().newKieSession(); 78 } 79 80 @Bean 81 @ConditionalOnMissingBean(KModuleBeanFactoryPostProcessor.class) 82 public KModuleBeanFactoryPostProcessor kiePostProcessor() { 83 return new KModuleBeanFactoryPostProcessor(); 84 } 85 86 87 88 89 90 91 }
创建service类:
1 package com.service; 2 3 import com.entity.Person; 4 import org.kie.api.runtime.KieSession; 5 import org.springframework.stereotype.Service; 6 7 import javax.annotation.Resource; 8 9 @Service 10 public class TestService { 11 12 @Resource 13 private KieSession kieSession; 14 15 public int testService01() { 16 Person p = new Person(); 17 p.setName("张三的歌"); 18 kieSession.insert(p); 19 int ruleFiredCount = kieSession.fireAllRules(); 20 return ruleFiredCount; 21 } 22 23 }
创建Controller类:
1 package com.controller; 2 3 import com.service.TestService; 4 import org.springframework.beans.factory.annotation.Autowired; 5 import org.springframework.stereotype.Controller; 6 import org.springframework.web.bind.annotation.RequestMapping; 7 import org.springframework.web.bind.annotation.ResponseBody; 8 9 @RequestMapping("/test") 10 @Controller 11 public class TestController { 12 13 @Autowired 14 private TestService testService; 15 16 @ResponseBody 17 @RequestMapping("/test001") 18 public void test() { 19 int count = testService.testService01(); 20 System.out.println("执行规则总数:" + count); 21 } 22 23 }
创建启动类:
1 package com; 2 3 import org.slf4j.Logger; 4 import org.slf4j.LoggerFactory; 5 import org.springframework.boot.SpringApplication; 6 import org.springframework.boot.autoconfigure.SpringBootApplication; 7 8 @SpringBootApplication 9 public class Application { 10 11 protected static Logger logger = LoggerFactory.getLogger(Application.class); 12 13 public static void main(String[] args) { 14 SpringApplication.run(Application.class, args); 15 logger.info("SpringBoot Start Success"); 16 } 17 18 }
启动项目,运行结果:
测试SpringBootDrools
测试insertPerson值
执行规则总数:2
SpringBoot + Drools(2)
创建maven项目,pom.xml引入依赖
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <parent> 6 <artifactId>Code</artifactId> 7 <groupId>com.sunlei</groupId> 8 <version>1.0-SNAPSHOT</version> 9 </parent> 10 <modelVersion>4.0.0</modelVersion> 11 12 <artifactId>DroolsSpringBootTest2</artifactId> 13 14 <properties> 15 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 16 <drools.version>7.10.0.Final</drools.version> 17 <log4j2.version>2.5</log4j2.version> 18 </properties> 19 20 <dependencies> 21 <!--start drools --> 22 <dependency> 23 <groupId>org.drools</groupId> 24 <artifactId>drools-core</artifactId> 25 <version>${drools.version}</version> 26 </dependency> 27 28 <dependency> 29 <groupId>org.drools</groupId> 30 <artifactId>drools-compiler</artifactId> 31 <version>${drools.version}</version> 32 </dependency> 33 34 <dependency> 35 <groupId>org.kie</groupId> 36 <artifactId>kie-spring</artifactId> 37 <version>${drools.version}</version> 38 </dependency> 39 40 <dependency> 41 <groupId>org.kie</groupId> 42 <artifactId>kie-internal</artifactId> 43 <version>${drools.version}</version> 44 </dependency> 45 46 <dependency> 47 <groupId>org.drools</groupId> 48 <artifactId>drools-templates</artifactId> 49 <version>${drools.version}</version> 50 </dependency> 51 <!--end drools --> 52 53 <!--Spring boot start --> 54 <dependency> 55 <groupId>org.springframework.boot</groupId> 56 <artifactId>spring-boot-starter-web</artifactId> 57 <exclusions> 58 <exclusion> 59 <groupId>org.springframework.boot</groupId> 60 <artifactId>spring-boot-starter-logging</artifactId> 61 </exclusion> 62 </exclusions> 63 </dependency> 64 65 <dependency> 66 <groupId>org.springframework.boot</groupId> 67 <artifactId>spring-boot-starter-test</artifactId> 68 <scope>test</scope> 69 </dependency> 70 71 <dependency> 72 <groupId>org.springframework.boot</groupId> 73 <artifactId>spring-boot-starter-data-redis</artifactId> 74 </dependency> 75 76 <!-- mysql --> 77 <dependency> 78 <groupId>mysql</groupId> 79 <artifactId>mysql-connector-java</artifactId> 80 <version>5.1.20</version> 81 </dependency> 82 <!-- mysql --> 83 84 <!-- mybatis --> 85 <dependency> 86 <groupId>org.mybatis.spring.boot</groupId> 87 <artifactId>mybatis-spring-boot-starter</artifactId> 88 <version>1.1.1</version> 89 </dependency> 90 91 <dependency> 92 <groupId>com.alibaba</groupId> 93 <artifactId>fastjson</artifactId> 94 <version>RELEASE</version> 95 </dependency> 96 97 <dependency> 98 <groupId>com.google.code.json</groupId> 99 <artifactId>gson</artifactId> 100 </dependency> 101 <!--Spring boot end --> 102 103 </dependencies> 104 105 <build> 106 <plugins> 107 <plugin> 108 <groupId>org.springframework.boot</groupId> 109 <artifactId>spring-boot-maven-plugin</artifactId> 110 <configuration> 111 <fork>true</fork> 112 </configuration> 113 </plugin> 114 <plugin> 115 <groupId>org.apache.maven.plugins</groupId> 116 <artifactId>maven-compiler-plugin</artifactId> 117 <configuration> 118 <source>7</source> 119 <target>7</target> 120 </configuration> 121 </plugin> 122 </plugins> 123 </build> 124 125 126 127 </project>
配置文件和Mapper文件:
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 3 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 4 5 <mapper namespace="com.dao.PersonDao"> 6 7 <sql id = "Base_Column_List"> 8 rule_name 9 </sql> 10 11 <select id="listAll" resultType="java.lang.String"> 12 select * from person 13 </select> 14 </mapper>
server.port=8081 spring.jpa.show-sql=true spring.datasource.url=jdbc:mysql://127.0.0.1:3306/droolstest?useUnicode=true&characterEncoding=UTF-8 spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.max-active=10 spring.datasource.max-idle=5 spring.datasource.min-idle=0
创建controller
1 package com.controller; 2 3 4 import com.service.PersonService; 5 import com.entity.Person; 6 import org.springframework.stereotype.Controller; 7 import org.springframework.web.bind.annotation.RequestBody; 8 import org.springframework.web.bind.annotation.RequestMapping; 9 import org.springframework.web.bind.annotation.RequestMethod; 10 import org.springframework.web.bind.annotation.ResponseBody; 11 12 import javax.annotation.Resource; 13 import javax.servlet.http.HttpServletRequest; 14 import java.util.HashMap; 15 import java.util.List; 16 import java.util.Map; 17 18 19 @RequestMapping("/person") 20 @Controller 21 public class PersonController { 22 23 @Resource 24 private PersonService promoteService; 25 26 @ResponseBody 27 @RequestMapping(value = "/list", method = RequestMethod.POST, produces = "application/json") 28 public Map<String, Object> testList(HttpServletRequest request, @RequestBody String requestBody) { 29 Map<String, Object> map = new HashMap<>(); 30 List<Person> personList = promoteService.listPerson(); 31 map.put("personList", personList); 32 return map; 33 } 34 }
创建启动类文件:
package com; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.tomcat.jdbc.pool.DataSource; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; @SpringBootApplication @MapperScan("org.dao") public class Application { protected static Logger logger = LoggerFactory.getLogger(Application.class); @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource dataSource() { return new DataSource(); } @Bean public SqlSessionFactory sqlSessionFactoryBean() throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource()); PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath*:mapper/*.xml")); return sqlSessionFactoryBean.getObject(); } @Bean public PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(dataSource()); } public static void main(String[] args) { SpringApplication.run(Application.class, args); logger.info("SpringBoot2 Start Success"); } }
...