Drools规则引擎技术指南
上QQ阅读APP看书,第一时间看更新

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所示。

069-1

图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所示。

070-1

图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
072-1

图4-4 设置no-loop发生死循环的效果

执行testNoLoop5()方法,结果如图4-5所示。

072-2

图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所示。

073-1

图4-6 再次发生死循环

总结:当一个规则文件中,一个Fact(事实)对象通过Drools函数被修改,规则体将被再次激活。也就是说,在RHS部分使用了与update相类似的语法(insert同理),变更了Fact对象在规则中的内容,就会导致规则重新被激活和匹配。

再次激活的前提条件是被修改的事实对象与规则LHS部分的约束条件是包含关系。一个规则事实对象的变更会影响其他规则的结果,这一点在对象引用章节中有过简单说明。