Drools基礎語法


1. Drools基礎語法

1.1 規則文件構成

在使用Drools時非常重要的一個工作就是編寫規則文件,通常規則文件的后綴為.drl。

drl是Drools Rule Language的縮寫。在規則文件中編寫具體的規則內容。

一套完整的規則文件內容構成如下:

關鍵字 描述
package 包名,只限於邏輯上的管理,同一個包名下的查詢或者函數可以直接調用
import 用於導入類或者靜態方法
global 全局變量
function 自定義函數
query 查詢
rule end 規則體

Drools支持的規則文件,除了drl形式,還有Excel文件類型的。

1.2 規則體語法結構

規則體是規則文件內容中的重要組成部分,是進行業務規則判斷、處理業務結果的部分。

規則體語法結構如下:

rule "ruleName"
    attributes
    when
        LHS
    then
        RHS
end

rule:關鍵字,表示規則開始,參數為規則的唯一名稱。

attributes:規則屬性,是rule與when之間的參數,為可選項。

when:關鍵字,后面跟規則的條件部分。

LHS(Left Hand Side):是規則的條件部分的通用名稱。它由零個或多個條件元素組成。如果LHS為空,則它將被視為始終為true的條件元素。

then:關鍵字,后面跟規則的結果部分。

RHS(Right Hand Side):是規則的后果或行動部分的通用名稱。

end:關鍵字,表示一個規則結束。

1.3 注釋

在drl形式的規則文件中使用注釋和Java類中使用注釋一致,分為單行注釋和多行注釋。

單行注釋用"//"進行標記,多行注釋以"/"開始,以"/"結束。如下示例:

//規則rule1的注釋,這是一個單行注釋
rule "rule1"
    when
    then
        System.out.println("rule1觸發");
end

/*
規則rule2的注釋,
這是一個多行注釋
*/
rule "rule2"
    when
    then
        System.out.println("rule2觸發");
end

1.4 Pattern模式匹配

前面我們已經知道了Drools中的匹配器可以將Rule Base中的所有規則與Working Memory中的Fact對象進行模式匹配,那么我們就需要在規則體的LHS部分定義規則並進行模式匹配。LHS部分由一個或者多個條件組成,條件又稱為pattern。

pattern的語法結構為:綁定變量名:Object(Field約束)

其中綁定變量名可以省略,通常綁定變量名的命名一般建議以$開始。如果定義了綁定變量名,就可以在規則體的RHS部分使用此綁定變量名來操作相應的Fact對象。Field約束部分是需要返回true或者false的0個或多個表達式。

例如我們的入門案例中:

//規則二:所購圖書總價在100到200元的優惠20元
rule "book_discount_2"
    when
        //Order為類型約束,originalPrice為屬性約束
        $order:Order(originalPrice < 200 && originalPrice >= 100)
    then
        $order.setRealPrice($order.getOriginalPrice() - 20);
        System.out.println("成功匹配到規則二:所購圖書總價在100到200元的優惠20元");
end

通過上面的例子我們可以知道,匹配的條件為:

1、工作內存中必須存在Order這種類型的Fact對象-----類型約束

2、Fact對象的originalPrice屬性值必須小於200------屬性約束

3、Fact對象的originalPrice屬性值必須大於等於100------屬性約束

以上條件必須同時滿足當前規則才有可能被激活。

綁定變量既可以用在對象上,也可以用在對象的屬性上。例如上面的例子可以改為:

//規則二:所購圖書總價在100到200元的優惠20元
rule "book_discount_2"
    when
        $order:Order($op:originalPrice < 200 && originalPrice >= 100)
    then
        System.out.println("$op=" + $op);
        $order.setRealPrice($order.getOriginalPrice() - 20);
        System.out.println("成功匹配到規則二:所購圖書總價在100到200元的優惠20元");
end

LHS部分還可以定義多個pattern,多個pattern之間可以使用and或者or進行連接,也可以不寫,默認連接為and。

//規則二:所購圖書總價在100到200元的優惠20元
rule "book_discount_2"
    when
        $order:Order($op:originalPrice < 200 && originalPrice >= 100) and
        $customer:Customer(age > 20 && gender=='male')
    then
        System.out.println("$op=" + $op);
        $order.setRealPrice($order.getOriginalPrice() - 20);
        System.out.println("成功匹配到規則二:所購圖書總價在100到200元的優惠20元");
end

1.5 比較操作符

Drools提供的比較操作符,如下表:

符號 說明
> 大於
< 小於
>= 大於等於
<= 小於等於
== 等於
!= 不等於
contains 檢查一個Fact對象的某個屬性值是否包含一個指定的對象值
not contains 檢查一個Fact對象的某個屬性值是否不包含一個指定的對象值
memberOf 判斷一個Fact對象的某個屬性是否在一個或多個集合中
not memberOf 判斷一個Fact對象的某個屬性是否不在一個或多個集合中
matches 判斷一個Fact對象的屬性是否與提供的標准的Java正則表達式進行匹配
not matches 判斷一個Fact對象的屬性是否不與提供的標准的Java正則表達式進行匹配

前6個比較操作符和Java中的完全相同,下面我們重點學習后6個比較操作符。

1.5.1 語法

  • contains | not contains語法結構

    Object(Field[Collection/Array] contains value)

    Object(Field[Collection/Array] not contains value)

  • memberOf | not memberOf語法結構

    Object(field memberOf value[Collection/Array])

    Object(field not memberOf value[Collection/Array])

  • matches | not matches語法結構

    Object(field matches "正則表達式")

    Object(field not matches "正則表達式")

1.5.2 操作步驟

第一步:創建實體類,用於測試比較操作符

package com.itheima.drools.entity;
import java.util.List;

/**
 * 實體類
 * 用於測試比較操作符
 */
public class ComparisonOperatorEntity {
    private String names;
    private List<String> list;

    public String getNames() {
        return names;
    }

    public void setNames(String names) {
        this.names = names;
    }

    public List<String> getList() {
        return list;
    }

    public void setList(List<String> list) {
        this.list = list;
    }
}

第二步:在/resources/rules下創建規則文件comparisonOperator.drl

package comparisonOperator
import com.itheima.drools.entity.ComparisonOperatorEntity
/*
 當前規則文件用於測試Drools提供的比較操作符
*/

//測試比較操作符contains
rule "rule_comparison_contains"
    when
        ComparisonOperatorEntity(names contains "張三")
        ComparisonOperatorEntity(list contains names)
    then
        System.out.println("規則rule_comparison_contains觸發");
end

//測試比較操作符not contains
rule "rule_comparison_notContains"
    when
        ComparisonOperatorEntity(names not contains "張三")
        ComparisonOperatorEntity(list not contains names)
    then
        System.out.println("規則rule_comparison_notContains觸發");
end

//測試比較操作符memberOf
rule "rule_comparison_memberOf"
    when
        ComparisonOperatorEntity(names memberOf list)
    then
        System.out.println("規則rule_comparison_memberOf觸發");
end

//測試比較操作符not memberOf
rule "rule_comparison_notMemberOf"
    when
        ComparisonOperatorEntity(names not memberOf list)
    then
        System.out.println("規則rule_comparison_notMemberOf觸發");
end

//測試比較操作符matches
rule "rule_comparison_matches"
    when
        ComparisonOperatorEntity(names matches "張.*")
    then
        System.out.println("規則rule_comparison_matches觸發");
end

//測試比較操作符not matches
rule "rule_comparison_notMatches"
    when
        ComparisonOperatorEntity(names not matches "張.*")
    then
        System.out.println("規則rule_comparison_notMatches觸發");
end

第三步:編寫單元測試

//測試比較操作符
@Test
public void test3(){
    KieServices kieServices = KieServices.Factory.get();
    KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
    KieSession kieSession = kieClasspathContainer.newKieSession();

    ComparisonOperatorEntity comparisonOperatorEntity = new ComparisonOperatorEntity();
    comparisonOperatorEntity.setNames("張三");
    List<String> list = new ArrayList<String>();
    list.add("張三");
    list.add("李四");
    comparisonOperatorEntity.setList(list);

    //將數據提供給規則引擎,規則引擎會根據提供的數據進行規則匹配,如果規則匹配成功則執行規則
    kieSession.insert(comparisonOperatorEntity);

    kieSession.fireAllRules();
    kieSession.dispose();
}

1.6 執行指定規則

通過前面的案例可以看到,我們在調用規則代碼時,滿足條件的規則都會被執行。那么如果我們只想執行其中的某個規則如何實現呢?

Drools給我們提供的方式是通過規則過濾器來實現執行指定規則。對於規則文件不用做任何修改,只需要修改Java代碼即可,如下:

KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();

ComparisonOperatorEntity comparisonOperatorEntity = new ComparisonOperatorEntity();
comparisonOperatorEntity.setNames("張三");
List<String> list = new ArrayList<String>();
list.add("張三");
list.add("李四");
comparisonOperatorEntity.setList(list);
kieSession.insert(comparisonOperatorEntity);

//通過規則過濾器實現只執行指定規則
kieSession.fireAllRules(new RuleNameEqualsAgendaFilter("rule_comparison_memberOf"));

kieSession.dispose();

1.7 關鍵字

Drools的關鍵字分為:硬關鍵字(Hard keywords)和軟關鍵字(Soft keywords)。

硬關鍵字是我們在規則文件中定義包名或者規則名時明確不能使用的,否則程序會報錯。軟關鍵字雖然可以使用,但是不建議使用。

硬關鍵字包括:true false null

軟關鍵字包括:lock-on-active date-effective date-expires no-loop auto-focus activation-group agenda-group ruleflow-group entry-point duration package import dialect salience enabled attributes rule extend when then template query declare function global eval not in or and exists forall accumulate collect from action reverse result end over init

1.8 Drools內置方法

規則文件的RHS部分的主要作用是通過插入,刪除或修改工作內存中的Fact數據,來達到控制規則引擎執行的目的。Drools提供了一些方法可以用來操作工作內存中的數據,操作完成后規則引擎會重新進行相關規則的匹配,原來沒有匹配成功的規則在我們修改數據完成后有可能就會匹配成功了。

創建如下實體類:

package com.itheima.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;
    }
}

1.8.1 update方法

update方法的作用是更新工作內存中的數據,並讓相關的規則重新匹配。

第一步:編寫規則文件/resources/rules/student.drl,文件內容如下

package student
import com.itheima.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();

通過控制台的輸出可以看到規則文件中定義的三個規則都觸發了。

在更新數據時需要注意防止發生死循環。

1.8.2 insert方法

insert方法的作用是向工作內存中插入數據,並讓相關的規則重新匹配。

第一步:修改student.drl文件內容如下

package student
import com.itheima.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();

通過控制台輸出可以發現,四個規則都觸發了,這是因為首先進行規則匹配時只有第一個規則可以匹配成功,但是在第一個規則中向工作內存中插入了一個數據導致重新進行規則匹配,此時第二個規則可以匹配成功。在第二個規則中進行了數據修改導致第三個規則也可以匹配成功,以此類推最終四個規則都匹配成功並執行了。

1.8.3 retract方法

retract方法的作用是刪除工作內存中的數據,並讓相關的規則重新匹配。

第一步:修改student.drl文件內容如下

package student
import com.itheima.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();

通過控制台輸出可以發現,只有第一個規則觸發了,因為在第一個規則中將工作內存中的數據刪除了導致第二個規則並沒有匹配成功。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM