drools中Fact的equality modes

huan1993 發表於 2022-05-14

一、equality modes介紹

drools中存在如下2種equality modes。

1、identity模式

identity:這是預設的情況。drools引擎使用IdentityHashMap儲存所有插入到工作記憶體中的Fact物件。對於每次插入一個新的物件,則會返回一個新的FactHandle物件。如果是重複插入物件,則返回已經存在的FactHandle物件。

舉例:

Person p1 = new Person("zhangsan", 20, "湖北羅田");
Person p2 = new Person("zhangsan", 20, "湖北黃岡羅田");

FactHandle factHandle1 = kieSession.insert(p1);
FactHandle factHandle2 = kieSession.insert(p2);
FactHandle factHandle3 = kieSession.insert(p2);

針對以上例子, factHandle1 != factHandle2但是 factHandle2 == factHandle3。即工作記憶體中會存在2個Person物件。

2、equality模式

equality:drools引擎使用HashMap儲存所有插入到工作記憶體中的Fact物件。在這種模式下,如果向drools中插入一個新的物件,只有這個物件不存在(根據物件的hashcodeequals判斷)才會返回一個新的FactHandle否則返回已經存在的FactHandle

舉例:

// 重寫了Person物件的hashcode和equals方法
Person p1 = new Person("zhangsan", 20, "湖北羅田");
Person p2 = new Person("zhangsan", 20, "湖北黃岡羅田");

FactHandle factHandle1 = kieSession.insert(p1);
FactHandle factHandle2 = kieSession.insert(p2);
FactHandle factHandle3 = kieSession.insert(p2);

針對以上例子, factHandle1 == factHandle2但是 factHandle2 == factHandle3。即工作記憶體中會存在1個Person物件。

二、需求

我們存在一個Person物件,存在如下3個屬性name,age和address,其中重寫物件的name和age的hashcode和equals方法。

  1. 多次向工作記憶體中插入物件,看產生的結果。
  2. 插入同一個物件看獲取到的FactHandle物件是否是同一個。

三、如何設定fact物件的equality行為

此處介紹一個通過kmodule.xml配置的方法

<kmodule xmlns="http://www.drools.org/xsd/kmodule">
    <kbase name="kbase-identity" packages="rules" default="false" equalsBehavior="identity">
        <ksession name="ksession-01" default="false" type="stateful"/>
    </kbase>
    <kbase name="kbase-equality" packages="rules" default="false" equalsBehavior="equality">
        <ksession name="ksession-02" default="false" type="stateful"/>
    </kbase>
</kmodule>

通過上方的程式碼可知是通過配置kbase下的equalsBehavior屬性來配置。

其餘的配置方法,參考下圖:
01-設定fact物件的equality行為

四、編碼實現

1、專案結構圖

02-專案結構圖

2、倒入jar包

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-bom</artifactId>
            <type>pom</type>
            <version>7.69.0.Final</version>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.drools</groupId>
        <artifactId>drools-compiler</artifactId>
    </dependency>
    <dependency>
        <groupId>org.drools</groupId>
        <artifactId>drools-mvel</artifactId>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.11</version>
    </dependency>
</dependencies>

3、編寫Person物件

public class Person {
    private String name;
    private Integer age;
    private String address;

    public Person(String name, Integer age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return Objects.equals(name, person.name) && Objects.equals(age, person.age);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

注意:
此物件需要重寫hashcode和equals方法。

4、編寫kmodule.xml檔案

在此配置檔案中,需要在kbase上指定equalsBehavior,用來確定Fact物件的equality modes。

<kmodule xmlns="http://www.drools.org/xsd/kmodule">
    <kbase name="kbase-identity" packages="rules" default="false" equalsBehavior="identity">
        <ksession name="ksession-01" default="false" type="stateful"/>
    </kbase>
    <kbase name="kbase-equality" packages="rules" default="false" equalsBehavior="equality">
        <ksession name="ksession-02" default="false" type="stateful"/>
    </kbase>
</kmodule>

注意:
需要看2個equalsBehavior的取值

5、編寫一個規則檔案

package rules

import com.huan.drools.Person

// 定義規則
rule "rule_01"
    when
        $p: Person()
    then
        System.out.println(Thread.currentThread().getName() + " name:"+$p.getName()+" age:"+$p.getAge());
end

規則檔案中的內容很簡單,只要工作記憶體中存在Person物件,那麼就輸出這個物件的nameage的值。

6、identity模式測試

1、編寫測試程式碼

public class DroolsApplication {
    public static void main(String[] args) {
        equalsBehaviorIdentity();
    }

    private static void equalsBehaviorIdentity() {
        KieServices kieServices = KieServices.get();
        KieContainer kieContainer = kieServices.getKieClasspathContainer();
        // 注意此處的 ksession-01
        KieSession kieSession = kieContainer.newKieSession("ksession-01");
        kieSession.addEventListener(new DebugRuleRuntimeEventListener());

        Person p1 = new Person("zhangsan", 20, "湖北羅田");
        Person p2 = new Person("zhangsan", 20, "湖北黃岡羅田");

        FactHandle factHandle1 = kieSession.insert(p1);
        FactHandle factHandle2 = kieSession.insert(p2);
        FactHandle factHandle3 = kieSession.insert(p2);
        kieSession.fireAllRules();

        kieSession.dispose();
    }   
}

2、執行結果

identity模式測試

具體的解釋見上圖中的說明。

7、equality模式測試

1、編寫測試程式碼

public class DroolsApplication {
    public static void main(String[] args) {
        equalsBehaviorEquality();
    }

    private static void equalsBehaviorEquality() {
        KieServices kieServices = KieServices.get();
        KieContainer kieContainer = kieServices.getKieClasspathContainer();
        KieSession kieSession = kieContainer.newKieSession("ksession-02");
        kieSession.addEventListener(new DebugRuleRuntimeEventListener());

        Person p1 = new Person("zhangsan", 20, "湖北羅田");
        Person p2 = new Person("zhangsan", 20, "湖北黃岡羅田");

        FactHandle factHandle1 = kieSession.insert(p1);
        FactHandle factHandle2 = kieSession.insert(p2);
        FactHandle factHandle3 = kieSession.insert(p2);
        kieSession.fireAllRules();

        kieSession.dispose();
    }
}

2、執行結果

equality

五、結論

針對如下程式碼,看看在不同equality modes下的行為

 Person p1 = new Person("zhangsan", 20, "湖北羅田");
 Person p2 = new Person("zhangsan", 20, "湖北黃岡羅田");

 FactHandle factHandle1 = kieSession.insert(p1);
 FactHandle factHandle2 = kieSession.insert(p2);
 FactHandle factHandle3 = kieSession.insert(p2);

Person物件的hashcode和equals方法進行重寫了,根據構造方法的前2個引數。

1、identity模式下

factHandle1 != factHandle2 因為p1和p2是2個不同的物件。
factHandle2 == factHandle3 因為是p2重複加入工作記憶體,這個時候工作記憶體中已經存在了,所以返回之前關聯的FactHandle

2、equality模式下

factHandle1 == factHandle2 == factHandle3 因為這種模式下,是需要根據物件的equalshashcode方法進行比較,而Person物件重寫了這2個方法,所以返回的是同一個。

六、完整程式碼

https://gitee.com/huan1993/spring-cloud-parent/tree/master/drools/drools-fact-equality-modes

七、參考連結

1、https://docs.drools.org/7.69.0.Final/drools-docs/html_single/index.html#fact-equality-modes-con_decision-engine