4.1 属性no-loop
默认值:false。
类型:Boolean。
属性说明:防止死循环,当规则通过update之类的函数修改了Fact对象时,可能使规则再次被激活,从而导致死循环。将no-loop设置为true的目的是避免当前规则then部分被修改后的事实对象再次被激活,从而防止死循环的发生,即执行下面的规则。
创建规则文件isNotLoop.drl,目录为rules/testNoLoop,其内容为:
rule "testNoLoop1" //no-loop true when $p:Person(age==30); then $p.setAge(30); update($p); System.out.println("testNoLoop1 不设置 no-loop时的效果"); end
修改kmodule.xml配置文件,并添加如下配置:
<kbase name="isNoLoop" packages="rules.isNoLoop"> <ksession name="isNoLoop"/> </kbase>
创建RulesNoLoop.java文件,目录为com.rulesAttributes,其内容为:
package com.rulesAttributes; import com.pojo.Person; import org.junit.Test; import org.kie.api.KieServices; import org.kie.api.runtime.KieContainer; import org.kie.api.runtime.KieSession; public class RulesNoLoop { @Test public void testNoLoop1() { KieServices kss = KieServices.Factory.get(); KieContainer kc = kss.getKieClasspathContainer(); KieSession ks = kc.newKieSession("isNoLoop"); Person person = new Person(); person.setName("张三"); person.setAge(30); person.setClassName("一班"); ks.insert(person); int count = ks.fireAllRules(); System.out.println("总执行了" + count + "条规则"); ks.dispose(); } }
执行testNoLoop1()方法,结果如图4-2所示。
图4-2 死循环执行规则
防止结果为死循环的方法之一是修改no-loop属性。编辑isNoLoop.drl规则文件,为保证代码的完整性,注释testNoLoop1规则,添加testNoLoop2规则,其代码为:
package rules.isNoLoop import com.pojo.Person; /*rule "testNoLoop1" //no-loop true when $p:Person(age==30); then $p.setAge(30); update($p); System.out.println("testNoLoop1 不设置 no-loop时的效果"); end*/ rule "testNoLoop2" no-loop true when $p:Person(age==30); then $p.setAge(30); update($p); System.out.println("testNoLoop2 设置 no-loop时的效果"); end
执行testNoLoop2()方法,结果如图4-3所示。
图4-3 使用no-loop属性后的效果
设置规则体属性为no-loop并不是万无一失的,在某些情况下设置了no-loop true,也会发生死循环。
编写isNoLoop.drl规则文件,其代码为:
package rules.isNoLoop import com.pojo.Person; /*rule "testNoLoop1" //no-loop true when $p:Person(age==30); then $p.setAge(30); update($p); System.out.println("testNoLoop1 不设置 no-loop时的效果"); end*/ /*rule "testNoLoop2" no-loop true when $p:Person(age==30); then $p.setAge(30); update($p); System.out.println("testNoLoop2 设置 no-loop时的效果"); end*/ rule "testNoLoop3" no-loop true when $p:Person(name=="张三"); then $p.setAge(30); update($p); System.out.println("testNoLoop3 设置 no-loop时的效果"); end rule "testNoLoop4" no-loop true when $p:Person(age==30); then $p.setName("张三"); update($p); System.out.println("testNoLoop4 设置 no-loop时的效果"); end
执行testNoLoop3()和testNoLoop4()方法,结果如图4-4所示。
测试过程中,只有在Fact对象发生变化时才会出现死循环,如果在LHS部分的比较值并非update的修改值,那么会不会也出现这样的问题呢?为保证源码的完整性,现在将所有的规则体注释,编写一个testNoLoop5规则,代码为(省略其他被注释的规则):
rule "testNoLoop5"
//no-loop true
when
$p:Person(name=="张三");
then
$p.setAge(30);
update($p);
System.out.println("testNoLoop5 不设置 no-loop时的效果");
end
图4-4 设置no-loop发生死循环的效果
执行testNoLoop5()方法,结果如图4-5所示。
图4-5 未发生死循环
如图4-5所示,这个结果与预期的并不一样,出现这一结果的原因是什么?操作的Fact对象是同一个,难道每一个属性都是一个事实对象?带着这样的疑问,添加规则文件testNoLoop6并注释rule testNoLoop5,其内容为:
rule testNoLoop5,其内容为:
rule "testNoLoop6"
//no-loop true
when
$p:Person(name=="张三",age==30);
then
$p.setAge(30);
update($p);
System.out.println("testNoLoop6 不设置 no-loop时的效果");
end
执行testNoLoop6()方法,结果如图4-6所示。
图4-6 再次发生死循环
总结:当一个规则文件中,一个Fact(事实)对象通过Drools函数被修改,规则体将被再次激活。也就是说,在RHS部分使用了与update相类似的语法(insert同理),变更了Fact对象在规则中的内容,就会导致规则重新被激活和匹配。
再次激活的前提条件是被修改的事实对象与规则LHS部分的约束条件是包含关系。一个规则事实对象的变更会影响其他规则的结果,这一点在对象引用章节中有过简单说明。