Drools:高級語法


global全局變量

global關鍵字用於在規則文件中定義全局變量,它可以讓應用程序的對象在規則文件中能夠被訪問。可以用來為規則文件提供數據或服務。

語法結構為:global 對象類型 對象名稱

在使用global定義的全局變量時有兩點需要注意:

1、如果對象類型為包裝類型時,在一個規則中改變了global的值,那么只針對當前規則有效,對其他規則中的global不會有影響。可以理解為它是當前規則代碼中的global副本,規則內部修改不會影響全局的使用。

2、如果對象類型為集合類型或JavaBean時,在一個規則中改變了global的值,對java代碼和所有規則都有效。

下面我們通過代碼進行驗證:

第一步:創建UserService類

package com.wj.drools.service;

public class UserService {
    public void save(){
        System.out.println("UserService.save()...");
    }
}

第二步:編寫規則文件/resources/rules/global.drl

package testglobal
/*
    此規則文件用於測試global全局變量
*/

global java.lang.Integer count //定義一個包裝類型的全局變量
global com.wj.drools.service.UserService userService //定義一個JavaBean類型的全局變量
global java.util.List gList //定義一個集合類型的全局變量

rule "rule_global_1"
    when
    then
        count += 10; //全局變量計算,只對當前規則有效,其他規則不受影響
        userService.save();//調用全局變量的方法
        gList.add("a");//向集合類型的全局變量中添加元素,Java代碼和所有規則都受影響
        gList.add("b");
        System.out.println("count=" + count);
        System.out.println("gList.size=" + gList.size());
end

rule "rule_global_2"
    when
    then
        userService.save();
        System.out.println("count=" + count);
        System.out.println("gList.size=" + gList.size());
end

第三步:編寫單元測試

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

        //設置全局變量,名稱和類型必須和規則文件中定義的全局變量名稱對應
        kieSession.setGlobal("userService",new UserService());
        kieSession.setGlobal("count",5);
        List<String> list = new ArrayList<>();//size為0
        kieSession.setGlobal("gList",list);

        kieSession.fireAllRules(new RuleNameStartsWithAgendaFilter("rule_global"));
        kieSession.dispose();

        //因為在規則中為全局變量添加了兩個元素,所以現在的size為2
        System.out.println(list.size());

測試效果:

image-20210916171138373

query查詢

query查詢提供了一種查詢working memory中符合約束條件的Fact對象的簡單方法。它僅包含規則文件中的LHS部分,不用指定“when”和“then”部分並且以end結束。具體語法結構如下:

query 查詢的名稱(可選參數)
    LHS
end

具體操作步驟:

第一步:編寫規則文件/resources/rules/query.drl

package testquery
import com.wj.drools.entity.Student

//不帶參數的查詢
//當前query用於查詢Working Memory中age>10的Student對象
query "query_1"
    $student:Student(age > 10)
end

//帶有參數的查詢
//當前query用於查詢Working Memory中age>10同時name需要和傳遞的參數name相同的Student對象
query "query_2"(String sname)
    $student:Student(age > 20 && name == sname)
end

第二步:編寫單元測試

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

Student student1 = new Student();
student1.setName("張三");
student1.setAge(12);

Student student2 = new Student();
student2.setName("李四");
student2.setAge(8);

Student student3 = new Student();
student3.setName("王五");
student3.setAge(22);

//將對象插入Working Memory中
kieSession.insert(student1);
kieSession.insert(student2);
kieSession.insert(student3);

//調用規則文件中的查詢
QueryResults results1 = kieSession.getQueryResults("query_1");
int size = results1.size();
System.out.println("size=" + size);
for (QueryResultsRow row : results1) {
    Student student = (Student) row.get("$student");
    System.out.println(student);
}

//調用規則文件中的查詢:帶有參數的查詢
QueryResults results2 = kieSession.getQueryResults("query_2","王五");
size = results2.size();
System.out.println("size=" + size);
for (QueryResultsRow row : results2) {
    Student student = (Student) row.get("$student");
    System.out.println(student);
}
//kieSession.fireAllRules();
kieSession.dispose();

image-20210916171451299

function函數

function關鍵字用於在規則文件中定義函數,就相當於java類中的方法一樣。可以在規則體中調用定義的函數。使用函數的好處是可以將業務邏輯集中放置在一個地方,根據需要可以對函數進行修改。

函數定義的語法結構如下:

function 返回值類型 函數名(可選參數){
    //邏輯代碼
}

具體操作步驟:

第一步:編寫規則文件/resources/rules/function.drl

package testfunction
import com.wj.drools.entity.Student
/*
    此規則文件用於測試function函數
*/

//定義一個函數
function String sayHello(String name){
    return "hello " + name;
}

rule "rule_function_1"
    when
        $student:Student(name != null)
    then
        //調用上面定義的函數
        String ret = sayHello($student.getName());
        System.out.println(ret);
end

第二步:編寫單元測試

KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
Student student = new Student();
student.setName("小明");
kieSession.insert(student);
kieSession.fireAllRules(new RuleNameEqualsAgendaFilter("rule_function_1"));
kieSession.dispose();

image-20210916172815478

LHS加強

前面我們已經知道了在規則體中的LHS部分是介於when和then之間的部分,主要用於模式匹配,只有匹配結果為true時,才會觸發RHS部分的執行。本章節我們會針對LHS部分學習幾個新的用法。

復合值限制in/not in

復合值限制是指超過一種匹配值的限制條件,類似於SQL語句中的in關鍵字。Drools規則體中的LHS部分可以使用in或者not in進行復合值的匹配。具體語法結構如下:

Object(field in (比較值1,比較值2...))

舉例:

$s:Student(name in ("張三","李四","王五"))
$s:Student(name not in ("張三","李四","王五"))

條件元素eval

eval用於規則體的LHS部分,並返回一個Boolean類型的值。語法結構如下:

eval(表達式)

舉例:

eval(true)
eval(false)
eval(1 == 1)

條件元素not

not用於判斷Working Memory中是否存在某個Fact對象,如果不存在則返回true,如果存在則返回false。語法結構如下:

not Object(可選屬性約束)

舉例:

not Student()
not Student(age < 10)

條件元素exists

exists的作用與not相反,用於判斷Working Memory中是否存在某個Fact對象,如果存在則返回true,不存在則返回false。語法結構如下:

exists Object(可選屬性約束)

舉例:

exists Student()
exists Student(age < 10 && name != null)

可能有人會有疑問,我們前面在LHS部分進行條件編寫時並沒有使用exists也可以達到判斷Working Memory中是否存在某個符合條件的Fact元素的目的,那么我們使用exists還有什么意義?

兩者的區別:當向Working Memory中加入多個滿足條件的Fact對象時,使用了exists的規則執行一次,不使用exists的規則會執行多次。

例如:

規則文件(只有規則體):

rule "使用exists的規則"
    when
        exists Student()
    then
        System.out.println("規則:使用exists的規則觸發");
end

rule "沒有使用exists的規則"
    when
        Student()
    then
        System.out.println("規則:沒有使用exists的規則觸發");
end

Java代碼:

kieSession.insert(new Student());
kieSession.insert(new Student());
kieSession.fireAllRules();

上面第一個規則只會執行一次,因為Working Memory中存在兩個滿足條件的Fact對象,第二個規則會執行兩次。

規則繼承

規則之間可以使用extends關鍵字進行規則條件部分的繼承,類似於java類之間的繼承。

例如:

rule "rule_1"
    when
        Student(age > 10)
    then
        System.out.println("規則:rule_1觸發");
end

rule "rule_2" extends "rule_1" //繼承上面的規則
    when
        /*
        此處的條件雖然只寫了一個,但是從上面的規則繼承了一個條件,
        所以當前規則存在兩個條件,即Student(age < 20)和Student(age > 10)
        */
        Student(age < 20) 
    then
        System.out.println("規則:rule_2觸發");
end

RHS加強

RHS部分是規則體的重要組成部分,當LHS部分的條件匹配成功后,對應的RHS部分就會觸發執行。一般在RHS部分中需要進行業務處理。

在RHS部分Drools為我們提供了一個內置對象,名稱就是drools。本小節我們來介紹幾個drools對象提供的方法。

halt

halt方法的作用是立即終止后面所有規則的執行。

package testhalt
rule "rule_halt_1"
    when
    then
        System.out.println("規則:rule_halt_1觸發");
        drools.halt();//立即終止后面所有規則執行
end

//當前規則並不會觸發,因為上面的規則調用了halt方法導致后面所有規則都不會執行
rule "rule_halt_2"
    when
    then
        System.out.println("規則:rule_halt_2觸發");
end

getWorkingMemory

getWorkingMemory方法的作用是返回工作內存對象。

package testgetWorkingMemory
rule "rule_getWorkingMemory"
    when
    then
        System.out.println(drools.getWorkingMemory());
end

getRule

getRule方法的作用是返回規則對象。

package testgetRule
rule "rule_getRule"
    when
    then
        System.out.println(drools.getRule());
end

規則文件編碼規范

我們在進行drl類型的規則文件編寫時盡量遵循如下規范:

  • 所有的規則文件(.drl)應統一放在一個規定的文件夾中,如:/rules文件夾
  • 書寫的每個規則應盡量加上注釋。注釋要清晰明了,言簡意賅
  • 同一類型的對象盡量放在一個規則文件中,如所有Student類型的對象盡量放在一個規則文件中
  • 規則結果部分(RHS)盡量不要有條件語句,如if(...),盡量不要有復雜的邏輯和深層次的嵌套語句
  • 每個規則最好都加上salience屬性,明確執行順序
  • Drools默認dialect為"Java",盡量避免使用dialect "mvel"


免責聲明!

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



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